Changes between Version 40 and Version 41 of NewPlugins


Ignore:
Timestamp:
Jul 19, 2011 12:25:47 AM (4 years ago)
Author:
thoughtpolice
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • NewPlugins

    v40 v41  
    11= New Plugins work =
    22
    3 Max originally did the work on [wiki:Plugins GHC plugins] in his GSoC 2008 hacking sprint. It involved the implementation of [wiki:Plugins/Annotations annotations] as well as a dynamic loading aspect to GHC. While the annotations work was included into GHC HEAD, the loading infrastructure was not. This document describes the current work (as of 2011) to get it integrated into GHC HEAD so you can write core plugins, and future extensions to the interface, primarily writing C-- passes.
     3Plugins are a new feature in GHC 7.2.1 that will allow users to write compiler passes (for things like optimizations) over GHC's internal intermediate language, [wiki:Commentary/Compiler/CoreSynType Core].
    44
    5 This page explains what the plug-in mechanism does, how to use it, and a little about the implementation.  For discussion, and the current state of play, see the ticket: #3843. If you're interested in writing plugins for GHC, '''please comment and give feedback, we want to do it right'''!
    6 
    7  * 1/17/11: I (Austin Seipp) am working on getting the patch cleaned up a little more and tidying it up before it gets integrated. Still need testsuite patches.
    8  * 5/10/11: I have been busy, but there's a new git repository!
    9 
    10 NB. Ridiculously incomplete writing/documentation.
    11 
    12 == Current overview ==
    13 
    14 I currently have a branch of GHC with plugins support, that is occasionally (once a week or so) merged with master for the latest fixes/updates. You can find it here:
    15 
    16 https://github.com/thoughtpolice/ghc/tree/plugins
    17 
    18 so just run:
     5GHC understands the `-fplugin` and `-fplugin-arg` options. You essentially install plugins for GHC by `cabal install`ing them, as they expose a module implementing an interface, and then calling GHC in the form of:
    196
    207{{{
    21 $ git remote add aseipp [email protected]:thoughtpolice/ghc.git
    22 $ git pull --all
    23 $ git checkout -b plugins_temp aseipp/plugins
     8$ ghc -fplugin=Some.Plugin.Module -fplugin-opt=Some.Plugin.Module:no-fizzbuzz a.hs
    249}}}
    2510
    26 Then build GHC like normal. You don't need to check out branches of any libraries, because the compiler work only touches the code under `./compiler`.
    27 
    28 Now GHC understands the `-fplugin` and `-fplugin-arg` options. You essentially install plugins for GHC by `cabal install`ing them, and then calling GHC in the form of:
    29 
    30 {{{
    31 $ ghc -fplugin=Some.Plugin.Module -fplugin-arg=Some.Plugin.Module:no-fizzbuzz a.hs
    32 }}}
    33 
    34 `Some.Plugin.Module` should export a symbol named 'plugin' - see the following repository for an example that does Common Subexpression Elimination:
     11`Some.Plugin.Module` should export a symbol named 'plugin' - see the following repositories for examples that do Common Subexpression Elimination, turn Haskell into a strict language, and implement a loop unroller:
    3512
    3613https://github.com/thoughtpolice/cse-ghc-plugin
     14https://github.com/thoughtpolice/strict-ghc-plugin
     15https://github.com/thoughtpolice/unroll-ghc-plugin
    3716
    3817=== Basic overview of the plugins API for Core ===
    3918
    40 Modules can be loaded by GHC as compiler plugins by exposing a declaration called 'plugin' of type 'GHCPlugins.Plugin', which is an ADT containing a function that installs a pass into the Core pipeline.
     19Modules can be loaded by GHC as compiler plugins by exposing a declaration called 'plugin' of type 'GhcPlugins.Plugin', which is an ADT containing a function that installs a pass into the Core pipeline.
    4120
    4221{{{
    4322module Some.Plugin.Module (plugin) where
    44 import GHCPlugins
     23import GhcPlugins
    4524
    4625plugin :: Plugin
     
    5938}}}
    6039
    61 We can think of `CoreToDo` as being a type synonym for `(Core -> Core)` - that is, an installation function inserts a pass into the list of core passes by just inserting itself into the list and returning it. For example, the CSE pass actually couples a simplification pass, followed by CSE into the front of the compilation pipeline:
     40We can think of `CoreToDo` as being a type synonym for `(Core -> Core)` - that is, the `install` function just inserts its own `CoreToDo` into the list of compiler passes. For example, the CSE pass actually couples a simplification pass, followed by CSE itself into the front of the compilation pipeline:
    6241
    6342{{{
     
    6746
    6847install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
    69 install _options todos = do
    70     -- You should probably run this with -fno-cse !
    71     return $ CoreDoPasses [defaultGentleSimplToDo, cse_pass] : todos
    72 
    73 cse_pass = CoreDoPluginPass "Plugged-in common sub-expression" (BindsToBindsPluginPass cseProgram)
     48install _ xs = return $ CoreDoPasses [defaultGentleSimplToDo, cse] : xs
     49  where cse = CoreDoPluginPass "Common Subexpression Elimination" (bindsOnlyPass cseProgram)
    7450
    7551cseProgram :: [CoreBind] -> CoreM [CoreBind]
     
    7854}}}
    7955
    80 More specifically, a `CoreToDo` describes some sort of particular pass over a Core program that can be invoked as many times as you like. For reference, `defaultGentlSimplToDo` is constructed using `CoreDoSimplify`. We use `CoreDoPasses` to just make it easy to run multiple `CoreToDo`'s together (it would be equivalent to just use `cse_pass` and preceed it with `defaultGentleSimplToDo` in the pipeline directly.) In this case, `cse_pass` is constructed using `CoreDoPluginsPass`, which takes a name and a `PluginPass` which looks like the following:
     56More specifically, a `CoreToDo` describes some sort of particular pass over a Core program that can be invoked as many times as you like. For reference, `defaultGentlSimplToDo` is constructed using `CoreDoSimplify`. In this case, `cse_pass` is constructed using `CoreDoPluginsPass`, which takes a name and a function of type `ModGuts -> CoreM ModGuts` - `ModGuts` is a type that represents the 1 module GHC is compiling at any time. You normally want to manipulate the field `mg_binds` of a `ModGuts`, which contains all the top-level bindings for the module.
    8157
    82 {{{
    83 data PluginPass = BindsToBindsPluginPass ([CoreBind] -> CoreM [CoreBind]) -- ^ Simple pass just mutating the Core bindings
    84                 | ModGutsToBindsPluginPass (ModGuts -> CoreM [CoreBind])  -- ^ Pass that has access to the information from a 'ModGuts'
    85                                                                           -- from which to generate it's bindings
    86                 | ModGutsToModGutsPluginPass (ModGuts -> CoreM ModGuts)   -- ^ Pass that can change everything about the module being compiled.
    87                                                                           -- Do not change any field other than 'HscTypes.mg_binds' unless you
    88                                                                           -- know what you're doing! Plugins using this are unlikely to be stable
    89                                                                           -- between GHC versions
    90 }}}
     58`bindsOnlyPass` is a function that merely lifts a function over binders to a function over ModGuts. It's the simple case where nothing else from the `ModGuts` is needed.
    9159
    92 Most people will be using the first case - that is, writing a `BindsToBindsPluginPass` that just manipulates every individual Core binding.
    93 
    94 === Reflections on the current API for Core passes ===
    95 
    96 Scala's compiler has a plugin API described by [1], with examples at [2]. Scala's compiler supports insertion of a plugin at almost any stage in the compiler, internal or external (so you can for example, write a 'check for division by zero' plugin that runs right after the parser/typechecker.) Furthermore, the Scala design actually involves annotating your plugin with a set of 'runsBefore' or 'runsAfter' constraints, which specify which other internal phases your plugin occurs before or after. After all plugins are loaded, internal and external (plugin-made) phases are constructed into a dependency graph and ordered using the constraints, to yield a sorted list of the final phases to run.
    97 
    98 Barring the feature to let you insert a compiler plugin just *anywhere* (we only care about intermediate representations,) it would be nice to adopt this dependency-graph based interface. Mostly, because it saves us a little headache in having to potentially sift through the list of core passes to install ourselves. Furthermore resolving the dependencies and finalizing the list of plugins ends a question about 'which plugin loaded and installed itself first' (intuitively we would like to say left-to-right on the command line, but that's a little fragile to depend on.) It also makes it easier to have passes which may run before a pass that occurs multiple times, etc. And we can still compose `CoreToDo`s using `CoreDoPasses`.
    9960
    10061= The Future =
     
    10869
    10970TODO: fixme
    110 
    111 ==== Composing a hoopl analysis ====
    112 
    113 
    114 = References =
    115 
    116  * [1] "Scala Compiler Phase and Plug-In Initialization for Scala 2.8" (PDF) - http://www.scala-lang.org/sites/default/files/sids/nielsen/Thu,%202009-05-28,%2008:13/compiler-phases-sid.pdf
    117  * [2] "Writing Scala Compiler Plugins" http://www.scala-lang.org/node/140