Implement INLINABLE pragma
[ghc-hetmet.git] / compiler / coreSyn / CoreSubst.lhs
index f1f02d9..7ca5a67 100644 (file)
@@ -12,8 +12,9 @@ module CoreSubst (
 
         -- ** Substituting into expressions and related types
        deShadowBinds, substSpec, substRulesForImportedIds,
-       substTy, substExpr, substBind, substUnfolding,
-       substInlineRuleGuidance, lookupIdSubst, lookupTvSubst, substIdOcc,
+       substTy, substExpr, substExprSC, substBind, substBindSC,
+        substUnfolding, substUnfoldingSC,
+       substUnfoldingSource, lookupIdSubst, lookupTvSubst, substIdOcc,
 
         -- ** Operations on substitutions
        emptySubst, mkEmptySubst, mkSubst, mkOpenSubst, substInScope, isEmptySubst, 
@@ -27,7 +28,7 @@ module CoreSubst (
        cloneIdBndr, cloneIdBndrs, cloneRecIdBndrs,
 
        -- ** Simple expression optimiser
-       simpleOptExpr
+       simpleOptPgm, simpleOptExpr
     ) where
 
 #include "HsVersions.h"
@@ -35,10 +36,13 @@ module CoreSubst (
 import CoreSyn
 import CoreFVs
 import CoreUtils
-import OccurAnal( occurAnalyseExpr )
+import PprCore
+import OccurAnal( occurAnalyseExpr, occurAnalysePgm )
 
 import qualified Type
 import Type     ( Type, TvSubst(..), TvSubstEnv )
+import Coercion           ( isIdentityCoercion )
+import OptCoercion ( optCoercion )
 import VarSet
 import VarEnv
 import Id
@@ -48,6 +52,9 @@ import IdInfo
 import Unique
 import UniqSupply
 import Maybes
+import ErrUtils
+import DynFlags   ( DynFlags, DynFlag(..) )
+import BasicTypes ( isAlwaysActive )
 import Outputable
 import PprCore         ()              -- Instances
 import FastString
@@ -200,7 +207,7 @@ extendTvSubstList (Subst in_scope ids tvs) prs = Subst in_scope ids (extendVarEn
 -- 'extendIdSubst' and 'extendTvSubst'
 extendSubst :: Subst -> Var -> CoreArg -> Subst
 extendSubst (Subst in_scope ids tvs) tv (Type ty)
-  = ASSERT( isTyVar tv ) Subst in_scope ids (extendVarEnv tvs tv ty)
+  = ASSERT( isTyCoVar tv ) Subst in_scope ids (extendVarEnv tvs tv ty)
 extendSubst (Subst in_scope ids tvs) id expr
   = ASSERT( isId id ) Subst in_scope (extendVarEnv ids id expr) tvs
 
@@ -210,13 +217,13 @@ extendSubstList subst []        = subst
 extendSubstList subst ((var,rhs):prs) = extendSubstList (extendSubst subst var rhs) prs
 
 -- | Find the substitution for an 'Id' in the 'Subst'
-lookupIdSubst :: Subst -> Id -> CoreExpr
-lookupIdSubst (Subst in_scope ids _) v
+lookupIdSubst :: SDoc -> Subst -> Id -> CoreExpr
+lookupIdSubst doc (Subst in_scope ids _) v
   | not (isLocalId v) = Var v
   | Just e  <- lookupVarEnv ids       v = e
   | Just v' <- lookupInScope in_scope v = Var v'
        -- Vital! See Note [Extending the Subst]
-  | otherwise = WARN( True, ptext (sLit "CoreSubst.lookupIdSubst") <+> ppr v $$ ppr in_scope ) 
+  | otherwise = WARN( True, ptext (sLit "CoreSubst.lookupIdSubst") <+> ppr v $$ ppr in_scope $$ doc) 
                Var v
 
 -- | Find the substitution for a 'TyVar' in the 'Subst'
@@ -227,10 +234,10 @@ lookupTvSubst (Subst _ _ tvs) v = lookupVarEnv tvs v `orElse` Type.mkTyVarTy v
 --   No left-right shadowing
 --   ie the substitution for   (\x \y. e) a1 a2
 --      so neither x nor y scope over a1 a2
-mkOpenSubst :: [(Var,CoreArg)] -> Subst
-mkOpenSubst pairs = Subst (mkInScopeSet (exprsFreeVars (map snd pairs)))
-                         (mkVarEnv [(id,e)  | (id, e) <- pairs, isId id])
-                         (mkVarEnv [(tv,ty) | (tv, Type ty) <- pairs])
+mkOpenSubst :: InScopeSet -> [(Var,CoreArg)] -> Subst
+mkOpenSubst in_scope pairs = Subst in_scope
+                                  (mkVarEnv [(id,e)  | (id, e) <- pairs, isId id])
+                                  (mkVarEnv [(tv,ty) | (tv, Type ty) <- pairs])
 
 ------------------------------
 isInScope :: Var -> Subst -> Bool
@@ -280,21 +287,37 @@ instance Outputable Subst where
 --
 -- Do *not* attempt to short-cut in the case of an empty substitution!
 -- See Note [Extending the Subst]
-substExpr :: Subst -> CoreExpr -> CoreExpr
-substExpr subst expr
+substExprSC :: SDoc -> Subst -> CoreExpr -> CoreExpr
+substExprSC _doc subst orig_expr
+  | isEmptySubst subst = orig_expr
+  | otherwise          = -- pprTrace "enter subst-expr" (doc $$ ppr orig_expr) $
+                         subst_expr subst orig_expr
+
+substExpr :: SDoc -> Subst -> CoreExpr -> CoreExpr
+substExpr _doc subst orig_expr = subst_expr subst orig_expr
+
+subst_expr :: Subst -> CoreExpr -> CoreExpr
+subst_expr subst expr
   = go expr
   where
-    go (Var v)        = lookupIdSubst subst v 
+    go (Var v)        = lookupIdSubst (text "subst_expr") subst v 
     go (Type ty)       = Type (substTy subst ty)
     go (Lit lit)       = Lit lit
     go (App fun arg)   = App (go fun) (go arg)
     go (Note note e)   = Note (go_note note) (go e)
-    go (Cast e co)     = Cast (go e) (substTy subst co)
-    go (Lam bndr body) = Lam bndr' (substExpr subst' body)
+    go (Cast e co) 
+      | isIdentityCoercion co' = go e
+      | otherwise              = Cast (go e) co'
+      where
+        co' = optCoercion (getTvSubst subst) co
+       -- Optimise coercions as we go; this is good, for example
+       -- in the RHS of rules, which are only substituted in
+
+    go (Lam bndr body) = Lam bndr' (subst_expr subst' body)
                       where
                         (subst', bndr') = substBndr subst bndr
 
-    go (Let bind body) = Let bind' (substExpr subst' body)
+    go (Let bind body) = Let bind' (subst_expr subst' body)
                       where
                         (subst', bind') = substBind subst bind
 
@@ -302,7 +325,7 @@ substExpr subst expr
                                 where
                                 (subst', bndr') = substBndr subst bndr
 
-    go_alt subst (con, bndrs, rhs) = (con, bndrs', substExpr subst' rhs)
+    go_alt subst (con, bndrs, rhs) = (con, bndrs', subst_expr subst' rhs)
                                 where
                                   (subst', bndrs') = substBndrs subst bndrs
 
@@ -310,16 +333,32 @@ substExpr subst expr
 
 -- | Apply a substititon to an entire 'CoreBind', additionally returning an updated 'Subst'
 -- that should be used by subsequent substitutons.
-substBind :: Subst -> CoreBind -> (Subst, CoreBind)
-substBind subst (NonRec bndr rhs) = (subst', NonRec bndr' (substExpr subst rhs))
+substBind, substBindSC :: Subst -> CoreBind -> (Subst, CoreBind)
+
+substBindSC subst bind           -- Short-cut if the substitution is empty
+  | not (isEmptySubst subst)
+  = substBind subst bind
+  | otherwise
+  = case bind of
+       NonRec bndr rhs -> (subst', NonRec bndr' rhs)
+          where
+           (subst', bndr') = substBndr subst bndr
+       Rec pairs -> (subst', Rec (bndrs' `zip` rhss'))
+          where
+            (bndrs, rhss)    = unzip pairs
+           (subst', bndrs') = substRecBndrs subst bndrs
+           rhss' | isEmptySubst subst' = rhss
+                  | otherwise          = map (subst_expr subst') rhss
+
+substBind subst (NonRec bndr rhs) = (subst', NonRec bndr' (subst_expr subst rhs))
                                  where
                                    (subst', bndr') = substBndr subst bndr
 
-substBind subst (Rec pairs) = (subst', Rec pairs')
+substBind subst (Rec pairs) = (subst', Rec (bndrs' `zip` rhss'))
                            where
-                               (subst', bndrs') = substRecBndrs subst (map fst pairs)
-                               pairs'  = bndrs' `zip` rhss'
-                               rhss'   = map (substExpr subst' . snd) pairs
+                                (bndrs, rhss)    = unzip pairs
+                               (subst', bndrs') = substRecBndrs subst bndrs
+                               rhss' = map (subst_expr subst') rhss
 \end{code}
 
 \begin{code}
@@ -354,8 +393,8 @@ preserve occ info in rules.
 -- 'IdInfo' is preserved by this process, although it is substituted into appropriately.
 substBndr :: Subst -> Var -> (Subst, Var)
 substBndr subst bndr
-  | isTyVar bndr  = substTyVarBndr subst bndr
-  | otherwise     = substIdBndr subst subst bndr
+  | isTyCoVar bndr  = substTyVarBndr subst bndr
+  | otherwise       = substIdBndr (text "var-bndr") subst subst bndr
 
 -- | Applies 'substBndr' to a number of 'Var's, accumulating a new 'Subst' left-to-right
 substBndrs :: Subst -> [Var] -> (Subst, [Var])
@@ -366,18 +405,20 @@ substRecBndrs :: Subst -> [Id] -> (Subst, [Id])
 substRecBndrs subst bndrs 
   = (new_subst, new_bndrs)
   where                -- Here's the reason we need to pass rec_subst to subst_id
-    (new_subst, new_bndrs) = mapAccumL (substIdBndr new_subst) subst bndrs
+    (new_subst, new_bndrs) = mapAccumL (substIdBndr (text "rec-bndr") new_subst) subst bndrs
 \end{code}
 
 
 \begin{code}
-substIdBndr :: Subst           -- ^ Substitution to use for the IdInfo
+substIdBndr :: SDoc 
+            -> Subst           -- ^ Substitution to use for the IdInfo
            -> Subst -> Id      -- ^ Substitition and Id to transform
            -> (Subst, Id)      -- ^ Transformed pair
                                -- NB: unfolding may be zapped
 
-substIdBndr rec_subst subst@(Subst in_scope env tvs) old_id
-  = (Subst (in_scope `extendInScopeSet` new_id) new_env tvs, new_id)
+substIdBndr _doc rec_subst subst@(Subst in_scope env tvs) old_id
+  = -- pprTrace "substIdBndr" (doc $$ ppr old_id $$ ppr in_scope) $
+    (Subst (in_scope `extendInScopeSet` new_id) new_env tvs, new_id)
   where
     id1 = uniqAway in_scope old_id     -- id1 is cloned if necessary
     id2 | no_type_change = id1
@@ -409,7 +450,7 @@ It also unconditionally zaps the OccInfo.
 
 \begin{code}
 -- | Very similar to 'substBndr', but it always allocates a new 'Unique' for
--- each variable in its output and removes all 'IdInfo'
+-- each variable in its output.  It substitutes the IdInfo though.
 cloneIdBndr :: Subst -> UniqSupply -> Id -> (Subst, Id)
 cloneIdBndr subst us old_id
   = clone_id subst subst (old_id, uniqFromSupply us)
@@ -462,8 +503,10 @@ substTyVarBndr (Subst in_scope id_env tv_env) tv
 
 -- | See 'Type.substTy'
 substTy :: Subst -> Type -> Type 
-substTy (Subst in_scope _id_env tv_env) ty
-  = Type.substTy (TvSubst in_scope tv_env) ty
+substTy subst ty = Type.substTy (getTvSubst subst) ty
+
+getTvSubst :: Subst -> TvSubst
+getTvSubst (Subst in_scope _id_env tv_env) = TvSubst in_scope tv_env
 \end{code}
 
 
@@ -486,7 +529,6 @@ substIdType subst@(Subst _ _ tv_env) id
 
 ------------------
 -- | Substitute into some 'IdInfo' with regard to the supplied new 'Id'.
--- Always zaps the unfolding, to save substitution work
 substIdInfo :: Subst -> Id -> IdInfo -> Maybe IdInfo
 substIdInfo subst new_id info
   | nothing_to_do = Nothing
@@ -500,41 +542,56 @@ substIdInfo subst new_id info
 
 ------------------
 -- | Substitutes for the 'Id's within an unfolding
-substUnfolding :: Subst -> Unfolding -> Unfolding
+substUnfolding, substUnfoldingSC :: Subst -> Unfolding -> Unfolding
        -- Seq'ing on the returned Unfolding is enough to cause
        -- all the substitutions to happen completely
-substUnfolding subst (DFunUnfolding con args)
-  = DFunUnfolding con (map (substExpr subst) args)
 
-substUnfolding subst unf@(CoreUnfolding { uf_tmpl = tmpl, uf_guidance = guide@(InlineRule {}) })
+substUnfoldingSC subst unf      -- Short-cut version
+  | isEmptySubst subst = unf
+  | otherwise          = substUnfolding subst unf
+
+substUnfolding subst (DFunUnfolding ar con args)
+  = DFunUnfolding ar con (map (substExpr (text "dfun-unf") subst) args)
+
+substUnfolding subst unf@(CoreUnfolding { uf_tmpl = tmpl, uf_src = src })
        -- Retain an InlineRule!
+  | not (isStableSource src)  -- Zap an unstable unfolding, to save substitution work
+  = NoUnfolding
+  | otherwise                 -- But keep a stable one!
   = seqExpr new_tmpl `seq` 
-    new_mb_wkr `seq`
-    unf { uf_tmpl = new_tmpl, uf_guidance = guide { ug_ir_info = new_mb_wkr } }
+    new_src `seq`
+    unf { uf_tmpl = new_tmpl, uf_src = new_src }
   where
-    new_tmpl   = substExpr subst tmpl
-    new_mb_wkr = substInlineRuleGuidance subst (ug_ir_info guide)
+    new_tmpl = substExpr (text "subst-unf") subst tmpl
+    new_src  = substUnfoldingSource subst src
 
-substUnfolding _ (CoreUnfolding {}) = NoUnfolding      -- Discard
-       -- Always zap a CoreUnfolding, to save substitution work
-
-substUnfolding _ unf = unf     -- Otherwise no substitution to do
+substUnfolding _ unf = unf     -- NoUnfolding, OtherCon
 
 -------------------
-substInlineRuleGuidance :: Subst -> InlineRuleInfo -> InlineRuleInfo
-substInlineRuleGuidance subst (InlWrapper wkr)
-  = case lookupIdSubst subst wkr of
-      Var w1 -> InlWrapper w1
-      other  -> WARN( not (exprIsTrivial other), text "CoreSubst.substWorker:" <+> ppr wkr )
-               InlUnSat   -- Worker has got substituted away altogether
-                          -- (This can happen if it's trivial, via
-                          --  postInlineUnconditionally, hence only warning)
-substInlineRuleGuidance _ info = info
+substUnfoldingSource :: Subst -> UnfoldingSource -> UnfoldingSource
+substUnfoldingSource (Subst in_scope ids _) (InlineWrapper wkr)
+  | Just wkr_expr <- lookupVarEnv ids wkr 
+  = case wkr_expr of
+      Var w1 -> InlineWrapper w1
+      _other -> -- WARN( True, text "Interesting! CoreSubst.substWorker1:" <+> ppr wkr 
+                --             <+> ifPprDebug (equals <+> ppr wkr_expr) )   
+                             -- Note [Worker inlining]
+                InlineStable  -- It's not a wrapper any more, but still inline it!
+
+  | Just w1  <- lookupInScope in_scope wkr = InlineWrapper w1
+  | otherwise = -- WARN( True, text "Interesting! CoreSubst.substWorker2:" <+> ppr wkr )
+               -- This can legitimately happen.  The worker has been inlined and
+               -- dropped as dead code, because we don't treat the UnfoldingSource
+               -- as an "occurrence".
+                -- Note [Worker inlining]
+               InlineStable
+
+substUnfoldingSource _ src = src
 
 ------------------
 substIdOcc :: Subst -> Id -> Id
 -- These Ids should not be substituted to non-Ids
-substIdOcc subst v = case lookupIdSubst subst v of
+substIdOcc subst v = case lookupIdSubst (text "substIdOcc") subst v of
                        Var v' -> v'
                        other  -> pprPanic "substIdOcc" (vcat [ppr v <+> ppr other, ppr subst])
 
@@ -551,7 +608,9 @@ substSpec subst new_id (SpecInfo rules rhs_fvs)
 ------------------
 substRulesForImportedIds :: Subst -> [CoreRule] -> [CoreRule]
 substRulesForImportedIds subst rules 
-  = map (substRule subst (\name -> name)) rules
+  = map (substRule subst not_needed) rules
+  where
+    not_needed name = pprPanic "substRulesForImportedIds" (ppr name)
 
 ------------------
 substRule :: Subst -> (Name -> Name) -> CoreRule -> CoreRule
@@ -565,11 +624,14 @@ substRule :: Subst -> (Name -> Name) -> CoreRule -> CoreRule
 
 substRule _ _ rule@(BuiltinRule {}) = rule
 substRule subst subst_ru_fn rule@(Rule { ru_bndrs = bndrs, ru_args = args
-                                       , ru_fn = fn_name, ru_rhs = rhs })
+                                       , ru_fn = fn_name, ru_rhs = rhs
+                                       , ru_local = is_local })
   = rule { ru_bndrs = bndrs', 
-          ru_fn    = subst_ru_fn fn_name,
-          ru_args  = map (substExpr subst') args,
-          ru_rhs   = substExpr subst' rhs }
+          ru_fn    = if is_local 
+                       then subst_ru_fn fn_name 
+                       else fn_name,
+          ru_args  = map (substExpr (text "subst-rule" <+> ppr fn_name) subst') args,
+          ru_rhs   = substExpr (text "subst-rule" <+> ppr fn_name) subst' rhs }
   where
     (subst', bndrs') = substBndrs subst bndrs
 
@@ -579,10 +641,22 @@ substVarSet subst fvs
   = foldVarSet (unionVarSet . subst_fv subst) emptyVarSet fvs
   where
     subst_fv subst fv 
-       | isId fv   = exprFreeVars (lookupIdSubst subst fv)
+       | isId fv   = exprFreeVars (lookupIdSubst (text "substVarSet") subst fv)
        | otherwise = Type.tyVarsOfType (lookupTvSubst subst fv)
 \end{code}
 
+Note [Worker inlining]
+~~~~~~~~~~~~~~~~~~~~~~
+A worker can get sustituted away entirely.
+       - it might be trivial
+       - it might simply be very small
+We do not treat an InlWrapper as an 'occurrence' in the occurence 
+analyser, so it's possible that the worker is not even in scope any more.
+
+In all all these cases we simply drop the special case, returning to
+InlVanilla.  The WARN is just so I can see if it happens a lot.
+
+
 %************************************************************************
 %*                                                                     *
        The Very Simple Optimiser
@@ -601,7 +675,8 @@ simpleOptExpr :: CoreExpr -> CoreExpr
 -- may change radically
 
 simpleOptExpr expr
-  = go init_subst (occurAnalyseExpr expr)
+  = -- pprTrace "simpleOptExpr" (ppr init_subst $$ ppr expr)
+    simple_opt_expr init_subst (occurAnalyseExpr expr)
   where
     init_subst = mkEmptySubst (mkInScopeSet (exprFreeVars expr))
        -- It's potentially important to make a proper in-scope set
@@ -614,71 +689,191 @@ simpleOptExpr expr
        -- It's a bit painful to call exprFreeVars, because it makes
        -- three passes instead of two (occ-anal, and go)
 
-    go subst (Var v)          = lookupIdSubst subst v
-    go subst (App e1 e2)      = App (go subst e1) (go subst e2)
-    go subst (Type ty)        = Type (substTy subst ty)
-    go _     (Lit lit)        = Lit lit
-    go subst (Note note e)    = Note note (go subst e)
-    go subst (Cast e co)      = Cast (go subst e) (substTy subst co)
-    go subst (Let bind body)  = go_let subst bind body
-    go subst (Lam bndr body)  = Lam bndr' (go subst' body)
-                             where
-                               (subst', bndr') = substBndr subst bndr
-
-    go subst (Case e b ty as) = Case (go subst e) b' 
-                                    (substTy subst ty)
-                                    (map (go_alt subst') as)
-                             where
-                                (subst', b') = substBndr subst b
-
+----------------------
+simpleOptPgm :: DynFlags -> [CoreBind] -> [CoreRule] -> IO ([CoreBind], [CoreRule])
+simpleOptPgm dflags binds rules
+  = do { dumpIfSet_dyn dflags Opt_D_dump_occur_anal "Occurrence analysis"
+                      (pprCoreBindings occ_anald_binds);
 
-    ----------------------
-    go_alt subst (con, bndrs, rhs) = (con, bndrs', go subst' rhs)
-                                where
-                                  (subst', bndrs') = substBndrs subst bndrs
+       ; return (reverse binds', substRulesForImportedIds subst' rules) }
+  where
+    occ_anald_binds  = occurAnalysePgm binds rules
+    (subst', binds') = foldl do_one (emptySubst, []) occ_anald_binds
+                       
+    do_one (subst, binds') bind 
+      = case simple_opt_bind subst bind of
+          (subst', Nothing)    -> (subst', binds')
+          (subst', Just bind') -> (subst', bind':binds')
+
+----------------------
+type InVar   = Var
+type OutVar  = Var
+type InId    = Id
+type OutId   = Id
+type InExpr  = CoreExpr
+type OutExpr = CoreExpr
+
+-- In these functions the substitution maps InVar -> OutExpr
+
+----------------------
+simple_opt_expr :: Subst -> InExpr -> OutExpr
+simple_opt_expr subst expr
+  = go expr
+  where
+    go (Var v)          = lookupIdSubst (text "simpleOptExpr") subst v
+    go (App e1 e2)      = App (go e1) (go e2)
+    go (Type ty)        = Type (substTy subst ty)
+    go (Lit lit)        = Lit lit
+    go (Note note e)    = Note note (go e)
+    go (Cast e co)      | isIdentityCoercion co' = go e
+                               | otherwise              = Cast (go e) co' 
+                        where
+                          co' = substTy subst co
+
+    go (Let bind body)  = maybeLet mb_bind (simple_opt_expr subst' body)
+                        where
+                          (subst', mb_bind) = simple_opt_bind subst bind
+    go lam@(Lam {})     = go_lam [] subst lam
+    go (Case e b ty as) = Case (go e) b' (substTy subst ty)
+                                      (map (go_alt subst') as)
+                               where
+                                 (subst', b') = subst_opt_bndr subst b
 
     ----------------------
-    go_let subst (Rec prs) body
-      = Let (Rec (reverse rev_prs')) (go subst'' body)
+    go_alt subst (con, bndrs, rhs) 
+      = (con, bndrs', simple_opt_expr subst' rhs)
       where
-       (subst', bndrs')    = substRecBndrs subst (map fst prs)
-       (subst'', rev_prs') = foldl do_pr (subst', []) (prs `zip` bndrs')
-       do_pr (subst, prs) ((b,r), b') = case go_bind subst b r of
-                                          Left subst' -> (subst', prs)
-                                          Right r'    -> (subst,  (b',r'):prs)
-
-    go_let subst (NonRec b r) body
-      = case go_bind subst b r of
-          Left subst' -> go subst' body
-         Right r'    -> Let (NonRec b' r') (go subst' body)
-                     where
-                        (subst', b') = substBndr subst b
-
+       (subst', bndrs') = subst_opt_bndrs subst bndrs
 
     ----------------------
-    go_bind :: Subst -> Var -> CoreExpr -> Either Subst CoreExpr
-        -- (go_bind subst old_var old_rhs)  
-       --   either extends subst with (old_var -> new_rhs)
-       --   or     return new_rhs for a binding new_var = new_rhs
-    go_bind subst b r
-      | Type ty <- r
-      , isTyVar b      -- let a::* = TYPE ty in <body>
-      = Left (extendTvSubst subst b (substTy subst ty))
-
-      | isId b         -- let x = e in <body>
-      , safe_to_inline (idOccInfo b) || exprIsTrivial r'
-      = Left (extendIdSubst subst b r')
-      
-      | otherwise
-      = Right r'
-      where
-        r' = go subst r
+    -- go_lam tries eta reduction
+    go_lam bs' subst (Lam b e) 
+       = go_lam (b':bs') subst' e
+       where
+         (subst', b') = subst_opt_bndr subst b
+    go_lam bs' subst e 
+       | Just etad_e <- tryEtaReduce bs e' = etad_e
+       | otherwise                         = mkLams bs e'
+       where
+         bs = reverse bs'
+         e' = simple_opt_expr subst e
+
+----------------------
+simple_opt_bind :: Subst -> CoreBind -> (Subst, Maybe CoreBind)
+simple_opt_bind subst (Rec prs)
+  = (subst'', Just (Rec (reverse rev_prs')))
+  where
+    (subst', bndrs')    = subst_opt_bndrs subst (map fst prs)
+    (subst'', rev_prs') = foldl do_pr (subst', []) (prs `zip` bndrs')
+    do_pr (subst, prs) ((b,r), b') = case simple_opt_pair subst b r of
+                                      Left subst' -> (subst', prs)
+                                      Right r'    -> (subst,  (b2,r'):prs)
+                                           where
+                                            b2 = add_info subst b b'
+
+simple_opt_bind subst (NonRec b r)
+  = case simple_opt_pair subst b r of
+      Left ext_subst -> (ext_subst, Nothing)
+      Right r'       -> (subst', Just (NonRec b2 r'))
+                    where
+                       (subst', b') = subst_opt_bndr subst b
+                       b2 = add_info subst' b b'
+
+----------------------
+simple_opt_pair :: Subst -> InVar -> InExpr -> Either Subst OutExpr
+    -- (simple_opt_pair subst in_var in_rhs)  
+    --   either extends subst with (in_var -> out_rhs)
+    --   or     return out_rhs for a binding out_var = out_rhs
+simple_opt_pair subst b r
+  | Type ty <- r       -- let a::* = TYPE ty in <body>
+  = ASSERT( isTyCoVar b )
+    Left (extendTvSubst subst b (substTy subst ty))
+
+  | isId b             -- let x = e in <body>
+  , safe_to_inline (idOccInfo b) 
+  , isAlwaysActive (idInlineActivation b)      -- Note [Inline prag in simplOpt]
+  , not (isStableUnfolding (idUnfolding b))
+  , not (isExportedId b)
+  = Left (extendIdSubst subst b r')
+  
+  | otherwise
+  = Right r'
+  where
+    r' = simple_opt_expr subst r
 
-    ----------------------
        -- Unconditionally safe to inline
     safe_to_inline :: OccInfo -> Bool
-    safe_to_inline IAmDead                  = True
-    safe_to_inline (OneOcc in_lam one_br _) = not in_lam && one_br
     safe_to_inline (IAmALoopBreaker {})     = False
-    safe_to_inline NoOccInfo                = False
+    safe_to_inline IAmDead                  = True
+    safe_to_inline (OneOcc in_lam one_br _) = (not in_lam && one_br) || exprIsTrivial r'
+    safe_to_inline NoOccInfo                = exprIsTrivial r'
+
+----------------------
+subst_opt_bndr :: Subst -> InVar -> (Subst, OutVar)
+subst_opt_bndr subst bndr
+  | isTyCoVar bndr  = substTyVarBndr subst bndr
+  | otherwise       = subst_opt_id_bndr subst bndr
+
+subst_opt_id_bndr :: Subst -> InId -> (Subst, OutId)
+-- Nuke all fragile IdInfo, unfolding, and RULES; 
+--    it gets added back later by add_info
+-- Rather like SimplEnv.substIdBndr
+--
+-- It's important to zap fragile OccInfo (which CoreSubst.SubstIdBndr 
+-- carefully does not do) because simplOptExpr invalidates it
+
+subst_opt_id_bndr subst@(Subst in_scope id_subst tv_subst) old_id
+  = (Subst new_in_scope new_id_subst tv_subst, new_id)
+  where
+    id1           = uniqAway in_scope old_id
+    id2    = setIdType id1 (substTy subst (idType old_id))
+    new_id = zapFragileIdInfo id2      -- Zaps rules, worker-info, unfolding
+                                       -- and fragile OccInfo
+    new_in_scope = in_scope `extendInScopeSet` new_id
+
+       -- Extend the substitution if the unique has changed,
+       -- or there's some useful occurrence information
+       -- See the notes with substTyVarBndr for the delSubstEnv
+    new_id_subst | new_id /= old_id
+                = extendVarEnv id_subst old_id (Var new_id)
+                | otherwise 
+                = delVarEnv id_subst old_id
+
+----------------------
+subst_opt_bndrs :: Subst -> [InVar] -> (Subst, [OutVar])
+subst_opt_bndrs subst bndrs
+  = mapAccumL subst_opt_bndr subst bndrs
+
+----------------------
+add_info :: Subst -> InVar -> OutVar -> OutVar
+add_info subst old_bndr new_bndr 
+ | isTyCoVar old_bndr = new_bndr
+ | otherwise          = maybeModifyIdInfo mb_new_info new_bndr
+ where
+   mb_new_info = substIdInfo subst new_bndr (idInfo old_bndr)
+
+----------------------
+maybeLet :: Maybe CoreBind -> CoreExpr -> CoreExpr
+maybeLet Nothing  e = e
+maybeLet (Just b) e = Let b e
 \end{code}
+
+Note [Inline prag in simplOpt]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If there's an INLINE/NOINLINE pragma that restricts the phase in 
+which the binder can be inlined, we don't inline here; after all,
+we don't know what phase we're in.  Here's an example
+
+  foo :: Int -> Int -> Int
+  {-# INLINE foo #-}
+  foo m n = inner m
+     where
+       {-# INLINE [1] inner #-}
+       inner m = m+n
+
+  bar :: Int -> Int
+  bar n = foo n 1
+
+When inlining 'foo' in 'bar' we want the let-binding for 'inner' 
+to remain visible until Phase 1
+