[Git][ghc/ghc][wip/24254] 2 commits: add test for late plugins
Finley McIlwaine (@FinleyMcIlwaine)
gitlab at gitlab.haskell.org
Wed Dec 13 15:11:32 UTC 2023
Finley McIlwaine pushed to branch wip/24254 at Glasgow Haskell Compiler / GHC
Commits:
f4fe234b by Finley McIlwaine at 2023-12-13T07:11:01-08:00
add test for late plugins
- - - - -
14e6fd8b by Finley McIlwaine at 2023-12-13T07:11:14-08:00
Document late plugins
- - - - -
6 changed files:
- docs/users_guide/9.10.1-notes.rst
- docs/users_guide/extending_ghc.rst
- testsuite/tests/plugins/Makefile
- testsuite/tests/plugins/all.T
- + testsuite/tests/plugins/late-plugin/LatePlugin.hs
- + testsuite/tests/plugins/test-late-plugin.hs
Changes:
=====================================
docs/users_guide/9.10.1-notes.rst
=====================================
@@ -54,6 +54,9 @@ Compiler
- Defaulting plugins can now propose solutions to entangled sets of type variables. This allows defaulting
of multi-parameter type classes. See :ghc-ticket:`23832`.
+- Late plugins have been added. These are plugins which can access and/or modify
+ the core of a module after optimization and after interface creation. See :ghc-ticket:`24254`.
+
GHCi
~~~~
=====================================
docs/users_guide/extending_ghc.rst
=====================================
@@ -510,6 +510,57 @@ in a module it compiles:
return bndr
printBind _ bndr = return bndr
+.. _late-plugins:
+
+Late Plugins
+^^^^^^^^^^^^
+
+If the ``CoreProgram`` of a module is modified in a normal core plugin, the
+modified bindings can end up in unfoldings the interface file for the module.
+This may be undesireable, as the plugin could make changes which affect inlining
+or optimization.
+
+Late plugins can be used to avoid introducing such changes into the interface
+file. Late plugins are a bit different than typical core plugins:
+
+1. They do not run in the ``CoreM`` monad. Instead, they are explicitly passed
+ the ``HscEnv`` and they run in ``IO``.
+2. They are given ``CgGuts`` instead of ``ModGuts``. ``CgGuts`` are a restricted
+ form of ``ModGuts`` intended for code generation. The ``CoreProgram`` held in
+ the ``CgGuts`` given to a late plugin will already be fully optimized.
+3. They must maintain a ``CostCentreState`` and track any cost centres they
+ introduce by adding them to the ``cg_ccs`` field of ``CgGuts``. This is
+ because the automatic collection of cost centres happens before the late
+ plugin stage. If a late plugin does not introduce any cost centres, it may
+ simply return the given cost centre state.
+
+Here is a very simply example of a late plugin that changes the value of a
+binding in a module. If it finds a non-recursive top-level binding named
+``testBinding``, it will change its value to the ``Int`` expression ``111111``.
+
+::
+
+ plugin :: Plugin
+ plugin = defaultPlugin { latePlugin = lateP }
+
+ lateP :: LatePlugin
+ lateP _ _ (cg_guts, cc_state) = do
+ binds' <- editCoreBinding (cg_binds cg_guts)
+ return (cg_guts { cg_binds = binds' }, cc_state)
+
+ editCoreBinding :: CoreProgram -> IO CoreProgram
+ editCoreBinding pgm = pure . go
+ where
+ go :: [CoreBind] -> [CoreBind]
+ go (b@(NonRec v e) : bs)
+ | occNameString (getOccName v) == "testBinding" =
+ NonRec v (mkUncheckedIntExpr 111111) : bs
+ go (b:bs) = b : go bs
+ go [] = []
+
+Since this is a late plugin, the changed binding value will not end up in the
+interface file.
+
.. _getting-annotations:
Using Annotations
=====================================
testsuite/tests/plugins/Makefile
=====================================
@@ -224,3 +224,13 @@ plugins-external:
cp shared-plugin/pkg.plugins01/dist/build/$(call DLL,HSsimple-plugin*) $(call DLL,HSsimple-plugin)
"$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) --make -v0 -fplugin-library "$(PWD)/$(call DLL,HSsimple-plugin);simple-plugin-1234;Simple.Plugin;[\"Plugin\",\"loaded\",\"from\",\"a shared lib\"]" plugins-external.hs
./plugins-external
+
+# Runs a plugin that is both a core plugin and a late plugin, then makes sure
+# only the changes from the core plugin end up in the interface files.
+test-late-plugin:
+ "$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) -O -package ghc $@.hs
+ SHOW_IFACE="$$($(TEST_HC) --show-iface $@.hi)" ; \
+ ContainsEarlyBinding=$$(echo $$SHOW_IFACE | grep -o 111111) ; \
+ ContainsLateBinding=$$(echo $$SHOW_IFACE | grep -o 222222) ; \
+ echo "$$ContainsLateBinding" ; \
+ [ "$$ContainsEarlyBinding" = "111111" ] && [ "$$ContainLateBinding" = "" ]
=====================================
testsuite/tests/plugins/all.T
=====================================
@@ -358,3 +358,8 @@ test('test-log-hooks-plugin',
pre_cmd('$MAKE -s --no-print-directory -C hooks-plugin package.test-log-hooks-plugin TOP={top}')],
compile_fail,
['-package-db hooks-plugin/pkg.test-log-hooks-plugin/local.package.conf -fplugin Hooks.LogPlugin -package hooks-plugin ' + config.plugin_way_flags])
+
+test('test-late-plugin',
+ [extra_files(['late-plugin/LatePlugin.hs']), ignore_stdout],
+ makefile_test,
+ [])
=====================================
testsuite/tests/plugins/late-plugin/LatePlugin.hs
=====================================
@@ -0,0 +1,49 @@
+module LatePlugin where
+
+import Data.Bool
+import GHC.Core
+import GHC.Driver.Monad
+import GHC.Plugins
+import GHC.Types.Avail
+import GHC.Types.Var
+import GHC.Types.Id
+import System.IO
+
+-- | Both a core plugin and a late plugin. The Core plugin edits the binding in
+-- the test file (testBinding) to be the integer "111111". The late plugin then
+-- edits the binding to be the integer "222222". Then we make sure the "222222"
+-- did not make it in the interface file and the "111111" did.
+plugin :: Plugin
+plugin =
+ defaultPlugin
+ { installCoreToDos = earlyP
+ , latePlugin = lateP
+ }
+
+earlyP :: CorePlugin
+earlyP _ todos = do
+ return
+ . (: todos)
+ $ CoreDoPluginPass "earlyP"
+ $ \mgs -> liftIO $ do
+ binds' <- editCoreBinding True (moduleName (mg_module mgs)) (mg_binds mgs)
+ return mgs { mg_binds = binds' }
+
+lateP :: LatePlugin
+lateP _ opts (cg_guts, cc_state) = do
+ binds' <- editCoreBinding False (moduleName (cg_module cg_guts)) (cg_binds cg_guts)
+ return (cg_guts { cg_binds = binds' }, cc_state)
+
+editCoreBinding :: Bool -> ModuleName -> CoreProgram -> IO CoreProgram
+editCoreBinding early modName pgm = do
+ putStrLn $
+ bool "late " "early " early ++ "plugin running on module " ++
+ moduleNameString modName
+ pure $ go pgm
+ where
+ go :: [CoreBind] -> [CoreBind]
+ go (b@(NonRec v e) : bs)
+ | occNameString (getOccName v) == "testBinding" =
+ NonRec v (mkUncheckedIntExpr $ bool 222222 111111 early) : bs
+ go (b:bs) = b : go bs
+ go [] = []
=====================================
testsuite/tests/plugins/test-late-plugin.hs
=====================================
@@ -0,0 +1,15 @@
+{-# LANGUAGE MagicHash #-}
+{-# OPTIONS_GHC -fplugin=LatePlugin #-}
+
+module TestLatePlugin (testBinding) where
+
+import GHC.Exts
+
+-- This file is edited by a core plugin at the beginning of the core pipeline so
+-- that the value of testBinding becomes 111111. Then, a late plugin edits the
+-- binding to set testBinding to 222222. The test then checks that the early
+-- binding value is what makes it into the interface file, just to be sure that
+-- changes from late plugins do not end up in interface files.
+
+testBinding :: Int
+testBinding = -1
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e94ec1a817dcb157128c708e2ef4c90c24611928...14e6fd8b492c331e1a1db22d174e1ae4f3101979
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e94ec1a817dcb157128c708e2ef4c90c24611928...14e6fd8b492c331e1a1db22d174e1ae4f3101979
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20231213/d24e09e1/attachment-0001.html>
More information about the ghc-commits
mailing list