Don't import FastString in HsVersions.h
[ghc-hetmet.git] / compiler / simplCore / CSE.lhs
index 2e8489a..38c1f58 100644 (file)
@@ -11,8 +11,7 @@ module CSE (
 #include "HsVersions.h"
 
 import DynFlags        ( DynFlag(..), DynFlags )
-import Id              ( Id, idType, idWorkerInfo )
-import IdInfo          ( workerExists )
+import Id              ( Id, idType, idInlinePragma )
 import CoreUtils       ( hashExpr, cheapEqExpr, exprIsBig, mkAltExpr, exprIsCheap )
 import DataCon         ( isUnboxedTupleCon )
 import Type            ( tyConAppArgs )
@@ -20,8 +19,13 @@ import CoreSyn
 import VarEnv  
 import CoreLint                ( showPass, endPass )
 import Outputable
-import Util            ( mapAccumL, lengthExceeds )
+import StaticFlags     ( opt_PprStyle_Debug )
+import BasicTypes      ( isAlwaysActive )
+import Util            ( lengthExceeds )
 import UniqFM
+import FastString
+
+import Data.List
 \end{code}
 
 
@@ -107,6 +111,65 @@ Instead, we shoudl replace (f x) by (# a,b #).  That is, the "reverse mapping" i
        f x --> (# a,b #)
 That is why the CSEMap has pairs of expressions.
 
+Note [CSE for INLINE and NOINLINE]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We are careful to do no CSE inside functions that the user has marked as
+INLINE or NOINLINE.  In terms of Core, that means 
+
+       a) we do not do CSE inside (Note InlineMe e)
+
+       b) we do not do CSE on the RHS of a binding b=e
+          unless b's InlinePragma is AlwaysActive
+
+Here's why (examples from Roman Leshchinskiy).  Consider
+
+       yes :: Int
+       {-# NOINLINE yes #-}
+       yes = undefined
+
+       no :: Int
+       {-# NOINLINE no #-}
+       no = undefined
+
+       foo :: Int -> Int -> Int
+       {-# NOINLINE foo #-}
+       foo m n = n
+
+       {-# RULES "foo/no" foo no = id #-}
+
+       bar :: Int -> Int
+       bar = foo yes
+
+We do not expect the rule to fire.  But if we do CSE, then we get
+yes=no, and the rule does fire.  Worse, whether we get yes=no or
+no=yes depends on the order of the definitions.
+
+In general, CSE should probably never touch things with INLINE pragmas
+as this could lead to surprising results.  Consider
+
+       {-# INLINE foo #-}
+       foo = <rhs>
+
+       {-# NOINLINE bar #-}
+       bar = <rhs>     -- Same rhs as foo
+
+If CSE produces
+       foo = bar
+then foo will never be inlined (when it should be); but if it produces
+       bar = foo
+bar will be inlined (when it should not be). Even if we remove INLINE foo,
+we'd still like foo to be inlined if rhs is small. This won't happen
+with foo = bar.
+
+Not CSE-ing inside INLINE also solves an annoying bug in CSE. Consider
+a worker/wrapper, in which the worker has turned into a single variable:
+       $wf = h
+       f = \x -> ...$wf...
+Now CSE may transform to
+       f = \x -> ...h...
+But the WorkerInfo for f still says $wf, which is now dead!  This won't
+happen now that we don't look inside INLINEs (which wrappers are).
+
 
 %************************************************************************
 %*                                                                     *
@@ -125,7 +188,7 @@ cseProgram dflags binds
     }
 
 cseBinds :: CSEnv -> [CoreBind] -> [CoreBind]
-cseBinds env []     = []
+cseBinds _   []     = []
 cseBinds env (b:bs) = (b':bs')
                    where
                      (env1, b') = cseBind  env  b
@@ -138,6 +201,7 @@ cseBind env (Rec pairs)  = let (env', pairs') = mapAccumL do_one env pairs
                           in (env', Rec pairs')
                         
 
+do_one :: CSEnv -> (Id, CoreExpr) -> (CSEnv, (Id, CoreExpr))
 do_one env (id, rhs) 
   = case lookupCSEnv env rhs' of
        Just (Var other_id) -> (extendSubst env' id other_id,     (id', Var other_id))
@@ -145,20 +209,12 @@ do_one env (id, rhs)
        Nothing             -> (addCSEnvItem env' rhs' (Var id'), (id', rhs'))
   where
     (env', id') = addBinder env id
-    rhs' | not (workerExists (idWorkerInfo id)) = cseExpr env' rhs
-
-               -- Hack alert: don't do CSE on wrapper RHSs.
-               -- Otherwise we find:
-               --      $wf = h
-               --      f = \x -> ...$wf...
-               -- ===>
-               --      f = \x -> ...h...
-               -- But the WorkerInfo for f still says $wf, which is now dead!
-         | otherwise = rhs
-
+    rhs' | isAlwaysActive (idInlinePragma id) = cseExpr env' rhs
+        | otherwise                          = rhs
+               -- See Note [CSE for INLINE and NOINLINE]
 
 tryForCSE :: CSEnv -> CoreExpr -> CoreExpr
-tryForCSE env (Type t) = Type t
+tryForCSE _   (Type t) = Type t
 tryForCSE env expr     = case lookupCSEnv env expr' of
                            Just smaller_expr -> smaller_expr
                            Nothing           -> expr'
@@ -166,11 +222,13 @@ tryForCSE env expr     = case lookupCSEnv env expr' of
                         expr' = cseExpr env expr
 
 cseExpr :: CSEnv -> CoreExpr -> CoreExpr
-cseExpr env (Type t)              = Type t
-cseExpr env (Lit lit)             = Lit lit
+cseExpr _   (Type t)               = Type t
+cseExpr _   (Lit lit)              = Lit lit
 cseExpr env (Var v)               = Var (lookupSubst env v)
 cseExpr env (App f a)             = App (cseExpr env f) (tryForCSE env a)
+cseExpr _   (Note InlineMe e)      = Note InlineMe e    -- See Note [CSE for INLINE and NOINLINE]
 cseExpr env (Note n e)            = Note n (cseExpr env e)
+cseExpr env (Cast e co)            = Cast (cseExpr env e) co
 cseExpr env (Lam b e)             = let (env', b') = addBinder env b
                                     in Lam b' (cseExpr env' e)
 cseExpr env (Let bind e)          = let (env', bind') = cseBind env bind
@@ -181,7 +239,9 @@ cseExpr env (Case scrut bndr ty alts) = Case scrut' bndr' ty (cseAlts env' scrut
                                     (env', bndr') = addBinder env bndr
 
 
-cseAlts env scrut' bndr bndr' [(DataAlt con, args, rhs)]
+cseAlts :: CSEnv -> CoreExpr -> CoreBndr -> CoreBndr -> [CoreAlt] -> [CoreAlt]
+
+cseAlts env scrut' bndr _bndr' [(DataAlt con, args, rhs)]
   | isUnboxedTupleCon con
        -- Unboxed tuples are special because the case binder isn't
        -- a real values.  See [Note: unboxed tuple case binders]
@@ -200,7 +260,7 @@ cseAlts env scrut' bndr bndr' alts
                Var v' -> (v',    extendSubst env bndr v')      -- See [Note: case binder 1]
                                                                -- map: bndr -> v'
 
-               other ->  (bndr', extendCSEnv env scrut' (Var  bndr'))  -- See [Note: case binder 2]
+               _      ->  (bndr', extendCSEnv env scrut' (Var  bndr')) -- See [Note: case binder 2]
                                                                        -- map: scrut' -> bndr'
 
     arg_tys = tyConAppArgs (idType bndr)
@@ -240,6 +300,7 @@ type CSEMap = UniqFM [(CoreExpr, CoreExpr)] -- This is the reverse mapping
        -- This means that it's good to replace e by e'
        -- INVARIANT: The expr in the range has already been CSE'd
 
+emptyCSEnv :: CSEnv
 emptyCSEnv = CS emptyUFM emptyInScopeSet emptyVarEnv
 
 lookupCSEnv :: CSEnv -> CoreExpr -> Maybe CoreExpr
@@ -249,35 +310,42 @@ lookupCSEnv (CS cs _ _) expr
        Just pairs -> lookup_list pairs expr
 
 lookup_list :: [(CoreExpr,CoreExpr)] -> CoreExpr -> Maybe CoreExpr
-lookup_list [] expr = Nothing
+lookup_list [] _ = Nothing
 lookup_list ((e,e'):es) expr | cheapEqExpr e expr = Just e'
                             | otherwise          = lookup_list es expr
 
+addCSEnvItem :: CSEnv -> CoreExpr -> CoreExpr -> CSEnv
 addCSEnvItem env expr expr' | exprIsBig expr = env
                            | otherwise      = extendCSEnv env expr expr'
    -- We don't try to CSE big expressions, because they are expensive to compare
    -- (and are unlikely to be the same anyway)
 
+extendCSEnv :: CSEnv -> CoreExpr -> CoreExpr -> CSEnv
 extendCSEnv (CS cs in_scope sub) expr expr'
   = CS (addToUFM_C combine cs hash [(expr, expr')]) in_scope sub
   where
-    hash   = hashExpr expr
-    combine old new = WARN( result `lengthExceeds` 4, text "extendCSEnv: long list:" <+> ppr result )
-                     result
-                   where
-                     result = new ++ old
+    hash = hashExpr expr
+    combine old new 
+       = WARN( result `lengthExceeds` 4, short_msg $$ nest 2 long_msg ) result
+       where
+         result = new ++ old
+         short_msg = ptext SLIT("extendCSEnv: long list, length") <+> int (length result)
+         long_msg | opt_PprStyle_Debug = (text "hash code" <+> text (show hash)) $$ ppr result 
+                  | otherwise          = empty
 
+lookupSubst :: CSEnv -> Id -> Id
 lookupSubst (CS _ _ sub) x = case lookupVarEnv sub x of
                               Just y  -> y
                               Nothing -> x
 
+extendSubst :: CSEnv -> Id  -> Id -> CSEnv
 extendSubst (CS cs in_scope sub) x y = CS cs in_scope (extendVarEnv sub x y)
 
 addBinder :: CSEnv -> Id -> (CSEnv, Id)
-addBinder env@(CS cs in_scope sub) v
+addBinder (CS cs in_scope sub) v
   | not (v `elemInScopeSet` in_scope) = (CS cs (extendInScopeSet in_scope v)  sub,                    v)
   | isId v                           = (CS cs (extendInScopeSet in_scope v') (extendVarEnv sub v v'), v')
-  | not (isId v)                     = WARN( True, ppr v )
+  | otherwise                        = WARN( True, ppr v )
                                        (CS emptyUFM in_scope                 sub,                     v)
        -- This last case is the unusual situation where we have shadowing of
        -- a type variable; we have to discard the CSE mapping