[project @ 1999-04-13 08:55:33 by kglynn]
[ghc-hetmet.git] / ghc / compiler / stranal / WwLib.lhs
index 93de682..95007d6 100644 (file)
@@ -14,21 +14,27 @@ module WwLib (
 #include "HsVersions.h"
 
 import CoreSyn
-import Id              ( Id, idType, mkSysLocal, getIdDemandInfo, setIdDemandInfo )
-import Const           ( Con(..) )
+import Id              ( Id, idType, mkSysLocal, getIdDemandInfo, setIdDemandInfo,
+                          mkWildId )
+import IdInfo          ( CprInfo(..), noCprInfo )
+import Const           ( Con(..), DataCon )
 import DataCon         ( dataConArgTys )
 import Demand          ( Demand(..) )
 import PrelVals                ( aBSENT_ERROR_ID )
-import TysWiredIn      ( unitTy, unitDataCon )
+import TysWiredIn      ( unitTy, unitDataCon, 
+                          unboxedTupleCon, unboxedTupleTyCon )
 import Type            ( isUnLiftedType, mkTyVarTys, mkTyVarTy, mkFunTys,
                          splitForAllTys, splitFunTys,
-                         splitAlgTyConApp_maybe, 
+                         splitAlgTyConApp_maybe, mkTyConApp,
                          Type
                        )
+import TyCon            ( isNewTyCon,
+                          TyCon )
 import BasicTypes      ( NewOrData(..) )
 import Var              ( TyVar )
-import UniqSupply      ( returnUs, thenUs, getUniqueUs, getUniquesUs, UniqSM )
-import Util            ( zipWithEqual )
+import UniqSupply      ( returnUs, thenUs, getUniqueUs, getUniquesUs, 
+                          mapUs, UniqSM )
+import Util            ( zipWithEqual, zipEqual )
 import Outputable
 \end{code}
 
@@ -227,9 +233,10 @@ the function and the name of its worker, and we want to make its body (the wrapp
 \begin{code}
 mkWrapper :: Type              -- Wrapper type
          -> [Demand]           -- Wrapper strictness info
+         -> CprInfo            -- Wrapper cpr info
          -> UniqSM (Id -> CoreExpr)    -- Wrapper body, missing worker Id
 
-mkWrapper fun_ty demands
+mkWrapper fun_ty demands cpr_info
   = let
        n_wrap_args = length demands
     in
@@ -249,7 +256,7 @@ mkWrapper fun_ty demands
        leftover_arg_tys   = drop n_wrap_args arg_tys
        final_body_ty      = mkFunTys leftover_arg_tys body_ty
     in
-    mkWwBodies tyvars wrap_args final_body_ty demands  `thenUs` \ (wrap_fn, _, _) ->
+    mkWwBodies tyvars wrap_args final_body_ty demands cpr_info `thenUs` \ (wrap_fn, _, _) ->
     returnUs wrap_fn
 \end{code}
 
@@ -258,11 +265,12 @@ mkWrapper fun_ty demands
 \begin{code}
 mkWwBodies :: [TyVar] -> [Id] -> Type          -- Original fn args and body type
           -> [Demand]                          -- Strictness info for original fn; corresp 1-1 with args
+          -> CprInfo                           -- Result of CPR analysis 
           -> UniqSM (Id -> CoreExpr,           -- Wrapper body, lacking only the worker Id
                      CoreExpr -> CoreExpr,     -- Worker body, lacking the original function body
                      [Demand])                 -- Strictness info for worker
 
-mkWwBodies tyvars args body_ty demands
+mkWwBodies tyvars args body_ty demands cpr_info
   | allAbsent demands &&
     isUnLiftedType body_ty
   =    -- Horrid special case.  If the worker would have no arguments, and the
@@ -282,17 +290,21 @@ mkWwBodies tyvars args body_ty demands
              \ body    -> mkLams (tyvars ++ [void_arg]) body,
              [WwLazy True])
 
-mkWwBodies tyvars wrap_args body_ty demands
+mkWwBodies tyvars wrap_args body_ty demands cpr_info
   | otherwise
   = let
-       wrap_args_w_demands = zipWithEqual "mkWwBodies" setIdDemandInfo wrap_args demands
+        -- demands may be longer than number of args.  If we aren't doing w/w
+        -- for strictness then demands is an infinite list of 'lazy' args.
+       wrap_args_w_demands = zipWith setIdDemandInfo wrap_args demands
     in
     mkWW wrap_args_w_demands           `thenUs` \ (wrap_fn, work_args_w_demands, work_fn) ->
+    mkWWcpr body_ty cpr_info
+                                        `thenUs` \ (wrap_fn_w_cpr, work_fn_w_cpr) ->
     returnUs (\ work_id -> mkLams tyvars $ mkLams wrap_args_w_demands $
-                          wrap_fn (mkTyApps (Var work_id) (mkTyVarTys tyvars)),
+                          (wrap_fn_w_cpr . wrap_fn) (mkTyApps (Var work_id) (mkTyVarTys tyvars)),
 
              \ body    -> mkLams tyvars $ mkLams work_args_w_demands $
-                          work_fn body,
+                          (work_fn_w_cpr . work_fn) body,
 
              map getIdDemandInfo work_args_w_demands)
 \end{code}    
@@ -363,6 +375,167 @@ mkWW (arg : ds)
                  work_fn)
 \end{code}
 
+@mkWWcpr@ takes the worker/wrapper pair produced from the strictness
+info and adds in the CPR transformation.  The worker returns an
+unboxed tuple containing non-CPR components.  The wrapper takes this
+tuple and re-produces the correct structured output.
+
+The non-CPR results appear ordered in the unboxed tuple as if by a
+left-to-right traversal of the result structure.
+
+
+\begin{code}
+
+mkWWcpr ::    Type                              -- function body type
+           -> CprInfo                           -- CPR analysis results
+           -> UniqSM (CoreExpr -> CoreExpr,             -- New wrapper 
+                      CoreExpr -> CoreExpr)            -- New worker
+
+mkWWcpr body_ty NoCPRInfo 
+    = returnUs (id, id)      -- Must be just the strictness transf.
+mkWWcpr body_ty (CPRInfo cpr_args)
+    = getUniqueUs              `thenUs` \ body_arg_uniq ->
+      let
+        body_var = mk_ww_local body_arg_uniq body_ty
+      in
+      cpr_reconstruct body_ty cpr_info'                   `thenUs` \reconst_fn ->
+      cpr_flatten body_ty cpr_info'                       `thenUs` \flatten_fn ->
+      returnUs (reconst_fn, flatten_fn)
+    -- We only make use of the outer level of CprInfo,  otherwise we
+    -- may lose laziness.  :-(  Hopefully,  we will find a use for the
+    -- extra info some day (e.g. creating versions specialized to 
+    -- the use made of the components of the result by the callee)
+    where cpr_info' = CPRInfo (map (const NoCPRInfo) cpr_args) 
+\end{code}
+
+
+@cpr_flatten@ takes the result type produced by the body and the info
+from the CPR analysis and flattens the constructed product components.
+These are returned in an unboxed tuple.
+
+\begin{code}
+
+cpr_flatten :: Type -> CprInfo -> UniqSM (CoreExpr -> CoreExpr)
+cpr_flatten ty cpr_info
+    = mk_cpr_case (ty, cpr_info)       `thenUs` \(res_id, tup_ids, flatten_exp) ->
+      returnUs (\body -> Case body res_id
+                         [(DEFAULT, [], flatten_exp (fst $ mk_unboxed_tuple tup_ids))])
+
+
+
+mk_cpr_case :: (Type, CprInfo) -> 
+               UniqSM (CoreBndr,                     -- Name of binder for this part of result 
+                      [(CoreExpr, Type)],            -- expressions for flattened result
+                      CoreExpr -> CoreExpr)          -- add in code to flatten result
+
+mk_cpr_case (ty, NoCPRInfo) 
+      -- this component must be returned as a component of the unboxed tuple result
+    = getUniqueUs            `thenUs`     \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty in
+        returnUs (id_id, [(Var id_id, ty)], id)
+mk_cpr_case (ty, cpr_info@(CPRInfo ci_args))
+    | isNewTyCon tycon  -- a new type: under the coercions must be a 
+                        -- constructed product
+    = ASSERT ( null $ tail inst_con_arg_tys )
+      mk_cpr_case (head inst_con_arg_tys, cpr_info) 
+                                 `thenUs`  \(arg, tup, exp) ->
+      getUniqueUs                `thenUs`  \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty 
+          new_exp_case = \var -> Case (Note (Coerce (idType arg) ty) (Var id_id))
+                                     arg
+                                     [(DEFAULT,[], exp var)]
+      in
+        returnUs (id_id, tup, new_exp_case)
+
+    | otherwise            -- a data type
+                           -- flatten components
+    = mapUs mk_cpr_case (zip inst_con_arg_tys ci_args) 
+                                 `thenUs`  \sub_builds ->
+      getUniqueUs                `thenUs`  \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty 
+          (args, tup, exp) = unzip3 sub_builds
+          con_app = mkConApp data_con (map Var args) 
+          new_tup = concat tup
+          new_exp_case = \var -> Case (Var id_id) (mkWildId ty)
+                                [(DataCon data_con, args, 
+                                  foldl (\e f -> f e) var exp)]
+      in
+        returnUs (id_id, new_tup, new_exp_case)
+    where
+      (data_con, tycon, tycon_arg_tys, inst_con_arg_tys) = splitType "mk_cpr_case" ty
+
+\end{code}
+
+@cpr_reconstruct@ does the opposite of @cpr_flatten@.  It takes the unboxed
+tuple produced by the worker and reconstructs the structured result.
+
+\begin{code}
+cpr_reconstruct :: Type -> CprInfo -> UniqSM (CoreExpr -> CoreExpr)
+cpr_reconstruct ty cpr_info
+    = mk_cpr_let (ty,cpr_info)     `thenUs`  \(res_id, tup_ids, reconstruct_exp) ->
+      returnUs (\worker -> Case worker (mkWildId $ worker_type tup_ids)
+                           [(DataCon $ unboxedTupleCon $ length tup_ids,
+                           tup_ids, reconstruct_exp $ Var res_id)])
+                            
+    where
+       worker_type ids = mkTyConApp (unboxedTupleTyCon (length ids)) (map idType ids) 
+
+
+mk_cpr_let :: (Type, CprInfo) -> 
+              UniqSM (CoreBndr,                -- Binder for this component of result 
+                      [CoreBndr],              -- Binders which will appear in worker's result
+                      CoreExpr -> CoreExpr)    -- Code to produce structured result.
+mk_cpr_let (ty, NoCPRInfo)
+      -- this component will appear explicitly in the unboxed tuple.
+    = getUniqueUs            `thenUs`     \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty in
+        returnUs (id_id, [id_id], id)
+mk_cpr_let (ty, cpr_info@(CPRInfo ci_args))
+    | isNewTyCon tycon   -- a new type: must coerce the argument to this type
+    = ASSERT ( null $ tail inst_con_arg_tys )
+      mk_cpr_let (head inst_con_arg_tys, cpr_info) 
+                                 `thenUs`  \(arg, tup, exp) ->
+      getUniqueUs                `thenUs`  \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty 
+          new_exp = \var -> exp (Let (NonRec id_id (Note (Coerce ty (idType arg)) (Var arg))) var) 
+      in
+        returnUs (id_id, tup, new_exp)
+
+    | otherwise     -- a data type
+                    -- reconstruct components then apply data con
+    = mapUs mk_cpr_let (zip inst_con_arg_tys ci_args) 
+                                 `thenUs`  \sub_builds ->
+      getUniqueUs                `thenUs`  \id_uniq   ->
+      let id_id = mk_ww_local id_uniq ty 
+          (args, tup, exp) = unzip3 sub_builds
+          con_app = mkConApp data_con $ (map Type tycon_arg_tys) ++ (map Var args) 
+          new_tup = concat tup
+          new_exp = \var -> foldl (\e f -> f e) (Let (NonRec id_id con_app) var) exp 
+      in
+        returnUs (id_id, new_tup, new_exp)
+    where
+      (data_con, tycon, tycon_arg_tys, inst_con_arg_tys) = splitType "mk_cpr_let" ty
+
+splitType :: String -> Type -> (DataCon, TyCon, [Type], [Type])
+splitType fname ty = (data_con, tycon, tycon_arg_tys, dataConArgTys data_con tycon_arg_tys) 
+    where
+      (data_con, tycon, tycon_arg_tys)
+         = case (splitAlgTyConApp_maybe ty) of
+             Just (arg_tycon, tycon_arg_tys, [data_con]) ->
+                   -- The main event: a single-constructor data type
+                  (data_con, arg_tycon, tycon_arg_tys)
+
+             Just (_, _, data_cons) ->
+                  pprPanic (fname ++ ":") 
+                           (text "not one constr (interface files not consistent/up to date?)"
+                           $$ ppr ty)
+
+             Nothing           ->
+                  pprPanic (fname ++ ":") 
+                            (text "not a datatype" $$ ppr ty)
+
+
+\end{code}
 
 %************************************************************************
 %*                                                                     *
@@ -406,4 +579,15 @@ mk_pk_let DataType arg boxing_con con_tys unpk_args body
 
 
 mk_ww_local uniq ty = mkSysLocal SLIT("ww") uniq ty
+
+
+mk_unboxed_tuple :: [(CoreExpr, Type)] -> (CoreExpr, Type)
+mk_unboxed_tuple contents
+    = (mkConApp (unboxedTupleCon (length contents)) 
+                (map (Type . snd) contents ++
+                 map fst contents),
+       mkTyConApp (unboxedTupleTyCon (length contents)) 
+                  (map snd contents))
+
+
 \end{code}