Commit
This patch introduces "SourceNote" tickishs that carry a reference to the original source code. They are meant to be passed along the compilation pipeline with as little disturbance to optimization processes as possible. Generation is triggered by command line parameter -g. It's free and fits with the intended end result (generation of DWARF). Internally we say that we compile with "debugging", which is probably at least slightly confusing given the plethora of other debugging options we have. Note that this pass creates *lots* of tick nodes. We take care to remove duplicated and overlapping source ticks, which gets rid of most of them. Possible optimization could be to make Tick carry a list of Tickishs instead of one at a time. Keeping ticks from getting into the way of Core transformations is tricky, but doable. The changes in this patch produce identical Core in all cases I tested (nofib). We should probably look for a way to make a test-case out of this.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -966,6 +966,9 @@ simple_app subst (Lam b e) (a:as) | |
where | ||
(subst', b') = subst_opt_bndr subst b | ||
b2 = add_info subst' b b' | ||
simple_app subst (Tick t e) as | ||
This comment has been minimized.
Sorry, something went wrong.
simonpj
|
||
| tickishFloatable t | ||
This comment has been minimized.
Sorry, something went wrong.
simonpj
|
||
= mkTick t $ simple_app subst e as | ||
simple_app subst e as | ||
= foldl App (simple_opt_expr subst e) as | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,8 +44,10 @@ module CoreSyn ( | |
isValArg, isTypeArg, isTyCoArg, valArgCount, valBndrCount, | ||
isRuntimeArg, isRuntimeVar, | ||
tickishCounts, tickishScoped, tickishIsCode, mkNoCount, mkNoScope, | ||
tickishCanSplit, | ||
tickishCounts, tickishScoped, tickishSoftScope, tickishFloatable, | ||
tickishCanSplit, mkNoCount, mkNoScope, | ||
tickishIsCode, tickishIgnoreCheap, | ||
tickishContains, | ||
-- * Unfolding data types | ||
Unfolding(..), UnfoldingGuidance(..), UnfoldingSource(..), | ||
|
@@ -105,6 +107,7 @@ import DynFlags | |
import FastString | ||
import Outputable | ||
import Util | ||
import SrcLoc ( RealSrcSpan, containsSpan ) | ||
import Data.Data hiding (TyCon) | ||
import Data.Int | ||
|
@@ -472,6 +475,30 @@ data Tickish id = | |
-- Note [substTickish] in CoreSubst. | ||
} | ||
-- | A source note. | ||
-- | ||
-- In contrast to other ticks, 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 | ||
-- *might* provoke the meaning of generated code to change. On the | ||
-- flip-side, results of annotated code *must* be invariant against | ||
-- changes to all source code *except* the spans referenced in the | ||
-- source notes. | ||
This comment has been minimized.
Sorry, something went wrong.
simonmar
|
||
-- | ||
-- 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) | ||
|
@@ -488,36 +515,124 @@ tickishCounts :: Tickish id -> Bool | |
tickishCounts n@ProfNote{} = profNoteCount n | ||
tickishCounts HpcTick{} = True | ||
tickishCounts Breakpoint{} = True | ||
tickishCounts _ = False | ||
-- | Returns @True@ if code covered by this tick should be guaranteed to | ||
This comment has been minimized.
Sorry, something went wrong.
simonpj
|
||
-- stay covered. Note that this still allows code transformations | ||
-- inside the tick scope, as long as the result of these | ||
-- transformations are still covered by this tick. 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. | ||
tickishScoped :: Tickish id -> Bool | ||
tickishScoped n@ProfNote{} = profNoteScope n | ||
tickishScoped HpcTick{} = False | ||
tickishScoped Breakpoint{} = True | ||
-- 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{} = True | ||
-- | Returns @True@ for a scoped tick (@tickishScoped@) that allows | ||
-- inserting new cost into its execution scope. For example, this | ||
-- allows floating ticks up in a number of cases: | ||
-- | ||
-- case foo of x -> tick<...> bar | ||
-- ==> | ||
-- tick<...> case foo of x -> bar | ||
-- | ||
-- This can be important in order to expose optimization | ||
-- opportunities. Note that in constrast to @tickishScoped@, we | ||
-- consider execution scope and costs (cost-centre semantics), so the | ||
This comment has been minimized.
Sorry, something went wrong.
simonmar
|
||
-- following unfolding is allowed regardless of this flag: | ||
-- | ||
-- let x = foo in tick<...> x | ||
-- ==> | ||
-- tick<...> foo | ||
-- | ||
-- This is because even though new code was inserted into the lexical | ||
-- scope of the tick, the execution cost covered has actually been | ||
-- reduced (by the need to call through "x" to get to "foo"). | ||
tickishSoftScope :: Tickish id -> Bool | ||
This comment has been minimized.
Sorry, something went wrong.
simonpj
|
||
tickishSoftScope SourceNote{} = True | ||
tickishSoftScope HpcTick{} = True | ||
This comment has been minimized.
Sorry, something went wrong.
simonmar
|
||
tickishSoftScope _tickish = False -- all the rest for now | ||
-- | 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 = tickishSoftScope t && not (tickishCounts t) | ||
-- | For a tick that is both counting /and/ scoping, this function | ||
-- returns @True@ if it can be split into its (tick, scope) parts | ||
-- using 'mkNoScope' and 'mkNoTick' respectively. | ||
tickishCanSplit :: Tickish id -> Bool | ||
tickishCanSplit t | not (tickishCounts t) || not (tickishScoped t) | ||
= panic "tickishCanSplit: Split undefined!" | ||
tickishCanSplit ProfNote{} = 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 | not (tickishScoped n) = n | ||
| not (tickishCanSplit n) = panic "mkNoScope: Cannot split!" | ||
mkNoScope n@ProfNote{} = n {profNoteScope = False} | ||
mkNoScope Breakpoint{} = panic "mkNoScope: Breakpoint" -- cannot split a BP | ||
mkNoScope HpcTick{} = panic "mkNoScope: HpcTick" | ||
mkNoScope _ = panic "mkNoScope: Undefined split!" | ||
-- | Return True if this source annotation compiles to some code, or will | ||
-- disappear before the backend. | ||
-- | 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 execution cost. | ||
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 | ||
-- | Whether the ticks should be removed on expressions that are cheap | ||
-- to the point of having no associated runtime costs whatsoever. For | ||
-- example in the following examples, the tick could be eliminated | ||
-- completely: | ||
-- | ||
-- f (tick<...> 1) | ||
-- f (tick<...> e) | ||
-- | ||
-- as literals or variables don't carry any cost on their own (even | ||
-- though their usage might well cause cost). Along the same lines, we | ||
-- can do the following for our ticks: | ||
-- | ||
-- let a = tick<...> A e0 e1 in foo | ||
-- ==> | ||
-- let a = A (tick<...> e0) (tick<...> e1) in foo | ||
-- | ||
-- As we see the actual allocation cost coming from the usage site | ||
-- (the let) rather then the constructor itself. | ||
tickishIgnoreCheap :: Tickish id -> Bool | ||
tickishIgnoreCheap ProfNote{} = True | ||
tickishIgnoreCheap _other = False | ||
This comment has been minimized.
Sorry, something went wrong.
simonmar
|
||
-- | Returns whether one tick "contains" the other one, therefore | ||
-- making the second tick redundant. | ||
tickishContains :: Tickish Id -> Tickish Id -> Bool | ||
tickishContains (SourceNote sp1 n1) (SourceNote sp2 n2) | ||
= n1 == n2 && containsSpan sp1 sp2 | ||
tickishContains t1 t2 | ||
= t1 == t2 | ||
\end{code} | ||
|
@@ -1375,8 +1490,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] -> () | ||
|
@@ -1387,6 +1502,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` () | ||
|
1 comment
on commit d233ea4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, this is looking good. The main comment I have is that some of the changes to the simplifier won't be obvious enough to e.g. Simon PJ next time he's working in that area, and things are likely to get broken. So two things:
- we need tests for this stuff, so that we know when it breaks. Preferably small unit tests that check the behaviour for small examples. There's a good example of how to do this in
testsuite/tests/callarity/CallArity1.hs
. - some more comments specifically for the rationale behind the behaviour of transformations in
Simplify.hs
would help. Put yourself in the place of Simon PJ next time he's modifying the simplifier - what do you want to know about what this code is doing?
Worth a comment to explain what is happening here