#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}
\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
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}
\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
\ 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}
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}
%************************************************************************
%* *
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}