summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/basicTypes/SrcLoc.hs30
-rw-r--r--compiler/coreSyn/CoreArity.hs39
-rw-r--r--compiler/coreSyn/CoreFVs.hs2
-rw-r--r--compiler/coreSyn/CorePrep.hs31
-rw-r--r--compiler/coreSyn/CoreSubst.hs34
-rw-r--r--compiler/coreSyn/CoreSyn.hs269
-rw-r--r--compiler/coreSyn/CoreUnfold.hs9
-rw-r--r--compiler/coreSyn/CoreUtils.hs266
-rw-r--r--compiler/coreSyn/PprCore.hs10
-rw-r--r--compiler/deSugar/Coverage.hs19
-rw-r--r--compiler/deSugar/Desugar.hs1
-rw-r--r--compiler/iface/IfaceSyn.hs21
-rw-r--r--compiler/iface/MkIface.hs1
-rw-r--r--compiler/iface/TcIface.hs1
-rw-r--r--compiler/main/DynFlags.hs10
-rw-r--r--compiler/main/HscMain.hs6
-rw-r--r--compiler/simplCore/CSE.hs28
-rw-r--r--compiler/simplCore/FloatIn.hs17
-rw-r--r--compiler/simplCore/FloatOut.hs14
-rw-r--r--compiler/simplCore/OccurAnal.hs84
-rw-r--r--compiler/simplCore/SetLevels.hs2
-rw-r--r--compiler/simplCore/SimplCore.hs45
-rw-r--r--compiler/simplCore/SimplEnv.hs20
-rw-r--r--compiler/simplCore/SimplUtils.hs54
-rw-r--r--compiler/simplCore/Simplify.hs99
-rw-r--r--compiler/specialise/Rules.hs61
-rw-r--r--compiler/specialise/SpecConstr.hs9
-rw-r--r--compiler/stgSyn/CoreToStg.hs3
-rw-r--r--compiler/utils/OrdList.hs10
29 files changed, 873 insertions, 322 deletions
diff --git a/compiler/basicTypes/SrcLoc.hs b/compiler/basicTypes/SrcLoc.hs
index 8e17561651..03e415b816 100644
--- a/compiler/basicTypes/SrcLoc.hs
+++ b/compiler/basicTypes/SrcLoc.hs
@@ -43,7 +43,7 @@ module SrcLoc (
srcSpanStart, srcSpanEnd,
realSrcSpanStart, realSrcSpanEnd,
srcSpanFileName_maybe,
- showUserSpan,
+ showUserSpan, pprUserRealSpan,
-- ** Unsafely deconstructing SrcSpan
-- These are dubious exports, because they crash on some inputs
@@ -53,6 +53,7 @@ module SrcLoc (
-- ** Predicates on SrcSpan
isGoodSrcSpan, isOneLineSpan,
+ containsSpan,
-- * Located
Located,
@@ -264,8 +265,8 @@ data SrcSpan =
| UnhelpfulSpan !FastString -- Just a general indication
-- also used to indicate an empty span
- deriving (Eq, Typeable, Show) -- Show is used by Lexer.x, because we
- -- derive Show for Token
+ deriving (Eq, Ord, Typeable, Show) -- Show is used by Lexer.x, because we
+ -- derive Show for Token
-- | Built-in "bad" 'SrcSpan's for common sources of location uncertainty
noSrcSpan, wiredInSrcSpan :: SrcSpan
@@ -348,9 +349,19 @@ isOneLineSpan :: SrcSpan -> Bool
isOneLineSpan (RealSrcSpan s) = srcSpanStartLine s == srcSpanEndLine s
isOneLineSpan (UnhelpfulSpan _) = False
+-- | Tests whether the first span "contains" the other span, meaning
+-- that it covers at least as much source code. True where spans are equal.
+containsSpan :: RealSrcSpan -> RealSrcSpan -> Bool
+containsSpan s1 s2
+ = srcSpanFile s1 == srcSpanFile s2
+ && (srcSpanStartLine s1, srcSpanStartCol s1)
+ <= (srcSpanStartLine s2, srcSpanStartCol s2)
+ && (srcSpanEndLine s1, srcSpanEndCol s1)
+ >= (srcSpanEndLine s2, srcSpanEndCol s2)
+
{-
-************************************************************************
-* *
+%************************************************************************
+%* *
\subsection[SrcSpan-unsafe-access-fns]{Unsafe access functions}
* *
************************************************************************
@@ -418,11 +429,12 @@ srcSpanFileName_maybe (UnhelpfulSpan _) = Nothing
************************************************************************
-}
--- We want to order SrcSpans first by the start point, then by the end point.
-instance Ord SrcSpan where
+-- We want to order RealSrcSpans first by the start point, then by the
+-- end point.
+instance Ord RealSrcSpan where
a `compare` b =
- (srcSpanStart a `compare` srcSpanStart b) `thenCmp`
- (srcSpanEnd a `compare` srcSpanEnd b)
+ (realSrcSpanStart a `compare` realSrcSpanStart b) `thenCmp`
+ (realSrcSpanEnd a `compare` realSrcSpanEnd b)
instance Show RealSrcLoc where
show (SrcLoc filename row col)
diff --git a/compiler/coreSyn/CoreArity.hs b/compiler/coreSyn/CoreArity.hs
index 5128891763..07ef3980c7 100644
--- a/compiler/coreSyn/CoreArity.hs
+++ b/compiler/coreSyn/CoreArity.hs
@@ -822,6 +822,23 @@ Note that SCCs are not treated specially by etaExpand. If we have
etaExpand 2 (\x -> scc "foo" e)
= (\xy -> (scc "foo" e) y)
So the costs of evaluating 'e' (not 'e y') are attributed to "foo"
+
+Note [Eta expansion and source notes]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+CorePrep puts floatable ticks outside of value applications, but not
+type applications. As a result we might be trying to eta-expand an
+expression like
+
+ (src<...> v) @a
+
+which we want to lead to code like
+
+ \x -> src<...> v @a x
+
+This means that we need to look through type applications and be ready
+to re-add floats on the top.
+
-}
-- | @etaExpand n us e ty@ returns an expression with
@@ -854,13 +871,21 @@ etaExpand n orig_expr
go 0 expr = expr
go n (Lam v body) | isTyVar v = Lam v (go n body)
| otherwise = Lam v (go (n-1) body)
- go n (Cast expr co) = Cast (go n expr) co
- go n expr = -- pprTrace "ee" (vcat [ppr orig_expr, ppr expr, ppr etas]) $
- etaInfoAbs etas (etaInfoApp subst' expr etas)
- where
- in_scope = mkInScopeSet (exprFreeVars expr)
- (in_scope', etas) = mkEtaWW n orig_expr in_scope (exprType expr)
- subst' = mkEmptySubst in_scope'
+ go n (Cast expr co) = Cast (go n expr) co
+ go n expr
+ = -- pprTrace "ee" (vcat [ppr orig_expr, ppr expr, ppr etas]) $
+ retick $ etaInfoAbs etas (etaInfoApp subst' sexpr etas)
+ where
+ in_scope = mkInScopeSet (exprFreeVars expr)
+ (in_scope', etas) = mkEtaWW n orig_expr in_scope (exprType expr)
+ subst' = mkEmptySubst in_scope'
+
+ -- Find ticks behind type apps.
+ -- See Note [Eta expansion and source notes]
+ (expr', args) = collectArgs expr
+ (ticks, expr'') = stripTicksTop tickishFloatable expr'
+ sexpr = foldl App expr'' args
+ retick expr = foldr mkTick expr ticks
-- Wrapper Unwrapper
--------------
diff --git a/compiler/coreSyn/CoreFVs.hs b/compiler/coreSyn/CoreFVs.hs
index af475bab3f..cce313df4b 100644
--- a/compiler/coreSyn/CoreFVs.hs
+++ b/compiler/coreSyn/CoreFVs.hs
@@ -248,7 +248,7 @@ exprOrphNames e
go (Coercion co) = orphNamesOfCo co
go (App e1 e2) = go e1 `unionNameSet` go e2
go (Lam v e) = go e `delFromNameSet` idName v
- go (Tick _ e) = go e
+ go (Tick _ e) = go e
go (Cast e co) = go e `unionNameSet` orphNamesOfCo co
go (Let (NonRec _ r) e) = go e `unionNameSet` go r
go (Let (Rec prs) e) = exprsOrphNames (map snd prs) `unionNameSet` go e
diff --git a/compiler/coreSyn/CorePrep.hs b/compiler/coreSyn/CorePrep.hs
index 62efae2919..f1bdd73a59 100644
--- a/compiler/coreSyn/CorePrep.hs
+++ b/compiler/coreSyn/CorePrep.hs
@@ -54,6 +54,8 @@ import Outputable
import Platform
import FastString
import Config
+import Name ( NamedThing(..), nameSrcSpan )
+import SrcLoc ( SrcSpan(..), realSrcLocSpan, mkRealSrcLoc )
import Data.Bits
import Data.List ( mapAccumL )
import Control.Monad
@@ -158,13 +160,14 @@ type CpeRhs = CoreExpr -- Non-terminal 'rhs'
************************************************************************
-}
-corePrepPgm :: DynFlags -> HscEnv -> CoreProgram -> [TyCon] -> IO CoreProgram
-corePrepPgm dflags hsc_env binds data_tycons = do
+corePrepPgm :: HscEnv -> ModLocation -> CoreProgram -> [TyCon] -> IO CoreProgram
+corePrepPgm hsc_env mod_loc binds data_tycons = do
+ let dflags = hsc_dflags hsc_env
showPass dflags "CorePrep"
us <- mkSplitUniqSupply 's'
initialCorePrepEnv <- mkInitialCorePrepEnv dflags hsc_env
- let implicit_binds = mkDataConWorkers data_tycons
+ let implicit_binds = mkDataConWorkers dflags mod_loc data_tycons
-- NB: we must feed mkImplicitBinds through corePrep too
-- so that they are suitably cloned and eta-expanded
@@ -195,14 +198,26 @@ corePrepTopBinds initialCorePrepEnv binds
binds' <- go env' binds
return (bind' `appendFloats` binds')
-mkDataConWorkers :: [TyCon] -> [CoreBind]
+mkDataConWorkers :: DynFlags -> ModLocation -> [TyCon] -> [CoreBind]
-- See Note [Data constructor workers]
-- c.f. Note [Injecting implicit bindings] in TidyPgm
-mkDataConWorkers data_tycons
- = [ NonRec id (Var id) -- The ice is thin here, but it works
+mkDataConWorkers dflags mod_loc data_tycons
+ = [ NonRec id (tick_it (getName data_con) (Var id))
+ -- The ice is thin here, but it works
| tycon <- data_tycons, -- CorePrep will eta-expand it
data_con <- tyConDataCons tycon,
- let id = dataConWorkId data_con ]
+ let id = dataConWorkId data_con
+ ]
+ where
+ -- If we want to generate debug info, we put a source note on the
+ -- worker. This is useful, especially for heap profiling.
+ tick_it name
+ | not (gopt Opt_Debug dflags) = id
+ | RealSrcSpan span <- nameSrcSpan name = tick span
+ | Just file <- ml_hs_file mod_loc = tick (span1 file)
+ | otherwise = tick (span1 "???")
+ where tick span = Tick (SourceNote span $ showSDoc dflags (ppr name))
+ span1 file = realSrcLocSpan $ mkRealSrcLoc (mkFastString file) 1 1
{-
Note [Floating out of top level bindings]
@@ -579,7 +594,7 @@ rhsToBody :: CpeRhs -> UniqSM (Floats, CpeBody)
-- Remove top level lambdas by let-binding
rhsToBody (Tick t expr)
- | not (tickishScoped t) -- we can only float out of non-scoped annotations
+ | tickishScoped t == NoScope -- only float out of non-scoped annotations
= do { (floats, expr') <- rhsToBody expr
; return (floats, Tick t expr') }
diff --git a/compiler/coreSyn/CoreSubst.hs b/compiler/coreSyn/CoreSubst.hs
index 82e18ca5ba..b381dc8db7 100644
--- a/compiler/coreSyn/CoreSubst.hs
+++ b/compiler/coreSyn/CoreSubst.hs
@@ -372,7 +372,7 @@ subst_expr subst expr
go (Coercion co) = Coercion (substCo subst co)
go (Lit lit) = Lit lit
go (App fun arg) = App (go fun) (go arg)
- go (Tick tickish e) = Tick (substTickish subst tickish) (go e)
+ go (Tick tickish e) = mkTick (substTickish subst tickish) (go e)
go (Cast e co) = Cast (go e) (substCo subst co)
-- Do not optimise even identity coercions
-- Reason: substitution applies to the LHS of RULES, and
@@ -892,7 +892,7 @@ simple_opt_expr subst expr
go (Type ty) = Type (substTy subst ty)
go (Coercion co) = Coercion (optCoercion (getCvSubst subst) co)
go (Lit lit) = Lit lit
- go (Tick tickish e) = Tick (substTickish subst tickish) (go e)
+ go (Tick tickish e) = mkTick (substTickish subst tickish) (go e)
go (Cast e co) | isReflCo co' = go e
| otherwise = Cast (go e) co'
where
@@ -956,6 +956,10 @@ simple_app subst (Var v) as
| isCompulsoryUnfolding (idUnfolding v)
-- See Note [Unfold compulsory unfoldings in LHSs]
= simple_app subst (unfoldingTemplate (idUnfolding v)) as
+simple_app subst (Tick t e) as
+ -- Okay to do "(Tick t e) x ==> Tick t (e x)"?
+ | t `tickishScopesLike` SoftScope
+ = mkTick t $ simple_app subst e as
simple_app subst e as
= foldl App (simple_opt_expr subst e) as
@@ -1348,36 +1352,44 @@ Currently, it is used in Rules.match, and is required to make
"map coerce = coerce" match.
-}
-exprIsLambda_maybe :: InScopeEnv -> CoreExpr -> Maybe (Var, CoreExpr)
+exprIsLambda_maybe :: InScopeEnv -> CoreExpr
+ -> Maybe (Var, CoreExpr,[Tickish Id])
-- See Note [exprIsLambda_maybe]
-- The simple case: It is a lambda already
exprIsLambda_maybe _ (Lam x e)
- = Just (x, e)
+ = Just (x, e, [])
+
+-- Still straightforward: Ticks that we can float out of the way
+exprIsLambda_maybe (in_scope_set, id_unf) (Tick t e)
+ | tickishFloatable t
+ , Just (x, e, ts) <- exprIsLambda_maybe (in_scope_set, id_unf) e
+ = Just (x, e, t:ts)
-- Also possible: A casted lambda. Push the coercion inside
exprIsLambda_maybe (in_scope_set, id_unf) (Cast casted_e co)
- | Just (x, e) <- exprIsLambda_maybe (in_scope_set, id_unf) casted_e
+ | Just (x, e,ts) <- exprIsLambda_maybe (in_scope_set, id_unf) casted_e
-- Only do value lambdas.
-- this implies that x is not in scope in gamma (makes this code simpler)
, not (isTyVar x) && not (isCoVar x)
, ASSERT( not $ x `elemVarSet` tyCoVarsOfCo co) True
- , let res = pushCoercionIntoLambda in_scope_set x e co
- = -- pprTrace "exprIsLambda_maybe:Cast" (vcat [ppr casted_e, ppr co, ppr res])
+ , Just (x',e') <- pushCoercionIntoLambda in_scope_set x e co
+ , let res = Just (x',e',ts)
+ = --pprTrace "exprIsLambda_maybe:Cast" (vcat [ppr casted_e,ppr co,ppr res)])
res
-- Another attempt: See if we find a partial unfolding
exprIsLambda_maybe (in_scope_set, id_unf) e
- | (Var f, as) <- collectArgs e
+ | (Var f, as, ts) <- collectArgsTicks tickishFloatable e
, idArity f > length (filter isValArg as)
-- Make sure there is hope to get a lambda
, Just rhs <- expandUnfolding_maybe (id_unf f)
-- Optimize, for beta-reduction
, let e' = simpleOptExprWith (mkEmptySubst in_scope_set) (rhs `mkApps` as)
-- Recurse, because of possible casts
- , Just (x', e'') <- exprIsLambda_maybe (in_scope_set, id_unf) e'
- , let res = Just (x', e'')
- = -- pprTrace "exprIsLambda_maybe:Unfold" (vcat [ppr e, ppr res])
+ , Just (x', e'', ts') <- exprIsLambda_maybe (in_scope_set, id_unf) e'
+ , let res = Just (x', e'', ts++ts')
+ = -- pprTrace "exprIsLambda_maybe:Unfold" (vcat [ppr e, ppr (x',e'')])
res
exprIsLambda_maybe _ _e
diff --git a/compiler/coreSyn/CoreSyn.hs b/compiler/coreSyn/CoreSyn.hs
index 0c6ee7c38e..1a1f8404cc 100644
--- a/compiler/coreSyn/CoreSyn.hs
+++ b/compiler/coreSyn/CoreSyn.hs
@@ -8,7 +8,8 @@
-- | CoreSyn holds all the main data types for use by for the Glasgow Haskell Compiler midsection
module CoreSyn (
-- * Main data types
- Expr(..), Alt, Bind(..), AltCon(..), Arg, Tickish(..),
+ Expr(..), Alt, Bind(..), AltCon(..), Arg,
+ Tickish(..), TickishScoping(..), TickishPlacement(..),
CoreProgram, CoreExpr, CoreAlt, CoreBind, CoreArg, CoreBndr,
TaggedExpr, TaggedAlt, TaggedBind, TaggedArg, TaggedBndr(..), deTagExpr,
@@ -31,13 +32,15 @@ module CoreSyn (
-- ** Simple 'Expr' access functions and predicates
bindersOf, bindersOfBinds, rhssOfBind, rhssOfAlts,
collectBinders, collectTyBinders, collectValBinders, collectTyAndValBinders,
- collectArgs, flattenBinds,
+ collectArgs, collectArgsTicks, flattenBinds,
isValArg, isTypeArg, isTyCoArg, valArgCount, valBndrCount,
isRuntimeArg, isRuntimeVar,
- tickishCounts, tickishScoped, tickishIsCode, mkNoCount, mkNoScope,
- tickishCanSplit,
+ tickishCounts, tickishScoped, tickishScopesLike, tickishFloatable,
+ tickishCanSplit, mkNoCount, mkNoScope,
+ tickishIsCode, tickishPlace,
+ tickishContains,
-- * Unfolding data types
Unfolding(..), UnfoldingGuidance(..), UnfoldingSource(..),
@@ -62,7 +65,7 @@ module CoreSyn (
AnnExpr, AnnExpr'(..), AnnBind(..), AnnAlt,
-- ** Operations on annotated expressions
- collectAnnArgs,
+ collectAnnArgs, collectAnnArgsTicks,
-- ** Operations on annotations
deAnnotate, deAnnotate', deAnnAlt, collectAnnBndrs,
@@ -97,6 +100,7 @@ import DynFlags
import FastString
import Outputable
import Util
+import SrcLoc ( RealSrcSpan, containsSpan )
import Data.Data hiding (TyCon)
import Data.Int
@@ -466,6 +470,28 @@ data Tickish id =
-- Note [substTickish] in CoreSubst.
}
+ -- | A source note.
+ --
+ -- Source notes are pure annotations: Their presence should neither
+ -- influence compilation nor execution. The semantics are given by
+ -- causality: The presence of a source note means that a local
+ -- change in the referenced source code span will possibly provoke
+ -- the generated code to change. On the flip-side, the functionality
+ -- of annotated code *must* be invariant against changes to all
+ -- source code *except* the spans referenced in the source notes
+ -- (see "Causality of optimized Haskell" paper for details).
+ --
+ -- Therefore extending the scope of any given source note is always
+ -- valid. Note that it is still undesirable though, as this reduces
+ -- their usefulness for debugging and profiling. Therefore we will
+ -- generally try only to make use of this property where it is
+ -- neccessary to enable optimizations.
+ | SourceNote
+ { sourceSpan :: RealSrcSpan -- ^ Source covered
+ , sourceName :: String -- ^ Name for source location
+ -- (uses same names as CCs)
+ }
+
deriving (Eq, Ord, Data, Typeable)
@@ -477,41 +503,200 @@ data Tickish id =
-- However, we still allow the simplifier to increase or decrease
-- sharing, so in practice the actual number of ticks may vary, except
-- that we never change the value from zero to non-zero or vice versa.
---
tickishCounts :: Tickish id -> Bool
tickishCounts n@ProfNote{} = profNoteCount n
tickishCounts HpcTick{} = True
tickishCounts Breakpoint{} = True
-
-tickishScoped :: Tickish id -> Bool
-tickishScoped n@ProfNote{} = profNoteScope n
-tickishScoped HpcTick{} = False
-tickishScoped Breakpoint{} = True
+tickishCounts _ = False
+
+
+-- | Specifies the scoping behaviour of ticks. This governs the
+-- behaviour of ticks that care about the covered code and the cost
+-- associated with it. Important for ticks relating to profiling.
+data TickishScoping =
+ -- | No scoping: The tick does not care about what code it
+ -- covers. Transformations can freely move code inside as well as
+ -- outside without any additional annotation obligations
+ NoScope
+
+ -- | Soft scoping: We want all code that is covered to stay
+ -- covered. Note that this scope type does not forbid
+ -- transformations from happening, as as long as all results of
+ -- the transformations are still covered by this tick or a copy of
+ -- it. For example
+ --
+ -- let x = tick<...> (let y = foo in bar) in baz
+ -- ===>
+ -- let x = tick<...> bar; y = tick<...> foo in baz
+ --
+ -- Is a valid transformation as far as "bar" and "foo" is
+ -- concerned, because both still are scoped over by the tick.
+ --
+ -- Note though that one might object to the "let" not being
+ -- covered by the tick any more. However, we are generally lax
+ -- with this - constant costs don't matter too much, and given
+ -- that the "let" was effectively merged we can view it as having
+ -- lost its identity anyway.
+ --
+ -- Also note that this scoping behaviour allows floating a tick
+ -- "upwards" in pretty much any situation. For example:
+ --
+ -- case foo of x -> tick<...> bar
+ -- ==>
+ -- tick<...> case foo of x -> bar
+ --
+ -- While this is always leagl, we want to make a best effort to
+ -- only make us of this where it exposes transformation
+ -- opportunities.
+ | SoftScope
+
+ -- | Cost centre scoping: We don't want any costs to move to other
+ -- cost-centre stacks. This means we not only want no code or cost
+ -- to get moved out of their cost centres, but we also object to
+ -- code getting associated with new cost-centre ticks - or
+ -- changing the order in which they get applied.
+ --
+ -- A rule of thumb is that we don't want any code to gain new
+ -- annotations. However, there are notable exceptions, for
+ -- example:
+ --
+ -- let f = \y -> foo in tick<...> ... (f x) ...
+ -- ==>
+ -- tick<...> ... foo[x/y] ...
+ --
+ -- In-lining lambdas like this is always legal, because inlining a
+ -- function does not change the cost-centre stack when the
+ -- function is called.
+ | CostCentreScope
+
+ deriving (Eq)
+
+-- | Returns the intended scoping rule for a Tickish
+tickishScoped :: Tickish id -> TickishScoping
+tickishScoped n@ProfNote{}
+ | profNoteScope n = CostCentreScope
+ | otherwise = NoScope
+tickishScoped HpcTick{} = NoScope
+tickishScoped Breakpoint{} = CostCentreScope
-- Breakpoints are scoped: eventually we're going to do call
-- stacks, but also this helps prevent the simplifier from moving
-- breakpoints around and changing their result type (see #1531).
+tickishScoped SourceNote{} = SoftScope
+
+-- | Returns whether the tick scoping rule is at least as permissive
+-- as the given scoping rule.
+tickishScopesLike :: Tickish id -> TickishScoping -> Bool
+tickishScopesLike t scope = tickishScoped t `like` scope
+ where NoScope `like` _ = True
+ _ `like` NoScope = False
+ SoftScope `like` _ = True
+ _ `like` SoftScope = False
+ CostCentreScope `like` _ = True
+
+-- | Returns @True@ for ticks that can be floated upwards easily even
+-- where it might change execution counts, such as:
+--
+-- Just (tick<...> foo)
+-- ==>
+-- tick<...> (Just foo)
+--
+-- This is a combination of @tickishSoftScope@ and
+-- @tickishCounts@. Note that in principle splittable ticks can become
+-- floatable using @mkNoTick@ -- even though there's currently no
+-- tickish for which that is the case.
+tickishFloatable :: Tickish id -> Bool
+tickishFloatable t = t `tickishScopesLike` SoftScope && not (tickishCounts t)
+
+-- | Returns @True@ for a tick that is both counting /and/ scoping and
+-- can be split into its (tick, scope) parts using 'mkNoScope' and
+-- 'mkNoTick' respectively.
+tickishCanSplit :: Tickish id -> Bool
+tickishCanSplit ProfNote{profNoteScope = True, profNoteCount = True}
+ = True
+tickishCanSplit _ = False
mkNoCount :: Tickish id -> Tickish id
-mkNoCount n@ProfNote{} = n {profNoteCount = False}
-mkNoCount Breakpoint{} = panic "mkNoCount: Breakpoint" -- cannot split a BP
-mkNoCount HpcTick{} = panic "mkNoCount: HpcTick"
+mkNoCount n | not (tickishCounts n) = n
+ | not (tickishCanSplit n) = panic "mkNoCount: Cannot split!"
+mkNoCount n@ProfNote{} = n {profNoteCount = False}
+mkNoCount _ = panic "mkNoCount: Undefined split!"
mkNoScope :: Tickish id -> Tickish id
-mkNoScope n@ProfNote{} = n {profNoteScope = False}
-mkNoScope Breakpoint{} = panic "mkNoScope: Breakpoint" -- cannot split a BP
-mkNoScope HpcTick{} = panic "mkNoScope: HpcTick"
-
--- | Return True if this source annotation compiles to some code, or will
--- disappear before the backend.
+mkNoScope n | tickishScoped n == NoScope = n
+ | not (tickishCanSplit n) = panic "mkNoScope: Cannot split!"
+mkNoScope n@ProfNote{} = n {profNoteScope = False}
+mkNoScope _ = panic "mkNoScope: Undefined split!"
+
+-- | Return @True@ if this source annotation compiles to some backend
+-- code. Without this flag, the tickish is seen as a simple annotation
+-- that does not have any associated evaluation code.
+--
+-- What this means that we are allowed to disregard the tick if doing
+-- so means that we can skip generating any code in the first place. A
+-- typical example is top-level bindings:
+--
+-- foo = tick<...> \y -> ...
+-- ==>
+-- foo = \y -> tick<...> ...
+--
+-- Here there is just no operational difference between the first and
+-- the second version. Therefore code generation should simply
+-- translate the code as if it found the latter.
tickishIsCode :: Tickish id -> Bool
-tickishIsCode _tickish = True -- all of them for now
-
--- | Return True if this Tick can be split into (tick,scope) parts with
--- 'mkNoScope' and 'mkNoCount' respectively.
-tickishCanSplit :: Tickish Id -> Bool
-tickishCanSplit Breakpoint{} = False
-tickishCanSplit HpcTick{} = False
-tickishCanSplit _ = True
+tickishIsCode SourceNote{} = False
+tickishIsCode _tickish = True -- all the rest for now
+
+
+-- | Governs the kind of expression that the tick gets placed on when
+-- annotating for example using @mkTick@. If we find that we want to
+-- put a tickish on an expression ruled out here, we try to float it
+-- inwards until we find a suitable expression.
+data TickishPlacement =
+
+ -- | Place ticks exactly on run-time expressions. We can still
+ -- move the tick through pure compile-time constructs such as
+ -- other ticks, casts or type lambdas. This is the most
+ -- restrictive placement rule for ticks, as all tickishs have in
+ -- common that they want to track runtime processes. The only
+ -- legal placement rule for counting ticks.
+ PlaceRuntime
+
+ -- | As @PlaceRuntime@, but we float the tick through all
+ -- lambdas. This makes sense where there is little difference
+ -- between annotating the lambda and annotating the lambda's code.
+ | PlaceNonLam
+
+ -- | In addition to floating through lambdas, cost-centre style
+ -- tickishs can also be moved from constructors, non-function
+ -- variables and literals. For example:
+ --
+ -- let x = scc<...> C (scc<...> y) (scc<...> 3) in ...
+ --
+ -- Neither the constructor application, the variable or the
+ -- literal are likely to have any cost worth mentioning. And even
+ -- if y names a thunk, the call would not care about the
+ -- evaluation context. Therefore removing all annotations in the
+ -- above example is safe.
+ | PlaceCostCentre
+
+ deriving (Eq)
+
+-- | Placement behaviour we want for the ticks
+tickishPlace :: Tickish id -> TickishPlacement
+tickishPlace n@ProfNote{}
+ | profNoteCount n = PlaceRuntime
+ | otherwise = PlaceCostCentre
+tickishPlace HpcTick{} = PlaceRuntime
+tickishPlace Breakpoint{} = PlaceRuntime
+tickishPlace SourceNote{} = PlaceNonLam
+
+-- | Returns whether one tick "contains" the other one, therefore
+-- making the second tick redundant.
+tickishContains :: Eq b => Tickish b -> Tickish b -> Bool
+tickishContains (SourceNote sp1 n1) (SourceNote sp2 n2)
+ = n1 == n2 && containsSpan sp1 sp2
+tickishContains t1 t2
+ = t1 == t2
{-
************************************************************************
@@ -1324,6 +1509,19 @@ collectArgs expr
go (App f a) as = go f (a:as)
go e as = (e, as)
+-- | Like @collectArgs@, but also collects looks through floatable
+-- ticks if it means that we can find more arguments.
+collectArgsTicks :: (Tickish Id -> Bool) -> Expr b
+ -> (Expr b, [Arg b], [Tickish Id])
+collectArgsTicks skipTick expr
+ = go expr [] []
+ where
+ go (App f a) as ts = go f (a:as) ts
+ go (Tick t e) as ts
+ | skipTick t = go e as (t:ts)
+ go e as ts = (e, as, reverse ts)
+
+
{-
************************************************************************
* *
@@ -1388,8 +1586,8 @@ seqExpr (Lam b e) = seqBndr b `seq` seqExpr e
seqExpr (Let b e) = seqBind b `seq` seqExpr e
seqExpr (Case e b t as) = seqExpr e `seq` seqBndr b `seq` seqType t `seq` seqAlts as
seqExpr (Cast e co) = seqExpr e `seq` seqCo co
-seqExpr (Tick n e) = seqTickish n `seq` seqExpr e
-seqExpr (Type t) = seqType t
+seqExpr (Tick n e) = seqTickish n `seq` seqExpr e
+seqExpr (Type t) = seqType t
seqExpr (Coercion co) = seqCo co
seqExprs :: [CoreExpr] -> ()
@@ -1400,6 +1598,7 @@ seqTickish :: Tickish Id -> ()
seqTickish ProfNote{ profNoteCC = cc } = cc `seq` ()
seqTickish HpcTick{} = ()
seqTickish Breakpoint{ breakpointFVs = ids } = seqBndrs ids
+seqTickish SourceNote{} = ()
seqBndr :: CoreBndr -> ()
seqBndr b = b `seq` ()
@@ -1468,6 +1667,16 @@ collectAnnArgs expr
go (_, AnnApp f a) as = go f (a:as)
go e as = (e, as)
+collectAnnArgsTicks :: (Tickish Var -> Bool) -> AnnExpr b a
+ -> (AnnExpr b a, [AnnExpr b a], [Tickish Var])
+collectAnnArgsTicks tickishOk expr
+ = go expr [] []
+ where
+ go (_, AnnApp f a) as ts = go f (a:as) ts
+ go (_, AnnTick t e) as ts | tickishOk t
+ = go e as (t:ts)
+ go e as ts = (e, as, reverse ts)
+
deAnnotate :: AnnExpr bndr annot -> Expr bndr
deAnnotate (_, e) = deAnnotate' e
diff --git a/compiler/coreSyn/CoreUnfold.hs b/compiler/coreSyn/CoreUnfold.hs
index dc9f95e73a..1dbd5edd7a 100644
--- a/compiler/coreSyn/CoreUnfold.hs
+++ b/compiler/coreSyn/CoreUnfold.hs
@@ -328,6 +328,9 @@ calcUnfoldingGuidance
:: DynFlags
-> CoreExpr -- Expression to look at
-> UnfoldingGuidance
+calcUnfoldingGuidance dflags (Tick t expr)
+ | not (tickishIsCode t) -- non-code ticks don't matter for unfolding
+ = calcUnfoldingGuidance dflags expr
calcUnfoldingGuidance dflags expr
= case sizeExpr dflags (iUnbox bOMB_OUT_SIZE) val_bndrs body of
TooBig -> UnfNever
@@ -576,6 +579,7 @@ sizeExpr dflags bOMB_OUT_SIZE top_args expr
| otherwise = size_up arg `addSizeNSD`
size_up_app fun (arg:args) voids
size_up_app (Var fun) args voids = size_up_call fun args voids
+ size_up_app (Tick _ expr) args voids = size_up_app expr args voids
size_up_app other args voids = size_up other `addSizeN` (length args - voids)
------------
@@ -623,8 +627,9 @@ sizeExpr dflags bOMB_OUT_SIZE top_args expr
isRealWorldId id = idType id `eqType` realWorldStatePrimTy
-- an expression of type State# RealWorld must be a variable
- isRealWorldExpr (Var id) = isRealWorldId id
- isRealWorldExpr _ = False
+ isRealWorldExpr (Var id) = isRealWorldId id
+ isRealWorldExpr (Tick _ e) = isRealWorldExpr e
+ isRealWorldExpr _ = False
-- | Finds a nominal size of a string literal.
litSize :: Literal -> Int
diff --git a/compiler/coreSyn/CoreUtils.hs b/compiler/coreSyn/CoreUtils.hs
index ffb327523c..c5340b867b 100644
--- a/compiler/coreSyn/CoreUtils.hs
+++ b/compiler/coreSyn/CoreUtils.hs
@@ -12,7 +12,7 @@ Utility functions on @Core@ syntax
module CoreUtils (
-- * Constructing expressions
mkCast,
- mkTick, mkTickNoHNF, tickHNFArgs,
+ mkTick, mkTicks, mkTickNoHNF, tickHNFArgs,
bindNonRec, needsCaseBinding,
mkAltExpr,
@@ -33,14 +33,17 @@ module CoreUtils (
CoreStats(..), coreBindsStats,
-- * Equality
- cheapEqExpr, eqExpr,
+ cheapEqExpr, cheapEqExpr', eqExpr,
-- * Eta reduction
tryEtaReduce,
-- * Manipulating data constructors and types
applyTypeToArgs, applyTypeToArg,
- dataConRepInstPat, dataConRepFSInstPat
+ dataConRepInstPat, dataConRepFSInstPat,
+
+ -- * Working with ticks
+ stripTicksTop, stripTicksTopE, stripTicksTopT, stripTicks,
) where
#include "HsVersions.h"
@@ -70,7 +73,13 @@ import Maybes
import Platform
import Util
import Pair
+import Data.Function ( on )
import Data.List
+import Control.Applicative
+#if __GLASGOW_HASKELL__ < 709
+import Data.Traversable ( traverse )
+#endif
+import OrdList
{-
************************************************************************
@@ -211,6 +220,9 @@ mkCast (Cast expr co2) co
, ptext (sLit "co:") <+> ppr co ]) )
mkCast expr (mkTransCo co2 co)
+mkCast (Tick t expr) co
+ = Tick t (mkCast expr co)
+
mkCast expr co
= let Pair from_ty _to_ty = coercionKind co in
-- if to_ty `eqType` from_ty
@@ -222,48 +234,84 @@ mkCast expr co
-- | Wraps the given expression in the source annotation, dropping the
-- annotation if possible.
mkTick :: Tickish Id -> CoreExpr -> CoreExpr
+mkTick t orig_expr = mkTick' id id orig_expr
+ where
+ -- Some ticks (cost-centres) can be split in two, with the
+ -- non-counting part having laxer placement properties.
+ canSplit = tickishCanSplit t && tickishPlace (mkNoCount t) /= tickishPlace t
-mkTick t (Var x)
- | isFunTy (idType x) = Tick t (Var x)
- | otherwise
- = if tickishCounts t
- then if tickishScoped t && tickishCanSplit t
- then Tick (mkNoScope t) (Var x)
- else Tick t (Var x)
- else Var x
-
-mkTick t (Cast e co)
- = Cast (mkTick t e) co -- Move tick inside cast
-
-mkTick _ (Coercion co) = Coercion co
-
-mkTick t (Lit l)
- | not (tickishCounts t) = Lit l
-
-mkTick t expr@(App f arg)
- | not (isRuntimeArg arg) = App (mkTick t f) arg
- | isSaturatedConApp expr
- = if not (tickishCounts t)
- then tickHNFArgs t expr
- else if tickishScoped t && tickishCanSplit t
- then Tick (mkNoScope t) (tickHNFArgs (mkNoCount t) expr)
- else Tick t expr
-
-mkTick t (Lam x e)
- -- if this is a type lambda, or the tick does not count entries,
- -- then we can push the tick inside:
- | not (isRuntimeVar x) || not (tickishCounts t) = Lam x (mkTick t e)
- -- if it is both counting and scoped, we split the tick into its
- -- two components, keep the counting tick on the outside of the lambda
- -- and push the scoped tick inside. The point of this is that the
- -- counting tick can probably be floated, and the lambda may then be
- -- in a position to be beta-reduced.
- | tickishScoped t && tickishCanSplit t
- = Tick (mkNoScope t) (Lam x (mkTick (mkNoCount t) e))
- -- just a counting tick: leave it on the outside
- | otherwise = Tick t (Lam x e)
-
-mkTick t other = Tick t other
+ mkTick' :: (CoreExpr -> CoreExpr) -- ^ apply after adding tick (float through)
+ -> (CoreExpr -> CoreExpr) -- ^ apply before adding tick (float with)
+ -> CoreExpr -- ^ current expression
+ -> CoreExpr
+ mkTick' top rest expr = case expr of
+
+ -- Cost centre ticks should never be reordered relative to each
+ -- other. Therefore we can stop whenever two collide.
+ Tick t2 e
+ | ProfNote{} <- t2, ProfNote{} <- t -> top $ Tick t $ rest expr
+
+ -- Otherwise we assume that ticks of different placements float
+ -- through each other.
+ | tickishPlace t2 /= tickishPlace t -> mkTick' (top . Tick t2) rest e
+
+ -- For annotations this is where we make sure to not introduce
+ -- redundant ticks.
+ | tickishContains t t2 -> mkTick' top rest e
+ | tickishContains t2 t -> orig_expr
+ | otherwise -> mkTick' top (rest . Tick t2) e
+
+ -- Ticks don't care about types, so we just float all ticks
+ -- through them. Note that it's not enough to check for these
+ -- cases top-level. While mkTick will never produce Core with type
+ -- expressions below ticks, such constructs can be the result of
+ -- unfoldings. We therefore make an effort to put everything into
+ -- the right place no matter what we start with.
+ Cast e co -> mkTick' (top . flip Cast co) rest e
+ Coercion co -> Coercion co
+
+ Lam x e
+ -- Always float through type lambdas. Even for non-type lambdas,
+ -- floating is allowed for all but the most strict placement rule.
+ | not (isRuntimeVar x) || tickishPlace t /= PlaceRuntime
+ -> mkTick' (top . Lam x) rest e
+
+ -- If it is both counting and scoped, we split the tick into its
+ -- two components, often allowing us to keep the counting tick on
+ -- the outside of the lambda and push the scoped tick inside.
+ -- The point of this is that the counting tick can probably be
+ -- floated, and the lambda may then be in a position to be
+ -- beta-reduced.
+ | canSplit
+ -> top $ Tick (mkNoScope t) $ rest $ Lam x $ mkTick (mkNoCount t) e
+
+ App f arg
+ -- Always float through type applications.
+ | not (isRuntimeArg arg)
+ -> mkTick' (top . flip App arg) rest f
+
+ -- We can also float through constructor applications, placement
+ -- permitting. Again we can split.
+ | isSaturatedConApp expr && (tickishPlace t==PlaceCostCentre || canSplit)
+ -> if tickishPlace t == PlaceCostCentre
+ then top $ rest $ tickHNFArgs t expr
+ else top $ Tick (mkNoScope t) $ rest $ tickHNFArgs (mkNoCount t) expr
+
+ Var x
+ | not (isFunTy (idType x)) && tickishPlace t == PlaceCostCentre
+ -> orig_expr
+ | canSplit
+ -> top $ Tick (mkNoScope t) $ rest expr
+
+ Lit{}
+ | tickishPlace t == PlaceCostCentre
+ -> orig_expr
+
+ -- Catch-all: Annotate where we stand
+ _any -> top $ Tick t $ rest expr
+
+mkTicks :: [Tickish Id] -> CoreExpr -> CoreExpr
+mkTicks ticks expr = foldr mkTick expr ticks
isSaturatedConApp :: CoreExpr -> Bool
isSaturatedConApp e = go e []
@@ -286,6 +334,48 @@ tickHNFArgs t e = push t e
push t (App f arg) = App (push t f) (mkTick t arg)
push _t e = e
+-- | Strip ticks satisfying a predicate from top of an expression
+stripTicksTop :: (Tickish Id -> Bool) -> Expr b -> ([Tickish Id], Expr b)
+stripTicksTop p = go []
+ where go ts (Tick t e) | p t = go (t:ts) e
+ go ts other = (reverse ts, other)
+
+-- | Strip ticks satisfying a predicate from top of an expression,
+-- returning the remaining expresion
+stripTicksTopE :: (Tickish Id -> Bool) -> Expr b -> Expr b
+stripTicksTopE p = go
+ where go (Tick t e) | p t = go e
+ go other = other
+
+-- | Strip ticks satisfying a predicate from top of an expression,
+-- returning the ticks
+stripTicksTopT :: (Tickish Id -> Bool) -> Expr b -> [Tickish Id]
+stripTicksTopT p = go []
+ where go ts (Tick t e) | p t = go (t:ts) e
+ go ts _ = ts
+
+-- | Completely strip ticks satisfying a predicate from an
+-- expression. Note this is O(n) in the size of the expression!
+stripTicks :: (Tickish Id -> Bool) -> Expr b -> ([Tickish Id], Expr b)
+stripTicks p expr = (fromOL ticks, expr')
+ where (ticks, expr') = go expr
+ -- Note that OrdList (Tickish Id) is a Monoid, which makes
+ -- ((,) (OrdList (Tickish Id))) an Applicative.
+ go (App e a) = App <$> go e <*> go a
+ go (Lam b e) = Lam b <$> go e
+ go (Let b e) = Let <$> go_bs b <*> go e
+ go (Case e b t as) = Case <$> go e <*> pure b <*> pure t
+ <*> traverse go_a as
+ go (Cast e c) = Cast <$> go e <*> pure c
+ go (Tick t e)
+ | p t = let (ts, e') = go e in (t `consOL` ts, e')
+ | otherwise = Tick t <$> go e
+ go other = pure other
+ go_bs (NonRec b e) = NonRec b <$> go e
+ go_bs (Rec bs) = Rec <$> traverse go_b bs
+ go_b (b, e) = (,) <$> pure b <*> go e
+ go_a (c,bs,e) = (,,) <$> pure c <*> pure bs <*> go e
+
{-
************************************************************************
* *
@@ -541,18 +631,21 @@ saturating them.
Note [Tick trivial]
~~~~~~~~~~~~~~~~~~~
-Ticks are not trivial. If we treat "tick<n> x" as trivial, it will be
-inlined inside lambdas and the entry count will be skewed, for
-example. Furthermore "scc<n> x" will turn into just "x" in mkTick.
+
+Ticks are only trivial if they are pure annotations. If we treat
+"tick<n> x" as trivial, it will be inlined inside lambdas and the
+entry count will be skewed, for example. Furthermore "scc<n> x" will
+turn into just "x" in mkTick.
-}
exprIsTrivial :: CoreExpr -> Bool
exprIsTrivial (Var _) = True -- See Note [Variables are trivial]
-exprIsTrivial (Type _) = True
+exprIsTrivial (Type _) = True
exprIsTrivial (Coercion _) = True
exprIsTrivial (Lit lit) = litIsTrivial lit
exprIsTrivial (App e arg) = not (isRuntimeArg arg) && exprIsTrivial e
-exprIsTrivial (Tick _ _) = False -- See Note [Tick trivial]
+exprIsTrivial (Tick t e) = not (tickishIsCode t) && exprIsTrivial e
+ -- See Note [Tick trivial]
exprIsTrivial (Cast e _) = exprIsTrivial e
exprIsTrivial (Lam b body) = not (isRuntimeVar b) && exprIsTrivial body
exprIsTrivial _ = False
@@ -767,8 +860,9 @@ exprIsCheap' good_app (Case e _ _ alts) = exprIsCheap' good_app e &&
exprIsCheap' good_app (Tick t e)
| tickishCounts t = False
| otherwise = exprIsCheap' good_app e
- -- never duplicate ticks. If we get this wrong, then HPC's entry
- -- counts will be off (check test in libraries/hpc/tests/raytrace)
+ -- never duplicate counting ticks. If we get this wrong, then
+ -- HPC's entry counts will be off (check test in
+ -- libraries/hpc/tests/raytrace)
exprIsCheap' good_app (Let (NonRec _ b) e)
= exprIsCheap' good_app b && exprIsCheap' good_app e
@@ -807,6 +901,10 @@ exprIsCheap' good_app other_expr -- Applications and variables
-- always gives bottom; we treat this as cheap
-- because it certainly doesn't need to be shared!
+ go (Tick t e) args
+ | not (tickishCounts t) -- don't duplicate counting ticks, see above
+ = go e args
+
go _ _ = False
--------------
@@ -955,8 +1053,9 @@ expr_ok primop_ok (Case e _ _ alts)
expr_ok primop_ok other_expr
= case collectArgs other_expr of
- (Var f, args) -> app_ok primop_ok f args
- _ -> False
+ (expr, args) | Var f <- stripTicksTopE (not . tickishCounts) expr
+ -> app_ok primop_ok f args
+ _ -> False
-----------------------------
app_ok :: (PrimOp -> Bool) -> Id -> [Expr b] -> Bool
@@ -1313,29 +1412,40 @@ c.f. add_evals in Simplify.simplAlt
--
-- See also 'exprIsBig'
cheapEqExpr :: Expr b -> Expr b -> Bool
+cheapEqExpr = cheapEqExpr' (const False)
+
+-- | Cheap expression equality test, can ignore ticks by type.
+cheapEqExpr' :: (Tickish Id -> Bool) -> Expr b -> Expr b -> Bool
+cheapEqExpr' ignoreTick = go_s
+ where go_s = go `on` stripTicksTopE ignoreTick
+ go (Var v1) (Var v2) = v1 == v2
+ go (Lit lit1) (Lit lit2) = lit1 == lit2
+ go (Type t1) (Type t2) = t1 `eqType` t2
+ go (Coercion c1) (Coercion c2) = c1 `coreEqCoercion` c2
-cheapEqExpr (Var v1) (Var v2) = v1==v2
-cheapEqExpr (Lit lit1) (Lit lit2) = lit1 == lit2
-cheapEqExpr (Type t1) (Type t2) = t1 `eqType` t2
-cheapEqExpr (Coercion c1) (Coercion c2) = c1 `coreEqCoercion` c2
+ go (App f1 a1) (App f2 a2)
+ = f1 `go_s` f2 && a1 `go_s` a2
-cheapEqExpr (App f1 a1) (App f2 a2)
- = f1 `cheapEqExpr` f2 && a1 `cheapEqExpr` a2
+ go (Cast e1 t1) (Cast e2 t2)
+ = e1 `go_s` e2 && t1 `coreEqCoercion` t2
-cheapEqExpr (Cast e1 t1) (Cast e2 t2)
- = e1 `cheapEqExpr` e2 && t1 `coreEqCoercion` t2
+ go (Tick t1 e1) (Tick t2 e2)
+ = t1 == t2 && e1 `go_s` e2
-cheapEqExpr _ _ = False
+ go _ _ = False
+ {-# INLINE go #-}
+{-# INLINE cheapEqExpr' #-}
exprIsBig :: Expr b -> Bool
-- ^ Returns @True@ of expressions that are too big to be compared by 'cheapEqExpr'
exprIsBig (Lit _) = False
exprIsBig (Var _) = False
-exprIsBig (Type _) = False
+exprIsBig (Type _) = False
exprIsBig (Coercion _) = False
exprIsBig (Lam _ e) = exprIsBig e
exprIsBig (App f a) = exprIsBig f || exprIsBig a
exprIsBig (Cast e _) = exprIsBig e -- Hopefully coercions are not too big!
+exprIsBig (Tick _ e) = exprIsBig e
exprIsBig _ = True
eqExpr :: InScopeSet -> CoreExpr -> CoreExpr -> Bool
@@ -1612,9 +1722,15 @@ tryEtaReduce bndrs body
= Just (mkCast fun co) -- Check for any of the binders free in the result
-- including the accumulated coercion
+ go bs (Tick t e) co
+ | tickishFloatable t
+ = fmap (Tick t) $ go bs e co
+ -- Float app ticks: \x -> Tick t (e x) ==> Tick t e
+
go (b : bs) (App fun arg) co
- | Just co' <- ok_arg b arg co
- = go bs fun co'
+ | Just (co', ticks) <- ok_arg b arg co
+ = fmap (flip (foldr mkTick) ticks) $ go bs fun co'
+ -- Float arg ticks: \x -> e (Tick t x) ==> Tick t e
go _ _ _ = Nothing -- Failure!
@@ -1622,6 +1738,7 @@ tryEtaReduce bndrs body
-- Note [Eta reduction conditions]
ok_fun (App fun (Type {})) = ok_fun fun
ok_fun (Cast fun _) = ok_fun fun
+ ok_fun (Tick _ expr) = ok_fun expr
ok_fun (Var fun_id) = ok_fun_id fun_id || all ok_lam bndrs
ok_fun _fun = False
@@ -1646,19 +1763,26 @@ tryEtaReduce bndrs body
ok_arg :: Var -- Of type bndr_t
-> CoreExpr -- Of type arg_t
-> Coercion -- Of kind (t1~t2)
- -> Maybe Coercion -- Of type (arg_t -> t1 ~ bndr_t -> t2)
+ -> Maybe (Coercion -- Of type (arg_t -> t1 ~ bndr_t -> t2)
-- (and similarly for tyvars, coercion args)
+ , [Tickish Var])
-- See Note [Eta reduction with casted arguments]
ok_arg bndr (Type ty) co
| Just tv <- getTyVar_maybe ty
- , bndr == tv = Just (mkForAllCo tv co)
+ , bndr == tv = Just (mkForAllCo tv co, [])
ok_arg bndr (Var v) co
- | bndr == v = Just (mkFunCo Representational
- (mkReflCo Representational (idType bndr)) co)
- ok_arg bndr (Cast (Var v) co_arg) co
- | bndr == v = Just (mkFunCo Representational (mkSymCo co_arg) co)
+ | bndr == v = let reflCo = mkReflCo Representational (idType bndr)
+ in Just (mkFunCo Representational reflCo co, [])
+ ok_arg bndr (Cast e co_arg) co
+ | (ticks, Var v) <- stripTicksTop tickishFloatable e
+ , bndr == v
+ = Just (mkFunCo Representational (mkSymCo co_arg) co, ticks)
-- The simplifier combines multiple casts into one,
-- so we can have a simple-minded pattern match here
+ ok_arg bndr (Tick t arg) co
+ | tickishFloatable t, Just (co', ticks) <- ok_arg bndr arg co
+ = Just (co', t:ticks)
+
ok_arg _ _ _ = Nothing
{-
diff --git a/compiler/coreSyn/PprCore.hs b/compiler/coreSyn/PprCore.hs
index acc6c79fa1..59c5214581 100644
--- a/compiler/coreSyn/PprCore.hs
+++ b/compiler/coreSyn/PprCore.hs
@@ -29,6 +29,7 @@ import BasicTypes
import Util
import Outputable
import FastString
+import SrcLoc ( pprUserRealSpan )
{-
************************************************************************
@@ -216,7 +217,10 @@ ppr_expr add_par (Let bind expr)
NonRec _ _ -> (sLit "let {")
ppr_expr add_par (Tick tickish expr)
- = add_par (sep [ppr tickish, pprCoreExpr expr])
+ = sdocWithDynFlags $ \dflags ->
+ if gopt Opt_PprShowTicks dflags
+ then add_par (sep [ppr tickish, pprCoreExpr expr])
+ else ppr_expr add_par expr
pprCoreAlt :: OutputableBndr a => (AltCon, [a] , Expr a) -> SDoc
pprCoreAlt (con, args, rhs)
@@ -490,7 +494,7 @@ pprRule (Rule { ru_name = name, ru_act = act, ru_fn = fn,
instance Outputable id => Outputable (Tickish id) where
ppr (HpcTick modl ix) =
- hcat [ptext (sLit "tick<"),
+ hcat [ptext (sLit "hpc<"),
ppr modl, comma,
ppr ix,
ptext (sLit ">")]
@@ -506,6 +510,8 @@ instance Outputable id => Outputable (Tickish id) where
(True,True) -> hcat [ptext (sLit "scctick<"), ppr cc, char '>']
(True,False) -> hcat [ptext (sLit "tick<"), ppr cc, char '>']
_ -> hcat [ptext (sLit "scc<"), ppr cc, char '>']
+ ppr (SourceNote span _) =
+ hcat [ ptext (sLit "src<"), pprUserRealSpan True span, char '>']
{-
-----------------------------------------------------
diff --git a/compiler/deSugar/Coverage.hs b/compiler/deSugar/Coverage.hs
index f57cc9e9f6..b9faf26e93 100644
--- a/compiler/deSugar/Coverage.hs
+++ b/compiler/deSugar/Coverage.hs
@@ -90,11 +90,12 @@ addTicksToBinds dflags mod mod_loc exports tyCons binds =
, density = mkDensity dflags
, this_mod = mod
, tickishType = case hscTarget dflags of
- HscInterpreted -> Breakpoints
- _ | gopt Opt_Hpc dflags -> HpcTicks
+ HscInterpreted -> Breakpoints
+ _ | gopt Opt_Hpc dflags -> HpcTicks
| gopt Opt_SccProfilingOn dflags
- -> ProfNotes
- | otherwise -> error "addTicksToBinds: No way to annotate!"
+ -> ProfNotes
+ | gopt Opt_Debug dflags -> SourceNotes
+ | otherwise -> error "addTicksToBinds: No way to annotate!"
})
(TT
{ tickBoxCount = 0
@@ -184,13 +185,14 @@ data TickDensity
mkDensity :: DynFlags -> TickDensity
mkDensity dflags
- | gopt Opt_Hpc dflags = TickForCoverage
+ | gopt Opt_Hpc dflags
+ || gopt Opt_Debug dflags = TickForCoverage
| HscInterpreted <- hscTarget dflags = TickForBreakPoints
| ProfAutoAll <- profAuto dflags = TickAllFunctions
| ProfAutoTop <- profAuto dflags = TickTopFunctions
| ProfAutoExports <- profAuto dflags = TickExportedFunctions
| ProfAutoCalls <- profAuto dflags = TickCallSites
- | otherwise = panic "desnity"
+ | otherwise = panic "density"
-- ToDo: -fhpc is taking priority over -fprof-auto here. It seems
-- that coverage works perfectly well with profiling, but you don't
-- get any auto-generated SCCs. It would make perfect sense to
@@ -939,7 +941,7 @@ data TickTransEnv = TTE { fileName :: FastString
-- deriving Show
-data TickishType = ProfNotes | HpcTicks | Breakpoints
+data TickishType = ProfNotes | HpcTicks | Breakpoints | SourceNotes
-- | Tickishs that only make sense when their source code location
@@ -1113,6 +1115,9 @@ mkTickish boxLabel countEntries topOnly pos fvs decl_path =
HpcTicks -> HpcTick (this_mod env) c
ProfNotes -> ProfNote cc count True{-scopes-}
Breakpoints -> Breakpoint c ids
+ SourceNotes | RealSrcSpan pos' <- pos
+ -> SourceNote pos' cc_name
+ _otherwise -> panic "mkTickish: bad source span!"
in
( tickish
, fvs
diff --git a/compiler/deSugar/Desugar.hs b/compiler/deSugar/Desugar.hs
index 9f6748b882..6d754c6d0b 100644
--- a/compiler/deSugar/Desugar.hs
+++ b/compiler/deSugar/Desugar.hs
@@ -104,6 +104,7 @@ deSugar hsc_env
target = hscTarget dflags
hpcInfo = emptyHpcInfo other_hpc_info
want_ticks = gopt Opt_Hpc dflags
+ || gopt Opt_Debug dflags
|| target == HscInterpreted
|| (gopt Opt_SccProfilingOn dflags
&& case profAuto dflags of
diff --git a/compiler/iface/IfaceSyn.hs b/compiler/iface/IfaceSyn.hs
index 6bb34838c5..d850f66de1 100644
--- a/compiler/iface/IfaceSyn.hs
+++ b/compiler/iface/IfaceSyn.hs
@@ -48,6 +48,7 @@ import BasicTypes
import Outputable
import FastString
import Module
+import SrcLoc
import Fingerprint
import Binary
import BooleanFormula ( BooleanFormula )
@@ -426,6 +427,7 @@ data IfaceExpr
data IfaceTickish
= IfaceHpcTick Module Int -- from HpcTick x
| IfaceSCC CostCentre Bool Bool -- from ProfNote
+ | IfaceSource RealSrcSpan String -- from SourceNote
-- no breakpoints: we never export these into interface files
type IfaceAlt = (IfaceConAlt, [IfLclName], IfaceExpr)
@@ -969,6 +971,8 @@ pprIfaceTickish (IfaceHpcTick m ix)
= braces (text "tick" <+> ppr m <+> ppr ix)
pprIfaceTickish (IfaceSCC cc tick scope)
= braces (pprCostCentreCore cc <+> ppr tick <+> ppr scope)
+pprIfaceTickish (IfaceSource src _names)
+ = braces (pprUserRealSpan True src)
------------------
pprIfaceApp :: IfaceExpr -> [SDoc] -> SDoc
@@ -1775,6 +1779,14 @@ instance Binary IfaceTickish where
put_ bh cc
put_ bh tick
put_ bh push
+ put_ bh (IfaceSource src name) = do
+ putByte bh 2
+ put_ bh (srcSpanFile src)
+ put_ bh (srcSpanStartLine src)
+ put_ bh (srcSpanStartCol src)
+ put_ bh (srcSpanEndLine src)
+ put_ bh (srcSpanEndCol src)
+ put_ bh name
get bh = do
h <- getByte bh
@@ -1786,6 +1798,15 @@ instance Binary IfaceTickish where
tick <- get bh
push <- get bh
return (IfaceSCC cc tick push)
+ 2 -> do file <- get bh
+ sl <- get bh
+ sc <- get bh
+ el <- get bh
+ ec <- get bh
+ let start = mkRealSrcLoc file sl sc
+ end = mkRealSrcLoc file el ec
+ name <- get bh
+ return (IfaceSource (mkRealSrcSpan start end) name)
_ -> panic ("get IfaceTickish " ++ show h)
instance Binary IfaceConAlt where
diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs
index b3321c19de..c0a603fb98 100644
--- a/compiler/iface/MkIface.hs
+++ b/compiler/iface/MkIface.hs
@@ -1981,6 +1981,7 @@ toIfaceOneShot id | isId id
toIfaceTickish :: Tickish Id -> Maybe IfaceTickish
toIfaceTickish (ProfNote cc tick push) = Just (IfaceSCC cc tick push)
toIfaceTickish (HpcTick modl ix) = Just (IfaceHpcTick modl ix)
+toIfaceTickish (SourceNote src names) = Just (IfaceSource src names)
toIfaceTickish (Breakpoint {}) = Nothing
-- Ignore breakpoints, since they are relevant only to GHCi, and
-- should not be serialised (Trac #8333)
diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs
index 9864364b89..96e72df502 100644
--- a/compiler/iface/TcIface.hs
+++ b/compiler/iface/TcIface.hs
@@ -1041,6 +1041,7 @@ tcIfaceApps fun arg
tcIfaceTickish :: IfaceTickish -> IfM lcl (Tickish Id)
tcIfaceTickish (IfaceHpcTick modl ix) = return (HpcTick modl ix)
tcIfaceTickish (IfaceSCC cc tick push) = return (ProfNote cc tick push)
+tcIfaceTickish (IfaceSource src name) = return (SourceNote src name)
-------------------------
tcIfaceLit :: Literal -> IfL Literal
diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs
index d6b75afecc..844fa97e35 100644
--- a/compiler/main/DynFlags.hs
+++ b/compiler/main/DynFlags.hs
@@ -419,6 +419,7 @@ data GeneralFlag
| Opt_ErrorSpans -- Include full span info in error messages,
-- instead of just the start position.
| Opt_PprCaseAsLet
+ | Opt_PprShowTicks
-- Suppress all coercions, them replacing with '...'
| Opt_SuppressCoercions
@@ -455,6 +456,9 @@ data GeneralFlag
| Opt_DistrustAllPackages
| Opt_PackageTrust
+ -- debugging flags
+ | Opt_Debug
+
deriving (Eq, Show, Enum)
data WarningFlag =
@@ -887,7 +891,7 @@ data ProfAuto
| ProfAutoTop -- ^ top-level functions annotated only
| ProfAutoExports -- ^ exported functions annotated only
| ProfAutoCalls -- ^ annotate call-sites
- deriving (Enum)
+ deriving (Eq,Enum)
data Settings = Settings {
sTargetPlatform :: Platform, -- Filled in by SysTools
@@ -2649,6 +2653,9 @@ dynamic_flags = [
, defFlag "fno-safe-infer" (noArg (\d -> d { safeInfer = False } ))
, defGhcFlag "fPIC" (NoArg (setGeneralFlag Opt_PIC))
, defGhcFlag "fno-PIC" (NoArg (unSetGeneralFlag Opt_PIC))
+
+ ------ Debugging flags ----------------------------------------------
+ , defGhcFlag "g" (NoArg (setGeneralFlag Opt_Debug))
]
++ map (mkFlag turnOn "" setGeneralFlag ) negatableFlags
++ map (mkFlag turnOff "no-" unSetGeneralFlag) negatableFlags
@@ -2861,6 +2868,7 @@ dFlags = [
-- See Note [Supporting CLI completion]
-- Please keep the list of flags below sorted alphabetically
flagSpec "ppr-case-as-let" Opt_PprCaseAsLet,
+ flagSpec "ppr-ticks" Opt_PprShowTicks,
flagSpec "suppress-coercions" Opt_SuppressCoercions,
flagSpec "suppress-idinfo" Opt_SuppressIdInfo,
flagSpec "suppress-module-prefixes" Opt_SuppressModulePrefixes,
diff --git a/compiler/main/HscMain.hs b/compiler/main/HscMain.hs
index c00663b6ab..7b3712de78 100644
--- a/compiler/main/HscMain.hs
+++ b/compiler/main/HscMain.hs
@@ -1206,7 +1206,7 @@ hscGenHardCode hsc_env cgguts mod_summary output_filename = do
-- PREPARE FOR CODE GENERATION
-- Do saturation and convert to A-normal form
prepd_binds <- {-# SCC "CorePrep" #-}
- corePrepPgm dflags hsc_env core_binds data_tycons ;
+ corePrepPgm hsc_env location core_binds data_tycons ;
----------------- Convert to STG ------------------
(stg_binds, cost_centre_info)
<- {-# SCC "CoreToStg" #-}
@@ -1269,7 +1269,7 @@ hscInteractive hsc_env cgguts mod_summary = do
-- PREPARE FOR CODE GENERATION
-- Do saturation and convert to A-normal form
prepd_binds <- {-# SCC "CorePrep" #-}
- corePrepPgm dflags hsc_env core_binds data_tycons
+ corePrepPgm hsc_env location core_binds data_tycons
----------------- Generate byte code ------------------
comp_bc <- byteCodeGen dflags this_mod prepd_binds data_tycons mod_breaks
------------------ Create f-x-dynamic C-side stuff ---
@@ -1493,7 +1493,7 @@ hscDeclsWithLocation hsc_env0 str source linenumber =
{- Prepare For Code Generation -}
-- Do saturation and convert to A-normal form
prepd_binds <- {-# SCC "CorePrep" #-}
- liftIO $ corePrepPgm dflags hsc_env core_binds data_tycons
+ liftIO $ corePrepPgm hsc_env iNTERACTIVELoc core_binds data_tycons
{- Generate byte code -}
cbc <- liftIO $ byteCodeGen dflags this_mod
diff --git a/compiler/simplCore/CSE.hs b/compiler/simplCore/CSE.hs
index 7dbf892f9e..a30c695181 100644
--- a/compiler/simplCore/CSE.hs
+++ b/compiler/simplCore/CSE.hs
@@ -14,7 +14,8 @@ import CoreSubst
import Var ( Var )
import Id ( Id, idType, idInlineActivation, zapIdOccInfo )
import CoreUtils ( mkAltExpr
- , exprIsTrivial)
+ , exprIsTrivial
+ , stripTicks, stripTicksTopE, mkTick, mkTicks )
import Type ( tyConAppArgs )
import CoreSyn
import Outputable
@@ -171,13 +172,13 @@ cseBind env (Rec pairs)
cseRhs :: CSEnv -> (OutBndr, InExpr) -> (CSEnv, OutExpr)
cseRhs env (id',rhs)
- = case lookupCSEnv env rhs' of
+ = case lookupCSEnv env rhs'' of
Nothing
| always_active -> (extendCSEnv env rhs' id', rhs')
| otherwise -> (env, rhs')
Just id
- | always_active -> (extendCSSubst env id' id, Var id)
- | otherwise -> (env, Var id)
+ | always_active -> (extendCSSubst env id' id, mkTicks ticks $ Var id)
+ | otherwise -> (env, mkTicks ticks $ Var id)
-- In the Just case, we have
-- x = rhs
-- ...
@@ -189,16 +190,23 @@ cseRhs env (id',rhs)
where
rhs' = cseExpr env rhs
+ (ticks, rhs'') = stripTicks tickishFloatable rhs'
+ -- We don't want to lose the source notes when a common sub
+ -- expression gets eliminated. Hence we push all (!) of them on
+ -- top of the replaced sub-expression. This is probably not too
+ -- useful in practice, but upholds our semantics.
+
always_active = isAlwaysActive (idInlineActivation id')
-- See Note [CSE for INLINE and NOINLINE]
tryForCSE :: CSEnv -> InExpr -> OutExpr
tryForCSE env expr
- | exprIsTrivial expr' = expr' -- No point
- | Just smaller <- lookupCSEnv env expr' = Var smaller
- | otherwise = expr'
+ | exprIsTrivial expr' = expr' -- No point
+ | Just smaller <- lookupCSEnv env expr'' = foldr mkTick (Var smaller) ticks
+ | otherwise = expr'
where
expr' = cseExpr env expr
+ (ticks, expr'') = stripTicks tickishFloatable expr'
cseExpr :: CSEnv -> InExpr -> OutExpr
cseExpr env (Type t) = Type (substTy (csEnvSubst env) t)
@@ -228,8 +236,9 @@ cseAlts :: CSEnv -> OutExpr -> InBndr -> InBndr -> [InAlt] -> [OutAlt]
cseAlts env scrut' bndr bndr' alts
= map cse_alt alts
where
+ scrut'' = stripTicksTopE tickishFloatable scrut'
(con_target, alt_env)
- = case scrut' of
+ = case scrut'' of
Var v' -> (v', extendCSSubst env bndr v') -- See Note [Case binders 1]
-- map: bndr -> v'
@@ -286,7 +295,8 @@ lookupCSEnv (CS { cs_map = csmap }) expr
extendCSEnv :: CSEnv -> OutExpr -> Id -> CSEnv
extendCSEnv cse expr id
- = cse { cs_map = extendCoreMap (cs_map cse) expr (expr,id) }
+ = cse { cs_map = extendCoreMap (cs_map cse) sexpr (sexpr,id) }
+ where (_, sexpr) = stripTicks tickishFloatable expr
csEnvSubst :: CSEnv -> Subst
csEnvSubst = cs_subst
diff --git a/compiler/simplCore/FloatIn.hs b/compiler/simplCore/FloatIn.hs
index 34252881ab..2f1b31874e 100644
--- a/compiler/simplCore/FloatIn.hs
+++ b/compiler/simplCore/FloatIn.hs
@@ -20,7 +20,8 @@ module FloatIn ( floatInwards ) where
import CoreSyn
import MkCore
-import CoreUtils ( exprIsDupable, exprIsExpandable, exprType, exprOkForSideEffects )
+import CoreUtils ( exprIsDupable, exprIsExpandable, exprType,
+ exprOkForSideEffects, mkTicks )
import CoreFVs ( CoreExprWithFVs, freeVars, freeVarsOf, idRuleAndUnfoldingVars )
import Id ( isOneShotBndr, idType )
import Var
@@ -151,11 +152,12 @@ pull out any silly ones.
-}
fiExpr dflags to_drop ann_expr@(_,AnnApp {})
- = wrapFloats drop_here $ wrapFloats extra_drop $
+ = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
mkApps (fiExpr dflags fun_drop ann_fun)
(zipWith (fiExpr dflags) arg_drops ann_args)
where
- (ann_fun@(fun_fvs, _), ann_args) = collectAnnArgs ann_expr
+ (ann_fun@(fun_fvs, _), ann_args, ticks)
+ = collectAnnArgsTicks tickishFloatable ann_expr
fun_ty = exprType (deAnnotate ann_fun)
((_,extra_fvs), arg_fvs) = mapAccumL mk_arg_fvs (fun_ty, emptyVarSet) ann_args
@@ -244,13 +246,12 @@ We don't float lets inwards past an SCC.
-}
fiExpr dflags to_drop (_, AnnTick tickish expr)
- | tickishScoped tickish
- = -- Wimp out for now - we could push values in
- wrapFloats to_drop (Tick tickish (fiExpr dflags [] expr))
-
- | otherwise
+ | tickish `tickishScopesLike` SoftScope
= Tick tickish (fiExpr dflags to_drop expr)
+ | otherwise -- Wimp out for now - we could push values in
+ = wrapFloats to_drop (Tick tickish (fiExpr dflags [] expr))
+
{-
For @Lets@, the possible ``drop points'' for the \tr{to_drop}
bindings are: (a)~in the body, (b1)~in the RHS of a NonRec binding,
diff --git a/compiler/simplCore/FloatOut.hs b/compiler/simplCore/FloatOut.hs
index 4cd871334d..7f7b921fa8 100644
--- a/compiler/simplCore/FloatOut.hs
+++ b/compiler/simplCore/FloatOut.hs
@@ -280,18 +280,20 @@ floatExpr lam@(Lam (TB _ lam_spec) _)
(add_to_stats fs floats, floats, mkLams bndrs body') }
floatExpr (Tick tickish expr)
- | tickishScoped tickish
+ | tickish `tickishScopesLike` SoftScope -- not scoped, can just float
= case (floatExpr expr) of { (fs, floating_defns, expr') ->
- let
- -- Annotate bindings floated outwards past an scc expression
+ (fs, floating_defns, Tick tickish expr') }
+
+ | not (tickishCounts tickish) || tickishCanSplit tickish
+ = case (floatExpr expr) of { (fs, floating_defns, expr') ->
+ let -- Annotate bindings floated outwards past an scc expression
-- with the cc. We mark that cc as "duplicated", though.
annotated_defns = wrapTick (mkNoCount tickish) floating_defns
in
(fs, annotated_defns, Tick tickish expr') }
- | otherwise -- not scoped, can just float
- = case (floatExpr expr) of { (fs, floating_defns, expr') ->
- (fs, floating_defns, Tick tickish expr') }
+ | otherwise
+ = pprPanic "floatExpr tick" (ppr tickish)
floatExpr (Cast expr co)
= case (floatExpr expr) of { (fs, floating_defns, expr') ->
diff --git a/compiler/simplCore/OccurAnal.hs b/compiler/simplCore/OccurAnal.hs
index 26aec9dcc7..c15026c539 100644
--- a/compiler/simplCore/OccurAnal.hs
+++ b/compiler/simplCore/OccurAnal.hs
@@ -21,7 +21,8 @@ module OccurAnal (
import CoreSyn
import CoreFVs
-import CoreUtils ( exprIsTrivial, isDefaultAlt, isExpandableApp )
+import CoreUtils ( exprIsTrivial, isDefaultAlt, isExpandableApp,
+ stripTicksTopE, mkTicks )
import Id
import Name( localiseName )
import BasicTypes
@@ -40,6 +41,7 @@ import Util
import Outputable
import FastString
import Data.List
+import Control.Arrow ( second )
{-
************************************************************************
@@ -1179,18 +1181,19 @@ we can sort them into the right place when doing dependency analysis.
-}
occAnal env (Tick tickish body)
+ | tickish `tickishScopesLike` SoftScope
+ = (usage, Tick tickish body')
+
| Breakpoint _ ids <- tickish
- = (mapVarEnv markInsideSCC usage
- +++ mkVarEnv (zip ids (repeat NoOccInfo)), Tick tickish body')
+ = (usage_lam +++ mkVarEnv (zip ids (repeat NoOccInfo)), Tick tickish body')
-- never substitute for any of the Ids in a Breakpoint
- | tickishScoped tickish
- = (mapVarEnv markInsideSCC usage, Tick tickish body')
-
| otherwise
- = (usage, Tick tickish body')
+ = (usage_lam, Tick tickish body')
where
!(usage,body') = occAnal env body
+ -- for a non-soft tick scope, we can inline lambdas only
+ usage_lam = mapVarEnv markInsideLam usage
occAnal env (Cast expr co)
= case occAnal env expr of { (usage, expr') ->
@@ -1204,7 +1207,7 @@ occAnal env (Cast expr co)
}
occAnal env app@(App _ _)
- = occAnalApp env (collectArgs app)
+ = occAnalApp env (collectArgsTicks tickishFloatable app)
-- Ignore type variables altogether
-- (a) occurrences inside type lambdas only not marked as InsideLam
@@ -1271,6 +1274,13 @@ occAnal env (Case scrut bndr ty alts)
= (mkOneOcc env v True, Var v) -- The 'True' says that the variable occurs
-- in an interesting context; the case has
-- at least one non-default alternative
+ occ_anal_scrut (Tick t e) alts
+ | t `tickishScopesLike` SoftScope
+ -- No reason to not look through all ticks here, but only
+ -- for soft-scoped ticks we can do so without having to
+ -- update returned occurance info (see occAnal)
+ = second (Tick t) $ occ_anal_scrut e alts
+
occ_anal_scrut scrut _alts
= occAnal (vanillaCtxt env) scrut -- No need for rhsCtxt
@@ -1312,23 +1322,25 @@ Constructors are rather like lambdas in this way.
-}
occAnalApp :: OccEnv
- -> (Expr CoreBndr, [Arg CoreBndr])
+ -> (Expr CoreBndr, [Arg CoreBndr], [Tickish Id])
-> (UsageDetails, Expr CoreBndr)
-occAnalApp env (Var fun, args)
- = case args_stuff of { (args_uds, args') ->
- let
- final_args_uds = markManyIf (isRhsEnv env && is_exp) args_uds
- -- We mark the free vars of the argument of a constructor or PAP
- -- as "many", if it is the RHS of a let(rec).
- -- This means that nothing gets inlined into a constructor argument
- -- position, which is what we want. Typically those constructor
- -- arguments are just variables, or trivial expressions.
- --
- -- This is the *whole point* of the isRhsEnv predicate
- -- See Note [Arguments of let-bound constructors]
- in
- (fun_uds +++ final_args_uds, mkApps (Var fun) args') }
+occAnalApp env (Var fun, args, ticks)
+ | null ticks = (uds, mkApps (Var fun) args')
+ | otherwise = (uds, mkTicks ticks $ mkApps (Var fun) args')
where
+ uds = fun_uds +++ final_args_uds
+
+ !(args_uds, args') = occAnalArgs env args one_shots
+ !final_args_uds = markManyIf (isRhsEnv env && is_exp) args_uds
+ -- We mark the free vars of the argument of a constructor or PAP
+ -- as "many", if it is the RHS of a let(rec).
+ -- This means that nothing gets inlined into a constructor argument
+ -- position, which is what we want. Typically those constructor
+ -- arguments are just variables, or trivial expressions.
+ --
+ -- This is the *whole point* of the isRhsEnv predicate
+ -- See Note [Arguments of let-bound constructors]
+
n_val_args = valArgCount args
fun_uds = mkOneOcc env fun (n_val_args > 0)
is_exp = isExpandableApp fun n_val_args
@@ -1339,26 +1351,17 @@ occAnalApp env (Var fun, args)
one_shots = argsOneShots (idStrictness fun) n_val_args
-- See Note [Use one-shot info]
- args_stuff = occAnalArgs env args one_shots
-
- -- (foldr k z xs) may call k many times, but it never
- -- shares a partial application of k; hence [False,True]
- -- This means we can optimise
- -- foldr (\x -> let v = ...x... in \y -> ...v...) z xs
- -- by floating in the v
-
-occAnalApp env (fun, args)
- = case occAnal (addAppCtxt env args) fun of { (fun_uds, fun') ->
+occAnalApp env (fun, args, ticks)
+ = (fun_uds +++ args_uds, mkTicks ticks $ mkApps fun' args')
+ where
+ !(fun_uds, fun') = occAnal (addAppCtxt env args) fun
-- The addAppCtxt is a bit cunning. One iteration of the simplifier
-- often leaves behind beta redexs like
-- (\x y -> e) a1 a2
-- Here we would like to mark x,y as one-shot, and treat the whole
-- thing much like a let. We do this by pushing some True items
-- onto the context stack.
-
- case occAnalArgs env args [] of { (args_uds, args') ->
- (fun_uds +++ args_uds, mkApps fun' args') }}
-
+ !(args_uds, args') = occAnalArgs env args []
markManyIf :: Bool -- If this is true
-> UsageDetails -- Then do markMany on this
@@ -1731,7 +1734,7 @@ mkAltEnv :: OccEnv -> CoreExpr -> Id -> (OccEnv, Maybe (Id, CoreExpr))
-- c) returns a proxy mapping, binding the scrutinee
-- to the case binder, if possible
mkAltEnv env@(OccEnv { occ_gbl_scrut = pe }) scrut case_bndr
- = case scrut of
+ = case stripTicksTopE (const True) scrut of
Var v -> add_scrut v case_bndr'
Cast (Var v) co -> add_scrut v (Cast case_bndr' (mkSymCo co))
-- See Note [Case of cast]
@@ -1843,13 +1846,10 @@ mkOneOcc env id int_cxt
| otherwise
= emptyDetails
-markMany, markInsideLam, markInsideSCC :: OccInfo -> OccInfo
+markMany, markInsideLam :: OccInfo -> OccInfo
markMany _ = NoOccInfo
-markInsideSCC occ = markInsideLam occ
- -- inside an SCC, we can inline lambdas only.
-
markInsideLam (OneOcc _ one_br int_cxt) = OneOcc True one_br int_cxt
markInsideLam occ = occ
diff --git a/compiler/simplCore/SetLevels.hs b/compiler/simplCore/SetLevels.hs
index e7000409e7..c3ee112de4 100644
--- a/compiler/simplCore/SetLevels.hs
+++ b/compiler/simplCore/SetLevels.hs
@@ -592,6 +592,7 @@ notWorthFloating e abs_vars
go (_, AnnVar {}) n = n >= 0
go (_, AnnLit lit) n = ASSERT( n==0 )
litIsTrivial lit -- Note [Floating literals]
+ go (_, AnnTick t e) n = not (tickishIsCode t) && go e n
go (_, AnnCast e _) n = go e n
go (_, AnnApp e arg) n
| (_, AnnType {}) <- arg = go e n
@@ -606,6 +607,7 @@ notWorthFloating e abs_vars
is_triv (_, AnnCast e _) = is_triv e
is_triv (_, AnnApp e (_, AnnType {})) = is_triv e
is_triv (_, AnnApp e (_, AnnCoercion {})) = is_triv e
+ is_triv (_, AnnTick t e) = not (tickishIsCode t) && is_triv e
is_triv _ = False
{-
diff --git a/compiler/simplCore/SimplCore.hs b/compiler/simplCore/SimplCore.hs
index bdb21987b8..746e0d0724 100644
--- a/compiler/simplCore/SimplCore.hs
+++ b/compiler/simplCore/SimplCore.hs
@@ -20,7 +20,8 @@ import Rules ( RuleBase, emptyRuleBase, mkRuleBase, unionRuleBase,
import PprCore ( pprCoreBindings, pprCoreExpr )
import OccurAnal ( occurAnalysePgm, occurAnalyseExpr )
import IdInfo
-import CoreUtils ( coreBindsSize, coreBindsStats, exprSize )
+import CoreUtils ( coreBindsSize, coreBindsStats, exprSize,
+ mkTicks, stripTicksTop )
import CoreLint ( showPass, endPass, lintPassResult, dumpPassResult )
import Simplify ( simplTopBinds, simplExpr )
import SimplUtils ( simplEnvForGHCi, activeRule )
@@ -821,9 +822,28 @@ could be eliminated. But I don't think it's very common
and it's dangerous to do this fiddling in STG land
because we might elminate a binding that's mentioned in the
unfolding for something.
+
+Note [Indirection zapping and ticks]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unfortunately this is another place where we need a special case for
+ticks. The following happens quite regularly:
+
+ x_local = <expression>
+ x_exported = tick<x> x_local
+
+Which we want to become:
+
+ x_exported = tick<x> <expression>
+
+As it makes no sense to keep the tick and the expression on separate
+bindings. Note however that that this might increase the ticks scoping
+over the execution of x_local, so we can only do this for floatable
+ticks. More often than not, other references will be unfoldings of
+x_exported, and therefore carry the tick anyway.
-}
-type IndEnv = IdEnv Id -- Maps local_id -> exported_id
+type IndEnv = IdEnv (Id, [Tickish Var]) -- Maps local_id -> exported_id, ticks
shortOutIndirections :: CoreProgram -> CoreProgram
shortOutIndirections binds
@@ -832,8 +852,9 @@ shortOutIndirections binds
| otherwise = [Rec (flattenBinds binds')] -- for this no_need_to_flatten stuff
where
ind_env = makeIndEnv binds
- exp_ids = varSetElems ind_env -- These exported Ids are the subjects
- exp_id_set = mkVarSet exp_ids -- of the indirection-elimination
+ -- These exported Ids are the subjects of the indirection-elimination
+ exp_ids = map fst $ varEnvElts ind_env
+ exp_id_set = mkVarSet exp_ids
no_need_to_flatten = all (null . specInfoRules . idSpecialisation) exp_ids
binds' = concatMap zap binds
@@ -841,10 +862,12 @@ shortOutIndirections binds
zap (Rec pairs) = [Rec (concatMap zapPair pairs)]
zapPair (bndr, rhs)
- | bndr `elemVarSet` exp_id_set = []
- | Just exp_id <- lookupVarEnv ind_env bndr = [(transferIdInfo exp_id bndr, rhs),
- (bndr, Var exp_id)]
- | otherwise = [(bndr,rhs)]
+ | bndr `elemVarSet` exp_id_set = []
+ | Just (exp_id, ticks) <- lookupVarEnv ind_env bndr
+ = [(transferIdInfo exp_id bndr,
+ mkTicks ticks rhs),
+ (bndr, Var exp_id)]
+ | otherwise = [(bndr,rhs)]
makeIndEnv :: [CoreBind] -> IndEnv
makeIndEnv binds
@@ -855,8 +878,10 @@ makeIndEnv binds
add_bind (Rec pairs) env = foldr add_pair env pairs
add_pair :: (Id,CoreExpr) -> IndEnv -> IndEnv
- add_pair (exported_id, Var local_id) env
- | shortMeOut env exported_id local_id = extendVarEnv env local_id exported_id
+ add_pair (exported_id, exported) env
+ | (ticks, Var local_id) <- stripTicksTop tickishFloatable exported
+ , shortMeOut env exported_id local_id
+ = extendVarEnv env local_id (exported_id, ticks)
add_pair _ env = env
-----------------
diff --git a/compiler/simplCore/SimplEnv.hs b/compiler/simplCore/SimplEnv.hs
index a5d8551a3a..96c2fc046d 100644
--- a/compiler/simplCore/SimplEnv.hs
+++ b/compiler/simplCore/SimplEnv.hs
@@ -31,7 +31,7 @@ module SimplEnv (
-- Floats
Floats, emptyFloats, isEmptyFloats, addNonRec, addFloats, extendFloats,
- wrapFloats, setFloats, zapFloats, addRecFloats,
+ wrapFloats, setFloats, zapFloats, addRecFloats, mapFloats,
doFloatFromRhs, getFloatBinds
) where
@@ -486,18 +486,14 @@ isEmptyFloats :: SimplEnv -> Bool
isEmptyFloats (SimplEnv {seFloats = Floats bs _})
= isNilOL bs
-{-
--- mapFloats commented out: used only in a commented-out bit of Simplify,
--- concerning ticks
---
--- mapFloats :: SimplEnv -> ((Id,CoreExpr) -> (Id,CoreExpr)) -> SimplEnv
--- mapFloats env@SimplEnv { seFloats = Floats fs ff } fun
--- = env { seFloats = Floats (mapOL app fs) ff }
--- where
--- app (NonRec b e) = case fun (b,e) of (b',e') -> NonRec b' e'
--- app (Rec bs) = Rec (map fun bs)
-
+mapFloats :: SimplEnv -> ((Id,CoreExpr) -> (Id,CoreExpr)) -> SimplEnv
+mapFloats env@SimplEnv { seFloats = Floats fs ff } fun
+ = env { seFloats = Floats (mapOL app fs) ff }
+ where
+ app (NonRec b e) = case fun (b,e) of (b',e') -> NonRec b' e'
+ app (Rec bs) = Rec (map fun bs)
+{-
************************************************************************
* *
Substitution of Vars
diff --git a/compiler/simplCore/SimplUtils.hs b/compiler/simplCore/SimplUtils.hs
index 4fd855a828..ccc8a56cc0 100644
--- a/compiler/simplCore/SimplUtils.hs
+++ b/compiler/simplCore/SimplUtils.hs
@@ -63,6 +63,7 @@ import FastString
import Pair
import Control.Monad ( when )
+import Data.List ( partition )
{-
************************************************************************
@@ -961,9 +962,10 @@ preInlineUnconditionally dflags env top_lvl bndr rhs
-- canInlineInLam => free vars of rhs are (Once in_lam) or Many,
-- so substituting rhs inside a lambda doesn't change the occ info.
-- Sadly, not quite the same as exprIsHNF.
- canInlineInLam (Lit _) = True
- canInlineInLam (Lam b e) = isRuntimeVar b || canInlineInLam e
- canInlineInLam _ = False
+ canInlineInLam (Lit _) = True
+ canInlineInLam (Lam b e) = isRuntimeVar b || canInlineInLam e
+ canInlineInLam (Tick t e) = not (tickishIsCode t) && canInlineInLam e
+ canInlineInLam _ = False
-- not ticks. Counting ticks cannot be duplicated, and non-counting
-- ticks around a Lam will disappear anyway.
@@ -1184,6 +1186,10 @@ mkLam bndrs body cont
where
(bndrs1, body1) = collectBinders body
+ mkLam' dflags bndrs (Tick t expr)
+ | tickishFloatable t
+ = mkTick t <$> mkLam' dflags bndrs expr
+
mkLam' dflags bndrs body
| gopt Opt_DoEtaReduction dflags
, Just etad_lam <- tryEtaReduce bndrs body
@@ -1643,13 +1649,16 @@ defeats combineIdenticalAlts (see Trac #7360).
combineIdenticalAlts :: OutId -> [InAlt] -> SimplM [InAlt]
-- See Note [Combine identical alternatives]
combineIdenticalAlts case_bndr ((_con1,bndrs1,rhs1) : con_alts)
- | all isDeadBinder bndrs1 -- Remember the default
- , length filtered_alts < length con_alts -- alternative comes first
+ | all isDeadBinder bndrs1 -- Remember the default
+ , not (null eliminated_alts) -- alternative comes first
= do { tick (AltMerge case_bndr)
- ; return ((DEFAULT, [], rhs1) : filtered_alts) }
+ ; return ((DEFAULT, [], mkTicks (concat tickss) rhs1) : filtered_alts) }
where
- filtered_alts = filterOut identical_to_alt1 con_alts
- identical_to_alt1 (_con,bndrs,rhs) = all isDeadBinder bndrs && rhs `cheapEqExpr` rhs1
+ (eliminated_alts, filtered_alts) = partition identical_to_alt1 con_alts
+ cheapEqTicked e1 e2 = cheapEqExpr' tickishFloatable e1 e2
+ identical_to_alt1 (_con,bndrs,rhs)
+ = all isDeadBinder bndrs && rhs `cheapEqTicked` rhs1
+ tickss = map (fst . stripTicks tickishFloatable . thirdOf3) eliminated_alts
combineIdenticalAlts _ alts = return alts
@@ -1701,7 +1710,8 @@ mkCase, mkCase1, mkCase2
mkCase dflags scrut outer_bndr alts_ty ((DEFAULT, _, deflt_rhs) : outer_alts)
| gopt Opt_CaseMerge dflags
- , Case (Var inner_scrut_var) inner_bndr _ inner_alts <- deflt_rhs
+ , (ticks, Case (Var inner_scrut_var) inner_bndr _ inner_alts)
+ <- stripTicksTop tickishFloatable deflt_rhs
, inner_scrut_var == outer_bndr
= do { tick (CaseMerge outer_bndr)
@@ -1725,7 +1735,8 @@ mkCase dflags scrut outer_bndr alts_ty ((DEFAULT, _, deflt_rhs) : outer_alts)
-- When we merge, we must ensure that e1 takes
-- precedence over e2 as the value for A!
- ; mkCase1 dflags scrut outer_bndr alts_ty merged_alts
+ ; fmap (mkTicks ticks) $
+ mkCase1 dflags scrut outer_bndr alts_ty merged_alts
}
-- Warning: don't call mkCase recursively!
-- Firstly, there's no point, because inner alts have already had
@@ -1742,17 +1753,24 @@ mkCase dflags scrut bndr alts_ty alts = mkCase1 dflags scrut bndr alts_ty alts
mkCase1 _dflags scrut case_bndr _ alts@((_,_,rhs1) : _) -- Identity case
| all identity_alt alts
= do { tick (CaseIdentity case_bndr)
- ; return (re_cast scrut rhs1) }
+ ; return (mkTicks ticks $ re_cast scrut rhs1) }
where
+ ticks = concatMap (fst . stripTicks tickishFloatable . thirdOf3) (tail alts)
identity_alt (con, args, rhs) = check_eq rhs con args
- check_eq (Cast rhs co) con args = not (any (`elemVarSet` tyCoVarsOfCo co) args)
- {- See Note [RHS casts] -} && check_eq rhs con args
- check_eq (Lit lit) (LitAlt lit') _ = lit == lit'
- check_eq (Var v) _ _ | v == case_bndr = True
- check_eq (Var v) (DataAlt con) [] = v == dataConWorkId con -- Optimisation only
- check_eq rhs (DataAlt con) args = rhs `cheapEqExpr` mkConApp con (arg_tys ++ varsToCoreExprs args)
- check_eq _ _ _ = False
+ check_eq (Cast rhs co) con args
+ = not (any (`elemVarSet` tyCoVarsOfCo co) args) && check_eq rhs con args
+ -- See Note [RHS casts]
+ check_eq (Lit lit) (LitAlt lit') _ = lit == lit'
+ check_eq (Var v) _ _ | v == case_bndr = True
+ check_eq (Var v) (DataAlt con) [] = v == dataConWorkId con
+ -- Optimisation only
+ check_eq (Tick t e) alt args = tickishFloatable t &&
+ check_eq e alt args
+ check_eq rhs (DataAlt con) args = cheapEqExpr' tickishFloatable rhs $
+ mkConApp con (arg_tys ++
+ varsToCoreExprs args)
+ check_eq _ _ _ = False
arg_tys = map Type (tyConAppArgs (idType case_bndr))
diff --git a/compiler/simplCore/Simplify.hs b/compiler/simplCore/Simplify.hs
index 18b4c9dee3..b950f570b8 100644
--- a/compiler/simplCore/Simplify.hs
+++ b/compiler/simplCore/Simplify.hs
@@ -330,12 +330,14 @@ simplLazyBind env top_lvl is_rec bndr bndr1 rhs rhs_se
(tvs, body) = case collectTyBinders rhs of
(tvs, body) | not_lam body -> (tvs,body)
| otherwise -> ([], rhs)
- not_lam (Lam _ _) = False
- not_lam _ = True
+ not_lam (Lam _ _) = False
+ not_lam (Tick t e) | not (tickishFloatable t)
+ = not_lam e -- eta-reduction could float
+ not_lam _ = True
-- Do not do the "abstract tyyvar" thing if there's
-- a lambda inside, because it defeats eta-reduction
-- f = /\a. \x. g a x
- -- should eta-reduce
+ -- should eta-reduce.
; (body_env, tvs') <- simplBinders rhs_env tvs
@@ -486,6 +488,21 @@ prepareRhs top_lvl env0 _ rhs0
-- The definition of is_exp should match that in
-- OccurAnal.occAnalApp
+ go n_val_args env (Tick t rhs)
+ -- We want to be able to float bindings past this
+ -- tick. Non-scoping ticks don't care.
+ | tickishScoped t == NoScope
+ = do { (is_exp, env', rhs') <- go n_val_args env rhs
+ ; return (is_exp, env', Tick t rhs') }
+ -- On the other hand, for scoping ticks we need to be able to
+ -- copy them on the floats, which in turn is only allowed if
+ -- we can obtain non-counting ticks.
+ | not (tickishCounts t) || tickishCanSplit t
+ = do { (is_exp, env', rhs') <- go n_val_args (zapFloats env) rhs
+ ; let tickIt (id, expr) = (id, mkTick (mkNoCount t) expr)
+ floats' = seFloats $ env `addFloats` mapFloats env' tickIt
+ ; return (is_exp, env' { seFloats = floats' }, Tick t rhs') }
+
go _ env other
= return (False, env, other)
@@ -1019,58 +1036,48 @@ simplTick env tickish expr cont
-- | tickishScoped tickish && not (tickishCounts tickish)
-- = simplExprF env expr (TickIt tickish cont)
- -- For non-scoped ticks, we push the continuation inside the
- -- tick. This has the effect of moving the tick to the outside of a
- -- case or application context, allowing the normal case and
- -- application optimisations to fire.
- | not (tickishScoped tickish)
+ -- For unscoped or soft-scoped ticks, we are allowed to float in new
+ -- cost, so we simply push the continuation inside the tick. This
+ -- has the effect of moving the tick to the outside of a case or
+ -- application context, allowing the normal case and application
+ -- optimisations to fire.
+ | tickish `tickishScopesLike` SoftScope
= do { (env', expr') <- simplExprF env expr cont
; return (env', mkTick tickish expr')
}
- -- For breakpoints, we cannot do any floating of bindings around the
- -- tick, because breakpoints cannot be split into tick/scope pairs.
- | not (tickishCanSplit tickish)
- = no_floating_past_tick
-
- | interesting_cont, Just expr' <- push_tick_inside tickish expr
- -- see Note [case-of-scc-of-case]
+ -- Push tick inside if the context looks like this will allow us to
+ -- do a case-of-case - see Note [case-of-scc-of-case]
+ | Select {} <- cont, Just expr' <- push_tick_inside
= simplExprF env expr' cont
+ -- We don't want to move the tick, but we might still want to allow
+ -- floats to pass through with appropriate wrapping (or not, see
+ -- wrap_floats below)
+ --- | not (tickishCounts tickish) || tickishCanSplit tickish
+ -- = wrap_floats
+
| otherwise
- = no_floating_past_tick -- was: wrap_floats, see below
+ = no_floating_past_tick
where
- interesting_cont = case cont of
- Select {} -> True
- _ -> False
-
- push_tick_inside t expr0
- = ASSERT(tickishScoped t)
- case expr0 of
- Tick t' expr
- -- scc t (tick t' E)
- -- Pull the tick to the outside
- -- This one is important for #5363
- | not (tickishScoped t')
- -> Just (Tick t' (Tick t expr))
-
- -- scc t (scc t' E)
- -- Try to push t' into E first, and if that works,
- -- try to push t in again
- | Just expr' <- push_tick_inside t' expr
- -> push_tick_inside t expr'
-
- | otherwise -> Nothing
-
- Case scrut bndr ty alts
- | not (tickishCanSplit t) -> Nothing
- | otherwise -> Just (Case (mkTick t scrut) bndr ty alts')
- where t_scope = mkNoCount t -- drop the tick on the dup'd ones
- alts' = [ (c,bs, mkTick t_scope e) | (c,bs,e) <- alts]
-
- _other -> Nothing
- where
+
+ -- Try to push tick inside a case, see Note [case-of-scc-of-case].
+ push_tick_inside =
+ case expr0 of
+ Case scrut bndr ty alts
+ -> Just $ Case (tickScrut scrut) bndr ty (map tickAlt alts)
+ _other -> Nothing
+ where (ticks, expr0) = stripTicksTop movable (Tick tickish expr)
+ movable t = not (tickishCounts t) ||
+ t `tickishScopesLike` NoScope ||
+ tickishCanSplit t
+ tickScrut e = foldr mkTick e ticks
+ -- Alternatives get annotated with all ticks that scope in some way,
+ -- but we don't want to count entries.
+ tickAlt (c,bs,e) = (c,bs, foldr mkTick e ts_scope)
+ ts_scope = map mkNoCount $
+ filter (not . (`tickishScopesLike` NoScope)) ticks
no_floating_past_tick =
do { let (inc,outc) = splitCont cont
diff --git a/compiler/specialise/Rules.hs b/compiler/specialise/Rules.hs
index a768896763..b66d973248 100644
--- a/compiler/specialise/Rules.hs
+++ b/compiler/specialise/Rules.hs
@@ -35,7 +35,8 @@ import CoreSyn -- All of it
import CoreSubst
import OccurAnal ( occurAnalyseExpr )
import CoreFVs ( exprFreeVars, exprsFreeVars, bindFreeVars, rulesFreeVars )
-import CoreUtils ( exprType, eqExpr )
+import CoreUtils ( exprType, eqExpr, mkTick, mkTicks,
+ stripTicksTopT, stripTicksTopE )
import PprCore ( pprRules )
import Type ( Type )
import TcType ( tcSplitTyConApp_maybe )
@@ -194,6 +195,8 @@ roughTopName (App f _) = roughTopName f
roughTopName (Var f) | isGlobalId f -- Note [Care with roughTopName]
, isDataConWorkId f || idArity f > 0
= Just (idName f)
+roughTopName (Tick t e) | tickishFloatable t
+ = roughTopName e
roughTopName _ = Nothing
ruleCantMatch :: [Maybe Name] -> [Maybe Name] -> Bool
@@ -361,20 +364,28 @@ lookupRule dflags in_scope is_active fn args rules
= -- pprTrace "matchRules" (ppr fn <+> ppr args $$ ppr rules ) $
case go [] rules of
[] -> Nothing
- (m:ms) -> Just (findBest (fn,args) m ms)
+ (m:ms) -> Just (findBest (fn,args') m ms)
where
rough_args = map roughTopName args
+ -- Strip ticks from arguments, see note [Tick annotations in RULE
+ -- matching]. We only collect ticks if a rule actually matches -
+ -- this matters for performance tests.
+ args' = map (stripTicksTopE tickishFloatable) args
+ ticks = concatMap (stripTicksTopT tickishFloatable) args
+
go :: [(CoreRule,CoreExpr)] -> [CoreRule] -> [(CoreRule,CoreExpr)]
- go ms [] = ms
- go ms (r:rs) = case (matchRule dflags in_scope is_active fn args rough_args r) of
- Just e -> go ((r,e):ms) rs
- Nothing -> -- pprTrace "match failed" (ppr r $$ ppr args $$
- -- ppr [ (arg_id, unfoldingTemplate unf)
- -- | Var arg_id <- args
- -- , let unf = idUnfolding arg_id
- -- , isCheapUnfolding unf] )
- go ms rs
+ go ms [] = ms
+ go ms (r:rs)
+ | Just e <- matchRule dflags in_scope is_active fn args' rough_args r
+ = go ((r,mkTicks ticks e):ms) rs
+ | otherwise
+ = -- pprTrace "match failed" (ppr r $$ ppr args $$
+ -- ppr [ (arg_id, unfoldingTemplate unf)
+ -- | Var arg_id <- args
+ -- , let unf = idUnfolding arg_id
+ -- , isCheapUnfolding unf] )
+ go ms rs
findBest :: (Id, [CoreExpr])
-> (CoreRule,CoreExpr) -> [(CoreRule,CoreExpr)] -> (CoreRule,CoreExpr)
@@ -609,6 +620,14 @@ match :: RuleMatchEnv
-> CoreExpr -- Target
-> Maybe RuleSubst
+-- We look through certain ticks. See note [Tick annotations in RULE matching]
+match renv subst e1 (Tick t e2)
+ | tickishFloatable t
+ = match renv subst' e1 e2
+ where subst' = subst { rs_binds = rs_binds subst . mkTick t }
+match _ _ e@Tick{} _
+ = pprPanic "Tick in rule" (ppr e)
+
-- See the notes with Unify.match, which matches types
-- Everything is very similar for terms
@@ -675,10 +694,11 @@ match renv subst (App f1 a1) (App f2 a2)
; match renv subst' a1 a2 }
match renv subst (Lam x1 e1) e2
- | Just (x2, e2) <- exprIsLambda_maybe (rvInScopeEnv renv) e2
+ | Just (x2, e2, ts) <- exprIsLambda_maybe (rvInScopeEnv renv) e2
= let renv' = renv { rv_lcl = rnBndr2 (rv_lcl renv) x1 x2
, rv_fltR = delBndr (rv_fltR renv) x2 }
- in match renv' subst e1 e2
+ subst' = subst { rs_binds = rs_binds subst . flip (foldr mkTick) ts }
+ in match renv' subst' e1 e2
match renv subst (Case e1 x1 ty1 alts1) (Case e2 x2 ty2 alts2)
= do { subst1 <- match_ty renv subst ty1 ty2
@@ -890,10 +910,17 @@ Hence, (a) the guard (not (isLocallyBoundR v2))
Note [Tick annotations in RULE matching]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We used to look through Notes in both template and expression being
-matched. This would be incorrect for ticks, which we cannot discard,
-so we do not look through Ticks at all. cf Note [Notes in call
-patterns] in SpecConstr
+
+We used to unconditionally look through Notes in both template and
+expression being matched. This is actually illegal for counting or
+cost-centre-scoped ticks, because we have no place to put them without
+changing entry counts and/or costs. So now we just fail the match in
+these cases.
+
+On the other hand, where we are allowed to insert new cost into the
+tick scope, we can float them upwards to the rule application site.
+
+cf Note [Notes in call patterns] in SpecConstr
Note [Matching lets]
~~~~~~~~~~~~~~~~~~~~
diff --git a/compiler/specialise/SpecConstr.hs b/compiler/specialise/SpecConstr.hs
index 11ba67e8d2..9b24604404 100644
--- a/compiler/specialise/SpecConstr.hs
+++ b/compiler/specialise/SpecConstr.hs
@@ -886,7 +886,8 @@ extendCaseBndrs env scrut case_bndr con alt_bndrs
= (env2, alt_bndrs')
where
live_case_bndr = not (isDeadBinder case_bndr)
- env1 | Var v <- scrut = extendValEnv env v cval
+ env1 | Var v <- stripTicksTopE (const True) scrut
+ = extendValEnv env v cval
| otherwise = env -- See Note [Add scrutinee to ValueEnv too]
env2 | live_case_bndr = extendValEnv env1 case_bndr cval
| otherwise = env1
@@ -1974,8 +1975,12 @@ isValue env (Lam b e)
Nothing -> Nothing
| otherwise = Just LambdaVal
+isValue env (Tick t e)
+ | not (tickishIsCode t)
+ = isValue env e
+
isValue _env expr -- Maybe it's a constructor application
- | (Var fun, args) <- collectArgs expr
+ | (Var fun, args, _) <- collectArgsTicks (not . tickishIsCode) expr
= case isDataConWorkId_maybe fun of
Just con | args `lengthAtLeast` dataConRepArity con
diff --git a/compiler/stgSyn/CoreToStg.hs b/compiler/stgSyn/CoreToStg.hs
index 5b22e67eaf..55a31d4255 100644
--- a/compiler/stgSyn/CoreToStg.hs
+++ b/compiler/stgSyn/CoreToStg.hs
@@ -398,6 +398,9 @@ coreToStgExpr (Tick (ProfNote cc tick push) expr)
coreToStgExpr (Tick Breakpoint{} _expr)
= panic "coreToStgExpr: breakpoint should not happen"
+coreToStgExpr (Tick _ expr)
+ = {- dropped for now ... -} coreToStgExpr expr
+
coreToStgExpr (Cast expr _)
= coreToStgExpr expr
diff --git a/compiler/utils/OrdList.hs b/compiler/utils/OrdList.hs
index ad72ca1d45..9e735e7d80 100644
--- a/compiler/utils/OrdList.hs
+++ b/compiler/utils/OrdList.hs
@@ -9,6 +9,7 @@ Provide trees (of instructions), so that lists of instructions
can be appended in linear time.
-}
+{-# LANGUAGE CPP #-}
module OrdList (
OrdList,
nilOL, isNilOL, unitOL, appOL, consOL, snocOL, concatOL,
@@ -17,6 +18,10 @@ module OrdList (
import Outputable
+#if __GLASGOW_HASKELL__ < 709
+import Data.Monoid ( Monoid(..) )
+#endif
+
infixl 5 `appOL`
infixl 5 `snocOL`
infixr 5 `consOL`
@@ -33,6 +38,11 @@ data OrdList a
instance Outputable a => Outputable (OrdList a) where
ppr ol = ppr (fromOL ol) -- Convert to list and print that
+instance Monoid (OrdList a) where
+ mempty = nilOL
+ mappend = appOL
+ mconcat = concatOL
+
nilOL :: OrdList a
isNilOL :: OrdList a -> Bool