2 module Vectorise( vectorise )
10 import HscTypes hiding ( MonadThings(..) )
12 import Module ( PackageId )
15 import CoreUnfold ( mkInlineRule )
16 import MkCore ( mkWildCase )
18 import CoreMonad ( CoreM, getHscEnv )
22 import FamInstEnv ( extendFamInstEnvList )
28 import BasicTypes ( isLoopBreaker )
30 import Literal ( Literal, mkMachInt )
32 import TysPrim ( intPrimTy )
36 import Util ( zipLazy )
38 import Data.List ( sortBy, unzip4 )
40 vectorise :: PackageId -> ModGuts -> CoreM ModGuts
41 vectorise backend guts = do
43 liftIO $ vectoriseIO backend hsc_env guts
45 -- | Vectorise a single monad, given its HscEnv (code gen environment).
46 vectoriseIO :: PackageId -> HscEnv -> ModGuts -> IO ModGuts
47 vectoriseIO backend hsc_env guts
48 = do -- Get information about currently loaded external packages.
51 -- Combine vectorisation info from the current module, and external ones.
52 let info = hptVectInfo hsc_env `plusVectInfo` eps_vect_info eps
54 -- Run the main VM computation.
55 Just (info', guts') <- initV backend hsc_env guts info (vectModule guts)
56 return (guts' { mg_vect_info = info' })
59 -- | Vectorise a single module, in the VM monad.
60 vectModule :: ModGuts -> VM ModGuts
62 = do -- Vectorise the type environment.
63 -- This may add new TyCons and DataCons.
64 -- TODO: What new binds do we get back here?
65 (types', fam_insts, tc_binds) <- vectTypeEnv (mg_types guts)
67 -- TODO: What is this?
68 let fam_inst_env' = extendFamInstEnvList (mg_fam_inst_env guts) fam_insts
69 updGEnv (setFamInstEnv fam_inst_env')
71 -- dicts <- mapM buildPADict pa_insts
72 -- workers <- mapM vectDataConWorkers pa_insts
74 -- Vectorise all the top level bindings.
75 binds' <- mapM vectTopBind (mg_binds guts)
77 return $ guts { mg_types = types'
78 , mg_binds = Rec tc_binds : binds'
79 , mg_fam_inst_env = fam_inst_env'
80 , mg_fam_insts = mg_fam_insts guts ++ fam_insts
84 -- | Try to vectorise a top-level binding.
85 -- If it doesn't vectorise then return it unharmed.
87 -- For example, for the binding
97 -- foo = \x -> vfoo $: x
99 -- v_foo :: Closure void vfoo lfoo
100 -- v_foo = closure vfoo lfoo void
102 -- vfoo :: Void -> Int -> Int
105 -- lfoo :: PData Void -> PData Int -> PData Int
109 -- @vfoo@ is the "vectorised", or scalar, version that does the same as the original
110 -- function foo, but takes an explicit environment.
112 -- @lfoo@ is the "lifted" version that works on arrays.
114 -- @v_foo@ combines both of these into a `Closure` that also contains the
117 -- The original binding @foo@ is rewritten to call the vectorised version
118 -- present in the closure.
120 vectTopBind :: CoreBind -> VM CoreBind
121 vectTopBind b@(NonRec var expr)
123 (inline, expr') <- vectTopRhs var expr
124 var' <- vectTopBinder var inline expr'
126 -- Vectorising the body may create other top-level bindings.
129 -- To get the same functionality as the original body we project
130 -- out its vectorised version from the closure.
131 cexpr <- tryConvert var var' expr
133 return . Rec $ (var, cexpr) : (var', expr') : hs
137 vectTopBind b@(Rec bs)
140 <- fixV $ \ ~(_, inlines, rhss) ->
141 do vars' <- sequence [vectTopBinder var inline rhs
142 | (var, ~(inline, rhs)) <- zipLazy vars (zip inlines rhss)]
144 <- mapAndUnzipM (uncurry vectTopRhs) bs
146 return (vars', inlines', exprs')
149 cexprs <- sequence $ zipWith3 tryConvert vars vars' exprs
150 return . Rec $ zip vars cexprs ++ zip vars' exprs' ++ hs
154 (vars, exprs) = unzip bs
157 -- | Make the vectorised version of this top level binder, and add the mapping
158 -- between it and the original to the state. For some binder @foo@ the vectorised
159 -- version is @$v_foo@
161 -- NOTE: vectTopBinder *MUST* be lazy in inline and expr because of how it is
162 -- used inside of fixV in vectTopBind
164 :: Var -- ^ Name of the binding.
165 -> Inline -- ^ Whether it should be inlined, used to annotate it.
166 -> CoreExpr -- ^ RHS of the binding, used to set the `Unfolding` of the returned `Var`.
167 -> VM Var -- ^ Name of the vectorised binding.
169 vectTopBinder var inline expr
171 -- Vectorise the type attached to the var.
172 vty <- vectType (idType var)
173 var' <- liftM (`setIdUnfolding` unfolding) $ cloneId mkVectOcc var vty
174 defGlobalVar var var'
177 unfolding = case inline of
178 Inline arity -> mkInlineRule expr (Just arity)
179 DontInline -> noUnfolding
182 -- | Vectorise the RHS of a top-level binding, in an empty local environment.
184 :: Var -- ^ Name of the binding.
185 -> CoreExpr -- ^ Body of the binding.
186 -> VM (Inline, CoreExpr)
189 = dtrace (vcat [text "vectTopRhs", ppr expr])
191 $ do (inline, vexpr) <- inBind var
192 $ vectPolyExpr (isLoopBreaker $ idOccInfo var)
194 return (inline, vectorised vexpr)
197 -- | Project out the vectorised version of a binding from some closure,
198 -- or return the original body if that doesn't work.
200 :: Var -- ^ Name of the original binding (eg @foo@)
201 -> Var -- ^ Name of vectorised version of binding (eg @$vfoo@)
202 -> CoreExpr -- ^ The original body of the binding.
205 tryConvert var vect_var rhs
206 = fromVect (idType var) (Var vect_var) `orElseV` return rhs
208 -- ----------------------------------------------------------------------------
211 -- | Vectorise a binder variable, along with its attached type.
212 vectBndr :: Var -> VM VVar
215 (vty, lty) <- vectAndLiftType (idType v)
216 let vv = v `Id.setIdType` vty
217 lv = v `Id.setIdType` lty
218 updLEnv (mapTo vv lv)
221 mapTo vv lv env = env { local_vars = extendVarEnv (local_vars env) v (vv, lv) }
224 -- | Vectorise a binder variable, along with its attached type,
225 -- but give the result a new name.
226 vectBndrNew :: Var -> FastString -> VM VVar
229 vty <- vectType (idType v)
230 vv <- newLocalVVar fs vty
234 upd vv env = env { local_vars = extendVarEnv (local_vars env) v vv }
237 -- | Vectorise a binder then run a computation with that binder in scope.
238 vectBndrIn :: Var -> VM a -> VM (VVar, a)
247 -- | Vectorise a binder, give it a new name, then run a computation with that binder in scope.
248 vectBndrNewIn :: Var -> FastString -> VM a -> VM (VVar, a)
252 vv <- vectBndrNew v fs
256 -- | Vectorise some binders, then run a computation with them in scope.
257 vectBndrsIn :: [Var] -> VM a -> VM ([VVar], a)
261 vvs <- mapM vectBndr vs
266 -- ----------------------------------------------------------------------------
269 -- | Vectorise a variable, producing the vectorised and lifted versions.
270 vectVar :: Var -> VM VExpr
273 -- lookup the variable from the environment.
277 Local (vv,lv) -> return (Var vv, Var lv)
280 lexpr <- liftPD vexpr
281 return (vexpr, lexpr)
283 -- | Like `vectVar` but also add type applications to the variables.
284 vectPolyVar :: Var -> [Type] -> VM VExpr
287 vtys <- mapM vectType tys
291 -> liftM2 (,) (polyApply (Var vv) vtys)
292 (polyApply (Var lv) vtys)
295 -> do vexpr <- polyApply (Var poly) vtys
296 lexpr <- liftPD vexpr
297 return (vexpr, lexpr)
300 -- | Lifted literals are created by replicating them.
301 vectLiteral :: Literal -> VM VExpr
304 lexpr <- liftPD (Lit lit)
305 return (Lit lit, lexpr)
308 -- | Vectorise a polymorphic expression
310 :: Bool -- ^ When vectorising the RHS of a binding, whether that
311 -- binding is a loop breaker.
313 -> VM (Inline, VExpr)
315 vectPolyExpr loop_breaker (_, AnnNote note expr)
316 = do (inline, expr') <- vectPolyExpr loop_breaker expr
317 return (inline, vNote note expr')
319 vectPolyExpr loop_breaker expr
321 arity <- polyArity tvs
322 polyAbstract tvs $ \args ->
324 (inline, mono') <- vectFnExpr False loop_breaker mono
325 return (addInlineArity inline arity,
326 mapVect (mkLams $ tvs ++ args) mono')
328 (tvs, mono) = collectAnnTypeBinders expr
331 -- | Vectorise a core expression.
332 vectExpr :: CoreExprWithFVs -> VM VExpr
333 vectExpr (_, AnnType ty)
334 = liftM vType (vectType ty)
336 vectExpr (_, AnnVar v)
339 vectExpr (_, AnnLit lit)
342 vectExpr (_, AnnNote note expr)
343 = liftM (vNote note) (vectExpr expr)
345 vectExpr e@(_, AnnApp _ arg)
347 = vectTyAppExpr fn tys
349 (fn, tys) = collectAnnTypeArgs e
351 vectExpr (_, AnnApp (_, AnnVar v) (_, AnnLit lit))
352 | Just con <- isDataConId_maybe v
355 let vexpr = App (Var v) (Lit lit)
356 lexpr <- liftPD vexpr
357 return (vexpr, lexpr)
359 is_special_con con = con `elem` [intDataCon, floatDataCon, doubleDataCon]
362 -- TODO: Avoid using closure application for dictionaries.
363 -- vectExpr (_, AnnApp fn arg)
364 -- | if is application of dictionary
365 -- just use regular app instead of closure app.
367 -- for lifted version.
368 -- do liftPD (sub a dNumber)
369 -- lift the result of the selection, not sub and dNumber seprately.
371 vectExpr (_, AnnApp fn arg)
373 arg_ty' <- vectType arg_ty
374 res_ty' <- vectType res_ty
378 mkClosureApp arg_ty' res_ty' fn' arg'
380 (arg_ty, res_ty) = splitFunTy . exprType $ deAnnotate fn
382 vectExpr (_, AnnCase scrut bndr ty alts)
383 | Just (tycon, ty_args) <- splitTyConApp_maybe scrut_ty
385 = vectAlgCase tycon ty_args scrut bndr ty alts
387 scrut_ty = exprType (deAnnotate scrut)
389 vectExpr (_, AnnLet (AnnNonRec bndr rhs) body)
391 vrhs <- localV . inBind bndr . liftM snd $ vectPolyExpr False rhs
392 (vbndr, vbody) <- vectBndrIn bndr (vectExpr body)
393 return $ vLet (vNonRec vbndr vrhs) vbody
395 vectExpr (_, AnnLet (AnnRec bs) body)
397 (vbndrs, (vrhss, vbody)) <- vectBndrsIn bndrs
399 (zipWithM vect_rhs bndrs rhss)
401 return $ vLet (vRec vbndrs vrhss) vbody
403 (bndrs, rhss) = unzip bs
405 vect_rhs bndr rhs = localV
408 $ vectPolyExpr (isLoopBreaker $ idOccInfo bndr) rhs
410 vectExpr e@(_, AnnLam bndr _)
411 | isId bndr = liftM snd $ vectFnExpr True False e
413 onlyIfV (isEmptyVarSet fvs) (vectScalarLam bs $ deAnnotate body)
414 `orElseV` vectLam True fvs bs body
416 (bs,body) = collectAnnValBinders e
419 vectExpr e = cantVectorise "Can't vectorise expression" (ppr $ deAnnotate e)
422 -- | Vectorise an expression with an outer lambda abstraction.
424 :: Bool -- ^ When the RHS of a binding, whether that binding should be inlined.
425 -> Bool -- ^ Whether the binding is a loop breaker.
426 -> CoreExprWithFVs -- ^ Expression to vectorise. Must have an outer `AnnLam`.
427 -> VM (Inline, VExpr)
429 vectFnExpr inline loop_breaker e@(fvs, AnnLam bndr _)
430 | isId bndr = onlyIfV (isEmptyVarSet fvs)
431 (mark DontInline . vectScalarLam bs $ deAnnotate body)
432 `orElseV` mark inlineMe (vectLam inline loop_breaker fvs bs body)
434 (bs,body) = collectAnnValBinders e
435 vectFnExpr _ _ e = mark DontInline $ vectExpr e
437 mark :: Inline -> VM a -> VM (Inline, a)
438 mark b p = do { x <- p; return (b,x) }
441 -- | Vectorise a function where are the args have scalar type, that is Int, Float or Double.
443 :: [Var] -- ^ Bound variables of function.
444 -> CoreExpr -- ^ Function body.
446 vectScalarLam args body
448 scalars <- globalScalars
449 onlyIfV (all is_scalar_ty arg_tys
450 && is_scalar_ty res_ty
451 && is_scalar (extendVarSetList scalars args) body
452 && uses scalars body)
454 fn_var <- hoistExpr (fsLit "fn") (mkLams args body) DontInline
455 zipf <- zipScalars arg_tys res_ty
456 clo <- scalarClosure arg_tys res_ty (Var fn_var)
457 (zipf `App` Var fn_var)
458 clo_var <- hoistExpr (fsLit "clo") clo DontInline
459 lclo <- liftPD (Var clo_var)
460 return (Var clo_var, lclo)
462 arg_tys = map idType args
463 res_ty = exprType body
466 | Just (tycon, []) <- splitTyConApp_maybe ty
468 || tycon == floatTyCon
469 || tycon == doubleTyCon
473 is_scalar vs (Var v) = v `elemVarSet` vs
474 is_scalar _ e@(Lit _) = is_scalar_ty $ exprType e
475 is_scalar vs (App e1 e2) = is_scalar vs e1 && is_scalar vs e2
476 is_scalar _ _ = False
478 -- A scalar function has to actually compute something. Without the check,
479 -- we would treat (\(x :: Int) -> x) as a scalar function and lift it to
480 -- (map (\x -> x)) which is very bad. Normal lifting transforms it to
481 -- (\n# x -> x) which is what we want.
482 uses funs (Var v) = v `elemVarSet` funs
483 uses funs (App e1 e2) = uses funs e1 || uses funs e2
488 :: Bool -- ^ When the RHS of a binding, whether that binding should be inlined.
489 -> Bool -- ^ Whether the binding is a loop breaker.
490 -> VarSet -- ^ The free variables in the body.
495 vectLam inline loop_breaker fvs bs body
497 tyvars <- localTyVars
498 (vs, vvs) <- readLEnv $ \env ->
499 unzip [(var, vv) | var <- varSetElems fvs
500 , Just vv <- [lookupVarEnv (local_vars env) var]]
502 arg_tys <- mapM (vectType . idType) bs
503 res_ty <- vectType (exprType $ deAnnotate body)
505 buildClosures tyvars vvs arg_tys res_ty
506 . hoistPolyVExpr tyvars (maybe_inline (length vs + length bs))
508 lc <- builtin liftingContext
509 (vbndrs, vbody) <- vectBndrsIn (vs ++ bs)
511 vbody' <- break_loop lc res_ty vbody
512 return $ vLams lc vbndrs vbody'
514 maybe_inline n | inline = Inline n
515 | otherwise = DontInline
517 break_loop lc ty (ve, le)
521 lty <- mkPDataType ty
522 return (ve, mkWildCase (Var lc) intPrimTy lty
524 (LitAlt (mkMachInt 0), [], empty)])
526 | otherwise = return (ve, le)
529 vectTyAppExpr :: CoreExprWithFVs -> [Type] -> VM VExpr
530 vectTyAppExpr (_, AnnVar v) tys = vectPolyVar v tys
531 vectTyAppExpr e tys = cantVectorise "Can't vectorise expression"
532 (ppr $ deAnnotate e `mkTyApps` tys)
536 -- case e :: t of v { ... }
540 -- V: let v' = e in case v' of _ { ... }
541 -- L: let v' = e in case v' `cast` ... of _ { ... }
543 -- When lifting, we have to do it this way because v must have the type
544 -- [:V(T):] but the scrutinee must be cast to the representation type. We also
545 -- have to handle the case where v is a wild var correctly.
548 -- FIXME: this is too lazy
549 vectAlgCase :: TyCon -> [Type] -> CoreExprWithFVs -> Var -> Type
550 -> [(AltCon, [Var], CoreExprWithFVs)]
552 vectAlgCase _tycon _ty_args scrut bndr ty [(DEFAULT, [], body)]
554 vscrut <- vectExpr scrut
555 (vty, lty) <- vectAndLiftType ty
556 (vbndr, vbody) <- vectBndrIn bndr (vectExpr body)
557 return $ vCaseDEFAULT vscrut vbndr vty lty vbody
559 vectAlgCase _tycon _ty_args scrut bndr ty [(DataAlt _, [], body)]
561 vscrut <- vectExpr scrut
562 (vty, lty) <- vectAndLiftType ty
563 (vbndr, vbody) <- vectBndrIn bndr (vectExpr body)
564 return $ vCaseDEFAULT vscrut vbndr vty lty vbody
566 vectAlgCase _tycon _ty_args scrut bndr ty [(DataAlt dc, bndrs, body)]
568 (vty, lty) <- vectAndLiftType ty
569 vexpr <- vectExpr scrut
570 (vbndr, (vbndrs, (vect_body, lift_body)))
574 let (vect_bndrs, lift_bndrs) = unzip vbndrs
575 (vscrut, lscrut, pdata_tc, _arg_tys) <- mkVScrut (vVar vbndr)
576 vect_dc <- maybeV (lookupDataCon dc)
577 let [pdata_dc] = tyConDataCons pdata_tc
579 let vcase = mk_wild_case vscrut vty vect_dc vect_bndrs vect_body
580 lcase = mk_wild_case lscrut lty pdata_dc lift_bndrs lift_body
582 return $ vLet (vNonRec vbndr vexpr) (vcase, lcase)
584 vect_scrut_bndr | isDeadBinder bndr = vectBndrNewIn bndr (fsLit "scrut")
585 | otherwise = vectBndrIn bndr
587 mk_wild_case expr ty dc bndrs body
588 = mkWildCase expr (exprType expr) ty [(DataAlt dc, bndrs, body)]
590 vectAlgCase tycon _ty_args scrut bndr ty alts
592 vect_tc <- maybeV (lookupTyCon tycon)
593 (vty, lty) <- vectAndLiftType ty
595 let arity = length (tyConDataCons vect_tc)
596 sel_ty <- builtin (selTy arity)
597 sel_bndr <- newLocalVar (fsLit "sel") sel_ty
598 let sel = Var sel_bndr
600 (vbndr, valts) <- vect_scrut_bndr
601 $ mapM (proc_alt arity sel vty lty) alts'
602 let (vect_dcs, vect_bndrss, lift_bndrss, vbodies) = unzip4 valts
604 vexpr <- vectExpr scrut
605 (vect_scrut, lift_scrut, pdata_tc, _arg_tys) <- mkVScrut (vVar vbndr)
606 let [pdata_dc] = tyConDataCons pdata_tc
608 let (vect_bodies, lift_bodies) = unzip vbodies
610 vdummy <- newDummyVar (exprType vect_scrut)
611 ldummy <- newDummyVar (exprType lift_scrut)
612 let vect_case = Case vect_scrut vdummy vty
613 (zipWith3 mk_vect_alt vect_dcs vect_bndrss vect_bodies)
615 lc <- builtin liftingContext
616 lbody <- combinePD vty (Var lc) sel lift_bodies
617 let lift_case = Case lift_scrut ldummy lty
618 [(DataAlt pdata_dc, sel_bndr : concat lift_bndrss,
621 return . vLet (vNonRec vbndr vexpr)
622 $ (vect_case, lift_case)
624 vect_scrut_bndr | isDeadBinder bndr = vectBndrNewIn bndr (fsLit "scrut")
625 | otherwise = vectBndrIn bndr
627 alts' = sortBy (\(alt1, _, _) (alt2, _, _) -> cmp alt1 alt2) alts
629 cmp (DataAlt dc1) (DataAlt dc2) = dataConTag dc1 `compare` dataConTag dc2
630 cmp DEFAULT DEFAULT = EQ
633 cmp _ _ = panic "vectAlgCase/cmp"
635 proc_alt arity sel _ lty (DataAlt dc, bndrs, body)
637 vect_dc <- maybeV (lookupDataCon dc)
638 let ntag = dataConTagZ vect_dc
639 tag = mkDataConTag vect_dc
640 fvs = freeVarsOf body `delVarSetList` bndrs
642 sel_tags <- liftM (`App` sel) (builtin (selTags arity))
643 lc <- builtin liftingContext
644 elems <- builtin (selElements arity ntag)
650 binds <- mapM (pack_var (Var lc) sel_tags tag)
653 (ve, le) <- vectExpr body
654 return (ve, Case (elems `App` sel) lc lty
655 [(DEFAULT, [], (mkLets (concat binds) le))])
656 -- empty <- emptyPD vty
657 -- return (ve, Case (elems `App` sel) lc lty
658 -- [(DEFAULT, [], Let (NonRec flags_var flags_expr)
659 -- $ mkLets (concat binds) le),
660 -- (LitAlt (mkMachInt 0), [], empty)])
661 let (vect_bndrs, lift_bndrs) = unzip vbndrs
662 return (vect_dc, vect_bndrs, lift_bndrs, vbody)
664 proc_alt _ _ _ _ _ = panic "vectAlgCase/proc_alt"
666 mk_vect_alt vect_dc bndrs body = (DataAlt vect_dc, bndrs, body)
668 pack_var len tags t v
675 expr <- packByTagPD (idType vv) (Var lv) len tags t
676 updLEnv (\env -> env { local_vars = extendVarEnv
677 (local_vars env) v (vv, lv') })
678 return [(NonRec lv' expr)]