diff options
-rw-r--r-- | compiler/deSugar/DsMeta.hs | 4 | ||||
-rw-r--r-- | compiler/hsSyn/HsBinds.hs | 24 | ||||
-rw-r--r-- | compiler/parser/Parser.y | 38 | ||||
-rw-r--r-- | compiler/rename/RnBinds.hs | 9 | ||||
-rw-r--r-- | compiler/typecheck/TcBinds.hs | 23 | ||||
-rw-r--r-- | compiler/typecheck/TcSigs.hs | 3 | ||||
-rw-r--r-- | docs/users_guide/8.2.1-notes.rst | 6 | ||||
-rw-r--r-- | docs/users_guide/profiling.rst | 21 | ||||
-rw-r--r-- | testsuite/config/ghc | 2 | ||||
-rw-r--r-- | testsuite/tests/profiling/should_run/all.T | 7 | ||||
-rw-r--r-- | testsuite/tests/profiling/should_run/toplevel_scc_1.hs | 23 | ||||
-rw-r--r-- | testsuite/tests/profiling/should_run/toplevel_scc_1.prof.sample | 30 | ||||
-rw-r--r-- | testsuite/tests/profiling/should_run/toplevel_scc_1.stdin | 1 | ||||
-rw-r--r-- | testsuite/tests/profiling/should_run/toplevel_scc_1.stdout | 1 |
14 files changed, 172 insertions, 20 deletions
diff --git a/compiler/deSugar/DsMeta.hs b/compiler/deSugar/DsMeta.hs index 01c4903c54..427a56f479 100644 --- a/compiler/deSugar/DsMeta.hs +++ b/compiler/deSugar/DsMeta.hs @@ -695,8 +695,7 @@ rep_sigs sigs = do locs_cores <- rep_sigs' sigs rep_sigs' :: [LSig Name] -> DsM [(SrcSpan, Core TH.DecQ)] -- We silently ignore ones we don't recognise -rep_sigs' sigs = do { sigs1 <- mapM rep_sig sigs ; - return (concat sigs1) } +rep_sigs' = concatMapM rep_sig rep_sig :: LSig Name -> DsM [(SrcSpan, Core TH.DecQ)] rep_sig (L loc (TypeSig nms ty)) = mapM (rep_wc_ty_sig sigDName loc ty) nms @@ -711,6 +710,7 @@ rep_sig (L loc (SpecSig nm tys ispec)) = concatMapM (\t -> rep_specialise nm t ispec loc) tys rep_sig (L loc (SpecInstSig _ ty)) = rep_specialiseInst ty loc rep_sig (L _ (MinimalSig {})) = notHandled "MINIMAL pragmas" empty +rep_sig (L _ (SCCFunSig {})) = notHandled "SCC pragmas" empty rep_ty_sig :: Name -> SrcSpan -> LHsSigType Name -> Located Name -> DsM (SrcSpan, Core TH.DecQ) diff --git a/compiler/hsSyn/HsBinds.hs b/compiler/hsSyn/HsBinds.hs index 8772619e85..58b33fb810 100644 --- a/compiler/hsSyn/HsBinds.hs +++ b/compiler/hsSyn/HsBinds.hs @@ -796,6 +796,18 @@ data Sig name | MinimalSig SourceText (LBooleanFormula (Located name)) -- Note [Pragma source text] in BasicTypes + -- | A "set cost centre" pragma for declarations + -- + -- > {-# SCC funName #-} + -- + -- or + -- + -- > {-# SCC funName "cost_centre_name" #-} + + | SCCFunSig SourceText -- Note [Pragma source text] in BasicTypes + (Located name) -- Function name + (Maybe StringLiteral) + deriving instance (DataId name) => Data (Sig name) @@ -855,6 +867,7 @@ isPragLSig :: LSig name -> Bool -- Identifies pragmas isPragLSig (L _ (SpecSig {})) = True isPragLSig (L _ (InlineSig {})) = True +isPragLSig (L _ (SCCFunSig {})) = True isPragLSig _ = False isInlineLSig :: LSig name -> Bool @@ -864,7 +877,11 @@ isInlineLSig _ = False isMinimalLSig :: LSig name -> Bool isMinimalLSig (L _ (MinimalSig {})) = True -isMinimalLSig _ = False +isMinimalLSig _ = False + +isSCCFunSig :: LSig name -> Bool +isSCCFunSig (L _ (SCCFunSig {})) = True +isSCCFunSig _ = False hsSigDoc :: Sig name -> SDoc hsSigDoc (TypeSig {}) = text "type signature" @@ -878,6 +895,7 @@ hsSigDoc (InlineSig _ prag) = ppr (inlinePragmaSpec prag) <+> text "pragma" hsSigDoc (SpecInstSig {}) = text "SPECIALISE instance pragma" hsSigDoc (FixSig {}) = text "fixity declaration" hsSigDoc (MinimalSig {}) = text "MINIMAL pragma" +hsSigDoc (SCCFunSig {}) = text "SCC pragma" {- Check if signatures overlap; this is used when checking for duplicate @@ -903,6 +921,10 @@ ppr_sig (SpecInstSig _ ty) ppr_sig (MinimalSig _ bf) = pragBrackets (pprMinimalSig bf) ppr_sig (PatSynSig names sig_ty) = text "pattern" <+> pprVarSig (map unLoc names) (ppr sig_ty) +ppr_sig (SCCFunSig _ fn Nothing) + = pragBrackets (text "SCC" <+> ppr fn) +ppr_sig (SCCFunSig _ fn (Just str)) + = pragBrackets (text "SCC" <+> ppr fn <+> ppr (sl_st str)) instance OutputableBndr name => Outputable (FixitySig name) where ppr (FixitySig names fixity) = sep [ppr fixity, pprops] diff --git a/compiler/parser/Parser.y b/compiler/parser/Parser.y index e8d60ec611..fea9203811 100644 --- a/compiler/parser/Parser.y +++ b/compiler/parser/Parser.y @@ -835,7 +835,7 @@ topdecl :: { LHsDecl RdrName } -- The $(..) form is one possible form of infixexp -- but we treat an arbitrary expression just as if -- it had a $(..) wrapped around it - | infixexp { sLL $1 $> $ mkSpliceDecl $1 } + | infixexp_top { sLL $1 $> $ mkSpliceDecl $1 } -- Type classes -- @@ -1989,7 +1989,7 @@ decl_no_th :: { LHsDecl RdrName } -- Turn it all into an expression so that -- checkPattern can check that bangs are enabled - | infixexp opt_sig rhs {% do { (ann,r) <- checkValDef empty $1 (snd $2) $3; + | infixexp_top opt_sig rhs {% do { (ann,r) <- checkValDef empty $1 (snd $2) $3; let { l = comb2 $1 $> }; case r of { (FunBind n _ _ _ _) -> @@ -2029,7 +2029,7 @@ gdrh :: { LGRHS RdrName (LHsExpr RdrName) } sigdecl :: { LHsDecl RdrName } : -- See Note [Declaration/signature overlap] for why we need infixexp here - infixexp '::' sigtypedoc + infixexp_top '::' sigtypedoc {% do v <- checkValSigLhs $1 ; _ <- ams (sLL $1 $> ()) [mu AnnDcolon $2] ; return (sLL $1 $> $ SigD $ @@ -2056,6 +2056,16 @@ sigdecl :: { LHsDecl RdrName } (snd $2))))) ((mo $1:fst $2) ++ [mc $4]) } + | '{-# SCC' qvar '#-}' + {% ams (sLL $1 $> (SigD (SCCFunSig (getSCC_PRAGs $1) $2 Nothing))) + [mo $1, mc $3] } + + | '{-# SCC' qvar STRING '#-}' + {% do { scc <- getSCC $3 + ; let str_lit = StringLiteral (getSTRINGs $3) scc + ; ams (sLL $1 $> (SigD (SCCFunSig (getSCC_PRAGs $1) $2 (Just str_lit)))) + [mo $1, mc $4] } } + | '{-# SPECIALISE' activation qvar '::' sigtypes1 '#-}' {% ams ( let inl_prag = mkInlinePragma (getSPEC_PRAGs $1) @@ -2121,14 +2131,18 @@ exp :: { LHsExpr RdrName } | infixexp { $1 } infixexp :: { LHsExpr RdrName } - : exp10 { $1 } - | infixexp qop exp10 {% ams (sLL $1 $> - (OpApp $1 $2 placeHolderFixity $3)) - [mj AnnVal $2] } + : exp10 { $1 } + | infixexp qop exp10 {% ams (sLL $1 $> (OpApp $1 $2 placeHolderFixity $3)) + [mj AnnVal $2] } -- AnnVal annotation for NPlusKPat, which discards the operator +infixexp_top :: { LHsExpr RdrName } + : exp10_top { $1 } + | infixexp_top qop exp10_top + {% ams (sLL $1 $> (OpApp $1 $2 placeHolderFixity $3)) + [mj AnnVal $2] } -exp10 :: { LHsExpr RdrName } +exp10_top :: { LHsExpr RdrName } : '\\' apat apats opt_asig '->' exp {% ams (sLL $1 $> $ HsLam (mkMatchGroup FromSource [sLL $1 $> $ Match { m_ctxt = LambdaExpr @@ -2170,9 +2184,6 @@ exp10 :: { LHsExpr RdrName } (mkHsDo MDoExpr (snd $ unLoc $2))) (mj AnnMdo $1:(fst $ unLoc $2)) } - | scc_annot exp {% ams (sLL $1 $> $ HsSCC (snd $ fst $ unLoc $1) (snd $ unLoc $1) $2) - (fst $ fst $ unLoc $1) } - | hpc_annot exp {% ams (sLL $1 $> $ HsTickPragma (snd $ fst $ fst $ unLoc $1) (snd $ fst $ unLoc $1) (snd $ unLoc $1) $2) (fst $ fst $ fst $ unLoc $1) } @@ -2191,6 +2202,11 @@ exp10 :: { LHsExpr RdrName } -- hdaume: core annotation | fexp { $1 } +exp10 :: { LHsExpr RdrName } + : exp10_top { $1 } + | scc_annot exp {% ams (sLL $1 $> $ HsSCC (snd $ fst $ unLoc $1) (snd $ unLoc $1) $2) + (fst $ fst $ unLoc $1) } + optSemi :: { ([Located a],Bool) } : ';' { ([$1],True) } | {- empty -} { ([],False) } diff --git a/compiler/rename/RnBinds.hs b/compiler/rename/RnBinds.hs index 2e4f4dbc64..a965a65e63 100644 --- a/compiler/rename/RnBinds.hs +++ b/compiler/rename/RnBinds.hs @@ -933,6 +933,10 @@ renameSig ctxt sig@(PatSynSig vs ty) ty_ctxt = GenericCtx (text "a pattern synonym signature for" <+> ppr_sig_bndrs vs) +renameSig ctxt sig@(SCCFunSig st v s) + = do { new_v <- lookupSigOccRn ctxt sig v + ; return (SCCFunSig st new_v s, emptyFVs) } + ppr_sig_bndrs :: [Located RdrName] -> SDoc ppr_sig_bndrs bs = quotes (pprWithCommas ppr bs) @@ -971,6 +975,9 @@ okHsSig ctxt (L _ sig) (MinimalSig {}, ClsDeclCtxt {}) -> True (MinimalSig {}, _) -> False + (SCCFunSig {}, HsBootCtxt {}) -> False + (SCCFunSig {}, _) -> True + ------------------- findDupSigs :: [LSig RdrName] -> [[(Located RdrName, Sig RdrName)]] -- Check for duplicates on RdrName version, @@ -989,6 +996,7 @@ findDupSigs sigs expand_sig sig@(TypeSig ns _) = [(n,sig) | n <- ns] expand_sig sig@(ClassOpSig _ ns _) = [(n,sig) | n <- ns] expand_sig sig@(PatSynSig ns _ ) = [(n,sig) | n <- ns] + expand_sig sig@(SCCFunSig _ n _) = [(n,sig)] expand_sig _ = [] matching_sig (L _ n1,sig1) (L _ n2,sig2) = n1 == n2 && mtch sig1 sig2 @@ -997,6 +1005,7 @@ findDupSigs sigs mtch (TypeSig {}) (TypeSig {}) = True mtch (ClassOpSig d1 _ _) (ClassOpSig d2 _ _) = d1 == d2 mtch (PatSynSig _ _) (PatSynSig _ _) = True + mtch (SCCFunSig{}) (SCCFunSig{}) = True mtch _ _ = False -- Warn about multiple MINIMAL signatures diff --git a/compiler/typecheck/TcBinds.hs b/compiler/typecheck/TcBinds.hs index 3e9c3918d2..ba63051c80 100644 --- a/compiler/typecheck/TcBinds.hs +++ b/compiler/typecheck/TcBinds.hs @@ -18,7 +18,10 @@ import {-# SOURCE #-} TcMatches ( tcGRHSsPat, tcMatchesFun ) import {-# SOURCE #-} TcExpr ( tcMonoExpr ) import {-# SOURCE #-} TcPatSyn ( tcInferPatSynDecl, tcCheckPatSynDecl , tcPatSynBuilderBind ) +import CoreSyn (Tickish (..)) +import CostCentre (mkUserCC) import DynFlags +import FastString import HsSyn import HscTypes( isHsBootOrSig ) import TcSigs @@ -57,6 +60,7 @@ import BasicTypes import Outputable import PrelNames( gHC_PRIM, ipClassName ) import TcValidity (checkValidType) +import Unique (getUnique) import UniqFM import qualified GHC.LanguageExtensions as LangExt @@ -659,11 +663,12 @@ tcPolyCheck prag_fn ; spec_prags <- tcSpecPrags poly_id prag_sigs ; poly_id <- addInlinePrags poly_id prag_sigs + ; mod <- getModule ; let bind' = FunBind { fun_id = L nm_loc mono_id , fun_matches = matches' , fun_co_fn = co_fn , bind_fvs = placeHolderNamesTc - , fun_tick = [] } + , fun_tick = funBindTicks nm_loc mono_id mod prag_sigs } abs_bind = L loc $ AbsBindsSig { abs_sig_export = poly_id @@ -678,6 +683,22 @@ tcPolyCheck prag_fn tcPolyCheck _prag_fn sig bind = pprPanic "tcPolyCheck" (ppr sig $$ ppr bind) +funBindTicks :: SrcSpan -> TcId -> Module -> [LSig Name] -> [Tickish TcId] +funBindTicks loc fun_id mod sigs + | (mb_cc_str : _) <- [ cc_name | L _ (SCCFunSig _ _ cc_name) <- sigs ] + -- this can only be a singleton list, as duplicate pragmas are rejected + -- by the renamer + , let cc_str + | Just cc_str <- mb_cc_str + = sl_fs cc_str + | otherwise + = getOccFS (Var.varName fun_id) + cc_name = moduleNameFS (moduleName mod) `appendFS` consFS '.' cc_str + cc = mkUserCC cc_name mod loc (getUnique fun_id) + = [ProfNote cc True True] + | otherwise + = [] + {- Note [Instantiate sig with fresh variables] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It's vital to instantiate a type signature with fresh variables. diff --git a/compiler/typecheck/TcSigs.hs b/compiler/typecheck/TcSigs.hs index bcf8b9e5a7..9c4fd2bf8a 100644 --- a/compiler/typecheck/TcSigs.hs +++ b/compiler/typecheck/TcSigs.hs @@ -490,6 +490,7 @@ mkPragEnv sigs binds get_sig :: LSig Name -> Maybe (Name, LSig Name) get_sig (L l (SpecSig lnm@(L _ nm) ty inl)) = Just (nm, L l $ SpecSig lnm ty (add_arity nm inl)) get_sig (L l (InlineSig lnm@(L _ nm) inl)) = Just (nm, L l $ InlineSig lnm (add_arity nm inl)) + get_sig (L l (SCCFunSig st lnm@(L _ nm) str)) = Just (nm, L l $ SCCFunSig st lnm str) get_sig _ = Nothing add_arity n inl_prag -- Adjust inl_sat field to match visible arity of function @@ -666,7 +667,7 @@ tcSpecPrags poly_id prag_sigs where spec_sigs = filter isSpecLSig prag_sigs bad_sigs = filter is_bad_sig prag_sigs - is_bad_sig s = not (isSpecLSig s || isInlineLSig s) + is_bad_sig s = not (isSpecLSig s || isInlineLSig s || isSCCFunSig s) warn_discarded_sigs = addWarnTc NoReason diff --git a/docs/users_guide/8.2.1-notes.rst b/docs/users_guide/8.2.1-notes.rst index 1d302ff5bf..5f45bf1002 100644 --- a/docs/users_guide/8.2.1-notes.rst +++ b/docs/users_guide/8.2.1-notes.rst @@ -13,6 +13,7 @@ Highlights The highlights since the 8.0 branch are: - TODO FIXME +- SCC annotations can now be used for declarations. Full details ------------ @@ -32,7 +33,10 @@ Compiler - Old profiling flags ``-auto-all``, ``-auto``, and ``-caf-all`` are deprecated and their usage provokes a compile-time warning. - + +- Support for adding cost centres to declarations is added. The same `SCC` + syntax can be used, in addition to a new form for specifying the cost centre + name. See :ref:`scc-pragma` for examples. GHCi ~~~~ diff --git a/docs/users_guide/profiling.rst b/docs/users_guide/profiling.rst index daae7805d5..20f2a83824 100644 --- a/docs/users_guide/profiling.rst +++ b/docs/users_guide/profiling.rst @@ -199,7 +199,7 @@ to the compiler, it automatically inserts a cost centre annotation around every binding not marked INLINE in your program, but you are entirely free to add cost centre annotations yourself. -The syntax of a cost centre annotation is :: +The syntax of a cost centre annotation for expressions is :: {-# SCC "name" #-} <expression> @@ -210,7 +210,24 @@ extends as far to the right as possible when parsing. (SCC stands for "Set Cost Centre"). The double quotes can be omitted if ``name`` is a Haskell identifier, for example: :: - {-# SCC my_function #-} <expression> + {-# SCC id #-} <expression> + +Cost centre annotations can also appear in the top-level or in a +declaration context. In that case you need to pass a function name +defined in the same module or scope with the annotation. Example: :: + + f x y = ... + where + g z = ... + {-# SCC g #-} + + {-# SCC f #-} + +If you want to give a cost centre different name than the function name, +you can pass a string to the annotation :: + + f x y = ... + {-# SCC f "cost_centre_name" #-} Here is an example of a program with a couple of SCCs: :: diff --git a/testsuite/config/ghc b/testsuite/config/ghc index 68d4a64979..b0d84535d6 100644 --- a/testsuite/config/ghc +++ b/testsuite/config/ghc @@ -76,6 +76,7 @@ config.way_flags = { 'optllvm' : ['-O', '-fllvm'], 'debugllvm' : ['-fllvm', '-keep-llvm-files'], 'prof' : ['-prof', '-static', '-fprof-auto', '-fasm'], + 'prof_no_auto' : ['-prof', '-static', '-fasm'], 'profasm' : ['-O', '-prof', '-static', '-fprof-auto'], 'profthreaded' : ['-O', '-prof', '-static', '-fprof-auto', '-threaded'], 'ghci' : ['--interactive', '-v0', '-ignore-dot-ghci', '-fno-ghci-history', '+RTS', '-I0.1', '-RTS'], @@ -111,6 +112,7 @@ config.way_rts_flags = { 'optllvm' : [], 'debugllvm' : [], 'prof' : ['-p'], + 'prof_no_auto' : ['-p'], 'profasm' : ['-hc', '-p'], # test heap profiling too 'profthreaded' : ['-p'], 'ghci' : [], diff --git a/testsuite/tests/profiling/should_run/all.T b/testsuite/tests/profiling/should_run/all.T index 76956bd220..7aa7977350 100644 --- a/testsuite/tests/profiling/should_run/all.T +++ b/testsuite/tests/profiling/should_run/all.T @@ -104,7 +104,7 @@ test('T11627a', [extra_ways(extra_prof_ways)], compile_and_run, ['']) test('T11627b', [ extra_run_opts('+RTS -i0 -RTS') # census after each GC , extra_ways(extra_prof_ways) , when(opsys('mingw32'), - expect_broken_for(12236, ['prof_hc_hb'])) + expect_broken_for(12236, ['prof_hc_hb'])) ] , compile_and_run , ['']) @@ -112,3 +112,8 @@ test('T11627b', [ extra_run_opts('+RTS -i0 -RTS') # census after each GC test('T11978a', [only_ways(['profthreaded']), extra_run_opts('+RTS -hb -N10')], compile_and_run, ['']) + +test('toplevel_scc_1', + [extra_ways(['prof_no_auto']), only_ways(['prof_no_auto'])], + compile_and_run, + ['']) diff --git a/testsuite/tests/profiling/should_run/toplevel_scc_1.hs b/testsuite/tests/profiling/should_run/toplevel_scc_1.hs new file mode 100644 index 0000000000..6890511391 --- /dev/null +++ b/testsuite/tests/profiling/should_run/toplevel_scc_1.hs @@ -0,0 +1,23 @@ +module Main where + +f1 :: Int -> Int +f1 = (+ 1) + +f2 :: Int -> Int +f2 = f3 . f3 . f3 + where + f3 :: Int -> Int + f3 = (* 123) + {-# SCC f3 "bar" #-} + {-# NOINLINE f3 #-} + + + +main :: IO () +main = readLn >>= print . f2 . f1 + +{-# NOINLINE f1 #-} +{-# NOINLINE f2 #-} + +{-# SCC f1 #-} +{-# SCC f2 "foo" #-} diff --git a/testsuite/tests/profiling/should_run/toplevel_scc_1.prof.sample b/testsuite/tests/profiling/should_run/toplevel_scc_1.prof.sample new file mode 100644 index 0000000000..4eec28da28 --- /dev/null +++ b/testsuite/tests/profiling/should_run/toplevel_scc_1.prof.sample @@ -0,0 +1,30 @@ + Tue Jul 19 08:36 2016 Time and Allocation Profiling Report (Final) + + toplevel_scc_1 +RTS -p -RTS + + total time = 0.00 secs (0 ticks @ 1000 us, 1 processor) + total alloc = 79,792 bytes (excludes profiling overheads) + +COST CENTRE MODULE SRC %time %alloc + +CAF GHC.Read <entire-module> 0.0 1.2 +CAF GHC.IO.Handle.FD <entire-module> 0.0 64.9 +CAF GHC.IO.Encoding <entire-module> 0.0 3.5 +CAF Main <entire-module> 0.0 27.6 + + + individual inherited +COST CENTRE MODULE SRC no. entries %time %alloc %time %alloc + +MAIN MAIN <built-in> 105 0 0.0 0.4 0.0 100.0 + CAF Main <entire-module> 209 0 0.0 27.6 0.0 27.9 + Main.f1 Main toplevel_scc_1.hs:4:1-2 212 1 0.0 0.0 0.0 0.0 + Main.foo Main toplevel_scc_1.hs:7:1-2 210 1 0.0 0.2 0.0 0.3 + Main.bar Main toplevel_scc_1.hs:10:5-6 211 1 0.0 0.1 0.0 0.1 + CAF GHC.Conc.Signal <entire-module> 203 0 0.0 0.8 0.0 0.8 + CAF GHC.IO.Encoding <entire-module> 193 0 0.0 3.5 0.0 3.5 + CAF GHC.IO.Encoding.Iconv <entire-module> 191 0 0.0 0.3 0.0 0.3 + CAF GHC.IO.Handle.FD <entire-module> 183 0 0.0 64.9 0.0 64.9 + CAF GHC.IO.Handle.Text <entire-module> 181 0 0.0 0.1 0.0 0.1 + CAF GHC.Read <entire-module> 171 0 0.0 1.2 0.0 1.2 + CAF Text.Read.Lex <entire-module> 154 0 0.0 0.8 0.0 0.8 diff --git a/testsuite/tests/profiling/should_run/toplevel_scc_1.stdin b/testsuite/tests/profiling/should_run/toplevel_scc_1.stdin new file mode 100644 index 0000000000..190a18037c --- /dev/null +++ b/testsuite/tests/profiling/should_run/toplevel_scc_1.stdin @@ -0,0 +1 @@ +123 diff --git a/testsuite/tests/profiling/should_run/toplevel_scc_1.stdout b/testsuite/tests/profiling/should_run/toplevel_scc_1.stdout new file mode 100644 index 0000000000..bfb8a3a41f --- /dev/null +++ b/testsuite/tests/profiling/should_run/toplevel_scc_1.stdout @@ -0,0 +1 @@ +230747508 |