diff --git a/CHANGELOG.md b/CHANGELOG.md index efb9726..3632e05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,16 @@ within a binding. * Add `FuseTypes` annotation for making types locally fusible within a specific binding. -* Add `NoFuseTypes` annotation for overriding `Fuse` data type annotation +* Add `NoFuse` annotation for disabling any `Fuse` data type annotation inside a binding. +* Add `NoFuseTypes` annotation for disabling `Fuse` annotation for specific + types within a binding. * Add `MaxCoreSize` annotation to report if core size of a binding exceeds a certain threashold. +* Add `DumpCore` annotation to write the optimized core of a binding to a + file. +* Add `DumpCorePasses` annotation to write the core of a binding to a file + after every Core-to-core pass. ## 0.1.0 diff --git a/src/Fusion/Plugin/Types.hs b/src/Fusion/Plugin/Types.hs index 41770e0..03190eb 100644 --- a/src/Fusion/Plugin/Types.hs +++ b/src/Fusion/Plugin/Types.hs @@ -26,6 +26,7 @@ module Fusion.Plugin.Types Fuse(..) , FuseTypes(..) , NoFuseTypes(..) + , NoFuse(..) -- ** Inspection Annotations -- | Annotations to find fusion violations. @@ -35,6 +36,7 @@ module Fusion.Plugin.Types -- ** Debugging Annotations , DumpCore(..) + , DumpCorePasses(..) ) where @@ -83,7 +85,8 @@ newtype FuseTypes = FuseTypes [Name] -- | A GHC annotation attached to a specific top level binding (via an @ANN@ -- pragma on the binding, not on a type) that makes each of the listed types -- behave as if it were /not/ annotated with 'Fuse', but /only/ for the purpose --- of inlining within that one binding -- not anywhere else in the module. +-- of inlining within that one binding -- not anywhere else in the module. This +-- is applicable for any nested bindings within the scope of that binding. -- -- This is the local override of 'Fuse' (and of 'FuseTypes'). Whereas @{-\# ANN -- type Step Fuse #-}@ marks @Step@ as fusible everywhere it is used, @{-\# ANN @@ -100,6 +103,17 @@ newtype FuseTypes = FuseTypes [Name] newtype NoFuseTypes = NoFuseTypes [Name] deriving (Eq, Data) +-- | A GHC annotation attached to a specific top level binding (via an @ANN@ +-- pragma on the binding, not on a type) that disables forced inlining of that +-- binding and any nested bindings within the scope of that binding altogether, +-- regardless of which types are involved. +-- +-- @ +-- {-\# ANN myFunc NoFuse #-} +-- @ +data NoFuse = NoFuse + deriving (Eq, Data) + -- | A GHC annotation attached to a specific top level binding (via an @ANN@ -- pragma on the binding, not on a type) that requests a fusion report for just -- that binding. @@ -114,8 +128,10 @@ newtype NoFuseTypes = NoFuseTypes [Name] -- {-\# ANN function1 (ForbidFused [] []) #-} -- {-\# ANN function1a (ForbidFused [''Maybe] []) #-} -- {-\# ANN function1b (ForbidFused [''Maybe] [''Step]) #-} --- {-\# ANN function2 (ForbidTypes [''Maybe]) #-} --- {-\# ANN function3 (PermitTypes [''Int, ''IO]) #-} +-- {-\# ANN function2 (ForbidBoxedUse [''Maybe]) #-} +-- {-\# ANN function3 (PermitBoxedUse [''Int, ''IO]) #-} +-- {-\# ANN function4 (PermitPatternMatches [''Int, ''IO]) #-} +-- {-\# ANN function5 (PermitAllocations [''Int, ''IO]) #-} -- @ data InspectTypes = ForbidFused [Name] [Name] @@ -123,15 +139,25 @@ data InspectTypes -- -- plus any types named in the first (forbid) list, minus any types -- named in the second (allow) list. A name present in both lists is -- allowed. - | ForbidTypes [Name] + | ForbidBoxedUse [Name] -- ^ Blocklist: report occurrences of exactly the named types/constructors -- found anywhere in the binding, regardless of whether they carry a 'Fuse' -- annotation. Everything else in core is fine. - | PermitTypes [Name] + | PermitBoxedUse [Name] -- ^ Allowlist: report occurrences of literally every type/constructor -- found in the binding -- a general "boxing detector", not limited to -- 'Fuse'-annotated types -- except the named types, which may appear -- freely. + | PermitPatternMatches [Name] + -- ^ Like 'PermitBoxedUse' but only reports occurrences in a scrutinizing + -- (pattern-match, i.e. @case@) position -- a value being deconstructed -- + -- ignoring constructing positions. Reports every type pattern-matched in + -- the binding except the named types, which may appear freely. + | PermitAllocations [Name] + -- ^ Like 'PermitBoxedUse' but only reports occurrences in a constructing + -- (allocating) position -- a value being built -- ignoring scrutinizing + -- positions. Reports every type constructed in the binding except the + -- named types, which may appear freely. deriving (Eq, Data) -- | A GHC annotation attached to a specific top level binding (via an @ANN@ @@ -180,15 +206,16 @@ newtype MaxCoreSize = MaxCoreSize Int -- pragma on the binding) that makes the plugin dump the optimized Core of that -- binding, after all fusion-plugin passes have run, to a file. -- --- The Core is written to a file under the @fusion-plugin-output@ directory, in --- a subdirectory named after the package being compiled. For example, a --- binding @myFunction@ in module @Data.Stream@ of package @my-pkg@ is written --- to --- @fusion-plugin-output\/my-pkg\/Data.Stream.myFunction.dump-simpl@. +-- The Core is written to GHC's dump directory (as set by @-dumpdir@) if one is +-- set, otherwise to a per-package subdirectory of @fusion-plugin-output@. For +-- example, a binding @myFunction@ in module @Data.Stream@ of package @my-pkg@ +-- is written to @\\/Data.Stream.myFunction.dump-simpl@, or to +-- @fusion-plugin-output\/my-pkg\/Data.Stream.myFunction.dump-simpl@ when no +-- dump directory is set. -- --- Note that the output directory is created in the current directory from --- where GHC is invoked, when building with cabal it is usually the directory --- in which the cabal file of the package resides. +-- Note that the @fusion-plugin-output@ fallback directory is created in the +-- current directory from where GHC is invoked; when building with cabal that is +-- usually the directory in which the cabal file of the package resides. -- -- This is useful for inspecting the final Core of a hot binding without having -- to wade through the Core of the entire module. @@ -198,3 +225,41 @@ newtype MaxCoreSize = MaxCoreSize Int -- @ data DumpCore = DumpCore deriving (Eq, Data) + +-- | A GHC annotation attached to a specific top level binding (via an @ANN@ +-- pragma on the binding) that makes the plugin dump the Core of that binding +-- /after every Core-to-core pass/, each to its own file. This is the +-- per-binding counterpart of the @dump-core@ plugin option: whereas +-- @dump-core@ dumps the Core of the /whole module/ after each pass, +-- 'DumpCorePasses' dumps only the annotated binding (and the closure of top +-- level bindings it reaches). +-- +-- The files are written to the same directory and with the same +-- @\.@ prefix as the 'DumpCore' annotation output -- GHC's dump +-- directory (as set by @-dumpdir@) if one is set, otherwise a per-package +-- subdirectory of @fusion-plugin-output@ -- so all of a module's fusion-plugin +-- dumps cluster together under one prefix. Each file is named +-- @\.\.\.dump-simpl@, where the per-pass suffix (a +-- two digit pass counter and the pass name) is the same one the @dump-core@ +-- option uses for its per-pass files. For example, a binding @myFunction@ in +-- module @Data.Stream@ of package @my-pkg@ produces one file per pass: +-- +-- @ +-- -- with @-dumpdir dir@: +-- dir\/Data.Stream.myFunction.00-Initial.dump-simpl +-- dir\/Data.Stream.myFunction.01-After-...\.dump-simpl +-- ... +-- -- otherwise: +-- fusion-plugin-output\/my-pkg\/Data.Stream.myFunction.00-Initial.dump-simpl +-- ... +-- @ +-- +-- This is useful for understanding how the Core of a single binding evolves +-- across the optimizer pipeline, without wading through the Core of the entire +-- module at every pass. +-- +-- @ +-- {-\# ANN myFunction DumpCorePasses #-} +-- @ +data DumpCorePasses = DumpCorePasses + deriving (Eq, Data)