diff options
Diffstat (limited to 'ghc/compiler/stranal/WwLib.lhs')
-rw-r--r-- | ghc/compiler/stranal/WwLib.lhs | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/ghc/compiler/stranal/WwLib.lhs b/ghc/compiler/stranal/WwLib.lhs new file mode 100644 index 0000000000..5367ecff62 --- /dev/null +++ b/ghc/compiler/stranal/WwLib.lhs @@ -0,0 +1,470 @@ +% +% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995 +% +\section[WwLib]{A library for the ``worker/wrapper'' back-end to the strictness analyser} + +\begin{code} +#include "HsVersions.h" + +module WwLib ( + WwBinding(..), + + mkWwBodies, mAX_WORKER_ARGS, + + -- our friendly worker/wrapper monad: + WwM(..), + returnWw, thenWw, mapWw, + getUniqueWw, uniqSMtoWwM, + + -- and to make the interface self-sufficient... + GlobalSwitch, CoreBinding, CoreExpr, PlainCoreBinding(..), + PlainCoreExpr(..), Id, Demand, MaybeErr, + TyVar, UniType, Unique, SplitUniqSupply, SUniqSM(..) + + IF_ATTACK_PRAGMAS(COMMA splitUniqSupply COMMA getSUnique) + IF_ATTACK_PRAGMAS(COMMA mkUniqueGrimily) + ) where + +IMPORT_Trace +import Outputable -- ToDo: rm (debugging) +import Pretty + +import AbsPrel ( aBSENT_ERROR_ID, mkFunTy ) +import AbsUniType ( mkTyVarTy, isPrimType, getUniDataTyCon_maybe, + quantifyTy, TyVarTemplate + ) +import CmdLineOpts ( GlobalSwitch(..) ) +import Id ( mkWorkerId, mkSysLocal, getIdUniType, + getInstantiatedDataConSig, getIdInfo, + replaceIdInfo, addIdStrictness, DataCon(..) + ) +import IdInfo -- lots of things +import Maybes ( maybeToBool, Maybe(..), MaybeErr ) +import PlainCore +import SaLib +import SrcLoc ( mkUnknownSrcLoc ) +import SplitUniq +import Unique +import Util + +infixr 9 `thenWw` +\end{code} + +%************************************************************************ +%* * +\subsection[datatype-WwLib]{@WwBinding@: a datatype for worker/wrapper-ing} +%* * +%************************************************************************ + +In the worker/wrapper stuff, we want to carry around @CoreBindings@ in +an ``intermediate form'' that can later be turned into a \tr{let} or +\tr{case} (depending on strictness info). + +\begin{code} +data WwBinding + = WwLet [PlainCoreBinding] + | WwCase (PlainCoreExpr -> PlainCoreExpr) + -- the "case" will be a "strict let" of the form: + -- + -- case rhs of + -- <blah> -> body + -- + -- (instead of "let <blah> = rhs in body") + -- + -- The expr you pass to the function is "body" (the + -- expression that goes "in the corner"). +\end{code} + +%************************************************************************ +%* * +\subsection[mkWrapperAndWorker]{@mkWrapperAndWorker@} +%* * +%************************************************************************ + + ************ WARNING ****************** + these comments are rather out of date + ***************************************** + +@mkWrapperAndWorker@ is given: +\begin{enumerate} +\item +The {\em original function} \tr{f}, of the form: +\begin{verbatim} +f = /\ tyvars -> \ args -> body +\end{verbatim} +The original-binder \tr{f}, the \tr{tyvars}, \tr{args}, and \tr{body} +are given separately. + +We use the Id \tr{f} mostly to get its type. + +\item +Strictness information about \tr{f}, in the form of a list of +@Demands@. + +\item +A @UniqueSupply@. +\end{enumerate} + +@mkWrapperAndWorker@ produces (A BIT OUT-OF-DATE...): +\begin{enumerate} +\item +Maybe @Nothing@: no worker/wrappering going on in this case. This can +happen (a)~if the strictness info says that there is nothing +interesting to do or (b)~if *any* of the argument types corresponding +to ``active'' arg postitions is abstract or will be to the outside +world (i.e., {\em this} module can see the constructors, but nobody +else will be able to). An ``active'' arg position is one which the +wrapper has to unpack. An importing module can't do this unpacking, +so it simply has to give up and call the wrapper only. + +\item +Maybe \tr{Just (wrapper_Id, wrapper_body, worker_Id, worker_body)}. + +The @wrapper_Id@ is just the one that was passed in, with its +strictness IdInfo updated. +\end{enumerate} + +The \tr{body} of the original function may not be given (i.e., it's +BOTTOM), in which case you'd jolly well better not tug on the +worker-body output! + +Here's an example. The original function is: +\begin{verbatim} +g :: forall a . Int -> [a] -> a + +g = /\ a -> \ x ys -> + case x of + 0 -> head ys + _ -> head (tail ys) +\end{verbatim} + +From this, we want to produce: +\begin{verbatim} +-- wrapper (an unfolding) +g :: forall a . Int -> [a] -> a + +g = /\ a -> \ x ys -> + case x of + I# x# -> g.wrk a x# ys + -- call the worker; don't forget the type args! + +-- worker +g.wrk :: forall a . Int# -> [a] -> a + +g.wrk = /\ a -> \ x# ys -> + let + x = I# x# + in + case x of -- note: body of g moved intact + 0 -> head ys + _ -> head (tail ys) +\end{verbatim} + +Something we have to be careful about: Here's an example: +\begin{verbatim} +-- "f" strictness: U(P)U(P) +f (I# a) (I# b) = a +# b + +g = f -- "g" strictness same as "f" +\end{verbatim} +\tr{f} will get a worker all nice and friendly-like; that's good. +{\em But we don't want a worker for \tr{g}}, even though it has the +same strictness as \tr{f}. Doing so could break laziness, at best. + +Consequently, we insist that the number of strictness-info items is +exactly the same as the number of lambda-bound arguments. (This is +probably slightly paranoid, but OK in practice.) If it isn't the +same, we ``revise'' the strictness info, so that we won't propagate +the unusable strictness-info into the interfaces. + +========================== + +Here's the real fun... The wrapper's ``deconstructing'' of arguments +and the worker's putting them back together again are ``duals'' in +some sense. + +What we do is walk along the @Demand@ list, producing two +expressions (one for wrapper, one for worker...), each with a ``hole'' +in it, where we will later plug in more information. For our previous +example, the expressions-with-HOLES are: +\begin{verbatim} +\ x ys -> -- wrapper + case x of + I# x# -> <<HOLE>> x# ys + +\ x# ys -> -- worker + let + x = I# x# + in + <<HOLE>> +\end{verbatim} +(Actually, we add the lambda-bound arguments at the end...) (The big +Lambdas are added on the front later.) + +\begin{code} +mkWwBodies + :: UniType -- Type of the *body* of the orig + -- function; i.e. /\ tyvars -> \ vars -> body + -> [TyVar] -- Type lambda vars of original function + -> [Id] -- Args of original function + -> [Demand] -- Strictness info for those args + + -> SUniqSM (Maybe -- Nothing iff (a) no interesting split possible + -- (b) any unpack on abstract type + (Id -> PlainCoreExpr, -- Wrapper expr w/ + -- hole for worker id + PlainCoreExpr -> PlainCoreExpr, -- Worker expr w/ hole + -- for original fn body + StrictnessInfo, -- Worker strictness info + UniType -> UniType) -- Worker type w/ hole + ) -- for type of original fn body + + +mkWwBodies body_ty tyvars args arg_infos + = ASSERT(length args == length arg_infos) + -- or you can get disastrous user/definer-module mismatches + if (all_absent_args_and_unboxed_value body_ty arg_infos) + then returnSUs Nothing + + else -- the rest... + mk_ww_arg_processing args arg_infos (mAX_WORKER_ARGS - nonAbsentArgs arg_infos) + `thenUsMaybe` \ (wrap_frag, work_args_info, work_frag) -> + let + (work_args, wrkr_demands) = unzip work_args_info + + wrkr_strictness = mkStrictnessInfo wrkr_demands Nothing -- no worker-of-worker... + + wrapper_w_hole = \ worker_id -> + mkCoTyLam tyvars ( + mkCoLam args ( + wrap_frag ( + mkCoTyApps (CoVar worker_id) (map mkTyVarTy tyvars) + ))) + + worker_w_hole = \ orig_body -> + mkCoTyLam tyvars ( + mkCoLam work_args ( + work_frag orig_body + )) + + worker_ty_w_hole = \ body_ty -> + snd (quantifyTy tyvars ( + foldr mkFunTy body_ty (map getIdUniType work_args) + )) + in + returnSUs (Just (wrapper_w_hole, worker_w_hole, wrkr_strictness, worker_ty_w_hole)) + where + -- "all_absent_args_and_unboxed_value": + -- check for the obscure case of "\ x y z ... -> body" where + -- (a) *all* of the args x, y, z,... are absent, and + -- (b) the type of body is unboxed + -- If these conditions are true, we must *not* play worker/wrapper games! + + all_absent_args_and_unboxed_value body_ty arg_infos + = not (null arg_infos) + && all is_absent_arg arg_infos + && isPrimType body_ty + + is_absent_arg (WwLazy True) = True + is_absent_arg _ = False +\end{code} + +Important: mk_ww_arg_processing doesn't check +for an "interesting" split. It just races ahead and makes the +split, even if there's no unpacking at all. This is important for +when it calls itself recursively. + +It returns Nothing only if it encounters an abstract type in mid-flight. + +\begin{code} +mAX_WORKER_ARGS :: Int -- ToDo: set via flag +mAX_WORKER_ARGS = 6 -- Hmm... but this is an everything-must- + -- be-compiled-with-the-same-val thing... + +mk_ww_arg_processing + :: [Id] -- Args of original function + -> [Demand] -- Strictness info for those args + -- must be at least as long as args + + -> Int -- Number of extra args we are prepared to add. + -- This prevents over-eager unpacking, leading + -- to huge-arity functions. + + -> SUniqSM (Maybe -- Nothing iff any unpack on abstract type + (PlainCoreExpr -> PlainCoreExpr, -- Wrapper expr w/ + -- hole for worker id + -- applied to types + [(Id,Demand)], -- Worker's args + -- and their strictness info + PlainCoreExpr -> PlainCoreExpr) -- Worker body expr w/ hole + ) -- for original fn body + +mk_ww_arg_processing [] _ _ = returnSUs (Just (id, [], id)) + +mk_ww_arg_processing (arg : args) (WwLazy True : infos) max_extra_args + = -- Absent argument + -- So, finish args to the right... + --pprTrace "Absent; num_wrkr_args=" (ppInt num_wrkr_args) ( + let + arg_ty = getIdUniType arg + in + mk_ww_arg_processing args infos max_extra_args + -- we've already discounted for absent args, + -- so we don't change max_extra_args + `thenUsMaybe` \ (wrap_rest, work_args_info, work_rest) -> + + -- wrapper doesn't pass this arg to worker: + returnSUs (Just ( + -- wrapper: + \ hole -> wrap_rest hole, + + -- worker: + work_args_info, -- NB: no argument added + \ hole -> mk_absent_let arg arg_ty (work_rest hole) + )) + --) + where + mk_absent_let arg arg_ty body + = if not (isPrimType arg_ty) then + CoLet (CoNonRec arg (mkCoTyApp (CoVar aBSENT_ERROR_ID) arg_ty)) + body + else -- quite horrible + panic "WwLib: haven't done mk_absent_let for primitives yet" + + +mk_ww_arg_processing (arg : args) (WwUnpack cmpnt_infos : infos) max_extra_args + | new_max_extra_args > 0 -- Check that we are prepared to add arguments + = -- this is the complicated one. + --pprTrace "Unpack; num_wrkr_args=" (ppCat [ppInt num_wrkr_args, ppStr "; new_max=", ppInt new_num_wrkr_args, ppStr "; arg=", ppr PprDebug arg, ppr PprDebug (WwUnpack cmpnt_infos)]) ( + case getUniDataTyCon_maybe arg_ty of + + Nothing -> -- Not a data type + panic "mk_ww_arg_processing: not datatype" + + Just (_, _, []) -> -- An abstract type + -- We have to give up on the whole idea + returnSUs Nothing + Just (_, _, (_:_:_)) -> -- Two or more constructors; that's odd + panic "mk_ww_arg_processing: multi-constr" + + Just (arg_tycon, tycon_arg_tys, [data_con]) -> + -- The main event: a single-constructor data type + + let + (_,inst_con_arg_tys,_) + = getInstantiatedDataConSig data_con tycon_arg_tys + in + getSUniques (length inst_con_arg_tys) `thenSUs` \ uniqs -> + + let unpk_args = zipWith (\ u t -> mkSysLocal SLIT("upk") u t mkUnknownSrcLoc) + uniqs inst_con_arg_tys + in + -- In processing the rest, push the sub-component args + -- and infos on the front of the current bunch + mk_ww_arg_processing (unpk_args ++ args) (cmpnt_infos ++ infos) new_max_extra_args + `thenUsMaybe` \ (wrap_rest, work_args_info, work_rest) -> + + returnSUs (Just ( + -- wrapper: unpack the value + \ hole -> mk_unpk_case arg unpk_args + data_con arg_tycon + (wrap_rest hole), + + -- worker: expect the unpacked value; + -- reconstruct the orig value with a "let" + work_args_info, + \ hole -> work_rest (mk_pk_let arg data_con tycon_arg_tys unpk_args hole) + )) + --) + where + arg_ty = getIdUniType arg + + new_max_extra_args + = max_extra_args + + 1 -- We won't pass the original arg now + - nonAbsentArgs cmpnt_infos -- But we will pass an arg for each cmpt + + mk_unpk_case arg unpk_args boxing_con boxing_tycon body + = CoCase (CoVar arg) ( + CoAlgAlts [(boxing_con, unpk_args, body)] + CoNoDefault + ) + + mk_pk_let arg boxing_con con_tys unpk_args body + = CoLet (CoNonRec arg (CoCon boxing_con con_tys [CoVarAtom a | a <- unpk_args])) + body + +mk_ww_arg_processing (arg : args) (arg_demand : infos) max_extra_args + | otherwise + = -- For all others at the moment, we just + -- pass them to the worker unchanged. + --pprTrace "Other; num_wrkr_args=" (ppCat [ppInt num_wrkr_args, ppStr ";arg=", ppr PprDebug arg, ppr PprDebug arg_demand]) ( + + -- Finish args to the right... + mk_ww_arg_processing args infos max_extra_args + `thenUsMaybe` \ (wrap_rest, work_args_info, work_rest) -> + + returnSUs (Just ( + -- wrapper: + \ hole -> wrap_rest (CoApp hole (CoVarAtom arg)), + + -- worker: + (arg, arg_demand) : work_args_info, + \ hole -> work_rest hole + )) + --) +\end{code} + +%************************************************************************ +%* * +\subsection[monad-WwLib]{Simple monad for worker/wrapper} +%* * +%************************************************************************ + +In this monad, we thread a @UniqueSupply@, and we carry a +@GlobalSwitch@-lookup function downwards. + +\begin{code} +type WwM result + = SplitUniqSupply + -> (GlobalSwitch -> Bool) + -> result + +#ifdef __GLASGOW_HASKELL__ +{-# INLINE thenWw #-} +{-# INLINE returnWw #-} +#endif + +returnWw :: a -> WwM a +thenWw :: WwM a -> (a -> WwM b) -> WwM b +mapWw :: (a -> WwM b) -> [a] -> WwM [b] + +returnWw expr ns sw = expr + +thenWw m k us sw_chk + = case splitUniqSupply us of { (s1, s2) -> + case (m s1 sw_chk) of { m_res -> + k m_res s2 sw_chk }} + +mapWw f [] = returnWw [] +mapWw f (x:xs) + = f x `thenWw` \ x' -> + mapWw f xs `thenWw` \ xs' -> + returnWw (x':xs') +\end{code} + +\begin{code} +getUniqueWw :: WwM Unique +uniqSMtoWwM :: SUniqSM a -> WwM a + +getUniqueWw us sw_chk = getSUnique us + +uniqSMtoWwM u_obj us sw_chk = u_obj us + +thenUsMaybe :: SUniqSM (Maybe a) -> (a -> SUniqSM (Maybe b)) -> SUniqSM (Maybe b) +thenUsMaybe m k + = m `thenSUs` \ result -> + case result of + Nothing -> returnSUs Nothing + Just x -> k x +\end{code} |