diff --git a/compiler/GHC.hs b/compiler/GHC.hs
index 13d8ba5ec7db862d0c7ae6f8e7131aec7e105b64..cc8f93bba0574331c1bad0ea109787b1d6cd3e07 100644
--- a/compiler/GHC.hs
+++ b/compiler/GHC.hs
@@ -313,6 +313,7 @@ import GHC.Driver.Monad
 import GHC.Driver.Ppr
 
 import GHC.ByteCode.Types
+import GHC.Runtime.Loader
 import GHC.Runtime.Eval
 import GHC.Runtime.Eval.Types
 import GHC.Runtime.Interpreter
@@ -729,6 +730,8 @@ getProgramDynFlags :: GhcMonad m => m DynFlags
 getProgramDynFlags = getSessionDynFlags
 
 -- | Set the 'DynFlags' used to evaluate interactive expressions.
+-- Also initialise (load) plugins.
+--
 -- Note: this cannot be used for changes to packages.  Use
 -- 'setSessionDynFlags', or 'setProgramDynFlags' and then copy the
 -- 'unitState' into the interactive @DynFlags@.
@@ -736,7 +739,22 @@ setInteractiveDynFlags :: GhcMonad m => DynFlags -> m ()
 setInteractiveDynFlags dflags = do
   dflags' <- checkNewDynFlags dflags
   dflags'' <- checkNewInteractiveDynFlags dflags'
-  modifySession $ \h -> h{ hsc_IC = (hsc_IC h) { ic_dflags = dflags'' }}
+  modifySessionM $ \hsc_env0 -> do
+    let ic0 = hsc_IC hsc_env0
+
+    -- Initialise (load) plugins in the interactive environment with the new
+    -- DynFlags
+    plugin_env <- liftIO $ initializePlugins $ mkInteractiveHscEnv $
+                    hsc_env0 { hsc_IC = ic0 { ic_dflags = dflags'' }}
+
+    -- Update both plugins cache and DynFlags in the interactive context.
+    return $ hsc_env0
+                { hsc_IC = ic0
+                    { ic_plugins = hsc_plugins plugin_env
+                    , ic_dflags  = hsc_dflags  plugin_env
+                    }
+                }
+
 
 -- | Get the 'DynFlags' used to evaluate interactive expressions.
 getInteractiveDynFlags :: GhcMonad m => m DynFlags
diff --git a/compiler/GHC/Core/Opt/Pipeline.hs b/compiler/GHC/Core/Opt/Pipeline.hs
index 14fb11bcccd14991305528203735c05fbfbf0fcc..918d1308dde7ee6c08ec511134e008ff9e4f55f6 100644
--- a/compiler/GHC/Core/Opt/Pipeline.hs
+++ b/compiler/GHC/Core/Opt/Pipeline.hs
@@ -97,9 +97,8 @@ core2core hsc_env guts@(ModGuts { mg_module  = mod
        ; (guts2, stats) <- runCoreM hsc_env hpt_rule_base uniq_mask mod
                                     orph_mods print_unqual loc $
                            do { hsc_env' <- getHscEnv
-                              ; dflags' <- liftIO $ initializePlugins hsc_env'
-                                                      (hsc_dflags hsc_env')
-                              ; all_passes <- withPlugins dflags'
+                              ; hsc_env'' <- liftIO $ initializePlugins hsc_env'
+                              ; all_passes <- withPlugins hsc_env''
                                                 installCoreToDos
                                                 builtin_passes
                               ; runCorePasses all_passes guts }
diff --git a/compiler/GHC/Driver/Env.hs b/compiler/GHC/Driver/Env.hs
index f155fc0187a90bc8ae752187f08521e774c6639a..6bf83c576e400d476de59cdbc0722f21ee874158 100644
--- a/compiler/GHC/Driver/Env.hs
+++ b/compiler/GHC/Driver/Env.hs
@@ -26,7 +26,7 @@ import GHC.Prelude
 
 import GHC.Driver.Ppr
 import GHC.Driver.Session
-import GHC.Unit.Finder.Types
+import {-# SOURCE #-} GHC.Driver.Plugins
 
 import GHC.Runtime.Context
 import GHC.Runtime.Interpreter.Types (Interp)
@@ -39,6 +39,7 @@ import GHC.Unit.Module.ModDetails
 import GHC.Unit.Module.Deps
 import GHC.Unit.Home.ModInfo
 import GHC.Unit.External
+import GHC.Unit.Finder.Types
 
 import GHC.Core         ( CoreRule )
 import GHC.Core.FamInstEnv
@@ -94,14 +95,17 @@ runHsc hsc_env (Hsc hsc) = do
     printOrThrowWarnings (hsc_dflags hsc_env) w
     return a
 
+-- | Switches in the DynFlags and Plugins from the InteractiveContext
 mkInteractiveHscEnv :: HscEnv -> HscEnv
-mkInteractiveHscEnv hsc_env = hsc_env{ hsc_dflags = interactive_dflags }
-  where
-    interactive_dflags = ic_dflags (hsc_IC hsc_env)
+mkInteractiveHscEnv hsc_env =
+    let ic = hsc_IC hsc_env
+    in hsc_env { hsc_dflags  = ic_dflags  ic
+               , hsc_plugins = ic_plugins ic
+               }
 
-runInteractiveHsc :: HscEnv -> Hsc a -> IO a
--- A variant of runHsc that switches in the DynFlags from the
+-- | A variant of runHsc that switches in the DynFlags and Plugins from the
 -- InteractiveContext before running the Hsc computation.
+runInteractiveHsc :: HscEnv -> Hsc a -> IO a
 runInteractiveHsc hsc_env = runHsc (mkInteractiveHscEnv hsc_env)
 
 -- | HscEnv is like 'GHC.Driver.Monad.Session', except that some of the fields are immutable.
@@ -178,6 +182,21 @@ data HscEnv
         , hsc_home_unit :: !HomeUnit
                 -- ^ Home-unit
 
+        , hsc_plugins :: ![LoadedPlugin]
+                -- ^ plugins dynamically loaded after processing arguments. What
+                -- will be loaded here is directed by DynFlags.pluginModNames.
+                -- Arguments are loaded from DynFlags.pluginModNameOpts.
+                --
+                -- The purpose of this field is to cache the plugins so they
+                -- don't have to be loaded each time they are needed.  See
+                -- 'GHC.Runtime.Loader.initializePlugins'.
+
+        , hsc_static_plugins :: ![StaticPlugin]
+                -- ^ static plugins which do not need dynamic loading. These plugins are
+                -- intended to be added by GHC API users directly to this list.
+                --
+                -- To add dynamically loaded plugins through the GHC API see
+                -- 'addPluginModuleName' instead.
  }
 
 {-
diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs
index e924826b0c34c307f8324ee3567caec98932c2a9..7672dcb3e7b79f1c4b40a0a78bbee257deb1407f 100644
--- a/compiler/GHC/Driver/Main.hs
+++ b/compiler/GHC/Driver/Main.hs
@@ -241,18 +241,20 @@ newHscEnv dflags = do
     nc_var  <- newIORef (initNameCache us knownKeyNames)
     fc_var  <- newIORef emptyInstalledModuleEnv
     emptyLoader <- uninitializedLoader
-    return HscEnv {  hsc_dflags       = dflags
-                  ,  hsc_targets      = []
-                  ,  hsc_mod_graph    = emptyMG
-                  ,  hsc_IC           = emptyInteractiveContext dflags
-                  ,  hsc_HPT          = emptyHomePackageTable
-                  ,  hsc_EPS          = eps_var
-                  ,  hsc_NC           = nc_var
-                  ,  hsc_FC           = fc_var
-                  ,  hsc_type_env_var = Nothing
-                  ,  hsc_interp       = Nothing
-                  ,  hsc_loader       = emptyLoader
-                  ,  hsc_home_unit    = home_unit
+    return HscEnv {  hsc_dflags         = dflags
+                  ,  hsc_targets        = []
+                  ,  hsc_mod_graph      = emptyMG
+                  ,  hsc_IC             = emptyInteractiveContext dflags
+                  ,  hsc_HPT            = emptyHomePackageTable
+                  ,  hsc_EPS            = eps_var
+                  ,  hsc_NC             = nc_var
+                  ,  hsc_FC             = fc_var
+                  ,  hsc_type_env_var   = Nothing
+                  ,  hsc_interp         = Nothing
+                  ,  hsc_loader         = emptyLoader
+                  ,  hsc_home_unit      = home_unit
+                  ,  hsc_plugins        = []
+                  ,  hsc_static_plugins = []
                   }
 
 -- -----------------------------------------------------------------------------
@@ -454,7 +456,8 @@ hscParse' mod_summary
             -- apply parse transformation of plugins
             let applyPluginAction p opts
                   = parsedResultAction p opts mod_summary
-            withPlugins dflags applyPluginAction res
+            hsc_env <- getHscEnv
+            withPlugins hsc_env applyPluginAction res
 
 
 -- -----------------------------------------------------------------------------
@@ -764,12 +767,11 @@ hscIncrementalCompile :: Bool
                       -> SourceModified
                       -> Maybe ModIface
                       -> (Int,Int)
-                      -> IO (HscStatus, DynFlags)
+                      -> IO (HscStatus, HscEnv)
 hscIncrementalCompile always_do_basic_recompilation_check m_tc_result
     mHscMessage hsc_env' mod_summary source_modified mb_old_iface mod_index
   = do
-    dflags <- initializePlugins hsc_env' (hsc_dflags hsc_env')
-    let hsc_env'' = hsc_env' { hsc_dflags = dflags }
+    hsc_env'' <- initializePlugins hsc_env'
 
     -- One-shot mode needs a knot-tying mutable variable for interface
     -- files. See GHC.Tc.Utils.TcGblEnv.tcg_type_env_var.
@@ -804,14 +806,14 @@ hscIncrementalCompile always_do_basic_recompilation_check m_tc_result
                 -- any further typechecking.  It's much more useful
                 -- in make mode, since this HMI will go into the HPT.
                 genModDetails hsc_env' iface
-            return (HscUpToDate iface details, dflags)
+            return (HscUpToDate iface details, hsc_env')
         -- We finished type checking.  (mb_old_hash is the hash of
         -- the interface that existed on disk; it's possible we had
         -- to retypecheck but the resulting interface is exactly
         -- the same.)
         Right (FrontendTypecheck tc_result, mb_old_hash) -> do
             status <- finish mod_summary tc_result mb_old_hash
-            return (status, dflags)
+            return (status, hsc_env)
 
 -- Runs the post-typechecking frontend (desugar and simplify). We want to
 -- generate most of the interface as late as possible. This gets us up-to-date
diff --git a/compiler/GHC/Driver/Monad.hs b/compiler/GHC/Driver/Monad.hs
index 4787574465d8cd33342078c15bc24fa3a00abd63..d30f26999a4cd5e6c8649059cb914c2e3e51a4d5 100644
--- a/compiler/GHC/Driver/Monad.hs
+++ b/compiler/GHC/Driver/Monad.hs
@@ -16,7 +16,8 @@ module GHC.Driver.Monad (
         reflectGhc, reifyGhc,
         getSessionDynFlags,
         liftIO,
-        Session(..), withSession, modifySession, withTempSession,
+        Session(..), withSession, modifySession, modifySessionM,
+        withTempSession,
 
         -- ** Warnings
         logWarnings, printException,
@@ -73,6 +74,13 @@ modifySession :: GhcMonad m => (HscEnv -> HscEnv) -> m ()
 modifySession f = do h <- getSession
                      setSession $! f h
 
+-- | Set the current session to the result of applying the current session to
+-- the argument.
+modifySessionM :: GhcMonad m => (HscEnv -> m HscEnv) -> m ()
+modifySessionM f = do h <- getSession
+                      h' <- f h
+                      setSession $! h'
+
 withSavedSession :: GhcMonad m => m a -> m a
 withSavedSession m = do
   saved_session <- getSession
diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs
index c65c54ab12bded236f91efc2f56812b53cc40705..a2dc71d95763f10ab7d994c67a8ccfaaacbbc619 100644
--- a/compiler/GHC/Driver/Pipeline.hs
+++ b/compiler/GHC/Driver/Pipeline.hs
@@ -197,10 +197,12 @@ compileOne' m_tc_result mHscMessage
    debugTraceMsg dflags1 2 (text "compile: input file" <+> text input_fnpp)
 
    -- Run the pipeline up to codeGen (so everything up to, but not including, STG)
-   (status, plugin_dflags) <- hscIncrementalCompile
+   (status, plugin_hsc_env) <- hscIncrementalCompile
                         always_do_basic_recompilation_check
                         m_tc_result mHscMessage
                         hsc_env summary source_modified mb_old_iface (mod_index, nmods)
+   -- Use an HscEnv updated with the plugin info
+   let hsc_env' = plugin_hsc_env
 
    let flags = hsc_dflags hsc_env0
      in do unless (gopt Opt_KeepHiFiles flags) $
@@ -210,10 +212,6 @@ compileOne' m_tc_result mHscMessage
                addFilesToClean flags TFL_GhcSession $
                    [ml_obj_file $ ms_location summary]
 
-   -- Use an HscEnv with DynFlags updated with the plugin info (returned from
-   -- hscIncrementalCompile)
-   let hsc_env' = hsc_env{ hsc_dflags = plugin_dflags }
-
    case (status, bcknd) of
         (HscUpToDate iface hmi_details, _) ->
             -- TODO recomp014 triggers this assert. What's going on?!
@@ -1259,12 +1257,15 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn dflags0
 
   -- run the compiler!
         let msg hsc_env _ what _ = oneShotMsg hsc_env what
-        (result, plugin_dflags) <-
+        (result, plugin_hsc_env) <-
           liftIO $ hscIncrementalCompile True Nothing (Just msg) hsc_env'
                             mod_summary source_unchanged Nothing (1,1)
 
-        -- In the rest of the pipeline use the dflags with plugin info
-        setDynFlags plugin_dflags
+        -- In the rest of the pipeline use the loaded plugins
+        setPlugins (hsc_plugins        plugin_hsc_env)
+                   (hsc_static_plugins plugin_hsc_env)
+        -- "driver" plugins may have modified the DynFlags so we update them
+        setDynFlags (hsc_dflags plugin_hsc_env)
 
         return (HscOut src_flavour mod_name result,
                 panic "HscOut doesn't have an input filename")
diff --git a/compiler/GHC/Driver/Pipeline/Monad.hs b/compiler/GHC/Driver/Pipeline/Monad.hs
index b2db6170ecf45fd429ad33934acce9c662391495..03ee6e14f65c6137258e962f92b397d901fa6f73 100644
--- a/compiler/GHC/Driver/Pipeline/Monad.hs
+++ b/compiler/GHC/Driver/Pipeline/Monad.hs
@@ -7,7 +7,7 @@ module GHC.Driver.Pipeline.Monad (
   , PhasePlus(..)
   , PipeEnv(..), PipeState(..), PipelineOutput(..)
   , getPipeEnv, getPipeState, setDynFlags, setModLocation, setForeignOs, setIface
-  , pipeStateDynFlags, pipeStateModIface
+  , pipeStateDynFlags, pipeStateModIface, setPlugins
   ) where
 
 import GHC.Prelude
@@ -18,6 +18,7 @@ import GHC.Utils.Outputable
 import GHC.Driver.Session
 import GHC.Driver.Phases
 import GHC.Driver.Env
+import GHC.Driver.Plugins
 
 import GHC.SysTools.FileCleanup (TempFileLifetime)
 
@@ -69,9 +70,9 @@ data PipeEnv = PipeEnv {
 -- PipeState: information that might change during a pipeline run
 data PipeState = PipeState {
        hsc_env   :: HscEnv,
-          -- ^ only the DynFlags change in the HscEnv.  The DynFlags change
-          -- at various points, for example when we read the OPTIONS_GHC
-          -- pragmas in the Cpp phase.
+          -- ^ only the DynFlags and the Plugins change in the HscEnv.  The
+          -- DynFlags change at various points, for example when we read the
+          -- OPTIONS_GHC pragmas in the Cpp phase.
        maybe_loc :: Maybe ModLocation,
           -- ^ the ModLocation.  This is discovered during compilation,
           -- in the Hsc phase where we read the module header.
@@ -117,6 +118,11 @@ setDynFlags :: DynFlags -> CompPipeline ()
 setDynFlags dflags = P $ \_env state ->
   return (state{hsc_env= (hsc_env state){ hsc_dflags = dflags }}, ())
 
+setPlugins :: [LoadedPlugin] -> [StaticPlugin] -> CompPipeline ()
+setPlugins dyn static = P $ \_env state ->
+  let hsc_env' = (hsc_env state){ hsc_plugins = dyn, hsc_static_plugins = static }
+  in return (state{hsc_env = hsc_env'}, ())
+
 setModLocation :: ModLocation -> CompPipeline ()
 setModLocation loc = P $ \_env state ->
   return (state{ maybe_loc = Just loc }, ())
diff --git a/compiler/GHC/Driver/Plugins.hs b/compiler/GHC/Driver/Plugins.hs
index 3436cc34a6eb3b3e77d0251c5b70aa2a7ad7e6e2..2d8bc0ad856a0e39188c4c8a997497d43b87596f 100644
--- a/compiler/GHC/Driver/Plugins.hs
+++ b/compiler/GHC/Driver/Plugins.hs
@@ -50,7 +50,6 @@ module GHC.Driver.Plugins (
 import GHC.Prelude
 
 import GHC.Driver.Env
-import GHC.Driver.Session
 import GHC.Driver.Monad
 import GHC.Driver.Phases
 
@@ -98,13 +97,14 @@ data Plugin = Plugin {
   , holeFitPlugin :: HoleFitPlugin
     -- ^ An optional plugin to handle hole fits, which may re-order
     --   or change the list of valid hole fits and refinement hole fits.
-  , dynflagsPlugin :: [CommandLineOption] -> DynFlags -> IO DynFlags
-    -- ^ An optional plugin to update 'DynFlags', right after
-    --   plugin loading. This can be used to register hooks
-    --   or tweak any field of 'DynFlags' before doing
-    --   actual work on a module.
+
+  , driverPlugin :: [CommandLineOption] -> HscEnv -> IO HscEnv
+    -- ^ An optional plugin to update 'HscEnv', right after plugin loading. This
+    -- can be used to register hooks or tweak any field of 'DynFlags' before
+    -- doing actual work on a module.
     --
     --   @since 8.10.1
+
   , pluginRecompile :: [CommandLineOption] -> IO PluginRecompile
     -- ^ Specify how the plugin should affect recompilation.
   , parsedResultAction :: [CommandLineOption] -> ModSummary -> HsParsedModule
@@ -214,7 +214,7 @@ defaultPlugin = Plugin {
         installCoreToDos      = const return
       , tcPlugin              = const Nothing
       , holeFitPlugin         = const Nothing
-      , dynflagsPlugin        = const return
+      , driverPlugin          = const return
       , pluginRecompile       = impurePlugin
       , renamedResultAction   = \_ env grp -> return (env, grp)
       , parsedResultAction    = \_ _ -> return
@@ -242,25 +242,25 @@ keepRenamedSource _ gbl_env group =
 type PluginOperation m a = Plugin -> [CommandLineOption] -> a -> m a
 type ConstPluginOperation m a = Plugin -> [CommandLineOption] -> a -> m ()
 
-plugins :: DynFlags -> [PluginWithArgs]
-plugins df =
-  map lpPlugin (cachedPlugins df) ++
-  map spPlugin (staticPlugins df)
+plugins :: HscEnv -> [PluginWithArgs]
+plugins hsc_env =
+  map lpPlugin (hsc_plugins hsc_env) ++
+  map spPlugin (hsc_static_plugins hsc_env)
 
 -- | Perform an operation by using all of the plugins in turn.
-withPlugins :: Monad m => DynFlags -> PluginOperation m a -> a -> m a
-withPlugins df transformation input = foldM go input (plugins df)
+withPlugins :: Monad m => HscEnv -> PluginOperation m a -> a -> m a
+withPlugins hsc_env transformation input = foldM go input (plugins hsc_env)
   where
     go arg (PluginWithArgs p opts) = transformation p opts arg
 
-mapPlugins :: DynFlags -> (Plugin -> [CommandLineOption] -> a) -> [a]
-mapPlugins df f = map (\(PluginWithArgs p opts) -> f p opts) (plugins df)
+mapPlugins :: HscEnv -> (Plugin -> [CommandLineOption] -> a) -> [a]
+mapPlugins hsc_env f = map (\(PluginWithArgs p opts) -> f p opts) (plugins hsc_env)
 
 -- | Perform a constant operation by using all of the plugins in turn.
-withPlugins_ :: Monad m => DynFlags -> ConstPluginOperation m a -> a -> m ()
-withPlugins_ df transformation input
+withPlugins_ :: Monad m => HscEnv -> ConstPluginOperation m a -> a -> m ()
+withPlugins_ hsc_env transformation input
   = mapM_ (\(PluginWithArgs p opts) -> transformation p opts input)
-          (plugins df)
+          (plugins hsc_env)
 
 type FrontendPluginAction = [String] -> [(String, Maybe Phase)] -> Ghc ()
 data FrontendPlugin = FrontendPlugin {
diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs
index 2f26276eb7923f0da90cd9b5e4441dfe63996ea7..082cdf95a8a53a8c6fe384e374c8a8b39e1e5bda 100644
--- a/compiler/GHC/Driver/Session.hs
+++ b/compiler/GHC/Driver/Session.hs
@@ -235,9 +235,8 @@ import GHC.Unit.Home
 import GHC.Unit.Types
 import GHC.Unit.Parser
 import GHC.Unit.Module
-import {-# SOURCE #-} GHC.Driver.Plugins
-import {-# SOURCE #-} GHC.Driver.Hooks
 import GHC.Builtin.Names ( mAIN_NAME )
+import {-# SOURCE #-} GHC.Driver.Hooks
 import {-# SOURCE #-} GHC.Unit.State (UnitState, emptyUnitState, UnitDatabase)
 import GHC.Driver.Phases ( Phase(..), phaseInputExt )
 import GHC.Driver.Flags
@@ -561,18 +560,6 @@ data DynFlags = DynFlags {
   frontendPluginOpts    :: [String],
     -- ^ the @-ffrontend-opt@ flags given on the command line, in *reverse*
     -- order that they're specified on the command line.
-  cachedPlugins         :: [LoadedPlugin],
-    -- ^ plugins dynamically loaded after processing arguments. What will be
-    -- loaded here is directed by pluginModNames. Arguments are loaded from
-    -- pluginModNameOpts. The purpose of this field is to cache the plugins so
-    -- they don't have to be loaded each time they are needed.  See
-    -- 'GHC.Runtime.Loader.initializePlugins'.
-  staticPlugins            :: [StaticPlugin],
-    -- ^ static plugins which do not need dynamic loading. These plugins are
-    -- intended to be added by GHC API users directly to this list.
-    --
-    -- To add dynamically loaded plugins through the GHC API see
-    -- 'addPluginModuleName' instead.
 
   -- GHC API hooks
   hooks                 :: Hooks,
@@ -1220,8 +1207,6 @@ defaultDynFlags mySettings llvmConfig =
         pluginModNames          = [],
         pluginModNameOpts       = [],
         frontendPluginOpts      = [],
-        cachedPlugins           = [],
-        staticPlugins           = [],
         hooks                   = emptyHooks,
 
         outputFile_             = Nothing,
@@ -1878,7 +1863,7 @@ clearPluginModuleNames :: DynFlags -> DynFlags
 clearPluginModuleNames d =
     d { pluginModNames = []
       , pluginModNameOpts = []
-      , cachedPlugins = [] }
+      }
 
 addPluginModuleNameOption :: String -> DynFlags -> DynFlags
 addPluginModuleNameOption optflag d = d { pluginModNameOpts = (mkModuleName m, option) : (pluginModNameOpts d) }
diff --git a/compiler/GHC/HsToCore.hs b/compiler/GHC/HsToCore.hs
index 14de36906d9110c8ee4228e535a78ce069c0485a..9cf33aa02ac7f4d0722a761fd81f312069102af4 100644
--- a/compiler/GHC/HsToCore.hs
+++ b/compiler/GHC/HsToCore.hs
@@ -198,7 +198,7 @@ deSugar hsc_env
         ; endPassIO hsc_env print_unqual CoreDesugarOpt ds_binds ds_rules_for_imps
 
         ; let used_names = mkUsedNames tcg_env
-              pluginModules = map lpModule (cachedPlugins (hsc_dflags hsc_env))
+              pluginModules = map lpModule (hsc_plugins hsc_env)
               home_unit = hsc_home_unit hsc_env
         ; deps <- mkDependencies (homeUnitId home_unit)
                                  (map mi_module pluginModules) tcg_env
diff --git a/compiler/GHC/Iface/Load.hs b/compiler/GHC/Iface/Load.hs
index 308119327cbd18bbea7af7526595a918dc1aff39..4fb775db53ad87304603f0cbfe5647f8e44aa934 100644
--- a/compiler/GHC/Iface/Load.hs
+++ b/compiler/GHC/Iface/Load.hs
@@ -433,7 +433,6 @@ loadInterface doc_str mod from
         ; traceIf (text "Considering whether to load" <+> ppr mod <+> ppr from)
 
                 -- Check whether we have the interface already
-        ; dflags <- getDynFlags
         ; hsc_env <- getTopEnv
         ; let home_unit = hsc_home_unit hsc_env
         ; case lookupIfaceByModule hpt (eps_PIT eps) mod of {
@@ -551,7 +550,7 @@ loadInterface doc_str mod from
 
         ; -- invoke plugins with *full* interface, not final_iface, to ensure
           -- that plugins have access to declarations, etc.
-          res <- withPlugins dflags (\p -> interfaceLoadAction p) iface
+          res <- withPlugins hsc_env (\p -> interfaceLoadAction p) iface
         ; return (Succeeded res)
     }}}}
 
diff --git a/compiler/GHC/Iface/Make.hs b/compiler/GHC/Iface/Make.hs
index f9ad42a3c9392e9851229d0baf8064c1995b9bd0..f333525e4b06af8656ca884656615bcce626e0ab 100644
--- a/compiler/GHC/Iface/Make.hs
+++ b/compiler/GHC/Iface/Make.hs
@@ -193,7 +193,7 @@ mkIfaceTc hsc_env safe_mode mod_details
                     }
   = do
           let used_names = mkUsedNames tc_result
-          let pluginModules = map lpModule (cachedPlugins (hsc_dflags hsc_env))
+          let pluginModules = map lpModule (hsc_plugins hsc_env)
           let home_unit = hsc_home_unit hsc_env
           deps <- mkDependencies (homeUnitId home_unit)
                     (map mi_module pluginModules) tc_result
diff --git a/compiler/GHC/Iface/Recomp.hs b/compiler/GHC/Iface/Recomp.hs
index a123d24fba8a49f7b29b22c9425499e9a14adcdf..fe0f4439f5d8f55988491fc917ff2df3dbd61945 100644
--- a/compiler/GHC/Iface/Recomp.hs
+++ b/compiler/GHC/Iface/Recomp.hs
@@ -276,16 +276,16 @@ checkVersions hsc_env mod_summary iface
 
 -- | Check if any plugins are requesting recompilation
 checkPlugins :: HscEnv -> ModIface -> IfG RecompileRequired
-checkPlugins hsc iface = liftIO $ do
-  new_fingerprint <- fingerprintPlugins hsc
+checkPlugins hsc_env iface = liftIO $ do
+  new_fingerprint <- fingerprintPlugins hsc_env
   let old_fingerprint = mi_plugin_hash (mi_final_exts iface)
-  pr <- mconcat <$> mapM pluginRecompile' (plugins (hsc_dflags hsc))
+  pr <- mconcat <$> mapM pluginRecompile' (plugins hsc_env)
   return $
     pluginRecompileToRecompileRequired old_fingerprint new_fingerprint pr
 
 fingerprintPlugins :: HscEnv -> IO Fingerprint
 fingerprintPlugins hsc_env =
-  fingerprintPlugins' $ plugins (hsc_dflags hsc_env)
+  fingerprintPlugins' $ plugins hsc_env
 
 fingerprintPlugins' :: [PluginWithArgs] -> IO Fingerprint
 fingerprintPlugins' plugins = do
diff --git a/compiler/GHC/Runtime/Context.hs b/compiler/GHC/Runtime/Context.hs
index 7220dea503c0b07111ade6da0017d4e94901b554..932e499e4745eb5889fc1b97514917ee7efda4a6 100644
--- a/compiler/GHC/Runtime/Context.hs
+++ b/compiler/GHC/Runtime/Context.hs
@@ -18,6 +18,7 @@ import GHC.Prelude
 import GHC.Hs
 
 import GHC.Driver.Session
+import {-# SOURCE #-} GHC.Driver.Plugins
 
 import GHC.Runtime.Eval.Types ( Resume )
 
@@ -240,8 +241,12 @@ data InteractiveContext
              -- ^ The function that is used for printing results
              -- of expressions in ghci and -e mode.
 
-         ic_cwd :: Maybe FilePath
-             -- virtual CWD of the program
+         ic_cwd :: Maybe FilePath,
+             -- ^ virtual CWD of the program
+
+         ic_plugins :: ![LoadedPlugin]
+             -- ^ Cache of loaded plugins. We store them here to avoid having to
+             -- load them everytime we switch to the interctive context.
     }
 
 data InteractiveImport
@@ -270,7 +275,9 @@ emptyInteractiveContext dflags
        ic_int_print  = printName,    -- System.IO.print by default
        ic_default    = Nothing,
        ic_resume     = [],
-       ic_cwd        = Nothing }
+       ic_cwd        = Nothing,
+       ic_plugins    = []
+       }
 
 icInteractiveModule :: InteractiveContext -> Module
 icInteractiveModule (InteractiveContext { ic_mod_index = index })
diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs
index b66f959889595d8ebcfe895e550e5da570797046..692a4c718c9bbbf4e36a1648c9bb88ef63b76125 100644
--- a/compiler/GHC/Runtime/Eval.hs
+++ b/compiler/GHC/Runtime/Eval.hs
@@ -218,11 +218,9 @@ execStmt' stmt stmt_text ExecOptions{..} = do
 
     -- Turn off -fwarn-unused-local-binds when running a statement, to hide
     -- warnings about the implicit bindings we introduce.
-    -- (This is basically `mkInteractiveHscEnv hsc_env`, except we unset
-    -- -wwarn-unused-local-binds)
     let ic       = hsc_IC hsc_env -- use the interactive dflags
         idflags' = ic_dflags ic `wopt_unset` Opt_WarnUnusedLocalBinds
-        hsc_env' = mkInteractiveHscEnv (hsc_env{ hsc_IC = ic{ ic_dflags = idflags' } })
+        hsc_env' = mkInteractiveHscEnv (hsc_env{ hsc_IC = ic{ ic_dflags = idflags' }})
 
     r <- liftIO $ hscParsedStmt hsc_env' stmt
 
diff --git a/compiler/GHC/Runtime/Loader.hs b/compiler/GHC/Runtime/Loader.hs
index 93b39675256128ca5fede14c0969d7eea68ed9ce..592df3ccc867c493ba481aa30a0a6f6fa0c49eb4 100644
--- a/compiler/GHC/Runtime/Loader.hs
+++ b/compiler/GHC/Runtime/Loader.hs
@@ -69,21 +69,22 @@ import Unsafe.Coerce     ( unsafeCoerce )
 -- flags. Should be called after command line arguments are parsed, but before
 -- actual compilation starts. Idempotent operation. Should be re-called if
 -- pluginModNames or pluginModNameOpts changes.
-initializePlugins :: HscEnv -> DynFlags -> IO DynFlags
-initializePlugins hsc_env df
-  | map lpModuleName (cachedPlugins df)
-         == pluginModNames df -- plugins not changed
-     && all (\p -> paArguments (lpPlugin p)
-                       == argumentsForPlugin p (pluginModNameOpts df))
-            (cachedPlugins df) -- arguments not changed
-  = return df -- no need to reload plugins
+initializePlugins :: HscEnv -> IO HscEnv
+initializePlugins hsc_env
+    -- plugins not changed
+  | map lpModuleName (hsc_plugins hsc_env) == pluginModNames dflags
+   -- arguments not changed
+  , all same_args (hsc_plugins hsc_env)
+  = return hsc_env -- no need to reload plugins
   | otherwise
-  = do loadedPlugins <- loadPlugins (hsc_env { hsc_dflags = df })
-       let df' = df { cachedPlugins = loadedPlugins }
-       withPlugins df' runDflagsPlugin df'
-
-  where argumentsForPlugin p = map snd . filter ((== lpModuleName p) . fst)
-        runDflagsPlugin p opts dynflags = dynflagsPlugin p opts dynflags
+  = do loaded_plugins <- loadPlugins hsc_env
+       let hsc_env' = hsc_env { hsc_plugins = loaded_plugins }
+       withPlugins hsc_env' driverPlugin hsc_env'
+  where
+    plugin_args = pluginModNameOpts dflags
+    same_args p = paArguments (lpPlugin p) == argumentsForPlugin p plugin_args
+    argumentsForPlugin p = map snd . filter ((== lpModuleName p) . fst)
+    dflags = hsc_dflags hsc_env
 
 loadPlugins :: HscEnv -> IO [LoadedPlugin]
 loadPlugins hsc_env
diff --git a/compiler/GHC/Tc/Gen/Splice.hs b/compiler/GHC/Tc/Gen/Splice.hs
index ed248c09ccca446e98ac35e80f4d82beceeb55ef..468410400f295fb8943edeedd3237f92c0801e0e 100644
--- a/compiler/GHC/Tc/Gen/Splice.hs
+++ b/compiler/GHC/Tc/Gen/Splice.hs
@@ -932,7 +932,7 @@ runMeta' show_code ppr_hs run_and_convert expr
 
         -- run plugins
         ; hsc_env <- getTopEnv
-        ; expr' <- withPlugins (hsc_dflags hsc_env) spliceRunAction expr
+        ; expr' <- withPlugins hsc_env spliceRunAction expr
 
         -- Desugar
         ; ds_expr <- initDsTc (dsLExpr expr')
diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs
index 754059571b175bfce3937aa51b9d0edf63ad7830..44a92da7ae752bef2fc891822c0ad3a227f5e9ec 100644
--- a/compiler/GHC/Tc/Module.hs
+++ b/compiler/GHC/Tc/Module.hs
@@ -326,7 +326,7 @@ tcRnModuleTcRnM hsc_env mod_sum
                         reportUnusedNames tcg_env hsc_src
                       ; -- add extra source files to tcg_dependent_files
                         addDependentFiles src_files
-                      ; tcg_env <- runTypecheckerPlugin mod_sum hsc_env tcg_env
+                      ; tcg_env <- runTypecheckerPlugin mod_sum tcg_env
                       ; -- Dump output and return
                         tcDump tcg_env
                       ; return tcg_env }
@@ -3034,10 +3034,10 @@ Type Checker Plugins
 
 withTcPlugins :: HscEnv -> TcM a -> TcM a
 withTcPlugins hsc_env m =
-  do let plugins = getTcPlugins (hsc_dflags hsc_env)
-     case plugins of
-       [] -> m  -- Common fast case
-       _  -> do ev_binds_var <- newTcEvBinds
+    case getTcPlugins hsc_env of
+       []      -> m  -- Common fast case
+       plugins -> do
+                ev_binds_var <- newTcEvBinds
                 (solvers,stops) <- unzip `fmap` mapM (startPlugin ev_binds_var) plugins
                 -- This ensures that tcPluginStop is called even if a type
                 -- error occurs during compilation (Fix of #10078)
@@ -3052,13 +3052,13 @@ withTcPlugins hsc_env m =
     do s <- runTcPluginM start ev_binds_var
        return (solve s, stop s)
 
-getTcPlugins :: DynFlags -> [GHC.Tc.Utils.Monad.TcPlugin]
-getTcPlugins dflags = catMaybes $ mapPlugins dflags (\p args -> tcPlugin p args)
+getTcPlugins :: HscEnv -> [GHC.Tc.Utils.Monad.TcPlugin]
+getTcPlugins hsc_env = catMaybes $ mapPlugins hsc_env (\p args -> tcPlugin p args)
 
 
 withHoleFitPlugins :: HscEnv -> TcM a -> TcM a
 withHoleFitPlugins hsc_env m =
-  case (getHfPlugins (hsc_dflags hsc_env)) of
+  case getHfPlugins hsc_env of
     [] -> m  -- Common fast case
     plugins -> do (plugins,stops) <- unzip `fmap` mapM startPlugin plugins
                   -- This ensures that hfPluginStop is called even if a type
@@ -3074,18 +3074,19 @@ withHoleFitPlugins hsc_env m =
       do ref <- init
          return (plugin ref, stop ref)
 
-getHfPlugins :: DynFlags -> [HoleFitPluginR]
-getHfPlugins dflags =
-  catMaybes $ mapPlugins dflags (\p args -> holeFitPlugin p args)
+getHfPlugins :: HscEnv -> [HoleFitPluginR]
+getHfPlugins hsc_env =
+  catMaybes $ mapPlugins hsc_env (\p args -> holeFitPlugin p args)
 
 
 runRenamerPlugin :: TcGblEnv
                  -> HsGroup GhcRn
                  -> TcM (TcGblEnv, HsGroup GhcRn)
 runRenamerPlugin gbl_env hs_group = do
-    dflags <- getDynFlags
-    withPlugins dflags
-      (\p opts (e, g) -> ( mark_plugin_unsafe dflags >> renamedResultAction p opts e g))
+    hsc_env <- getTopEnv
+    withPlugins hsc_env
+      (\p opts (e, g) -> ( mark_plugin_unsafe (hsc_dflags hsc_env)
+                            >> renamedResultAction p opts e g))
       (gbl_env, hs_group)
 
 
@@ -3103,11 +3104,11 @@ getRenamedStuff tc_result
                     , tcg_rn_exports tc_result, tcg_doc_hdr tc_result ) )
          (tcg_rn_decls tc_result)
 
-runTypecheckerPlugin :: ModSummary -> HscEnv -> TcGblEnv -> TcM TcGblEnv
-runTypecheckerPlugin sum hsc_env gbl_env = do
-    let dflags = hsc_dflags hsc_env
-    withPlugins dflags
-      (\p opts env -> mark_plugin_unsafe dflags
+runTypecheckerPlugin :: ModSummary -> TcGblEnv -> TcM TcGblEnv
+runTypecheckerPlugin sum gbl_env = do
+    hsc_env <- getTopEnv
+    withPlugins hsc_env
+      (\p opts env -> mark_plugin_unsafe (hsc_dflags hsc_env)
                         >> typeCheckResultAction p opts sum env)
       gbl_env
 
diff --git a/docs/users_guide/extending_ghc.rst b/docs/users_guide/extending_ghc.rst
index 483334d17c1812d93cbf1f689754b1a73ad3fa42..dbd527cb2b6de795bd208ee04aa6dac71caa3853 100644
--- a/docs/users_guide/extending_ghc.rst
+++ b/docs/users_guide/extending_ghc.rst
@@ -1341,13 +1341,17 @@ this idea can be seen below:
     import GHC.Tc.Utils.Monad
 
     plugin :: Plugin
-    plugin = defaultPlugin { dynflagsPlugin = hooksP }
-
-    hooksP :: [CommandLineOption] -> DynFlags -> IO DynFlags
-    hooksP opts dflags = return $ dflags
-      { hooks = (hooks dflags)
-        { runMetaHook = Just (fakeRunMeta opts) }
-      }
+    plugin = driverPlugin { driverPlugin = hooksP }
+
+    hooksP :: [CommandLineOption] -> HscEnv -> IO HscEnv
+    hooksP opts hsc_env = do
+        let dflags  = hsc_dflags hsc_env
+            dflags' = dflags
+                { hooks = (hooks dflags)
+                    { runMetaHook = Just (fakeRunMeta opts) }
+                }
+            hsc_env' = hsc_env { hsc_dflags = dflags' }
+        return hsc_env'
 
     -- This meta hook doesn't actually care running code in splices,
     -- it just replaces any expression splice with the "0"
diff --git a/ghc/GHCi/UI.hs b/ghc/GHCi/UI.hs
index 025f82fa08b0c5a57d540a864e15d195b3418c37..d44eb79bd183c7b161b482cf6ec3ca1d795f6bcd 100644
--- a/ghc/GHCi/UI.hs
+++ b/ghc/GHCi/UI.hs
@@ -81,8 +81,6 @@ import GHC.Unit.Module.ModSummary
 import GHC.Data.StringBuffer
 import GHC.Utils.Outputable
 
-import GHC.Runtime.Loader ( initializePlugins )
-
 -- Other random utilities
 import GHC.Types.Basic hiding ( isTopLevel )
 import GHC.Settings.Config
@@ -2943,10 +2941,7 @@ newDynFlags interactive_only minus_opts = do
 
       when (interactive_only && packageFlagsChanged idflags1 idflags0) $ do
           liftIO $ hPutStrLn stderr "cannot set package flags with :seti; use :set"
-      -- Load any new plugins
-      hsc_env0 <- GHC.getSession
-      idflags2 <- liftIO (initializePlugins hsc_env0 idflags1)
-      GHC.setInteractiveDynFlags idflags2
+      GHC.setInteractiveDynFlags idflags1
       installInteractivePrint (interactivePrint idflags1) False
 
       dflags0 <- getDynFlags
diff --git a/ghc/Main.hs b/ghc/Main.hs
index b7992b10b8ab9e767134ddc78cac64aa57be9aea..db926fb85f75e1e0acc4707e60529fe03da0ee92 100644
--- a/ghc/Main.hs
+++ b/ghc/Main.hs
@@ -36,7 +36,6 @@ import GHC.Platform.Host
 
 #if defined(HAVE_INTERNAL_INTERPRETER)
 import GHCi.UI              ( interactiveUI, ghciWelcomeMsg, defaultGhciSettings )
-import GHC.Runtime.Loader   ( initializePlugins )
 #endif
 
 import GHC.Runtime.Loader   ( loadFrontendPlugin )
@@ -247,9 +246,8 @@ main' postLoadMode dflags0 args flagWarnings = do
        DoMake                 -> doMake srcs
        DoMkDependHS           -> doMkDependHS (map fst srcs)
        StopBefore p           -> liftIO (oneShot hsc_env p srcs)
-       DoInteractive          -> ghciUI hsc_env dflags6 srcs Nothing
-       DoEval exprs           -> ghciUI hsc_env dflags6 srcs $ Just $
-                                   reverse exprs
+       DoInteractive          -> ghciUI srcs Nothing
+       DoEval exprs           -> ghciUI srcs $ Just $ reverse exprs
        DoAbiHash              -> abiHash (map fst srcs)
        ShowPackages           -> liftIO $ showUnits dflags6
        DoFrontend f           -> doFrontend f srcs
@@ -257,16 +255,12 @@ main' postLoadMode dflags0 args flagWarnings = do
 
   liftIO $ dumpFinalStats dflags6
 
-ghciUI :: HscEnv -> DynFlags -> [(FilePath, Maybe Phase)] -> Maybe [String]
-       -> Ghc ()
+ghciUI :: [(FilePath, Maybe Phase)] -> Maybe [String] -> Ghc ()
 #if !defined(HAVE_INTERNAL_INTERPRETER)
-ghciUI _ _ _ _ =
+ghciUI _ _ =
   throwGhcException (CmdLineError "not built for interactive use")
 #else
-ghciUI hsc_env dflags0 srcs maybe_expr = do
-  dflags1 <- liftIO (initializePlugins hsc_env dflags0)
-  _ <- GHC.setSessionDynFlags dflags1
-  interactiveUI defaultGhciSettings srcs maybe_expr
+ghciUI srcs maybe_expr = interactiveUI defaultGhciSettings srcs maybe_expr
 #endif
 
 
diff --git a/testsuite/tests/plugins/hooks-plugin/Hooks/Plugin.hs b/testsuite/tests/plugins/hooks-plugin/Hooks/Plugin.hs
index 616d202d299fb056b8728915e1d3607d6bbeb246..361f40eb2495b0c6c246443f68b5de3031a32531 100644
--- a/testsuite/tests/plugins/hooks-plugin/Hooks/Plugin.hs
+++ b/testsuite/tests/plugins/hooks-plugin/Hooks/Plugin.hs
@@ -10,13 +10,17 @@ import GHC.Driver.Hooks
 import GHC.Tc.Utils.Monad
 
 plugin :: Plugin
-plugin = defaultPlugin { dynflagsPlugin = hooksP }
+plugin = defaultPlugin { driverPlugin = hooksP }
 
-hooksP :: [CommandLineOption] -> DynFlags -> IO DynFlags
-hooksP opts dflags = return $ dflags
-  { hooks = (hooks dflags)
-    { runMetaHook = Just (fakeRunMeta opts) }
-  }
+hooksP :: [CommandLineOption] -> HscEnv -> IO HscEnv
+hooksP opts hsc_env = do
+    let dflags  = hsc_dflags hsc_env
+        dflags' = dflags
+            { hooks = (hooks dflags)
+                { runMetaHook = Just (fakeRunMeta opts) }
+            }
+        hsc_env' = hsc_env { hsc_dflags = dflags' }
+    return hsc_env'
 
 -- This meta hook doesn't actually care running code in splices,
 -- it just replaces any expression splice with the "0"
diff --git a/testsuite/tests/plugins/static-plugins.hs b/testsuite/tests/plugins/static-plugins.hs
index af57614ffeaa63f1f6b128238008ad02f5aa2cf1..be0a503f84836357c000153d310f1737bd3a3b50 100644
--- a/testsuite/tests/plugins/static-plugins.hs
+++ b/testsuite/tests/plugins/static-plugins.hs
@@ -1,9 +1,13 @@
 module Main where
 
-import GHC.Types.Avail
-import Control.Monad.IO.Class
+import GHC.Driver.Env
 import GHC.Driver.Session
   (getDynFlags, parseDynamicFlagsCmdLine, defaultFatalMessager, defaultFlushOut)
+import GHC.Driver.Plugins
+import GHC.Driver.Monad
+
+import GHC.Types.Avail
+import Control.Monad.IO.Class
 import GHC
 import GHC.Fingerprint.Type
 import GHC.Hs.Decls
@@ -12,7 +16,6 @@ import GHC.Hs.Expr
 import GHC.Hs.Extension
 import GHC.Hs.ImpExp
 import GHC.Utils.Outputable
-import GHC.Driver.Plugins
 import System.Environment
 import GHC.Tc.Types
 
@@ -65,9 +68,10 @@ main = do
       target <- guessTarget "static-plugins-module.hs" Nothing
       setTargets [target]
 
+      modifySession (\hsc_env -> hsc_env { hsc_static_plugins = the_plugins})
+
       dflags <- getSessionDynFlags
-      setSessionDynFlags dflags { staticPlugins = the_plugins
-                                , outputFile_ = Nothing }
+      setSessionDynFlags dflags { outputFile_ = Nothing }
       load LoadAllTargets
 
 
diff --git a/utils/haddock b/utils/haddock
index 4d0498d503bd51b7d7626497580232685a2691a1..25fa8fde84701c010fa466c2648f8f6d10265e8f 160000
--- a/utils/haddock
+++ b/utils/haddock
@@ -1 +1 @@
-Subproject commit 4d0498d503bd51b7d7626497580232685a2691a1
+Subproject commit 25fa8fde84701c010fa466c2648f8f6d10265e8f