- is_ty_arg (TyArg _) = True
- is_ty_arg _ = False
-
-----------
-specId :: Id
- -> (CoreArg -> SpecM (CoreExpr, UsageDetails))
- -> SpecM (CoreExpr, UsageDetails)
-specId v
- = lookupId v `thenSM` \ vlookup ->
- case vlookup of
-
- Lifted vl vu
- -> thing_inside (VarArg vu) `thenSM` \ (expr, uds) ->
- returnSM (bindUnlift vl vu expr, singleFvUDs (VarArg vl) `unionUDs` uds)
-
- NoLift vatom
- -> thing_inside vatom `thenSM` \ (expr, uds) ->
- returnSM (expr, singleFvUDs vatom `unionUDs` uds)
-
-specArg :: CoreArg
- -> (CoreArg -> SpecM (CoreExpr, UsageDetails))
- -> SpecM (CoreExpr, UsageDetails))
-
-specArg (TyArg ty) thing_inside
- = specTy ty `thenSM` \ new_ty ->
- thing_inside (TyArg new_ty)
-
-specArg (LitArg lit)
- = thing_inside (LitArg lit)
-
-specArg (VarArg v)
-
-
-specArgs [] thing_inside
- = thing_inside []
-
-specArgs (arg:args) thing_inside
- = specArg arg $ \ arg' ->
- specArgs args $ \ args' ->
- thing_inside (arg' : args')
-\end{code}
-
-
-%************************************************************************
-%* *
-\subsubsection{Specialising bindings}
-%* *
-%************************************************************************
-
-A classic case of when having a polymorphic recursive function would help!
-
-\begin{code}
-data BindsOrExpr = ItsABinds [CoreBinding]
- | ItsAnExpr CoreExpr
-\end{code}
-
-\begin{code}
-specBindAndScope
- :: Bool -- True <=> a top level group
- -> CoreBinding -- As yet unprocessed
- -> SpecM (BindsOrExpr, UsageDetails) -- Something to do the scope of the bindings
- -> SpecM ([CoreBinding], -- Processed
- BindsOrExpr, -- Combined result
- UsageDetails) -- Usage details of the whole lot
-
-specBindAndScope top_lev bind scopeM
- = cloneLetBinders top_lev (is_rec bind) binders
- `thenSM` \ (new_binders, clone_infos) ->
-
- -- Two cases now: either this is a bunch of local dictionaries,
- -- in which case we float them; or its a bunch of other values,
- -- in which case we see if they correspond to any call-instances
- -- we have from processing the scope
-
- if not top_lev && all (isDictTy . idType) binders
- then
- -- Ha! A group of local dictionary bindings
-
- bindIds binders clone_infos (
-
- -- Process the dictionary bindings themselves
- specBind False True new_binders [] bind `thenSM` \ (bind, rhs_uds) ->
-
- -- Process their scope
- scopeM `thenSM` \ (thing, scope_uds) ->
- let
- -- Add the bindings to the current stuff
- final_uds = addDictBinds new_binders bind rhs_uds scope_uds
- in
- returnSM ([], thing, final_uds)
- )
- else
- -- Ho! A group of bindings
-
- fixSM (\ ~(_, _, _, rec_spec_infos) ->
-
- bindSpecIds binders clone_infos rec_spec_infos (
- -- It's ok to have new binders in scope in
- -- non-recursive decls too, cos name shadowing is gone by now
-
- -- Do the scope of the bindings
- scopeM `thenSM` \ (thing, scope_uds) ->
- let
- (call_insts, gotci_scope_uds) = getCIs top_lev new_binders scope_uds
-
- equiv_ciss = equivClasses cmpCI_tys call_insts
- inst_cis = map head equiv_ciss
- in
-
- -- Do the bindings themselves
- specBind top_lev False new_binders inst_cis bind
- `thenSM` \ (spec_bind, spec_uds) ->
-
- -- Create any necessary instances
- instBind top_lev new_binders bind equiv_ciss inst_cis
- `thenSM` \ (inst_binds, inst_uds, spec_infos) ->
-
- let
- -- NB: dumpUDs only worries about new_binders since the free var
- -- stuff only records free new_binders
- -- The spec_ids only appear in SpecInfos and final speced calls
-
- -- Build final binding group and usage details
- (final_binds, final_uds)
- = if top_lev then
- -- For a top-level binding we have to dumpUDs from
- -- spec_uds and inst_uds and scope_uds creating
- -- *global* dict bindings
- let
- (scope_dict_binds, final_scope_uds)
- = dumpUDs gotci_scope_uds True False [] new_binders []
- (spec_dict_binds, final_spec_uds)
- = dumpUDs spec_uds True False inst_cis new_binders []
- (inst_dict_binds, final_inst_uds)
- = dumpUDs inst_uds True False inst_cis new_binders []
- in
- ([spec_bind] ++ inst_binds ++ scope_dict_binds
- ++ spec_dict_binds ++ inst_dict_binds,
- final_spec_uds `unionUDs` final_scope_uds `unionUDs` final_inst_uds)
- else
- -- For a local binding we only have to dumpUDs from
- -- scope_uds since the UDs from spec_uds and inst_uds
- -- have already been dumped by specBind and instBind
- let
- (scope_dict_binds, final_scope_uds)
- = dumpUDs gotci_scope_uds False False [] new_binders []
- in
- ([spec_bind] ++ inst_binds ++ scope_dict_binds,
- spec_uds `unionUDs` final_scope_uds `unionUDs` inst_uds)
-
- -- inst_uds comes last, because there may be dict bindings
- -- floating outward in scope_uds which are mentioned
- -- in the call-instances, and hence in spec_uds.
- -- This ordering makes sure that the precedence order
- -- among the dict bindings finally floated out is maintained.
- in
- returnSM (final_binds, thing, final_uds, spec_infos)
- )
- ) `thenSM` \ (binds, thing, final_uds, spec_infos) ->
- returnSM (binds, thing, final_uds)
- where
- binders = bindersOf bind
-
- is_rec (NonRec _ _) = False
- is_rec _ = True
-\end{code}
-
-\begin{code}
-specBind :: Bool -> Bool -> [Id] -> [CallInstance]
- -> CoreBinding
- -> SpecM (CoreBinding, UsageDetails)
- -- The UsageDetails returned has already had stuff to do with this group
- -- of binders deleted; that's why new_binders is passed in.
-specBind top_lev floating new_binders inst_cis (NonRec binder rhs)
- = specOneBinding top_lev floating new_binders inst_cis (binder,rhs)
- `thenSM` \ ((binder,rhs), rhs_uds) ->
- returnSM (NonRec binder rhs, rhs_uds)
-
-specBind top_lev floating new_binders inst_cis (Rec pairs)
- = mapAndUnzipSM (specOneBinding top_lev floating new_binders inst_cis) pairs
- `thenSM` \ (pairs, rhs_uds_s) ->
- returnSM (Rec pairs, unionUDList rhs_uds_s)
-
-
-specOneBinding :: Bool -> Bool -> [Id] -> [CallInstance]
- -> (Id,CoreExpr)
- -> SpecM ((Id,CoreExpr), UsageDetails)
-
-specOneBinding top_lev floating new_binders inst_cis (binder, rhs)
- = lookupId binder `thenSM` \ blookup ->
- specExpr rhs [] `thenSM` \ (rhs, rhs_uds) ->
- let
- specid_maybe_maybe = isSpecPragmaId_maybe binder
- is_specid = maybeToBool specid_maybe_maybe
- Just specinfo_maybe = specid_maybe_maybe
- specid_with_info = maybeToBool specinfo_maybe
- Just spec_info = specinfo_maybe
-
- -- If we have a SpecInfo stored in a SpecPragmaId binder
- -- it will contain a SpecInfo with an explicit SpecId
- -- We add the explicit ci to the usage details
- -- Any ordinary cis for orig_id (there should only be one)
- -- will be ignored later
-
- pragma_uds
- = if is_specid && specid_with_info then
- let
- (SpecInfo spec_tys _ spec_id) = spec_info
- Just (orig_id, _) = isSpecId_maybe spec_id
- in
- ASSERT(toplevelishId orig_id) -- must not be cloned!
- explicitCI orig_id spec_tys spec_info
- else
- emptyUDs
-
- -- For a local binding we dump the usage details, creating
- -- any local dict bindings required
- -- At the top-level the uds will be dumped in specBindAndScope
- -- and the dict bindings made *global*
-
- (local_dict_binds, final_uds)
- = if not top_lev then
- dumpUDs rhs_uds False floating inst_cis new_binders []
- else
- ([], rhs_uds)
- in
- case blookup of
- Lifted lift_binder unlift_binder
- -> -- We may need to record an unboxed instance of
- -- the _Lift data type in the usage details
- mkTyConInstance liftDataCon [idType unlift_binder]
- `thenSM` \ lift_uds ->
- returnSM ((lift_binder,
- mkCoLetsNoUnboxed local_dict_binds (liftExpr unlift_binder rhs)),
- final_uds `unionUDs` pragma_uds `unionUDs` lift_uds)
-
- NoLift (VarArg binder)
- -> returnSM ((binder, mkCoLetsNoUnboxed local_dict_binds rhs),
- final_uds `unionUDs` pragma_uds)
-\end{code}
-
-
-%************************************************************************
-%* *
-\subsection{@instBind@}
-%* *
-%************************************************************************
-
-\begin{code}
-instBind top_lev new_ids@(first_binder:other_binders) bind equiv_ciss inst_cis
- | null equiv_ciss
- = returnSM ([], emptyUDs, [])
-
- | all same_overloading other_binders
- = -- For each call_inst, build an instance
- mapAndUnzip3SM do_this_class equiv_ciss
- `thenSM` \ (inst_binds, inst_uds_s, spec_infos) ->
-
- -- Add in the remaining UDs
- returnSM (catMaybes inst_binds,
- unionUDList inst_uds_s,
- spec_infos
- )
-
- | otherwise -- Incompatible overloadings; see below by same_overloading
- = (if not (null (filter isUnboxedCI (concat equiv_ciss)))
- then pprTrace "dumpCIs: not same overloading ... WITH UNBOXED TYPES!\n"
- else if top_lev
- then pprTrace "dumpCIs: not same overloading ... top level \n"
- else (\ x y -> y)
- ) (hang (hcat [ptext SLIT("{"),
- interppSP new_ids,
- ptext SLIT("}")])
- 4 (vcat [vcat (map (pprGenType . idType) new_ids),
- vcat (map pprCI (concat equiv_ciss))]))
- (returnSM ([], emptyUDs, []))
-
- where
- (tyvar_tmpls, class_tyvar_pairs) = getIdOverloading first_binder
- tyvar_tmpl_tys = mkTyVarTys tyvar_tmpls
-
- no_of_tyvars = length tyvar_tmpls
- no_of_dicts = length class_tyvar_pairs
-
- do_this_class equiv_cis
- = mkOneInst do_cis explicit_cis no_of_dicts top_lev inst_cis new_ids bind
- where
- (explicit_cis, normal_cis) = partition isExplicitCI equiv_cis
- do_cis = head (normal_cis ++ explicit_cis)
- -- must choose a normal_cis in preference since dict_args will
- -- not be defined for an explicit_cis
-
- -- same_overloading tests whether the types of all the binders
- -- are "compatible"; ie have the same type and dictionary abstractions
- -- Almost always this is the case, because a recursive group is abstracted
- -- all together. But, it can happen that it ain't the case, because of
- -- code generated from instance decls:
- --
- -- rec
- -- dfun.Foo.Int :: (forall a. a -> Int, Int)
- -- dfun.Foo.Int = (const.op1.Int, const.op2.Int)
- --
- -- const.op1.Int :: forall a. a -> Int
- -- const.op1.Int a = defm.Foo.op1 Int a dfun.Foo.Int
- --
- -- const.op2.Int :: Int
- -- const.op2.Int = 3
- --
- -- Note that the first two defns have different polymorphism, but they are
- -- mutually recursive!
-
- same_overloading :: Id -> Bool
- same_overloading id
- = no_of_tyvars == length this_id_tyvars
- -- Same no of tyvars
- && no_of_dicts == length this_id_class_tyvar_pairs
- -- Same no of vdicts
- && and (zipWith same_ov class_tyvar_pairs this_id_class_tyvar_pairs)
- && length class_tyvar_pairs == length this_id_class_tyvar_pairs
- -- Same overloading
- where
- (this_id_tyvars, this_id_class_tyvar_pairs) = getIdOverloading id
- tyvar_pairs = this_id_tyvars `zip` tyvar_tmpls
-
- same_ov (clas1,tyvar1) (clas2,tyvar2)
- = clas1 == clas2 &&
- tyvar1 == assoc "same_overloading" tyvar_pairs tyvar2
-\end{code}
-
-OK, so we have:
- - a call instance eg f [t1,t2,t3] [d1,d2]
- - the rhs of the function eg orig_rhs
- - a constraint vector, saying which of eg [T,F,T]
- the functions type args are constrained
- (ie overloaded)
-
-We return a new definition
-
- $f1 = /\a -> orig_rhs t1 a t3 d1 d2
-
-The SpecInfo for f will be:
-
- SpecInfo [t1, a, t3] (\d1 d2 -> $f1 a)
-
-Based on this SpecInfo, a call instance of f
-
- ...(f t1 t2 t3)...
-
-should get replaced by
-
- ...(\d1 d2 -> $f1 t2)...
-
-(But that is the business of the simplifier.)
-
-\begin{code}
-mkOneInst :: CallInstance
- -> [CallInstance] -- Any explicit cis for this inst
- -> Int -- No of dicts to specialise
- -> Bool -- Top level binders?
- -> [CallInstance] -- Instantiated call insts for binders
- -> [Id] -- New binders
- -> CoreBinding -- Unprocessed
- -> SpecM (Maybe CoreBinding, -- Instantiated version of input
- UsageDetails,
- [Maybe SpecInfo] -- One for each id in the original binding
- )
-
-mkOneInst do_cis@(CallInstance _ spec_tys dict_args _ _) explicit_cis
- no_of_dicts_to_specialise top_lev inst_cis new_ids orig_bind
- = newSpecIds new_ids spec_tys no_of_dicts_to_specialise
- `thenSM` \ spec_ids ->
- newTyVars (length [() | Nothing <- spec_tys]) `thenSM` \ poly_tyvars ->
- let
- -- arg_tys is spec_tys with tyvars instead of the Nothing spec_tys
- -- which correspond to unspecialised args
- arg_tys :: [Type]
- (_,arg_tys) = mapAccumL do_the_wotsit poly_tyvars spec_tys
-
- args :: [CoreArg]
- args = map TyArg arg_tys ++ dict_args
-
- (new_id:_) = new_ids
- (spec_id:_) = spec_ids
-
- do_bind (NonRec orig_id rhs)
- = do_one_rhs (spec_id, new_id, (orig_id,rhs))
- `thenSM` \ (maybe_spec, rhs_uds, spec_info) ->
- case maybe_spec of
- Just (spec_id, rhs) -> returnSM (Just (NonRec spec_id rhs), rhs_uds, [spec_info])
- Nothing -> returnSM (Nothing, rhs_uds, [spec_info])
-
- do_bind (Rec pairs)
- = mapAndUnzip3SM do_one_rhs (zip3 spec_ids new_ids pairs)
- `thenSM` \ (maybe_pairs, rhss_uds_s, spec_infos) ->
- returnSM (Just (Rec (catMaybes maybe_pairs)),
- unionUDList rhss_uds_s, spec_infos)
-
- do_one_rhs (spec_id, new_id, (orig_id, orig_rhs))
-
- -- Avoid duplicating a spec which has already been created ...
- -- This can arise in a Rec involving a dfun for which a
- -- a specialised instance has been created but specialisation
- -- "required" by one of the other Ids in the Rec
- | top_lev && maybeToBool lookup_orig_spec
- = (if opt_SpecialiseTrace
- then trace_nospec " Exists: " orig_id
- else id) (
-
- returnSM (Nothing, emptyUDs, Nothing)
- )
-
- -- Check for a (single) explicit call instance for this id
- | not (null explicit_cis_for_this_id)
- = ASSERT (length explicit_cis_for_this_id == 1)
- (if opt_SpecialiseTrace
- then trace_nospec " Explicit: " explicit_id
- else id) (
-
- returnSM (Nothing, tickSpecInsts emptyUDs, Just explicit_spec_info)
- )
-
- -- Apply the specialiser to (orig_rhs t1 a t3 d1 d2)
- | otherwise
- = ASSERT (no_of_dicts_to_specialise == length dict_args)
- specExpr orig_rhs args `thenSM` \ (inst_rhs, inst_uds) ->
- let
- -- For a local binding we dump the usage details, creating
- -- any local dict bindings required
- -- At the top-level the uds will be dumped in specBindAndScope
- -- and the dict bindings made *global*
-
- (local_dict_binds, final_uds)
- = if not top_lev then
- dumpUDs inst_uds False False inst_cis new_ids []
- else
- ([], inst_uds)
-
- spec_info = Just (SpecInfo spec_tys no_of_dicts_to_specialise spec_id)
- in
- if isUnboxedType (idType spec_id) then
- ASSERT (null poly_tyvars)
- liftId spec_id `thenSM` \ (lift_spec_id, unlift_spec_id) ->
- mkTyConInstance liftDataCon [idType unlift_spec_id]
- `thenSM` \ lift_uds ->
- returnSM (Just (lift_spec_id,
- mkCoLetsNoUnboxed local_dict_binds (liftExpr unlift_spec_id inst_rhs)),
- tickSpecInsts (final_uds `unionUDs` lift_uds), spec_info)
- else
- returnSM (Just (spec_id,
- mkCoLetsNoUnboxed local_dict_binds (mkTyLam poly_tyvars inst_rhs)),
- tickSpecInsts final_uds, spec_info)
- where
- lookup_orig_spec = matchSpecEnv (getIdSpecialisation orig_id) arg_tys
-
- explicit_cis_for_this_id = filter (isCIofTheseIds [new_id]) explicit_cis
- [CallInstance _ _ _ _ (Just explicit_spec_info)] = explicit_cis_for_this_id
- SpecInfo _ _ explicit_id = explicit_spec_info
-
- trace_nospec :: String -> Id -> a -> a
- trace_nospec str spec_id
- = pprTrace str
- (hsep [ppr new_id, hsep (map pp_ty arg_tys),
- ptext SLIT("==>"), ppr spec_id])
- in
- (if opt_SpecialiseTrace then
- pprTrace "Specialising:"
- (hang (hcat [char '{',
- interppSP new_ids,
- char '}'])
- 4 (vcat [
- hcat [ptext SLIT("types: "), hsep (map pp_ty arg_tys)],
- if isExplicitCI do_cis then empty else
- hcat [ptext SLIT("dicts: "), hsep (map pp_dict dict_args)],
- hcat [ptext SLIT("specs: "), ppr spec_ids]]))
- else id) (
-
- do_bind orig_bind `thenSM` \ (maybe_inst_bind, inst_uds, spec_infos) ->
-
- returnSM (maybe_inst_bind, inst_uds, spec_infos)