\section[Specialise]{Stamping out overloading, and (optionally) polymorphism}
\begin{code}
+{-# OPTIONS -w #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+
module Specialise ( specProgram ) where
#include "HsVersions.h"
import VarEnv
import CoreSyn
import CoreUtils ( applyTypeToArgs, mkPiTypes )
-import CoreFVs ( exprFreeVars, exprsFreeVars, idRuleVars )
+import CoreFVs ( exprFreeVars, exprsFreeVars, idFreeVars )
import CoreTidy ( tidyRules )
import CoreLint ( showPass, endPass )
import Rules ( addIdSpecialisations, mkLocalRule, lookupRule, emptyRuleBase, rulesOfBinds )
UniqSM, initUs_, thenUs, returnUs, getUniqueUs,
getUs, mapUs
)
-import Name ( nameOccName, mkSpecOcc, getSrcLoc )
+import Name
import MkId ( voidArgId, realWorldPrimId )
import FiniteMap
import Maybes ( catMaybes, maybeToBool )
specDefn subst calls (fn, rhs)
-- The first case is the interesting one
- | rhs_tyvars `lengthIs` n_tyvars -- Rhs of fn's defn has right number of big lambdas
- && rhs_bndrs `lengthAtLeast` n_dicts -- and enough dict args
+ | rhs_tyvars `lengthIs` n_tyvars -- Rhs of fn's defn has right number of big lambdas
+ && rhs_ids `lengthAtLeast` n_dicts -- and enough dict args
&& notNull calls_for_me -- And there are some calls to specialise
-- && not (certainlyWillInline (idUnfolding fn)) -- And it's not small
rhs_uds `plusUDs` plusUDList spec_uds)
| otherwise -- No calls or RHS doesn't fit our preconceptions
- = specExpr subst rhs `thenSM` \ (rhs', rhs_uds) ->
+ = WARN( notNull calls_for_me, ptext SLIT("Missed specialisation opportunity for") <+> ppr fn )
+ -- Note [Specialisation shape]
+ specExpr subst rhs `thenSM` \ (rhs', rhs_uds) ->
returnSM ((fn, rhs'), [], rhs_uds)
where
-- The rule to put in the function's specialisation is:
-- forall b,d, d1',d2'. f t1 b t3 d d1' d2' = f1 b d
spec_env_rule = mkLocalRule (mkFastString ("SPEC " ++ showSDoc (ppr fn)))
- AlwaysActive (idName fn)
+ inline_prag -- Note [Auto-specialisation and RULES]
+ (idName fn)
(poly_tyvars ++ rhs_dicts')
inst_args
(mkVarApps (Var spec_f) app_args)
where
my_zipEqual doc xs ys
- | not (equalLength xs ys) = pprPanic "my_zipEqual" (ppr xs $$ ppr ys $$ (ppr fn <+> ppr call_ts) $$ ppr rhs)
+#ifdef DEBUG
+ | not (equalLength xs ys) = pprPanic "my_zipEqual" (vcat
+ [ ppr xs, ppr ys
+ , ppr fn <+> ppr call_ts
+ , ppr (idType fn), ppr theta
+ , ppr n_dicts, ppr rhs_dicts
+ , ppr rhs])
+#endif
| otherwise = zipEqual doc xs ys
\end{code}
+Note [Auto-specialisation and RULES]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider:
+ g :: Num a => a -> a
+ g = ...
+
+ f :: (Int -> Int) -> Int
+ f w = ...
+ {-# RULE f g = 0 #-}
+
+Suppose that auto-specialisation makes a specialised version of
+g::Int->Int That version won't appear in the LHS of the RULE for f.
+So if the specialisation rule fires too early, the rule for f may
+never fire.
+
+It might be possible to add new rules, to "complete" the rewrite system.
+Thus when adding
+ RULE forall d. g Int d = g_spec
+also add
+ RULE f g_spec = 0
+
+But that's a bit complicated. For now we ask the programmer's help,
+by *copying the INLINE activation pragma* to the auto-specialised rule.
+So if g says {-# NOINLINE[2] g #-}, then the auto-spec rule will also
+not be active until phase 2.
+
+
+Note [Specialisation shape]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We only specialise a function if it has visible top-level lambdas
+corresponding to its overloading. E.g. if
+ f :: forall a. Eq a => ....
+then its body must look like
+ f = /\a. \d. ...
+
+Reason: when specialising the body for a call (f ty dexp), we want to
+substitute dexp for d, and pick up specialised calls in the body of f.
+
+This doesn't always work. One example I came across was htis:
+ newtype Gen a = MkGen{ unGen :: Int -> a }
+
+ choose :: Eq a => a -> Gen a
+ choose n = MkGen (\r -> n)
+
+ oneof = choose (1::Int)
+
+It's a silly exapmle, but we get
+ choose = /\a. g `cast` co
+where choose doesn't have any dict arguments. Thus far I have not
+tried to fix this (wait till there's a real example).
+
+
Note [Inline specialisations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We transfer to the specialised function any INLINE stuff from the
bndrs = map fst prs
rhs_fvs = unionVarSets (map pair_fvs prs)
-pair_fvs (bndr, rhs) = exprFreeVars rhs `unionVarSet` idRuleVars bndr
+pair_fvs (bndr, rhs) = exprFreeVars rhs `unionVarSet` idFreeVars bndr
-- Don't forget variables mentioned in the
-- rules of the bndr. C.f. OccAnal.addRuleUsage
-
+ -- Also tyvars mentioned in its type; they may not appear in the RHS
+ -- type T a = Int
+ -- x :: T a = 3
addDictBind (dict,rhs) uds = uds { dict_binds = mkDB (NonRec dict rhs) `consBag` dict_binds uds }
let
-- Give the new Id a similar occurrence name to the old one
name = idName old_id
- new_id = mkUserLocal (mkSpecOcc (nameOccName name)) uniq new_ty (getSrcLoc name)
+ new_id = mkUserLocal (mkSpecOcc (nameOccName name)) uniq new_ty (getSrcSpan name)
in
returnSM new_id
\end{code}