[project @ 2002-04-05 23:24:25 by sof]
[ghc-hetmet.git] / ghc / compiler / specialise / Specialise.lhs
index 76e3c3e..16d3748 100644 (file)
@@ -1,48 +1,47 @@
 %
-% (c) The GRASP/AQUA Project, Glasgow University, 1993-1996
+% (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
 %
 \section[Specialise]{Stamping out overloading, and (optionally) polymorphism}
 
 \begin{code}
-module Specialise (
-       specProgram
-    ) where
+module Specialise ( specProgram ) where
 
 #include "HsVersions.h"
 
-import Id              ( Id, DictVar, idType, mkUserLocal,
-
-                         getIdSpecialisation, addIdSpecialisation, isSpecPragmaId,
-
-                         IdSet, mkIdSet, addOneToIdSet, intersectIdSets, isEmptyIdSet, 
-                                emptyIdSet, unionIdSets, minusIdSet, unitIdSet, elementOfIdSet,
-
-                         IdEnv, mkIdEnv, lookupIdEnv, addOneToIdEnv
-                       )
-
-import Type            ( Type, mkTyVarTy, splitSigmaTy, instantiateTy, isDictTy,
-                         tyVarsOfType, tyVarsOfTypes, applyTys, mkForAllTys
-                       )
-import TyCon           ( TyCon )
-import TyVar           ( TyVar,
-                         TyVarSet, mkTyVarSet, isEmptyTyVarSet, intersectTyVarSets,
-                                   elementOfTyVarSet, unionTyVarSets, emptyTyVarSet,
-                         TyVarEnv, mkTyVarEnv 
+import CmdLineOpts     ( DynFlags, DynFlag(..) )
+import Id              ( Id, idName, idType, mkUserLocal, idSpecialisation, isDataConWrapId )
+import TcType          ( Type, mkTyVarTy, tcSplitSigmaTy, 
+                         tyVarsOfTypes, tyVarsOfTheta, 
+                         mkForAllTys, tcCmpType
                        )
-import CoreSyn 
-import OccurAnal       ( occurAnalyseGlobalExpr )
-import Name            ( NamedThing(..), getSrcLoc )
-import SpecEnv         ( addToSpecEnv )
+import Subst           ( Subst, mkSubst, substTy, mkSubst, extendSubstList, mkInScopeSet,
+                         simplBndr, simplBndrs, 
+                         substAndCloneId, substAndCloneIds, substAndCloneRecIds,
+                         lookupIdSubst, substInScope
+                       ) 
+import Var             ( zapSpecPragmaId )
+import VarSet
+import VarEnv
+import CoreSyn
+import CoreUtils       ( applyTypeToArgs )
+import CoreFVs         ( exprFreeVars, exprsFreeVars )
+import CoreTidy                ( pprTidyIdRules )
+import CoreLint                ( showPass, endPass )
+import Rules           ( addIdSpecialisations, lookupRule )
 
 import UniqSupply      ( UniqSupply,
-                         UniqSM, initUs, thenUs, returnUs, getUnique, mapUs
+                         UniqSM, initUs_, thenUs, returnUs, getUniqueUs, 
+                         getUs, mapUs
                        )
-
+import Name            ( nameOccName, mkSpecOcc, getSrcLoc )
 import FiniteMap
-import Maybes          ( MaybeErr(..) )
+import Maybes          ( catMaybes, maybeToBool )
+import ErrUtils                ( dumpIfSet_dyn )
+import BasicTypes      ( Activation( AlwaysActive ) )
 import Bag
 import List            ( partition )
-import Util            ( zipEqual )
+import Util            ( zipEqual, zipWithEqual, cmpList, lengthIs,
+                         equalLength, lengthAtLeast, notNull )
 import Outputable
 
 
@@ -56,10 +55,9 @@ infixr 9 `thenSM`
 %************************************************************************
 
 These notes describe how we implement specialisation to eliminate
-overloading, and optionally to eliminate unboxed polymorphism, and
-full polymorphism.
+overloading.
 
-The specialisation pass is a partial evaluator which works on Core
+The specialisation pass works on Core
 syntax, complete with all the explicit dictionary application,
 abstraction and construction as added by the type checker.  The
 existing type checker remains largely as it is.
@@ -97,12 +95,6 @@ applications could only arise as a result of transformation, and even
 then I think it's unlikely.  In any case, we simply don't accumulate such
 partial applications.)
 
-There's a choice of whether to collect details of all *polymorphic* functions
-or simply all *overloaded* ones.  How to sort this out?
-  Pass in a predicate on the function to say if it is "interesting"?
-  This is dependent on the user flags: SpecialiseOverloaded
-                                      SpecialiseUnboxed
-                                      SpecialiseAll
 
 STEP 2: EQUIVALENCES
 
@@ -123,12 +115,12 @@ and create a local instance of f, defined thus:
 
        f@t1/t2 = <f_rhs> t1 t2 d1 d2
 
-(f_rhs presumably has some big lambdas and dictionary lambdas, so lots
-of simplification will now result.)  Then we should recursively do
-everything again.
-
-The new id has its own unique, but its print-name (if exported) has
-an explicit representation of the instance types t1/t2.
+f_rhs presumably has some big lambdas and dictionary lambdas, so lots
+of simplification will now result.  However we don't actually *do* that
+simplification.  Rather, we leave it for the simplifier to do.  If we
+*did* do it, though, we'd get more call instances from the specialised
+RHS.  We can work out what they are by instantiating the call-instance
+set from f's RHS with the types t1, t2.
 
 Add this new id to f's IdInfo, to record that f has a specialised version.
 
@@ -155,8 +147,8 @@ becomes
                         in
                         fl
 
-We still have recusion for non-overloadd functions which we
-speciailise, but the recursive call should get speciailised to the
+We still have recusion for non-overloaded functions which we
+speciailise, but the recursive call should get specialised to the
 same recursive version.
 
 
@@ -238,22 +230,23 @@ polymorphic versions.  Thus:
 
        f@t1/ = /\b -> <f_rhs> t1 b d1 d2
 
-This seems pretty simple, and a Good Thing.
+We do this.
 
-Polymorphism 3 -- Unboxed
-~~~~~~~~~~~~~~
 
-If we are speciailising at unboxed types we must speciailise
-regardless of the overloading constraint.  In the exaple above it is
-worth speciailising at types Int/Int#, Int/Bool# and a/Int#, Int#/Int#
-etc.
+Dictionary floating
+~~~~~~~~~~~~~~~~~~~
+Consider this
 
-Note that specialising an overloaded type at an uboxed type requires
-an unboxed instance -- we cannot default to an unspecialised version!
+       f a (d::Num a) = let g = ...
+                        in
+                        ...(let d1::Ord a = Num.Ord.sel a d in g a d1)...
 
+Here, g is only called at one type, but the dictionary isn't in scope at the
+definition point for g.  Usually the type checker would build a
+definition for d1 which enclosed g, but the transformation system
+might have moved d1's defn inward.  Solution: float dictionary bindings
+outwards along with call instances.
 
-Dictionary floating
-~~~~~~~~~~~~~~~~~~~
 Consider
 
        f x = let g p q = p==q
@@ -282,11 +275,6 @@ at the defn of g.  Instead, we have to float out the (new) defn of deq
 to widen its scope.  Notice that this floating can't be done in advance -- it only
 shows up when specialisation is done.
 
-DELICATE MATTER: the way we tell a dictionary binding is by looking to
-see if it has a Dict type.  If the type has been "undictify'd", so that
-it looks like a tuple, then the dictionary binding won't be floated, and
-an opportunity to specialise might be lost.
-
 User SPECIALIZE pragmas
 ~~~~~~~~~~~~~~~~~~~~~~~
 Specialisation pragmas can be digested by the type checker, and implemented
@@ -358,13 +346,6 @@ ordinary function definitions:
 The information about what instance of the dfun exist gets added to
 the dfun's IdInfo in the same way as a user-defined function too.
 
-In fact, matters are a little bit more complicated than this.
-When we make one of these specialised instances, we are defining
-a constant dictionary, and so we want immediate access to its constant
-methods and superclasses.  Indeed, these constant methods and superclasses
-must be in the IdInfo for the class selectors!  We need help from the
-typechecker to sort this out, perhaps by generating a separate IdInfo
-for each.
 
 Automatic instance decl specialisation?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -375,6 +356,9 @@ for ordinary functions; and when we specialised their bodies, we might get
 new call-instances of the dfuns, and so on.  This all arises because of
 the unrestricted mutual recursion between instance decls and value decls.
 
+Still, there's no actual problem; it just means that we may not do all
+the specialisation we could theoretically do.
+
 Furthermore, instance decls are usually exported and used non-locally,
 so we'll want to compile enough to get those specialisations done.
 
@@ -383,33 +367,6 @@ survive solely by spitting out *usage* information, and then reading that
 back in as a pragma when next compiling the file.  So for now,
 we only specialise instance decls in response to pragmas.
 
-That means that even if an instance decl ain't otherwise exported it
-needs to be spat out as with a SPECIALIZE pragma.  Furthermore, it needs
-something to say which module defined the instance, so the usage info
-can be fed into the right reqts info file.  Blegh.
-
-
-SPECIAILISING DATA DECLARATIONS
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-With unboxed specialisation (or full specialisation) we also require
-data types (and their constructors) to be speciailised on unboxed
-type arguments.
-
-In addition to normal call instances we gather TyCon call instances at
-unboxed types, determine equivalence classes for the locally defined
-TyCons and build speciailised data constructor Ids for each TyCon and
-substitute these in the Con calls.
-
-We need the list of local TyCons to partition the TyCon instance info.
-We pass out a FiniteMap from local TyCons to Specialised Instances to
-give to the interface and code genertors.
-
-N.B. The specialised data constructors reference the original data
-constructor and type constructor which do not have the updated
-specialisation info attached.  Any specialisation info must be
-extracted from the TyCon map returned.
-
 
 SPITTING OUT USAGE INFORMATION
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -421,6 +378,8 @@ and data types. Then we equivalence-class it and spit it out.
 This is done at the top-level when all the call instances which escape
 must be for imported functions and data types.
 
+*** Not currently done ***
+
 
 Partial specialisation by pragmas
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -483,25 +442,27 @@ response to a SPECIALIZE pragma on the Eq [a] instance decl.
 In short, dfun Ids need IdInfo with a specialisation for each
 constant instance of their instance declaration.
 
+All this uses a single mechanism: the SpecEnv inside an Id
+
 
 What does the specialisation IdInfo look like?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-       SpecInfo
-               [Maybe Type] -- Instance types
-               Int             -- No of dicts to eat
-               Id              -- Specialised version
+The SpecEnv of an Id maps a list of types (the template) to an expression
+
+       [Type]  |->  Expr
 
 For example, if f has this SpecInfo:
 
-       SpecInfo [Just t1, Nothing, Just t3] 2 f'
+       [Int, a]  ->  \d:Ord Int. f' a
+
+it means that we can replace the call
 
-then
+       f Int t  ===>  (\d. f' t)
 
-       f t1 t2 t3 d1 d2  ===>  f t2
+This chucks one dictionary away and proceeds with the
+specialised version of f, namely f'.
 
-The "Nothings" identify type arguments in which the specialised
-version is polymorphic.
 
 What can't be done this way?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -526,98 +487,6 @@ Still, this is no great hardship, because we intend to eliminate
 overloading altogether anyway!
 
 
-Mutter mutter
-~~~~~~~~~~~~~
-What about types/classes mentioned in SPECIALIZE pragmas spat out,
-but not otherwise exported.  Even if they are exported, what about
-their original names.
-
-Suggestion: use qualified names in pragmas, omitting module for
-prelude and "this module".
-
-
-Mutter mutter 2
-~~~~~~~~~~~~~~~
-Consider this
-
-       f a (d::Num a) = let g = ...
-                        in
-                        ...(let d1::Ord a = Num.Ord.sel a d in g a d1)...
-
-Here, g is only called at one type, but the dictionary isn't in scope at the
-definition point for g.  Usually the type checker would build a
-definition for d1 which enclosed g, but the transformation system
-might have moved d1's defn inward.
-
-
-Unboxed bindings
-~~~~~~~~~~~~~~~~
-
-What should we do when a value is specialised to a *strict* unboxed value?
-
-       map_*_* f (x:xs) = let h = f x
-                              t = map f xs
-                          in h:t
-
-Could convert let to case:
-
-       map_*_Int# f (x:xs) = case f x of h# ->
-                             let t = map f xs
-                             in h#:t
-
-This may be undesirable since it forces evaluation here, but the value
-may not be used in all branches of the body. In the general case this
-transformation is impossible since the mutual recursion in a letrec
-cannot be expressed as a case.
-
-There is also a problem with top-level unboxed values, since our
-implementation cannot handle unboxed values at the top level.
-
-Solution: Lift the binding of the unboxed value and extract it when it
-is used:
-
-       map_*_Int# f (x:xs) = let h = case (f x) of h# -> _Lift h#
-                                 t = map f xs
-                             in case h of
-                                _Lift h# -> h#:t
-
-Now give it to the simplifier and the _Lifting will be optimised away.
-
-The benfit is that we have given the specialised "unboxed" values a
-very simplep lifted semantics and then leave it up to the simplifier to
-optimise it --- knowing that the overheads will be removed in nearly
-all cases.
-
-In particular, the value will only be evaluted in the branches of the
-program which use it, rather than being forced at the point where the
-value is bound. For example:
-
-       filtermap_*_* p f (x:xs)
-         = let h = f x
-               t = ...
-           in case p x of
-               True  -> h:t
-               False -> t
-   ==>
-       filtermap_*_Int# p f (x:xs)
-         = let h = case (f x) of h# -> _Lift h#
-               t = ...
-           in case p x of
-               True  -> case h of _Lift h#
-                          -> h#:t
-               False -> t
-
-The binding for h can still be inlined in the one branch and the
-_Lifting eliminated.
-
-
-Question: When won't the _Lifting be eliminated?
-
-Answer: When they at the top-level (where it is necessary) or when
-inlining would duplicate work (or possibly code depending on
-options). However, the _Lifting will still be eliminated if the
-strictness analyser deems the lifted binding strict.
-
 
 A note about non-tyvar dictionaries
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -706,15 +575,31 @@ Hence, the invariant is this:
 %************************************************************************
 
 \begin{code}
-specProgram :: UniqSupply -> [CoreBinding] -> [CoreBinding]
-specProgram us binds
-  = initSM us (go binds        `thenSM` \ (binds', _) ->
-              returnSM binds'
-             )
+specProgram :: DynFlags -> UniqSupply -> [CoreBind] -> IO [CoreBind]
+specProgram dflags us binds
+  = do
+       showPass dflags "Specialise"
+
+       let binds' = initSM us (go binds        `thenSM` \ (binds', uds') ->
+                               returnSM (dumpAllDictBinds uds' binds'))
+
+       endPass dflags "Specialise" Opt_D_dump_spec binds'
+
+       dumpIfSet_dyn dflags Opt_D_dump_rules "Top-level specialisations"
+                 (vcat (map pprTidyIdRules (concat (map bindersOf binds'))))
+
+       return binds'
   where
+       -- We need to start with a Subst that knows all the things
+       -- that are in scope, so that the substitution engine doesn't
+       -- accidentally re-use a unique that's already in use
+       -- Easiest thing is to do it all at once, as if all the top-level
+       -- decls were mutually recursive
+    top_subst      = mkSubst (mkInScopeSet (mkVarSet (bindersOfBinds binds))) emptySubstEnv
+
     go []          = returnSM ([], emptyUDs)
-    go (bind:binds) = go binds                 `thenSM` \ (binds', uds) ->
-                     specBind bind uds `thenSM` \ (bind', uds') ->
+    go (bind:binds) = go binds                                 `thenSM` \ (binds', uds) ->
+                     specBind top_subst bind uds       `thenSM` \ (bind', uds') ->
                      returnSM (bind' ++ binds', uds')
 \end{code}
 
@@ -725,91 +610,88 @@ specProgram us binds
 %************************************************************************
 
 \begin{code}
-specExpr :: CoreExpr -> SpecM (CoreExpr, UsageDetails)
+specVar :: Subst -> Id -> CoreExpr
+specVar subst v = case lookupIdSubst subst v of
+                       DoneEx e   -> e
+                       DoneId v _ -> Var v
+
+specExpr :: Subst -> CoreExpr -> SpecM (CoreExpr, UsageDetails)
+-- We carry a substitution down:
+--     a) we must clone any binding that might flaot outwards,
+--        to avoid name clashes
+--     b) we carry a type substitution to use when analysing
+--        the RHS of specialised bindings (no type-let!)
 
 ---------------- First the easy cases --------------------
-specExpr e@(Var _)    = returnSM (e, emptyUDs)
-specExpr e@(Lit _)    = returnSM (e, emptyUDs)
-specExpr e@(Con _ _)  = returnSM (e, emptyUDs)
-specExpr e@(Prim _ _) = returnSM (e, emptyUDs)
-
-specExpr (Coerce co ty body)
-  = specExpr body      `thenSM` \ (body', uds) ->
-    returnSM (Coerce co ty body', uds)
+specExpr subst (Type ty) = returnSM (Type (substTy subst ty), emptyUDs)
+specExpr subst (Var v)   = returnSM (specVar subst v,         emptyUDs)
+specExpr subst (Lit lit) = returnSM (Lit lit,                emptyUDs)
 
-specExpr (SCC cc body)
-  = specExpr body      `thenSM` \ (body', uds) ->
-    returnSM (SCC cc body', uds)
+specExpr subst (Note note body)
+  = specExpr subst body        `thenSM` \ (body', uds) ->
+    returnSM (Note (specNote subst note) body', uds)
 
 
 ---------------- Applications might generate a call instance --------------------
-specExpr e@(App fun arg)
-  = go fun [arg]
+specExpr subst expr@(App fun arg)
+  = go expr []
   where
-    go (App fun arg) args = go fun (arg:args)
-    go (Var f)       args = returnSM (e, mkCallUDs f args)
-    go other        args = specExpr other      `thenSM` \ (e', uds) ->
-                           returnSM (foldl App e' args, uds)
+    go (App fun arg) args = specExpr subst arg `thenSM` \ (arg', uds_arg) ->
+                           go fun (arg':args)  `thenSM` \ (fun', uds_app) ->
+                           returnSM (App fun' arg', uds_arg `plusUDs` uds_app)
+
+    go (Var f)       args = case specVar subst f of
+                               Var f' -> returnSM (Var f', mkCallUDs subst f' args)
+                               e'     -> returnSM (e', emptyUDs)       -- I don't expect this!
+    go other        args = specExpr subst other
 
 ---------------- Lambda/case require dumping of usage details --------------------
-specExpr e@(Lam _ _)
-  = specExpr body      `thenSM` \ (body', uds) ->
+specExpr subst e@(Lam _ _)
+  = specExpr subst' body       `thenSM` \ (body', uds) ->
     let
-       (filtered_uds, body'') = dumpUDs bndrs uds body'
+       (filtered_uds, body'') = dumpUDs bndrs' uds body'
     in
-    returnSM (foldr Lam body'' bndrs, filtered_uds)
+    returnSM (mkLams bndrs' body'', filtered_uds)
   where
-    (bndrs, body) = go [] e
-
+    (bndrs, body) = collectBinders e
+    (subst', bndrs') = simplBndrs subst bndrs
        -- More efficient to collect a group of binders together all at once
-    go bndrs (Lam bndr e) = go (bndr:bndrs) e
-    go bndrs e            = (reverse bndrs, e)
+       -- and we don't want to split a lambda group with dumped bindings
 
-
-specExpr (Case scrut alts)
-  = specExpr scrut     `thenSM` \ (scrut', uds_scrut) ->
-    spec_alts alts     `thenSM` \ (alts', uds_alts) ->
-    returnSM (Case scrut' alts', uds_scrut `plusUDs` uds_alts)
+specExpr subst (Case scrut case_bndr alts)
+  = specExpr subst scrut                       `thenSM` \ (scrut', uds_scrut) ->
+    mapAndCombineSM spec_alt alts      `thenSM` \ (alts', uds_alts) ->
+    returnSM (Case scrut' case_bndr' alts', uds_scrut `plusUDs` uds_alts)
   where
-    spec_alts (AlgAlts alts deflt)
-       = mapAndCombineSM spec_alg_alt alts     `thenSM` \ (alts', uds1) ->
-         spec_deflt deflt                      `thenSM` \ (deflt', uds2) ->
-         returnSM (AlgAlts alts' deflt', uds1 `plusUDs` uds2)
-
-    spec_alts (PrimAlts alts deflt)
-       = mapAndCombineSM spec_prim_alt alts    `thenSM` \ (alts', uds1) ->
-         spec_deflt deflt                      `thenSM` \ (deflt', uds2) ->
-         returnSM (PrimAlts alts' deflt', uds1 `plusUDs` uds2)
-
-    spec_alg_alt (con, args, rhs)
-       = specExpr rhs          `thenSM` \ (rhs', uds) ->
-         let
-            (uds', rhs'') = dumpUDs (map ValBinder args) uds rhs'
-         in
-         returnSM ((con, args, rhs''), uds')
+    (subst_alt, case_bndr') = simplBndr subst case_bndr
+       -- No need to clone case binder; it can't float like a let(rec)
 
-    spec_prim_alt (lit, rhs)
-       = specExpr rhs          `thenSM` \ (rhs', uds) ->
-         returnSM ((lit, rhs'), uds)
-
-    spec_deflt NoDefault = returnSM (NoDefault, emptyUDs)
-    spec_deflt (BindDefault arg rhs)
-       = specExpr rhs          `thenSM` \ (rhs', uds) ->
+    spec_alt (con, args, rhs)
+       = specExpr subst_rhs rhs                `thenSM` \ (rhs', uds) ->
          let
-            (uds', rhs'') = dumpUDs [ValBinder arg] uds rhs'
+            (uds', rhs'') = dumpUDs args uds rhs'
          in
-         returnSM (BindDefault arg rhs'', uds')
+         returnSM ((con, args', rhs''), uds')
+       where
+         (subst_rhs, args') = simplBndrs subst_alt args
 
 ---------------- Finally, let is the interesting case --------------------
-specExpr (Let bind body)
-  =    -- Deal with the body
-    specExpr body                              `thenSM` \ (body', body_uds) ->
+specExpr subst (Let bind body)
+  =    -- Clone binders
+    cloneBindSM subst bind                     `thenSM` \ (rhs_subst, body_subst, bind') ->
+       
+       -- Deal with the body
+    specExpr body_subst body                   `thenSM` \ (body', body_uds) ->
 
        -- Deal with the bindings
-    specBind bind body_uds                     `thenSM` \ (binds', uds) ->
+    specBind rhs_subst bind' body_uds          `thenSM` \ (binds', uds) ->
 
        -- All done
     returnSM (foldr Let body' binds', uds)
+
+-- Must apply the type substitution to coerceions
+specNote subst (Coerce t1 t2) = Coerce (substTy subst t1) (substTy subst t2)
+specNote subst note          = note
 \end{code}
 
 %************************************************************************
@@ -819,156 +701,220 @@ specExpr (Let bind body)
 %************************************************************************
 
 \begin{code}
-specBind :: CoreBinding
+specBind :: Subst                      -- Use this for RHSs
+        -> CoreBind
         -> UsageDetails                -- Info on how the scope of the binding
-        -> SpecM ([CoreBinding],       -- New bindings
+        -> SpecM ([CoreBind],          -- New bindings
                   UsageDetails)        -- And info to pass upstream
 
-specBind (NonRec bndr rhs) body_uds
-  | isDictTy (idType bndr)
-  =    -- It's a dictionary binding
-       -- Pick it up and float it outwards.
-    specExpr rhs                               `thenSM` \ (rhs', rhs_uds) ->
+specBind rhs_subst bind body_uds
+  = specBindItself rhs_subst bind (calls body_uds)     `thenSM` \ (bind', bind_uds) ->
     let
-       all_uds = rhs_uds `plusUDs` addDictBind body_uds bndr rhs'
+       bndrs   = bindersOf bind
+       all_uds = zapCalls bndrs (body_uds `plusUDs` bind_uds)
+                       -- It's important that the `plusUDs` is this way round,
+                       -- because body_uds may bind dictionaries that are
+                       -- used in the calls passed to specDefn.  So the
+                       -- dictionary bindings in bind_uds may mention 
+                       -- dictionaries bound in body_uds.
     in
-    returnSM ([], all_uds)
+    case splitUDs bndrs all_uds of
+
+       (_, ([],[]))    -- This binding doesn't bind anything needed
+                       -- in the UDs, so put the binding here
+                       -- This is the case for most non-dict bindings, except
+                       -- for the few that are mentioned in a dict binding
+                       -- that is floating upwards in body_uds
+               -> returnSM ([bind'], all_uds)
+
+       (float_uds, (dict_binds, calls))        -- This binding is needed in the UDs, so float it out
+               -> returnSM ([], float_uds `plusUDs` mkBigUD bind' dict_binds calls)
+   
+
+-- A truly gruesome function
+mkBigUD bind@(NonRec _ _) dbs calls
+  =    -- Common case: non-recursive and no specialisations
+       -- (if there were any specialistions it would have been made recursive)
+    MkUD { dict_binds = listToBag (mkDB bind : dbs),
+          calls = listToCallDetails calls }
+
+mkBigUD bind dbs calls
+  =    -- General case
+    MkUD { dict_binds = unitBag (mkDB (Rec (bind_prs bind ++ dbsToPairs dbs))),
+                       -- Make a huge Rec
+          calls = listToCallDetails calls }
+  where
+    bind_prs (NonRec b r) = [(b,r)]
+    bind_prs (Rec prs)    = prs
 
-  | isSpecPragmaId bndr
-       -- SpecPragmaIds are there solely to generate specialisations
-       -- Just drop the whole binding; keep only its usage details
-  = specExpr rhs                               `thenSM` \ (rhs', rhs_uds) ->
-    returnSM ([], rhs_uds `plusUDs` body_uds)
+    dbsToPairs []             = []
+    dbsToPairs ((bind,_):dbs) = bind_prs bind ++ dbsToPairs dbs
 
-  | otherwise
-  =   -- Deal with the RHS, specialising it according
-      -- to the calls found in the body
-    specDefn (calls body_uds) (bndr,rhs)       `thenSM` \ ((bndr',rhs'), spec_defns, spec_uds) ->
+-- specBindItself deals with the RHS, specialising it according
+-- to the calls found in the body (if any)
+specBindItself rhs_subst (NonRec bndr rhs) call_info
+  = specDefn rhs_subst call_info (bndr,rhs)    `thenSM` \ ((bndr',rhs'), spec_defns, spec_uds) ->
     let
-       (all_uds, (dict_binds, dump_calls)) 
-               = splitUDs [ValBinder bndr'] (spec_uds `plusUDs` body_uds)
+        new_bind | null spec_defns = NonRec bndr' rhs'
+                 | otherwise       = Rec ((bndr',rhs'):spec_defns)
+               -- bndr' mentions the spec_defns in its SpecEnv
+               -- Not sure why we couln't just put the spec_defns first
     in
-    returnSM (    [NonRec bndr' rhs']
-              ++ dict_binds
-              ++ spec_defns,
-              all_uds )
+    returnSM (new_bind, spec_uds)
 
-specBind (Rec pairs) body_uds
-  = mapSM (specDefn (calls body_uds)) pairs    `thenSM` \ stuff ->
+specBindItself rhs_subst (Rec pairs) call_info
+  = mapSM (specDefn rhs_subst call_info) pairs `thenSM` \ stuff ->
     let
        (pairs', spec_defns_s, spec_uds_s) = unzip3 stuff
        spec_defns = concat spec_defns_s
        spec_uds   = plusUDList spec_uds_s
-       (all_uds, (dict_binds, dump_calls)) 
-               = splitUDs (map (ValBinder . fst) pairs') (spec_uds `plusUDs` body_uds) 
+        new_bind   = Rec (spec_defns ++ pairs')
     in
-    returnSM (    [Rec pairs']
-               ++ dict_binds
-               ++ spec_defns,
-               all_uds )
+    returnSM (new_bind, spec_uds)
     
-specDefn :: CallDetails                        -- Info on how it is used in its scope
+
+specDefn :: Subst                      -- Subst to use for RHS
+        -> CallDetails                 -- Info on how it is used in its scope
         -> (Id, CoreExpr)              -- The thing being bound and its un-processed RHS
         -> SpecM ((Id, CoreExpr),      -- The thing and its processed RHS
                                        --      the Id may now have specialisations attached
-                  [CoreBinding],       -- Extra, specialised bindings
+                  [(Id,CoreExpr)],     -- Extra, specialised bindings
                   UsageDetails         -- Stuff to fling upwards from the RHS and its
            )                           --      specialised versions
 
-specDefn calls (fn, rhs)
+specDefn subst calls (fn, rhs)
        -- The first case is the interesting one
-  |  n_tyvars == length rhs_tyvars     -- Rhs of fn's defn has right number of big lambdas
-  && n_dicts  <= length rhs_bndrs      -- and enough dict args
-  && not (null calls_for_me)           -- And there are some calls to specialise
+  |  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
+  && notNull calls_for_me              -- And there are some calls to specialise
+  && not (isDataConWrapId fn)          -- And it's not a data con wrapper, which have
+                                       -- stupid overloading that simply discard the dictionary
+
+-- At one time I tried not specialising small functions
+-- but sometimes there are big functions marked INLINE
+-- that we'd like to specialise.  In particular, dictionary
+-- functions, which Marcin is keen to inline
+--  && not (certainlyWillInline fn)    -- And it's not small
+                                       -- If it's small, it's better just to inline
+                                       -- it than to construct lots of specialisations
   =   -- Specialise the body of the function
-    specExpr body                                      `thenSM` \ (body', body_uds) ->
-    let
-       (float_uds, bound_uds@(dict_binds,_)) = splitUDs rhs_bndrs body_uds
-    in
+    specExpr subst rhs                                 `thenSM` \ (rhs', rhs_uds) ->
 
       -- Make a specialised version for each call in calls_for_me
-    mapSM (spec_call bound_uds) calls_for_me           `thenSM` \ stuff ->
+    mapSM spec_call calls_for_me               `thenSM` \ stuff ->
     let
-       (spec_defns, spec_uds, spec_env_stuff) = unzip3 stuff
+       (spec_defns, spec_uds, spec_rules) = unzip3 stuff
 
-       fn'  = addIdSpecialisations fn spec_env_stuff
-       rhs' = foldr Lam (foldr Let body' dict_binds) rhs_bndrs 
+       fn' = addIdSpecialisations zapped_fn spec_rules
     in
     returnSM ((fn',rhs'), 
              spec_defns, 
-             float_uds `plusUDs` plusUDList spec_uds)
+             rhs_uds `plusUDs` plusUDList spec_uds)
 
   | otherwise  -- No calls or RHS doesn't fit our preconceptions
-  = specExpr rhs                       `thenSM` \ (rhs', rhs_uds) ->
-    returnSM ((fn, rhs'), [], rhs_uds)
+  = specExpr subst rhs                 `thenSM` \ (rhs', rhs_uds) ->
+    returnSM ((zapped_fn, rhs'), [], rhs_uds)
   
   where
-    fn_type              = idType fn
-    (tyvars, theta, tau)  = splitSigmaTy fn_type
-    n_tyvars             = length tyvars
-    n_dicts              = length theta
+    zapped_fn           = zapSpecPragmaId fn
+       -- If the fn is a SpecPragmaId, make it discardable
+       -- It's role as a holder for a call instance is o'er
+       -- But it might be alive for some other reason by now.
+
+    fn_type           = idType fn
+    (tyvars, theta, _) = tcSplitSigmaTy fn_type
+    n_tyvars          = length tyvars
+    n_dicts           = length theta
+
+       -- It's important that we "see past" any INLINE pragma
+       -- else we'll fail to specialise an INLINE thing
+    (inline_me, rhs')              = dropInline rhs
+    (rhs_tyvars, rhs_ids, rhs_body) = collectTyAndValBinders rhs'
 
-    (rhs_tyvars, rhs_ids, rhs_body) = collectBinders rhs
     rhs_dicts = take n_dicts rhs_ids
-    rhs_bndrs = map TyBinder rhs_tyvars ++ map ValBinder rhs_dicts
-    body      = mkValLam (drop n_dicts rhs_ids) rhs_body
+    rhs_bndrs = rhs_tyvars ++ rhs_dicts
+    body      = mkLams (drop n_dicts rhs_ids) rhs_body
                -- Glue back on the non-dict lambdas
 
     calls_for_me = case lookupFM calls fn of
                        Nothing -> []
                        Just cs -> fmToList cs
 
+    ----------------------------------------------------------
        -- Specialise to one particular call pattern
-    spec_call :: ProtoUsageDetails          -- From the original body, captured by
-                                           -- the dictionary lambdas
-              -> ([Maybe Type], [DictVar])  -- Call instance
-              -> SpecM (CoreBinding,             -- Specialised definition
-                       UsageDetails,             -- Usage details from specialised body
-                       ([Type], CoreExpr))       -- Info for the Id's SpecEnv
-    spec_call bound_uds (call_ts, call_ds)
-      = ASSERT( length call_ts == n_tyvars && length call_ds == n_dicts )
+    spec_call :: (CallKey, ([DictExpr], VarSet))       -- Call instance
+              -> SpecM ((Id,CoreExpr),                 -- Specialised definition
+                       UsageDetails,                   -- Usage details from specialised body
+                       CoreRule)                       -- Info for the Id's SpecEnv
+    spec_call (CallKey call_ts, (call_ds, call_fvs))
+      = ASSERT( call_ts `lengthIs` n_tyvars  && call_ds `lengthIs` n_dicts )
                -- Calls are only recorded for properly-saturated applications
        
-        -- Supppose the call is for f [Just t1, Nothing, Just t3, Nothing] [d1, d2]
-
-               -- Construct the new binding
-               --      f1 = /\ b d -> (..rhs of f..) t1 b t3 d d1 d2
-               -- and the type of this binder
+       -- Suppose f's defn is  f = /\ a b c d -> \ d1 d2 -> rhs        
+        -- Supppose the call is for f [Just t1, Nothing, Just t3, Nothing] [dx1, dx2]
+
+       -- Construct the new binding
+       --      f1 = SUBST[a->t1,c->t3, d1->d1', d2->d2'] (/\ b d -> rhs)
+       -- PLUS the usage-details
+       --      { d1' = dx1; d2' = dx2 }
+       -- where d1', d2' are cloned versions of d1,d2, with the type substitution applied.
+       --
+       -- Note that the substitution is applied to the whole thing.
+       -- This is convenient, but just slightly fragile.  Notably:
+       --      * There had better be no name clashes in a/b/c/d
+       --
         let
-           spec_tyvars = [tyvar | (tyvar, Nothing) <- tyvars `zip` call_ts]
-          spec_tys    = zipWith mk_spec_ty call_ts tyvars
-          spec_rhs    = mkTyLam spec_tyvars $
-                         mkGenApp rhs (map TyArg spec_tys ++ map VarArg call_ds)
-          spec_id_ty  = mkForAllTys spec_tyvars (applyTys fn_type spec_tys)
-
-           mk_spec_ty (Just ty) _     = ty
-           mk_spec_ty Nothing   tyvar = mkTyVarTy tyvar
+               -- poly_tyvars = [b,d] in the example above
+               -- spec_tyvars = [a,c] 
+               -- ty_args     = [t1,b,t3,d]
+          poly_tyvars = [tv | (tv, Nothing) <- rhs_tyvars `zip` call_ts]
+           spec_tyvars = [tv | (tv, Just _)  <- rhs_tyvars `zip` call_ts]
+          ty_args     = zipWithEqual "spec_call" mk_ty_arg rhs_tyvars call_ts
+                      where
+                        mk_ty_arg rhs_tyvar Nothing   = Type (mkTyVarTy rhs_tyvar)
+                        mk_ty_arg rhs_tyvar (Just ty) = Type ty
+          rhs_subst  = extendSubstList subst spec_tyvars [DoneTy ty | Just ty <- call_ts]
        in
-       newIdSM fn spec_id_ty           `thenSM` \ spec_f ->
-
-
-               -- Construct the stuff for f's spec env
-               --      [t1,b,t3,d]  |->  \d1 d2 -> f1 b d
+       cloneBinders rhs_subst rhs_dicts                `thenSM` \ (rhs_subst', rhs_dicts') ->
        let
-          spec_env_rhs  = mkValLam call_ds $
-                          mkTyApp (Var spec_f) $
-                          map mkTyVarTy spec_tyvars
-           spec_env_info = (spec_tys, spec_env_rhs)
-        in
+          inst_args = ty_args ++ map Var rhs_dicts'
 
-               -- Specialise the UDs from f's RHS
-       let
-          tv_env   = [ (rhs_tyvar,ty) 
-                     | (rhs_tyvar, Just ty) <- zipEqual "specUDs1" rhs_tyvars call_ts
-                     ]
-          dict_env = zipEqual "specUDs2" rhs_dicts call_ds
+               -- Figure out the type of the specialised function
+          spec_id_ty = mkForAllTys poly_tyvars (applyTypeToArgs rhs fn_type inst_args)
        in
-        specUDs tv_env dict_env bound_uds                      `thenSM` \ spec_uds ->
+       newIdSM fn spec_id_ty                           `thenSM` \ spec_f ->
+       specExpr rhs_subst' (mkLams poly_tyvars body)   `thenSM` \ (spec_rhs, rhs_uds) ->       
+       let
+               -- 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 = Rule (_PK_ ("SPEC " ++ showSDoc (ppr fn)))
+                               AlwaysActive
+                               (poly_tyvars ++ rhs_dicts')
+                               inst_args 
+                               (mkTyApps (Var spec_f) (map mkTyVarTy poly_tyvars))
+
+               -- Add the { d1' = dx1; d2' = dx2 } usage stuff
+          final_uds = foldr addDictBind rhs_uds (my_zipEqual "spec_call" rhs_dicts' call_ds)
+
+       -- NOTE: we don't add back in any INLINE pragma on the RHS, so even if
+       -- the original function said INLINE, the specialised copies won't.
+       -- The idea is that the point of inlining was precisely to specialise
+       -- the function at its call site, and that's not so important for the
+       -- specialised copies.   But it still smells like an ad hoc decision.
 
-        returnSM (NonRec spec_f spec_rhs,
-                 spec_uds,
-                 spec_env_info
-       )
+       in
+        returnSM ((spec_f, spec_rhs),  
+                 final_uds,
+                 spec_env_rule)
+
+      where
+       my_zipEqual doc xs ys 
+        | not (equalLength xs ys) = pprPanic "my_zipEqual" (ppr xs $$ ppr ys $$ (ppr fn <+> ppr call_ts) $$ ppr rhs)
+        | otherwise               = zipEqual doc xs ys
+
+dropInline :: CoreExpr -> (Bool, CoreExpr) 
+dropInline (Note InlineMe rhs) = (True, rhs)
+dropInline rhs                = (False, rhs)
 \end{code}
 
 %************************************************************************
@@ -978,66 +924,108 @@ specDefn calls (fn, rhs)
 %************************************************************************
 
 \begin{code}
-type FreeDicts = IdSet
-
 data UsageDetails 
   = MkUD {
-       dict_binds :: !(Bag (DictVar, CoreExpr, TyVarSet, FreeDicts)),
+       dict_binds :: !(Bag DictBind),
                        -- Floated dictionary bindings
                        -- The order is important; 
                        -- in ds1 `union` ds2, bindings in ds2 can depend on those in ds1
                        -- (Remember, Bags preserve order in GHC.)
-                       -- The FreeDicts is the free vars of the RHS
 
        calls     :: !CallDetails
     }
 
+type DictBind = (CoreBind, VarSet)
+       -- The set is the free vars of the binding
+       -- both tyvars and dicts
+
+type DictExpr = CoreExpr
+
 emptyUDs = MkUD { dict_binds = emptyBag, calls = emptyFM }
 
-type ProtoUsageDetails = ([CoreBinding],               -- Dict bindings
-                         [(Id, [Maybe Type], [DictVar])]
+type ProtoUsageDetails = ([DictBind],
+                         [(Id, CallKey, ([DictExpr], VarSet))]
                         )
 
 ------------------------------------------------------------                   
 type CallDetails  = FiniteMap Id CallInfo
-type CallInfo     = FiniteMap [Maybe Type]     -- Nothing => unconstrained type argument
-                             [DictVar]         -- Dict args
+newtype CallKey   = CallKey [Maybe Type]                       -- Nothing => unconstrained type argument
+type CallInfo     = FiniteMap CallKey
+                             ([DictExpr], VarSet)              -- Dict args and the vars of the whole
+                                                               -- call (including tyvars)
+                                                               -- [*not* include the main id itself, of course]
        -- The finite maps eliminate duplicates
        -- The list of types and dictionaries is guaranteed to
        -- match the type of f
 
-callDetailsToList calls = [ (id,tys,dicts)
-                         | (id,fm) <- fmToList calls,
-                           (tys,dicts) <- fmToList fm
-                         ]
+-- Type isn't an instance of Ord, so that we can control which
+-- instance we use.  That's tiresome here.  Oh well
+instance Eq CallKey where
+  k1 == k2 = case k1 `compare` k2 of { EQ -> True; other -> False }
 
-listToCallDetails calls  = foldr (unionCalls . singleCall) emptyFM calls
+instance Ord CallKey where
+  compare (CallKey k1) (CallKey k2) = cmpList cmp k1 k2
+               where
+                 cmp Nothing Nothing     = EQ
+                 cmp Nothing (Just t2)   = LT
+                 cmp (Just t1) Nothing   = GT
+                 cmp (Just t1) (Just t2) = tcCmpType t1 t2
 
 unionCalls :: CallDetails -> CallDetails -> CallDetails
 unionCalls c1 c2 = plusFM_C plusFM c1 c2
 
-singleCall (id, tys, dicts) = unitFM id (unitFM tys dicts)
+singleCall :: Id -> [Maybe Type] -> [DictExpr] -> CallDetails
+singleCall id tys dicts 
+  = unitFM id (unitFM (CallKey tys) (dicts, call_fvs))
+  where
+    call_fvs = exprsFreeVars dicts `unionVarSet` tys_fvs
+    tys_fvs  = tyVarsOfTypes (catMaybes tys)
+       -- The type args (tys) are guaranteed to be part of the dictionary
+       -- types, because they are just the constrained types,
+       -- and the dictionary is therefore sure to be bound
+       -- inside the binding for any type variables free in the type;
+       -- hence it's safe to neglect tyvars free in tys when making
+       -- the free-var set for this call
+       -- BUT I don't trust this reasoning; play safe and include tys_fvs
+       --
+       -- We don't include the 'id' itself.
+
+listToCallDetails calls
+  = foldr (unionCalls . mk_call) emptyFM calls
+  where
+    mk_call (id, tys, dicts_w_fvs) = unitFM id (unitFM tys dicts_w_fvs)
+       -- NB: the free vars of the call are provided
 
-mkCallUDs f args 
+callDetailsToList calls = [ (id,tys,dicts)
+                         | (id,fm) <- fmToList calls,
+                           (tys, dicts) <- fmToList fm
+                         ]
+
+mkCallUDs subst f args 
   | null theta
-  || length spec_tys /= n_tyvars
-  || length dicts    /= n_dicts
-  = emptyUDs   -- Not overloaded
+  || not (spec_tys `lengthIs` n_tyvars)
+  || not ( dicts   `lengthIs` n_dicts)
+  || maybeToBool (lookupRule (\act -> True) (substInScope subst) f args)
+       -- There's already a rule covering this call.  A typical case
+       -- is where there's an explicit user-provided rule.  Then
+       -- we don't want to create a specialised version 
+       -- of the function that overlaps.
+  = emptyUDs   -- Not overloaded, or no specialisation wanted
 
   | otherwise
   = MkUD {dict_binds = emptyBag, 
-         calls = singleCall (f, spec_tys, dicts)
+         calls      = singleCall f spec_tys dicts
     }
   where
-    (tyvars, theta, tau) = splitSigmaTy (idType f)
-    constrained_tyvars   = foldr (unionTyVarSets . tyVarsOfTypes . snd) emptyTyVarSet theta 
-    n_tyvars            = length tyvars
-    n_dicts             = length theta
+    (tyvars, theta, _) = tcSplitSigmaTy (idType f)
+    constrained_tyvars = tyVarsOfTheta theta 
+    n_tyvars          = length tyvars
+    n_dicts           = length theta
 
-    spec_tys = [mk_spec_ty tv ty | (tv, TyArg ty) <- tyvars `zip` args]
-    dicts    = [d | (_, VarArg d) <- theta `zip` (drop n_tyvars args)]
+    spec_tys = [mk_spec_ty tv ty | (tv, Type ty) <- tyvars `zip` args]
+    dicts    = [dict_expr | (_, dict_expr) <- theta `zip` (drop n_tyvars args)]
     
-    mk_spec_ty tyvar ty | tyvar `elementOfTyVarSet` constrained_tyvars
+    mk_spec_ty tyvar ty | tyvar `elemVarSet` constrained_tyvars
                        = Just ty
                        | otherwise
                        = Nothing
@@ -1046,29 +1034,41 @@ mkCallUDs f args
 plusUDs :: UsageDetails -> UsageDetails -> UsageDetails
 plusUDs (MkUD {dict_binds = db1, calls = calls1})
        (MkUD {dict_binds = db2, calls = calls2})
-  = MkUD {dict_binds, calls}
+  = MkUD {dict_binds = d, calls = c}
   where
-    dict_binds = db1    `unionBags`   db2 
-    calls      = calls1 `unionCalls`  calls2
+    d = db1    `unionBags`   db2 
+    c = calls1 `unionCalls`  calls2
 
 plusUDList = foldr plusUDs emptyUDs
 
-mkDB dict rhs = (dict, rhs, db_ftvs, db_fvs)
-             where
-               db_ftvs = tyVarsOfType (idType dict)    -- Superset of RHS fvs
-               db_fvs  = dictRhsFVs rhs
+-- zapCalls deletes calls to ids from uds
+zapCalls ids uds = uds {calls = delListFromFM (calls uds) ids}
+
+mkDB bind = (bind, bind_fvs bind)
+
+bind_fvs (NonRec bndr rhs) = exprFreeVars rhs
+bind_fvs (Rec prs)        = foldl delVarSet rhs_fvs bndrs
+                          where
+                            bndrs = map fst prs
+                            rhs_fvs = unionVarSets [exprFreeVars rhs | (bndr,rhs) <- prs]
 
-addDictBind uds dict rhs = uds { dict_binds = mkDB dict rhs `consBag` dict_binds uds }
+addDictBind (dict,rhs) uds = uds { dict_binds = mkDB (NonRec dict rhs) `consBag` dict_binds uds }
 
-dumpUDs :: [CoreBinder]
+dumpAllDictBinds (MkUD {dict_binds = dbs}) binds
+  = foldrBag add binds dbs
+  where
+    add (bind,_) binds = bind : binds
+
+dumpUDs :: [CoreBndr]
        -> UsageDetails -> CoreExpr
        -> (UsageDetails, CoreExpr)
 dumpUDs bndrs uds body
-  = (free_uds, foldr Let body dict_binds)
+  = (free_uds, foldr add_let body dict_binds)
   where
     (free_uds, (dict_binds, _)) = splitUDs bndrs uds
+    add_let (bind,_) body      = Let bind body
 
-splitUDs :: [CoreBinder]
+splitUDs :: [CoreBndr]
         -> UsageDetails
         -> (UsageDetails,              -- These don't mention the binders
             ProtoUsageDetails)         -- These do
@@ -1088,64 +1088,29 @@ splitUDs bndrs uds@(MkUD {dict_binds = orig_dbs,
        )
 
   where
-    tyvar_set    = mkTyVarSet [tv | TyBinder tv <- bndrs]
-    id_set       = mkIdSet    [id | ValBinder id <- bndrs]
+    bndr_set = mkVarSet bndrs
 
     (free_dbs, dump_dbs, dump_idset) 
-         = foldlBag dump_db (emptyBag, emptyBag, id_set) orig_dbs
+         = foldlBag dump_db (emptyBag, emptyBag, bndr_set) orig_dbs
                -- Important that it's foldl not foldr;
                -- we're accumulating the set of dumped ids in dump_set
 
        -- Filter out any calls that mention things that are being dumped
-       -- Don't need to worry about the tyvars because the dicts will
-       -- spot the captured ones; any fully polymorphic arguments will
-       -- be Nothings in the call details
-    orig_call_list = callDetailsToList orig_calls
-    (dump_calls, free_calls) = partition captured orig_call_list
-    captured (id,tys,dicts)  = any (`elementOfIdSet` dump_idset) (id:dicts)
-
-    dump_db (free_dbs, dump_dbs, dump_idset) db@(dict, rhs, ftvs, fvs)
-       |  isEmptyIdSet    (dump_idset `intersectIdSets`    fvs)
-       && isEmptyTyVarSet (tyvar_set  `intersectTyVarSets` ftvs)
-       = (free_dbs `snocBag` db, dump_dbs, dump_idset)
-
-       | otherwise     -- Dump it
-       = (free_dbs, dump_dbs `snocBag` NonRec dict rhs, 
-          dump_idset `addOneToIdSet` dict)
-\end{code}
-
-Given a type and value substitution, specUDs creates a specialised copy of
-the given UDs
-
-\begin{code}
-specUDs :: [(TyVar,Type)] -> [(DictVar,DictVar)] -> ProtoUsageDetails -> SpecM UsageDetails
-specUDs tv_env_list dict_env_list (dbs, calls)
-  = specDBs dict_env dbs               `thenSM` \ (dict_env', dbs') ->
-    returnSM (MkUD { dict_binds = dbs',
-                    calls      = listToCallDetails (map (inst_call dict_env') calls)
-    })
-  where
-    tv_env   = mkTyVarEnv tv_env_list
-    dict_env = mkIdEnv dict_env_list
-
-    inst_call dict_env (id, tys, dicts) = (id, map inst_maybe_ty tys, 
-                                              map (lookupId dict_env) dicts)
+    orig_call_list                = callDetailsToList orig_calls
+    (dump_calls, free_calls)      = partition captured orig_call_list
+    captured (id,tys,(dicts, fvs)) =  fvs `intersectsVarSet` dump_idset
+                                  || id `elemVarSet` dump_idset
 
-    inst_maybe_ty Nothing   = Nothing
-    inst_maybe_ty (Just ty) = Just (instantiateTy tv_env ty)
+    dump_db (free_dbs, dump_dbs, dump_idset) db@(bind, fvs)
+       | dump_idset `intersectsVarSet` fvs     -- Dump it
+       = (free_dbs, dump_dbs `snocBag` db,
+          dump_idset `unionVarSet` mkVarSet (bindersOf bind))
 
-    specDBs dict_env []
-       = returnSM (dict_env, emptyBag)
-    specDBs dict_env (NonRec dict rhs : dbs)
-       = newIdSM dict (instantiateTy tv_env (idType dict))     `thenSM` \ dict' ->
-         let
-           dict_env' = addOneToIdEnv dict_env dict dict'
-           rhs'      = instantiateDictRhs tv_env dict_env rhs
-         in
-         specDBs dict_env' dbs         `thenSM` \ (dict_env'', dbs') ->
-         returnSM ( dict_env'', mkDB dict' rhs' `consBag` dbs' )
+       | otherwise     -- Don't dump it
+       = (free_dbs `snocBag` db, dump_dbs, dump_idset)
 \end{code}
 
+
 %************************************************************************
 %*                                                                     *
 \subsubsection{Boring helper functions}
@@ -1153,64 +1118,116 @@ specUDs tv_env_list dict_env_list (dbs, calls)
 %************************************************************************
 
 \begin{code}
-lookupId:: IdEnv Id -> Id -> Id
-lookupId env id = case lookupIdEnv env id of
-                       Nothing  -> id
-                       Just id' -> id'
-
-instantiateDictRhs :: TyVarEnv Type -> IdEnv Id -> CoreExpr -> CoreExpr
-       -- Cheapo function for simple RHSs
-instantiateDictRhs ty_env id_env rhs
-  = go rhs
-  where
-    go (App e1 (VarArg a)) = App (go e1) (VarArg (lookupId id_env a))
-    go (App e1 (TyArg t))  = App (go e1) (TyArg (instantiateTy ty_env t))
-    go (Var v)            = Var (lookupId id_env v)
-    go (Lit l)            = Lit l
-
-dictRhsFVs :: CoreExpr -> IdSet
-       -- Cheapo function for simple RHSs
-dictRhsFVs (App e1 (VarArg a)) = dictRhsFVs e1 `addOneToIdSet` a
-dictRhsFVs (App e1 (TyArg t))  = dictRhsFVs e1
-dictRhsFVs (Var v)            = unitIdSet v
-dictRhsFVs (Lit l)            = emptyIdSet
-
-
-addIdSpecialisations id spec_stuff
-  = (if not (null errs) then
-       pprTrace "Duplicate specialisations" (vcat (map ppr errs))
-     else \x -> x
-    )
-    addIdSpecialisation id new_spec_env
-  where
-    (new_spec_env, errs) = foldr add (getIdSpecialisation id, []) spec_stuff
-
-    add (tys, template) (spec_env, errs)
-       = case addToSpecEnv spec_env tys (occurAnalyseGlobalExpr template) of
-               Succeeded spec_env' -> (spec_env', errs)
-               Failed err          -> (spec_env, err:errs)
-
-----------------------------------------
 type SpecM a = UniqSM a
 
 thenSM    = thenUs
 returnSM  = returnUs
-getUniqSM = getUnique
+getUniqSM = getUniqueUs
 mapSM     = mapUs
-initSM   = initUs
+initSM   = initUs_
 
 mapAndCombineSM f []     = returnSM ([], emptyUDs)
 mapAndCombineSM f (x:xs) = f x `thenSM` \ (y, uds1) ->
                           mapAndCombineSM f xs `thenSM` \ (ys, uds2) ->
                           returnSM (y:ys, uds1 `plusUDs` uds2)
 
+cloneBindSM :: Subst -> CoreBind -> SpecM (Subst, Subst, CoreBind)
+-- Clone the binders of the bind; return new bind with the cloned binders
+-- Return the substitution to use for RHSs, and the one to use for the body
+cloneBindSM subst (NonRec bndr rhs)
+  = getUs      `thenUs` \ us ->
+    let
+       (subst', bndr') = substAndCloneId subst us bndr
+    in
+    returnUs (subst, subst', NonRec bndr' rhs)
+
+cloneBindSM subst (Rec pairs)
+  = getUs      `thenUs` \ us ->
+    let
+       (subst', bndrs') = substAndCloneRecIds subst us (map fst pairs)
+    in
+    returnUs (subst', subst', Rec (bndrs' `zip` map snd pairs))
+
+cloneBinders subst bndrs
+  = getUs      `thenUs` \ us ->
+    returnUs (substAndCloneIds subst us bndrs)
+
 newIdSM old_id new_ty
-  = getUnique          `thenSM` \ uniq ->
-    returnSM (mkUserLocal (getOccName old_id) 
-                         uniq
-                         new_ty
-                         (getSrcLoc old_id)
-    )
+  = getUniqSM          `thenSM` \ uniq ->
+    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)
+    in
+    returnSM new_id
 \end{code}
 
 
+               Old (but interesting) stuff about unboxed bindings
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+What should we do when a value is specialised to a *strict* unboxed value?
+
+       map_*_* f (x:xs) = let h = f x
+                              t = map f xs
+                          in h:t
+
+Could convert let to case:
+
+       map_*_Int# f (x:xs) = case f x of h# ->
+                             let t = map f xs
+                             in h#:t
+
+This may be undesirable since it forces evaluation here, but the value
+may not be used in all branches of the body. In the general case this
+transformation is impossible since the mutual recursion in a letrec
+cannot be expressed as a case.
+
+There is also a problem with top-level unboxed values, since our
+implementation cannot handle unboxed values at the top level.
+
+Solution: Lift the binding of the unboxed value and extract it when it
+is used:
+
+       map_*_Int# f (x:xs) = let h = case (f x) of h# -> _Lift h#
+                                 t = map f xs
+                             in case h of
+                                _Lift h# -> h#:t
+
+Now give it to the simplifier and the _Lifting will be optimised away.
+
+The benfit is that we have given the specialised "unboxed" values a
+very simplep lifted semantics and then leave it up to the simplifier to
+optimise it --- knowing that the overheads will be removed in nearly
+all cases.
+
+In particular, the value will only be evaluted in the branches of the
+program which use it, rather than being forced at the point where the
+value is bound. For example:
+
+       filtermap_*_* p f (x:xs)
+         = let h = f x
+               t = ...
+           in case p x of
+               True  -> h:t
+               False -> t
+   ==>
+       filtermap_*_Int# p f (x:xs)
+         = let h = case (f x) of h# -> _Lift h#
+               t = ...
+           in case p x of
+               True  -> case h of _Lift h#
+                          -> h#:t
+               False -> t
+
+The binding for h can still be inlined in the one branch and the
+_Lifting eliminated.
+
+
+Question: When won't the _Lifting be eliminated?
+
+Answer: When they at the top-level (where it is necessary) or when
+inlining would duplicate work (or possibly code depending on
+options). However, the _Lifting will still be eliminated if the
+strictness analyser deems the lifted binding strict.
+