summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZubin Duggal <zubin.duggal@gmail.com>2022-02-09 17:01:38 +0530
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-02-20 13:56:15 -0500
commit4b04f7e175a01b30e098af63dfabe6ea068e9b0b (patch)
tree559e4dc5ba03f5ac8fc8917dcccef9f7c71e6507
parent67dd5724297094af93be1887ef000845722c6f2b (diff)
downloadhaskell-4b04f7e175a01b30e098af63dfabe6ea068e9b0b.tar.gz
Track object file dependencies for TH accurately (#20604)
`hscCompileCoreExprHook` is changed to return a list of `Module`s required by a splice. These modules are accumulated in the TcGblEnv (tcg_th_needed_mods). Dependencies on the object files of these modules are recording in the interface. The data structures in `LoaderState` are replaced with more efficient versions to keep track of all the information required. The MultiLayerModulesTH_Make allocations increase slightly but runtime is faster. Fixes #20604 ------------------------- Metric Increase: MultiLayerModulesTH_Make -------------------------
-rw-r--r--compiler/GHC.hs2
-rw-r--r--compiler/GHC/Driver/Hooks.hs8
-rw-r--r--compiler/GHC/Driver/Main.hs23
-rw-r--r--compiler/GHC/Driver/Make.hs9
-rw-r--r--compiler/GHC/Driver/Pipeline.hs2
-rw-r--r--compiler/GHC/Driver/Pipeline/Execute.hs2
-rw-r--r--compiler/GHC/Driver/Plugins.hs7
-rw-r--r--compiler/GHC/HsToCore.hs5
-rw-r--r--compiler/GHC/HsToCore/Usage.hs54
-rw-r--r--compiler/GHC/Iface/Make.hs5
-rw-r--r--compiler/GHC/Linker/Loader.hs194
-rw-r--r--compiler/GHC/Linker/Types.hs52
-rw-r--r--compiler/GHC/Runtime/Eval.hs4
-rw-r--r--compiler/GHC/Runtime/Loader.hs63
-rw-r--r--compiler/GHC/Tc/Gen/Splice.hs6
-rw-r--r--compiler/GHC/Tc/Types.hs5
-rw-r--r--compiler/GHC/Tc/Utils/Monad.hs22
-rw-r--r--compiler/GHC/Types/Basic.hs6
-rw-r--r--compiler/GHC/Types/Unique/DFM.hs4
-rw-r--r--compiler/GHC/Unit/Module/Deps.hs3
-rw-r--r--compiler/GHC/Unit/Module/Env.hs21
-rw-r--r--docs/users_guide/separate_compilation.rst18
-rw-r--r--ghc/Main.hs2
-rw-r--r--testsuite/tests/driver/T20604/A.hs4
-rw-r--r--testsuite/tests/driver/T20604/A1.hs3
-rw-r--r--testsuite/tests/driver/T20604/Makefile16
-rw-r--r--testsuite/tests/driver/T20604/T20604.stdout11
-rw-r--r--testsuite/tests/driver/T20604/all.T13
28 files changed, 365 insertions, 199 deletions
diff --git a/compiler/GHC.hs b/compiler/GHC.hs
index 6fa90108b7..4d31ae1e3a 100644
--- a/compiler/GHC.hs
+++ b/compiler/GHC.hs
@@ -856,7 +856,7 @@ setInteractiveDynFlags dflags = do
-- Initialise (load) plugins in the interactive environment with the new
-- DynFlags
- plugin_env <- liftIO $ flip initializePlugins Nothing $ mkInteractiveHscEnv $
+ plugin_env <- liftIO $ initializePlugins $ mkInteractiveHscEnv $
hsc_env0 { hsc_IC = ic0 { ic_dflags = dflags'' }}
-- Update both plugins cache and DynFlags in the interactive context.
diff --git a/compiler/GHC/Driver/Hooks.hs b/compiler/GHC/Driver/Hooks.hs
index ae00340d54..7978a5049d 100644
--- a/compiler/GHC/Driver/Hooks.hs
+++ b/compiler/GHC/Driver/Hooks.hs
@@ -72,6 +72,7 @@ import GHC.Data.Bag
import qualified Data.Kind
import System.Process
+import GHC.Linker.Types
{-
************************************************************************
@@ -134,16 +135,15 @@ data Hooks = Hooks
, tcForeignExportsHook :: !(Maybe ([LForeignDecl GhcRn]
-> TcM (LHsBinds GhcTc, [LForeignDecl GhcTc], Bag GlobalRdrElt)))
, hscFrontendHook :: !(Maybe (ModSummary -> Hsc FrontendResult))
- , hscCompileCoreExprHook ::
- !(Maybe (HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> CoreExpr -> IO ForeignHValue))
+ , hscCompileCoreExprHook :: !(Maybe (HscEnv -> SrcSpan -> CoreExpr -> IO (ForeignHValue, [Linkable], PkgsLoaded)))
, ghcPrimIfaceHook :: !(Maybe ModIface)
, runPhaseHook :: !(Maybe PhaseHook)
, runMetaHook :: !(Maybe (MetaHook TcM))
, linkHook :: !(Maybe (GhcLink -> DynFlags -> Bool
-> HomePackageTable -> IO SuccessFlag))
, runRnSpliceHook :: !(Maybe (HsSplice GhcRn -> RnM (HsSplice GhcRn)))
- , getValueSafelyHook :: !(Maybe (HscEnv -> Maybe ModuleNameWithIsBoot -> Name -> Type
- -> IO (Either Type HValue)))
+ , getValueSafelyHook :: !(Maybe (HscEnv -> Name -> Type
+ -> IO (Either Type (HValue, [Linkable], PkgsLoaded))))
, createIservProcessHook :: !(Maybe (CreateProcess -> IO ProcessHandle))
, stgToCmmHook :: !(Maybe (StgToCmmConfig -> InfoTableProvMap -> [TyCon] -> CollectedCCs
-> [CgStgTopBinding] -> HpcInfo -> Stream IO CmmGroup ModuleLFInfos))
diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs
index d0d29a83e7..fc9b96f2e7 100644
--- a/compiler/GHC/Driver/Main.hs
+++ b/compiler/GHC/Driver/Main.hs
@@ -1552,7 +1552,7 @@ hscSimplify' plugins ds_result = do
hsc_env <- getHscEnv
hsc_env_with_plugins <- if null plugins -- fast path
then return hsc_env
- else liftIO $ flip initializePlugins (Just $ mg_mnwib ds_result)
+ else liftIO $ initializePlugins
$ hscUpdateFlags (\dflags -> foldr addPluginModuleName dflags plugins)
hsc_env
{-# SCC "Core2Core" #-}
@@ -1955,7 +1955,7 @@ hscParsedStmt hsc_env stmt = runInteractiveHsc hsc_env $ do
-- for linking, else we try to link 'main' and can't find it.
-- Whereas the linker already knows to ignore 'interactive'
let src_span = srcLocSpan interactiveSrcLoc
- hval <- liftIO $ hscCompileCoreExpr hsc_env (src_span, Nothing) ds_expr
+ (hval,_,_) <- liftIO $ hscCompileCoreExpr hsc_env src_span ds_expr
return $ Just (ids, hval, fix_env)
@@ -2052,10 +2052,10 @@ hscParsedDecls hsc_env decls = runInteractiveHsc hsc_env $ do
stg_binds data_tycons mod_breaks
let src_span = srcLocSpan interactiveSrcLoc
- _ <- liftIO $ loadDecls interp hsc_env (src_span, Nothing) cbc
+ _ <- liftIO $ loadDecls interp hsc_env src_span cbc
{- Load static pointer table entries -}
- liftIO $ hscAddSptEntries hsc_env Nothing (cg_spt_entries tidy_cg)
+ liftIO $ hscAddSptEntries hsc_env (cg_spt_entries tidy_cg)
let tcs = filterOut isImplicitTyCon (mg_tcs simpl_mg)
patsyns = mg_patsyns simpl_mg
@@ -2080,12 +2080,13 @@ hscParsedDecls hsc_env decls = runInteractiveHsc hsc_env $ do
-- | Load the given static-pointer table entries into the interpreter.
-- See Note [Grand plan for static forms] in "GHC.Iface.Tidy.StaticPtrTable".
-hscAddSptEntries :: HscEnv -> Maybe ModuleNameWithIsBoot -> [SptEntry] -> IO ()
-hscAddSptEntries hsc_env mnwib entries = do
+hscAddSptEntries :: HscEnv -> [SptEntry] -> IO ()
+hscAddSptEntries hsc_env entries = do
let interp = hscInterp hsc_env
let add_spt_entry :: SptEntry -> IO ()
add_spt_entry (SptEntry i fpr) = do
- val <- loadName interp hsc_env mnwib (idName i)
+ -- These are only names from the current module
+ (val, _, _) <- loadName interp hsc_env (idName i)
addSptEntry interp fpr val
mapM_ add_spt_entry entries
@@ -2195,13 +2196,13 @@ hscParseThingWithLocation source linenumber parser str = do
%* *
%********************************************************************* -}
-hscCompileCoreExpr :: HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> CoreExpr -> IO ForeignHValue
+hscCompileCoreExpr :: HscEnv -> SrcSpan -> CoreExpr -> IO (ForeignHValue, [Linkable], PkgsLoaded)
hscCompileCoreExpr hsc_env loc expr =
case hscCompileCoreExprHook (hsc_hooks hsc_env) of
Nothing -> hscCompileCoreExpr' hsc_env loc expr
Just h -> h hsc_env loc expr
-hscCompileCoreExpr' :: HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> CoreExpr -> IO ForeignHValue
+hscCompileCoreExpr' :: HscEnv -> SrcSpan -> CoreExpr -> IO (ForeignHValue, [Linkable], PkgsLoaded)
hscCompileCoreExpr' hsc_env srcspan ds_expr
= do { {- Simplify it -}
-- Question: should we call SimpleOpt.simpleOptExpr here instead?
@@ -2240,10 +2241,10 @@ hscCompileCoreExpr' hsc_env srcspan ds_expr
[] Nothing
{- load it -}
- ; fv_hvs <- loadDecls (hscInterp hsc_env) hsc_env srcspan bcos
+ ; (fv_hvs, mods_needed, units_needed) <- loadDecls (hscInterp hsc_env) hsc_env srcspan bcos
{- Get the HValue for the root -}
; return (expectJust "hscCompileCoreExpr'"
- $ lookup (idName binding_id) fv_hvs) }
+ $ lookup (idName binding_id) fv_hvs, mods_needed, units_needed) }
{- **********************************************************************
diff --git a/compiler/GHC/Driver/Make.hs b/compiler/GHC/Driver/Make.hs
index a46ae37279..6023d3a914 100644
--- a/compiler/GHC/Driver/Make.hs
+++ b/compiler/GHC/Driver/Make.hs
@@ -1166,16 +1166,15 @@ upsweep_mod hsc_env mHscMessage old_hmi summary mod_index nmods = do
-- This function only does anything if the linkable produced is a BCO, which only happens with the
-- bytecode backend, no need to guard against the backend type additionally.
addSptEntries (hscUpdateHPT (\hpt -> addToHpt hpt (ms_mod_name summary) hmi) hsc_env)
- (ms_mnwib summary)
(hm_linkable hmi)
return hmi
-- | Add the entries from a BCO linkable to the SPT table, see
-- See Note [Grand plan for static forms] in GHC.Iface.Tidy.StaticPtrTable.
-addSptEntries :: HscEnv -> ModuleNameWithIsBoot -> Maybe Linkable -> IO ()
-addSptEntries hsc_env mnwib mlinkable =
- hscAddSptEntries hsc_env (Just mnwib)
+addSptEntries :: HscEnv -> Maybe Linkable -> IO ()
+addSptEntries hsc_env mlinkable =
+ hscAddSptEntries hsc_env
[ spt
| Just linkable <- [mlinkable]
, unlinked <- linkableUnlinked linkable
@@ -2523,7 +2522,7 @@ runPipelines _ _ _ [] = return ()
runPipelines n_job orig_hsc_env mHscMessager all_pipelines = do
liftIO $ label_self "main --make thread"
- plugins_hsc_env <- initializePlugins orig_hsc_env Nothing
+ plugins_hsc_env <- initializePlugins orig_hsc_env
case n_job of
1 -> runSeqPipelines plugins_hsc_env mHscMessager all_pipelines
_n -> runParPipelines n_job plugins_hsc_env mHscMessager all_pipelines
diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs
index 56e188395e..ab1fb9f76f 100644
--- a/compiler/GHC/Driver/Pipeline.hs
+++ b/compiler/GHC/Driver/Pipeline.hs
@@ -238,7 +238,7 @@ compileOne' mHscMessage
addFilesToClean tmpfs TFL_GhcSession $
[ml_obj_file $ ms_location summary]
- plugin_hsc_env <- initializePlugins hsc_env (Just (ms_mnwib summary))
+ plugin_hsc_env <- initializePlugins hsc_env
let pipe_env = mkPipeEnv NoStop input_fn pipelineOutput
status <- hscRecompStatus mHscMessage plugin_hsc_env upd_summary
mb_old_iface mb_old_linkable (mod_index, nmods)
diff --git a/compiler/GHC/Driver/Pipeline/Execute.hs b/compiler/GHC/Driver/Pipeline/Execute.hs
index 6bc9df7c6f..c0c7b5d338 100644
--- a/compiler/GHC/Driver/Pipeline/Execute.hs
+++ b/compiler/GHC/Driver/Pipeline/Execute.hs
@@ -671,7 +671,7 @@ runHscPhase pipe_env hsc_env0 input_fn src_flavour = do
-- run the compiler!
let msg :: Messager
msg hsc_env _ what _ = oneShotMsg (hsc_logger hsc_env) what
- plugin_hsc_env' <- initializePlugins hsc_env (Just $ ms_mnwib mod_summary)
+ plugin_hsc_env' <- initializePlugins hsc_env
-- Need to set the knot-tying mutable variable for interface
-- files. See GHC.Tc.Utils.TcGblEnv.tcg_type_env_var.
diff --git a/compiler/GHC/Driver/Plugins.hs b/compiler/GHC/Driver/Plugins.hs
index 4fbbd5ce32..9afb556311 100644
--- a/compiler/GHC/Driver/Plugins.hs
+++ b/compiler/GHC/Driver/Plugins.hs
@@ -79,6 +79,8 @@ import Data.List (sort)
import qualified Data.Semigroup
import Control.Monad
+import GHC.Linker.Types
+import GHC.Types.Unique.DFM
-- | Command line options gathered from the -PModule.Name:stuff syntax
-- are given to you as this type
@@ -269,10 +271,13 @@ data Plugins = Plugins
-- 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'.
+ , loadedPluginDeps :: !([Linkable], PkgsLoaded)
+ -- ^ The object files required by the loaded plugins
+ -- See Note [Plugin dependencies]
}
emptyPlugins :: Plugins
-emptyPlugins = Plugins [] []
+emptyPlugins = Plugins [] [] ([], emptyUDFM)
pluginsWithArgs :: Plugins -> [PluginWithArgs]
diff --git a/compiler/GHC/HsToCore.hs b/compiler/GHC/HsToCore.hs
index 0a210e0871..78d43b164f 100644
--- a/compiler/GHC/HsToCore.hs
+++ b/compiler/GHC/HsToCore.hs
@@ -213,9 +213,10 @@ deSugar hsc_env
; used_th <- readIORef tc_splice_used
; dep_files <- readIORef dependent_files
; safe_mode <- finalSafeMode dflags tcg_env
+ ; (needed_mods, needed_pkgs) <- readIORef (tcg_th_needed_deps tcg_env)
- ; usages <- mkUsageInfo hsc_env mod hsc_src (imp_mods imports) used_names
- dep_files merged
+ ; usages <- mkUsageInfo hsc_env mod (imp_mods imports) used_names
+ dep_files merged needed_mods needed_pkgs
-- id_mod /= mod when we are processing an hsig, but hsigs
-- never desugared and compiled (there's no code!)
-- Consequently, this should hold for any ModGuts that make
diff --git a/compiler/GHC/HsToCore/Usage.hs b/compiler/GHC/HsToCore/Usage.hs
index fbde84deda..2ef692c241 100644
--- a/compiler/GHC/HsToCore/Usage.hs
+++ b/compiler/GHC/HsToCore/Usage.hs
@@ -37,9 +37,9 @@ import Data.Map (Map)
import qualified Data.Map as Map
import GHC.Linker.Types
-import GHC.Linker.Loader ( getLoaderState )
-import GHC.Types.SourceFile
import GHC.Unit.Finder
+import GHC.Types.Unique.DFM
+import GHC.Driver.Plugins
{- Note [Module self-dependency]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -63,14 +63,14 @@ its dep_orphs. This was the cause of #14128.
mkUsedNames :: TcGblEnv -> NameSet
mkUsedNames TcGblEnv{ tcg_dus = dus } = allUses dus
-mkUsageInfo :: HscEnv -> Module -> HscSource -> ImportedMods -> NameSet -> [FilePath]
- -> [(Module, Fingerprint)] -> IO [Usage]
-mkUsageInfo hsc_env this_mod src dir_imp_mods used_names dependent_files merged
+mkUsageInfo :: HscEnv -> Module -> ImportedMods -> NameSet -> [FilePath]
+ -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded -> IO [Usage]
+mkUsageInfo hsc_env this_mod dir_imp_mods used_names dependent_files merged needed_links needed_pkgs
= do
eps <- hscEPS hsc_env
hashes <- mapM getFileHash dependent_files
-- Dependencies on object files due to TH and plugins
- object_usages <- mkObjectUsage (eps_PIT eps) hsc_env (GWIB (moduleName this_mod) (hscSourceToIsBoot src))
+ object_usages <- mkObjectUsage (eps_PIT eps) hsc_env needed_links needed_pkgs
let mod_usages = mk_mod_usage_info (eps_PIT eps) hsc_env this_mod
dir_imp_mods used_names
usages = mod_usages ++ [ UsageFile { usg_file_path = f
@@ -124,24 +124,38 @@ One way to improve this is to either:
of the module and the implementation hashes of its dependencies, and then
compare implementation hashes for recompilation. Creation of implementation
hashes is however potentially expensive.
+
+ A serious issue with the interface hash idea is that if you include an
+ interface hash, that hash also needs to depend on the hash of its
+ dependencies. Therefore, if any of the transitive dependencies of a modules
+ gets updated then you need to recompile the module in case the interface
+ hash has changed irrespective if the module uses TH or not.
+
+ This is important to maintain the invariant that the information in the
+ interface file is always up-to-date.
+
+
+ See #20790 (comment 3)
+-}
+
+{-
+Note [Object File Dependencies]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In addition to the Note [Plugin dependencies] above, for TH we also need to record
+the hashes of object files that the TH code is required to load. These are
+calculated by the loader in `getLinkDeps` and are accumulated in each individual
+`TcGblEnv`, in `tcg_th_needed_deps`. We read this just before compute the UsageInfo
+to inject the appropriate dependencies.
-}
-- | Find object files corresponding to the transitive closure of given home
-- modules and direct object files for pkg dependencies
-mkObjectUsage :: PackageIfaceTable -> HscEnv -> ModuleNameWithIsBoot -> IO [Usage]
-mkObjectUsage pit hsc_env mnwib = do
- case hsc_interp hsc_env of
- Just interp -> do
- mps <- getLoaderState interp
- case mps of
- Just ps -> do
- let ls = fromMaybe [] $ Map.lookup mnwib (module_deps ps)
- ds = hs_objs_loaded ps
- concat <$> sequence (map linkableToUsage ls ++ map librarySpecToUsage ds)
- Nothing -> return []
- Nothing -> return []
-
-
+mkObjectUsage :: PackageIfaceTable -> HscEnv -> [Linkable] -> PkgsLoaded -> IO [Usage]
+mkObjectUsage pit hsc_env th_links_needed th_pkgs_needed = do
+ let ls = ordNubOn linkableModule (th_links_needed ++ plugins_links_needed)
+ ds = concatMap loaded_pkg_hs_objs $ eltsUDFM (plusUDFM th_pkgs_needed plugin_pkgs_needed) -- TODO possibly record loaded_pkg_non_hs_objs as well
+ (plugins_links_needed, plugin_pkgs_needed) = loadedPluginDeps $ hsc_plugins hsc_env
+ concat <$> sequence (map linkableToUsage ls ++ map librarySpecToUsage ds)
where
linkableToUsage (LM _ m uls) = mapM (unlinkedToUsage m) uls
diff --git a/compiler/GHC/Iface/Make.hs b/compiler/GHC/Iface/Make.hs
index c0471cd413..19739ff3e3 100644
--- a/compiler/GHC/Iface/Make.hs
+++ b/compiler/GHC/Iface/Make.hs
@@ -211,6 +211,7 @@ mkIfaceTc hsc_env safe_mode mod_details mod_summary
let hpc_info = emptyHpcInfo other_hpc_info
used_th <- readIORef tc_splice_used
dep_files <- (readIORef dependent_files)
+ (needed_links, needed_pkgs) <- readIORef (tcg_th_needed_deps tc_result)
-- Do NOT use semantic module here; this_mod in mkUsageInfo
-- is used solely to decide if we should record a dependency
-- or not. When we instantiate a signature, the semantic
@@ -218,8 +219,8 @@ mkIfaceTc hsc_env safe_mode mod_details mod_summary
-- but if you pass that in here, we'll decide it's the local
-- module and does not need to be recorded as a dependency.
-- See Note [Identity versus semantic module]
- usages <- mkUsageInfo hsc_env this_mod hsc_src (imp_mods imports) used_names
- dep_files merged
+ usages <- mkUsageInfo hsc_env this_mod (imp_mods imports) used_names
+ dep_files merged needed_links needed_pkgs
(doc_hdr', doc_map, arg_map) <- extractDocs tc_result
diff --git a/compiler/GHC/Linker/Loader.hs b/compiler/GHC/Linker/Loader.hs
index 046ec5ffd7..7ac5fffab1 100644
--- a/compiler/GHC/Linker/Loader.hs
+++ b/compiler/GHC/Linker/Loader.hs
@@ -29,8 +29,6 @@ module GHC.Linker.Loader
, withExtendedLoadedEnv
, extendLoadedEnv
, deleteFromLoadedEnv
- -- * Misc
- , extendLoadedPkgs
)
where
@@ -66,6 +64,7 @@ import GHC.Types.Name
import GHC.Types.Name.Env
import GHC.Types.SrcLoc
import GHC.Types.Unique.DSet
+import GHC.Types.Unique.DFM
import GHC.Utils.Outputable
import GHC.Utils.Panic
@@ -88,7 +87,6 @@ import GHC.Unit.State as Packages
import qualified GHC.Data.ShortText as ST
import qualified GHC.Data.Maybe as Maybes
import GHC.Data.FastString
-import GHC.Data.List.SetOps
import GHC.Linker.MacOS
import GHC.Linker.Dynamic
@@ -98,9 +96,10 @@ import GHC.Linker.Types
import Control.Monad
import qualified Data.Set as Set
+import qualified Data.Map as M
import Data.Char (isSpace)
import Data.IORef
-import Data.List (intercalate, isPrefixOf, isSuffixOf, nub, partition, find)
+import Data.List (intercalate, isPrefixOf, isSuffixOf, nub, partition)
import Data.Maybe
import Control.Concurrent.MVar
import qualified Control.Monad.Catch as MC
@@ -115,14 +114,14 @@ import System.Win32.Info (getSystemDirectory)
#endif
import GHC.Utils.Exception
-import qualified Data.Map as M
-import Data.Either (partitionEithers)
import GHC.Unit.Module.Graph
import GHC.Types.SourceFile
import GHC.Utils.Misc
import GHC.Iface.Load
import GHC.Unit.Home
+import Data.Either
+import Control.Applicative
uninitialised :: a
uninitialised = panic "Loader not initialised"
@@ -147,11 +146,8 @@ emptyLoaderState = LoaderState
{ closure_env = emptyNameEnv
, itbl_env = emptyNameEnv
, pkgs_loaded = init_pkgs
- , bcos_loaded = []
- , objs_loaded = []
- , hs_objs_loaded = []
- , non_hs_objs_loaded = []
- , module_deps = M.empty
+ , bcos_loaded = emptyModuleEnv
+ , objs_loaded = emptyModuleEnv
, temp_sos = []
}
-- Packages that don't need loading, because the compiler
@@ -159,12 +155,7 @@ emptyLoaderState = LoaderState
--
-- The linker's symbol table is populated with RTS symbols using an
-- explicit list. See rts/Linker.c for details.
- where init_pkgs = [rtsUnitId]
-
-extendLoadedPkgs :: Interp -> [UnitId] -> IO ()
-extendLoadedPkgs interp pkgs =
- modifyLoaderState_ interp $ \s ->
- return s{ pkgs_loaded = pkgs ++ pkgs_loaded s }
+ where init_pkgs = unitUDFM rtsUnitId (LoadedPkgInfo rtsUnitId [] [] emptyUniqDSet)
extendLoadedEnv :: Interp -> [(Name,ForeignHValue)] -> IO ()
extendLoadedEnv interp new_bindings =
@@ -183,21 +174,21 @@ deleteFromLoadedEnv interp to_remove =
-- | Load the module containing the given Name and get its associated 'HValue'.
--
-- Throws a 'ProgramError' if loading fails or the name cannot be found.
-loadName :: Interp -> HscEnv -> Maybe ModuleNameWithIsBoot -> Name -> IO ForeignHValue
-loadName interp hsc_env mnwib name = do
+loadName :: Interp -> HscEnv -> Name -> IO (ForeignHValue, [Linkable], PkgsLoaded)
+loadName interp hsc_env name = do
initLoaderState interp hsc_env
modifyLoaderState interp $ \pls0 -> do
- pls <- if not (isExternalName name)
- then return pls0
+ (pls, links, pkgs) <- if not (isExternalName name)
+ then return (pls0, [], emptyUDFM)
else do
- (pls', ok) <- loadDependencies interp hsc_env pls0 (noSrcSpan, mnwib)
- [nameModule name]
+ (pls', ok, links, pkgs) <- loadDependencies interp hsc_env pls0 noSrcSpan
+ [nameModule name]
if failed ok
then throwGhcExceptionIO (ProgramError "")
- else return pls'
+ else return (pls', links, pkgs)
case lookupNameEnv (closure_env pls) name of
- Just (_,aa) -> return (pls,aa)
+ Just (_,aa) -> return (pls,(aa, links, pkgs))
Nothing -> assertPpr (isExternalName name) (ppr name) $
do let sym_to_find = nameToCLabel name "closure"
m <- lookupClosure interp (unpackFS sym_to_find)
@@ -205,14 +196,15 @@ loadName interp hsc_env mnwib name = do
Just hvref -> mkFinalizedHValue interp hvref
Nothing -> linkFail "GHC.Linker.Loader.loadName"
(unpackFS sym_to_find)
- return (pls,r)
+ return (pls,(r, links, pkgs))
loadDependencies
:: Interp
-> HscEnv
-> LoaderState
- -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> [Module]
- -> IO (LoaderState, SuccessFlag)
+ -> SrcSpan
+ -> [Module]
+ -> IO (LoaderState, SuccessFlag, [Linkable], PkgsLoaded) -- ^ returns the set of linkables required
loadDependencies interp hsc_env pls span needed_mods = do
-- initLoaderState (hsc_dflags hsc_env) dl
let dflags = hsc_dflags hsc_env
@@ -220,20 +212,23 @@ loadDependencies interp hsc_env pls span needed_mods = do
-- the "normal" way, i.e. no non-std ways like profiling or ticky-ticky.
-- So here we check the build tag: if we're building a non-standard way
-- then we need to find & link object files built the "normal" way.
- maybe_normal_osuf <- checkNonStdWay dflags interp (fst span)
+ maybe_normal_osuf <- checkNonStdWay dflags interp span
-- Find what packages and linkables are required
- (lnks, all_lnks, pkgs) <- getLinkDeps hsc_env pls
- maybe_normal_osuf (fst span) needed_mods
-
- let pls1 =
- case snd span of
- Just mn -> pls { module_deps = M.insertWith (++) mn all_lnks (module_deps pls) }
- Nothing -> pls
+ (lnks, all_lnks, pkgs, this_pkgs_needed)
+ <- getLinkDeps hsc_env pls
+ maybe_normal_osuf span needed_mods
-- Link the packages and modules required
- pls2 <- loadPackages' interp hsc_env pkgs pls1
- loadModules interp hsc_env pls2 lnks
+ pls1 <- loadPackages' interp hsc_env pkgs pls
+ (pls2, succ) <- loadModuleLinkables interp hsc_env pls1 lnks
+ let this_pkgs_loaded = udfmRestrictKeys all_pkgs_loaded $ getUniqDSet trans_pkgs_needed
+ all_pkgs_loaded = pkgs_loaded pls2
+ trans_pkgs_needed = unionManyUniqDSets (this_pkgs_needed : [ loaded_pkg_trans_deps pkg
+ | pkg_id <- uniqDSetToList this_pkgs_needed
+ , Just pkg <- [lookupUDFM all_pkgs_loaded pkg_id]
+ ])
+ return (pls2, succ, all_lnks, this_pkgs_loaded)
-- | Temporarily extend the loaded env.
@@ -266,9 +261,9 @@ showLoaderState interp = do
ls <- readMVar (loader_state (interpLoader interp))
let docs = case ls of
Nothing -> [ text "Loader not initialised"]
- Just pls -> [ text "Pkgs:" <+> ppr (pkgs_loaded pls)
- , text "Objs:" <+> ppr (objs_loaded pls)
- , text "BCOs:" <+> ppr (bcos_loaded pls)
+ Just pls -> [ text "Pkgs:" <+> ppr (map loaded_pkg_uid $ eltsUDFM $ pkgs_loaded pls)
+ , text "Objs:" <+> ppr (moduleEnvElts $ objs_loaded pls)
+ , text "BCOs:" <+> ppr (moduleEnvElts $ bcos_loaded pls)
]
return $ withPprStyle defaultDumpStyle
@@ -588,7 +583,7 @@ preloadLib interp hsc_env lib_paths framework_paths pls lib_spec = do
-- Raises an IO exception ('ProgramError') if it can't find a compiled
-- version of the dependents to load.
--
-loadExpr :: Interp -> HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> UnlinkedBCO -> IO ForeignHValue
+loadExpr :: Interp -> HscEnv -> SrcSpan -> UnlinkedBCO -> IO ForeignHValue
loadExpr interp hsc_env span root_ul_bco = do
-- Initialise the linker (if it's not been done already)
initLoaderState interp hsc_env
@@ -596,7 +591,7 @@ loadExpr interp hsc_env span root_ul_bco = do
-- Take lock for the actual work.
modifyLoaderState interp $ \pls0 -> do
-- Load the packages and modules required
- (pls, ok) <- loadDependencies interp hsc_env pls0 span needed_mods
+ (pls, ok, _, _) <- loadDependencies interp hsc_env pls0 span needed_mods
if failed ok
then throwGhcExceptionIO (ProgramError "")
else do
@@ -691,7 +686,9 @@ getLinkDeps :: HscEnv
-> Maybe FilePath -- replace object suffixes?
-> SrcSpan -- for error messages
-> [Module] -- If you need these
- -> IO ([Linkable], [Linkable], [UnitId]) -- ... then link these first
+ -> IO ([Linkable], [Linkable], [UnitId], UniqDSet UnitId) -- ... then link these first
+ -- The module and package dependencies for the needed modules are returned.
+ -- See Note [Object File Dependencies]
-- Fails with an IO exception if it can't find enough files
getLinkDeps hsc_env pls replace_osuf span mods
@@ -708,16 +705,16 @@ getLinkDeps hsc_env pls replace_osuf span mods
emptyUniqDSet emptyUniqDSet;
else do
(pkgs, mmods) <- unzip <$> mapM get_mod_info all_home_mods
- return (catMaybes mmods, Set.toList (Set.unions (init_pkg_set : pkgs)))
+ return (catMaybes mmods, unionManyUniqDSets (init_pkg_set : pkgs))
; let
-- 2. Exclude ones already linked
-- Main reason: avoid findModule calls in get_linkable
- (mods_needed, mods_got) = partitionEithers (map split_mods mods_s)
- pkgs_needed = pkgs_s `minusList` pkgs_loaded pls
+ (mods_needed, links_got) = partitionEithers (map split_mods mods_s)
+ pkgs_needed = eltsUDFM $ getUniqDSet pkgs_s `minusUDFM` pkgs_loaded pls
split_mods mod =
- let is_linked = find ((== mod) . (linkableModule)) (objs_loaded pls ++ bcos_loaded pls)
+ let is_linked = findModuleLinkable_maybe (objs_loaded pls) mod <|> findModuleLinkable_maybe (bcos_loaded pls) mod
in case is_linked of
Just linkable -> Right linkable
Nothing -> Left mod
@@ -728,7 +725,7 @@ getLinkDeps hsc_env pls replace_osuf span mods
; let { osuf = objectSuf dflags }
; lnks_needed <- mapM (get_linkable osuf) mods_needed
- ; return (lnks_needed, mods_got ++ lnks_needed, pkgs_needed) }
+ ; return (lnks_needed, links_got ++ lnks_needed, pkgs_needed, pkgs_s) }
where
dflags = hsc_dflags hsc_env
mod_graph = hsc_mod_graph hsc_env
@@ -741,7 +738,7 @@ getLinkDeps hsc_env pls replace_osuf span mods
-- It is also a matter of correctness to use the module graph so that dependencies between home units
-- is resolved correctly.
- make_deps_loop :: (Set.Set UnitId, Set.Set NodeKey) -> [ModNodeKeyWithUid] -> (Set.Set UnitId, Set.Set NodeKey)
+ make_deps_loop :: (UniqDSet UnitId, Set.Set NodeKey) -> [ModNodeKeyWithUid] -> (UniqDSet UnitId, Set.Set NodeKey)
make_deps_loop found [] = found
make_deps_loop found@(found_units, found_mods) (nk:nexts)
| NodeKey_Module nk `Set.member` found_mods = make_deps_loop found nexts
@@ -755,10 +752,10 @@ getLinkDeps hsc_env pls replace_osuf span mods
in make_deps_loop (found_units, deps `Set.union` found_mods) (todo_boot_mods ++ nexts)
Nothing ->
let (ModNodeKeyWithUid _ uid) = nk
- in make_deps_loop (uid `Set.insert` found_units, found_mods) nexts
+ in make_deps_loop (addOneToUniqDSet found_units uid, found_mods) nexts
mkNk m = ModNodeKeyWithUid (GWIB (moduleName m) NotBoot) (moduleUnitId m)
- (init_pkg_set, all_deps) = make_deps_loop (Set.empty, Set.empty) $ map mkNk (filterOut isInteractiveModule mods)
+ (init_pkg_set, all_deps) = make_deps_loop (emptyUniqDSet, Set.empty) $ map mkNk (filterOut isInteractiveModule mods)
all_home_mods = [with_uid | NodeKey_Module with_uid <- Set.toList all_deps]
@@ -770,7 +767,7 @@ getLinkDeps hsc_env pls replace_osuf span mods
HsBootFile -> link_boot_mod_error (mi_module iface)
_ -> return $ Just (mi_module iface)
- in (dep_direct_pkgs (mi_deps iface),) <$> mmod
+ in (mkUniqDSet $ Set.toList $ dep_direct_pkgs (mi_deps iface),) <$> mmod
Nothing ->
let err = text "getLinkDeps: Home module not loaded" <+> ppr (gwib_mod gwib) <+> ppr uid
in throwGhcExceptionIO (ProgramError (showSDoc dflags err))
@@ -786,9 +783,9 @@ getLinkDeps hsc_env pls replace_osuf span mods
follow_deps :: [Module] -- modules to follow
-> UniqDSet Module -- accum. module dependencies
-> UniqDSet UnitId -- accum. package dependencies
- -> IO ([Module], [UnitId]) -- result
+ -> IO ([Module], UniqDSet UnitId) -- result
follow_deps [] acc_mods acc_pkgs
- = return (uniqDSetToList acc_mods, uniqDSetToList acc_pkgs)
+ = return (uniqDSetToList acc_mods, acc_pkgs)
follow_deps (mod:mods) acc_mods acc_pkgs
= do
mb_iface <- initIfaceCheck (text "getLinkDeps") hsc_env $
@@ -892,14 +889,13 @@ getLinkDeps hsc_env pls replace_osuf span mods
adjust_ul _ l@(BCOs {}) = return l
-
{- **********************************************************************
Loading a Decls statement
********************************************************************* -}
-loadDecls :: Interp -> HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> CompiledByteCode -> IO [(Name, ForeignHValue)]
+loadDecls :: Interp -> HscEnv -> SrcSpan -> CompiledByteCode -> IO ([(Name, ForeignHValue)], [Linkable], PkgsLoaded)
loadDecls interp hsc_env span cbc@CompiledByteCode{..} = do
-- Initialise the linker (if it's not been done already)
initLoaderState interp hsc_env
@@ -907,7 +903,7 @@ loadDecls interp hsc_env span cbc@CompiledByteCode{..} = do
-- Take lock for the actual work.
modifyLoaderState interp $ \pls0 -> do
-- Link the packages and modules required
- (pls, ok) <- loadDependencies interp hsc_env pls0 span needed_mods
+ (pls, ok, links_needed, units_needed) <- loadDependencies interp hsc_env pls0 span needed_mods
if failed ok
then throwGhcExceptionIO (ProgramError "")
else do
@@ -921,7 +917,7 @@ loadDecls interp hsc_env span cbc@CompiledByteCode{..} = do
nms_fhvs <- makeForeignNamedHValueRefs interp new_bindings
let pls2 = pls { closure_env = extendClosureEnv ce nms_fhvs
, itbl_env = ie }
- return (pls2, nms_fhvs)
+ return (pls2, (nms_fhvs, links_needed, units_needed))
where
free_names = uniqDSetToList $
foldr (unionUniqDSets . bcoFreeNames) emptyUniqDSet bc_bcos
@@ -942,11 +938,11 @@ loadDecls interp hsc_env span cbc@CompiledByteCode{..} = do
********************************************************************* -}
-loadModule :: Interp -> HscEnv -> Maybe ModuleNameWithIsBoot -> Module -> IO ()
-loadModule interp hsc_env mnwib mod = do
+loadModule :: Interp -> HscEnv -> Module -> IO ()
+loadModule interp hsc_env mod = do
initLoaderState interp hsc_env
modifyLoaderState_ interp $ \pls -> do
- (pls', ok) <- loadDependencies interp hsc_env pls (noSrcSpan, mnwib) [mod]
+ (pls', ok, _, _) <- loadDependencies interp hsc_env pls noSrcSpan [mod]
if failed ok
then throwGhcExceptionIO (ProgramError "could not load module")
else return pls'
@@ -959,8 +955,8 @@ loadModule interp hsc_env mnwib mod = do
********************************************************************* -}
-loadModules :: Interp -> HscEnv -> LoaderState -> [Linkable] -> IO (LoaderState, SuccessFlag)
-loadModules interp hsc_env pls linkables
+loadModuleLinkables :: Interp -> HscEnv -> LoaderState -> [Linkable] -> IO (LoaderState, SuccessFlag)
+loadModuleLinkables interp hsc_env pls linkables
= mask_ $ do -- don't want to be interrupted by ^C in here
let (objs, bcos) = partition isObjectLinkable
@@ -989,14 +985,10 @@ partitionLinkable li
li {linkableUnlinked=li_uls_bco}]
_ -> [li]
-findModuleLinkable_maybe :: [Linkable] -> Module -> Maybe Linkable
-findModuleLinkable_maybe lis mod
- = case [LM time nm us | LM time nm us <- lis, nm == mod] of
- [] -> Nothing
- [li] -> Just li
- _ -> pprPanic "findModuleLinkable" (ppr mod)
+findModuleLinkable_maybe :: LinkableSet -> Module -> Maybe Linkable
+findModuleLinkable_maybe = lookupModuleEnv
-linkableInSet :: Linkable -> [Linkable] -> Bool
+linkableInSet :: Linkable -> LinkableSet -> Bool
linkableInSet l objs_loaded =
case findModuleLinkable_maybe objs_loaded (linkableModule l) of
Nothing -> False
@@ -1100,7 +1092,7 @@ dynLoadObjs interp hsc_env pls@LoaderState{..} objs = do
-- link all "loaded packages" so symbols in those can be resolved
-- Note: We are loading packages with local scope, so to see the
-- symbols in this link we must link all loaded packages again.
- linkDynLib logger tmpfs dflags2 unit_env objs pkgs_loaded
+ linkDynLib logger tmpfs dflags2 unit_env objs (loaded_pkg_uid <$> eltsUDFM pkgs_loaded)
-- if we got this far, extend the lifetime of the library file
changeTempFilesLifetime tmpfs TFL_GhcSession [soFile]
@@ -1111,9 +1103,9 @@ dynLoadObjs interp hsc_env pls@LoaderState{..} objs = do
where
msg = "GHC.Linker.Loader.dynLoadObjs: Loading temp shared object failed"
-rmDupLinkables :: [Linkable] -- Already loaded
+rmDupLinkables :: LinkableSet -- Already loaded
-> [Linkable] -- New linkables
- -> ([Linkable], -- New loaded set (including new ones)
+ -> (LinkableSet, -- New loaded set (including new ones)
[Linkable]) -- New linkables (excluding dups)
rmDupLinkables already ls
= go already [] ls
@@ -1121,7 +1113,7 @@ rmDupLinkables already ls
go already extras [] = (already, extras)
go already extras (l:ls)
| linkableInSet l already = go already extras ls
- | otherwise = go (l:already) (l:extras) ls
+ | otherwise = go (extendModuleEnv already (linkableModule l) l) (l:extras) ls
{- **********************************************************************
@@ -1232,9 +1224,9 @@ unload interp hsc_env linkables
let logger = hsc_logger hsc_env
debugTraceMsg logger 3 $
- text "unload: retaining objs" <+> ppr (objs_loaded new_pls)
+ text "unload: retaining objs" <+> ppr (moduleEnvElts $ objs_loaded new_pls)
debugTraceMsg logger 3 $
- text "unload: retaining bcos" <+> ppr (bcos_loaded new_pls)
+ text "unload: retaining bcos" <+> ppr (moduleEnvElts $ bcos_loaded new_pls)
return ()
unload_wkr
@@ -1250,32 +1242,32 @@ unload_wkr interp keep_linkables pls@LoaderState{..} = do
-- we're unloading some code. -fghci-leak-check with the tests in
-- testsuite/ghci can detect space leaks here.
- let (objs_to_keep, bcos_to_keep) = partition isObjectLinkable keep_linkables
+ let (objs_to_keep', bcos_to_keep') = partition isObjectLinkable keep_linkables
+ objs_to_keep = mkLinkableSet objs_to_keep'
+ bcos_to_keep = mkLinkableSet bcos_to_keep'
discard keep l = not (linkableInSet l keep)
(objs_to_unload, remaining_objs_loaded) =
- partition (discard objs_to_keep) objs_loaded
+ partitionModuleEnv (discard objs_to_keep) objs_loaded
(bcos_to_unload, remaining_bcos_loaded) =
- partition (discard bcos_to_keep) bcos_loaded
+ partitionModuleEnv (discard bcos_to_keep) bcos_loaded
+
+ linkables_to_unload = moduleEnvElts objs_to_unload ++ moduleEnvElts bcos_to_unload
- mapM_ unloadObjs objs_to_unload
- mapM_ unloadObjs bcos_to_unload
+ mapM_ unloadObjs linkables_to_unload
-- If we unloaded any object files at all, we need to purge the cache
-- of lookupSymbol results.
- when (not (null (objs_to_unload ++
- filter (not . null . linkableObjs) bcos_to_unload))) $
+ when (not (null (filter (not . null . linkableObjs) linkables_to_unload))) $
purgeLookupSymbolCache interp
- let !bcos_retained = mkModuleSet $ map linkableModule remaining_bcos_loaded
-
- -- Note that we want to remove all *local*
+ let -- Note that we want to remove all *local*
-- (i.e. non-isExternal) names too (these are the
-- temporary bindings from the command line).
keep_name :: (Name, a) -> Bool
keep_name (n,_) = isExternalName n &&
- nameModule n `elemModuleSet` bcos_retained
+ nameModule n `elemModuleEnv` remaining_bcos_loaded
itbl_env' = filterNameEnv keep_name itbl_env
closure_env' = filterNameEnv keep_name closure_env
@@ -1350,25 +1342,29 @@ loadPackages interp hsc_env new_pkgs = do
loadPackages' :: Interp -> HscEnv -> [UnitId] -> LoaderState -> IO LoaderState
loadPackages' interp hsc_env new_pks pls = do
- (pkgs', hs_objs, non_hs_objs) <- link (pkgs_loaded pls) new_pks
+ pkgs' <- link (pkgs_loaded pls) new_pks
return $! pls { pkgs_loaded = pkgs'
- , hs_objs_loaded = hs_objs ++ hs_objs_loaded pls
- , non_hs_objs_loaded = non_hs_objs ++ non_hs_objs_loaded pls }
+ }
where
- link :: [UnitId] -> [UnitId] -> IO ([UnitId], [LibrarySpec], [LibrarySpec])
+ link :: PkgsLoaded -> [UnitId] -> IO PkgsLoaded
link pkgs new_pkgs =
- foldM link_one (pkgs, [],[]) new_pkgs
+ foldM link_one pkgs new_pkgs
- link_one (pkgs, acc_hs, acc_non_hs) new_pkg
- | new_pkg `elem` pkgs -- Already linked
- = return (pkgs, acc_hs, acc_non_hs)
+ link_one pkgs new_pkg
+ | new_pkg `elemUDFM` pkgs -- Already linked
+ = return pkgs
| Just pkg_cfg <- lookupUnitId (hsc_units hsc_env) new_pkg
- = do { -- Link dependents first
- (pkgs', hs_cls', extra_cls') <- link pkgs (unitDepends pkg_cfg)
+ = do { let deps = unitDepends pkg_cfg
+ -- Link dependents first
+ ; pkgs' <- link pkgs deps
-- Now link the package itself
; (hs_cls, extra_cls) <- loadPackage interp hsc_env pkg_cfg
- ; return (new_pkg : pkgs', acc_hs ++ hs_cls ++ hs_cls', acc_non_hs ++ extra_cls ++ extra_cls') }
+ ; let trans_deps = unionManyUniqDSets [ addOneToUniqDSet (loaded_pkg_trans_deps loaded_pkg_info) dep_pkg
+ | dep_pkg <- deps
+ , Just loaded_pkg_info <- pure (lookupUDFM pkgs' dep_pkg)
+ ]
+ ; return (addToUDFM pkgs' new_pkg (LoadedPkgInfo new_pkg hs_cls extra_cls trans_deps)) }
| otherwise
= throwGhcExceptionIO (CmdLineError ("unknown package: " ++ unpackFS (unitIdFS new_pkg)))
diff --git a/compiler/GHC/Linker/Types.hs b/compiler/GHC/Linker/Types.hs
index 17bb46feb9..040b7f8deb 100644
--- a/compiler/GHC/Linker/Types.hs
+++ b/compiler/GHC/Linker/Types.hs
@@ -11,6 +11,9 @@ module GHC.Linker.Types
, LoaderState (..)
, uninitializedLoader
, Linkable(..)
+ , LinkableSet
+ , mkLinkableSet
+ , unionLinkableSet
, Unlinked(..)
, SptEntry(..)
, isObjectLinkable
@@ -21,11 +24,13 @@ module GHC.Linker.Types
, isInterpretable
, byteCodeOfObject
, LibrarySpec(..)
+ , LoadedPkgInfo(..)
+ , PkgsLoaded
)
where
import GHC.Prelude
-import GHC.Unit ( UnitId, Module, ModuleNameWithIsBoot )
+import GHC.Unit ( UnitId, Module )
import GHC.ByteCode.Types ( ItblEnv, CompiledByteCode )
import GHC.Fingerprint.Type ( Fingerprint )
import GHCi.RemoteTypes ( ForeignHValue )
@@ -40,7 +45,9 @@ import GHC.Utils.Panic
import Control.Concurrent.MVar
import Data.Time ( UTCTime )
import Data.Maybe
-import qualified Data.Map as M
+import GHC.Unit.Module.Env
+import GHC.Types.Unique.DSet
+import GHC.Types.Unique.DFM
{- **********************************************************************
@@ -75,19 +82,15 @@ data LoaderState = LoaderState
-- module in the image is replaced, the itbl_env must be updated
-- appropriately.
- , bcos_loaded :: ![Linkable]
+ , bcos_loaded :: !LinkableSet
-- ^ The currently loaded interpreted modules (home package)
- , objs_loaded :: ![Linkable]
+ , objs_loaded :: !LinkableSet
-- ^ And the currently-loaded compiled modules (home package)
- , pkgs_loaded :: ![UnitId]
+ , pkgs_loaded :: !PkgsLoaded
-- ^ The currently-loaded packages; always object code
- -- Held, as usual, in dependency order; though I am not sure if
- -- that is really important
- , hs_objs_loaded :: ![LibrarySpec]
- , non_hs_objs_loaded :: ![LibrarySpec]
- , module_deps :: M.Map ModuleNameWithIsBoot [Linkable]
+ -- haskell libraries, system libraries, transitive dependencies
, temp_sos :: ![(FilePath, String)]
-- ^ We need to remember the name of previous temporary DLL/.so
@@ -98,6 +101,23 @@ uninitializedLoader :: IO Loader
uninitializedLoader = Loader <$> newMVar Nothing
type ClosureEnv = NameEnv (Name, ForeignHValue)
+type PkgsLoaded = UniqDFM UnitId LoadedPkgInfo
+
+data LoadedPkgInfo
+ = LoadedPkgInfo
+ { loaded_pkg_uid :: !UnitId
+ , loaded_pkg_hs_objs :: ![LibrarySpec]
+ , loaded_pkg_non_hs_objs :: ![LibrarySpec]
+ , loaded_pkg_trans_deps :: UniqDSet UnitId
+ }
+
+instance Outputable LoadedPkgInfo where
+ ppr (LoadedPkgInfo uid hs_objs non_hs_objs trans_deps) =
+ vcat [ppr uid
+ , ppr hs_objs
+ , ppr non_hs_objs
+ , ppr trans_deps ]
+
-- | Information we can use to dynamically link modules into the compiler
data Linkable = LM {
@@ -111,6 +131,18 @@ data Linkable = LM {
-- INVARIANT: A valid linkable always has at least one 'Unlinked' item.
}
+type LinkableSet = ModuleEnv Linkable
+
+mkLinkableSet :: [Linkable] -> LinkableSet
+mkLinkableSet ls = mkModuleEnv [(linkableModule l, l) | l <- ls]
+
+unionLinkableSet :: LinkableSet -> LinkableSet -> LinkableSet
+unionLinkableSet = plusModuleEnv_C go
+ where
+ go l1 l2
+ | linkableTime l1 > linkableTime l2 = l1
+ | otherwise = l2
+
instance Outputable Linkable where
ppr (LM when_made mod unlinkeds)
= (text "LinkableM" <+> parens (text (show when_made)) <+> ppr mod)
diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs
index 5c2f6ff6cc..b4bf25b9b3 100644
--- a/compiler/GHC/Runtime/Eval.hs
+++ b/compiler/GHC/Runtime/Eval.hs
@@ -1278,13 +1278,13 @@ obtainTermFromVal hsc_env _bound _force _ty _x = case interpInstance interp of
obtainTermFromId :: HscEnv -> Int -> Bool -> Id -> IO Term
obtainTermFromId hsc_env bound force id = do
- hv <- Loader.loadName (hscInterp hsc_env) hsc_env Nothing (varName id)
+ (hv, _, _) <- Loader.loadName (hscInterp hsc_env) hsc_env (varName id)
cvObtainTerm hsc_env bound force (idType id) hv
-- Uses RTTI to reconstruct the type of an Id, making it less polymorphic
reconstructType :: HscEnv -> Int -> Id -> IO (Maybe Type)
reconstructType hsc_env bound id = do
- hv <- Loader.loadName (hscInterp hsc_env) hsc_env Nothing (varName id)
+ (hv, _, _) <- Loader.loadName (hscInterp hsc_env) hsc_env (varName id)
cvReconstructType hsc_env bound (idType id) hv
mkRuntimeUnkTyVar :: Name -> Kind -> TyVar
diff --git a/compiler/GHC/Runtime/Loader.hs b/compiler/GHC/Runtime/Loader.hs
index 3803bc39fe..393573fd24 100644
--- a/compiler/GHC/Runtime/Loader.hs
+++ b/compiler/GHC/Runtime/Loader.hs
@@ -65,14 +65,16 @@ import GHC.Utils.Exception
import Control.Monad ( unless )
import Data.Maybe ( mapMaybe )
import Unsafe.Coerce ( unsafeCoerce )
-import GHC.Unit.Types (ModuleNameWithIsBoot)
+import GHC.Linker.Types
+import GHC.Types.Unique.DFM
+import Data.List (unzip4)
-- | Loads the plugins specified in the pluginModNames field of the dynamic
-- 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 -> Maybe ModuleNameWithIsBoot -> IO HscEnv
-initializePlugins hsc_env mnwib
+initializePlugins :: HscEnv -> IO HscEnv
+initializePlugins hsc_env
-- plugins not changed
| loaded_plugins <- loadedPlugins (hsc_plugins hsc_env)
, map lpModuleName loaded_plugins == reverse (pluginModNames dflags)
@@ -80,8 +82,8 @@ initializePlugins hsc_env mnwib
, all same_args loaded_plugins
= return hsc_env -- no need to reload plugins FIXME: doesn't take static plugins into account
| otherwise
- = do loaded_plugins <- loadPlugins hsc_env mnwib
- let plugins' = (hsc_plugins hsc_env) { loadedPlugins = loaded_plugins }
+ = do (loaded_plugins, links, pkgs) <- loadPlugins hsc_env
+ let plugins' = (hsc_plugins hsc_env) { loadedPlugins = loaded_plugins, loadedPluginDeps = (links, pkgs) }
let hsc_env' = hsc_env { hsc_plugins = plugins' }
withPlugins (hsc_plugins hsc_env') driverPlugin hsc_env'
where
@@ -90,12 +92,14 @@ initializePlugins hsc_env mnwib
argumentsForPlugin p = map snd . filter ((== lpModuleName p) . fst)
dflags = hsc_dflags hsc_env
-loadPlugins :: HscEnv -> Maybe ModuleNameWithIsBoot -> IO [LoadedPlugin]
-loadPlugins hsc_env mnwib
+loadPlugins :: HscEnv -> IO ([LoadedPlugin], [Linkable], PkgsLoaded)
+loadPlugins hsc_env
= do { unless (null to_load) $
checkExternalInterpreter hsc_env
- ; plugins <- mapM loadPlugin to_load
- ; return $ zipWith attachOptions to_load plugins }
+ ; plugins_with_deps <- mapM loadPlugin to_load
+ ; let (plugins, ifaces, links, pkgs) = unzip4 plugins_with_deps
+ ; return (zipWith attachOptions to_load (zip plugins ifaces), concat links, foldl' plusUDFM emptyUDFM pkgs)
+ }
where
dflags = hsc_dflags hsc_env
to_load = reverse $ pluginModNames dflags
@@ -105,14 +109,16 @@ loadPlugins hsc_env mnwib
where
options = [ option | (opt_mod_nm, option) <- pluginModNameOpts dflags
, opt_mod_nm == mod_nm ]
- loadPlugin = loadPlugin' (mkVarOcc "plugin") pluginTyConName hsc_env mnwib
+ loadPlugin = loadPlugin' (mkVarOcc "plugin") pluginTyConName hsc_env
-loadFrontendPlugin :: HscEnv -> ModuleName -> IO FrontendPlugin
+loadFrontendPlugin :: HscEnv -> ModuleName -> IO (FrontendPlugin, [Linkable], PkgsLoaded)
loadFrontendPlugin hsc_env mod_name = do
checkExternalInterpreter hsc_env
- fst <$> loadPlugin' (mkVarOcc "frontendPlugin") frontendPluginTyConName
- hsc_env Nothing mod_name
+ (plugin, _iface, links, pkgs)
+ <- loadPlugin' (mkVarOcc "frontendPlugin") frontendPluginTyConName
+ hsc_env mod_name
+ return (plugin, links, pkgs)
-- #14335
checkExternalInterpreter :: HscEnv -> IO ()
@@ -121,8 +127,8 @@ checkExternalInterpreter hsc_env = case interpInstance <$> hsc_interp hsc_env of
-> throwIO (InstallationError "Plugins require -fno-external-interpreter")
_ -> pure ()
-loadPlugin' :: OccName -> Name -> HscEnv -> Maybe ModuleNameWithIsBoot -> ModuleName -> IO (a, ModIface)
-loadPlugin' occ_name plugin_name hsc_env mnwib mod_name
+loadPlugin' :: OccName -> Name -> HscEnv -> ModuleName -> IO (a, ModIface, [Linkable], PkgsLoaded)
+loadPlugin' occ_name plugin_name hsc_env mod_name
= do { let plugin_rdr_name = mkRdrQual mod_name occ_name
dflags = hsc_dflags hsc_env
; mb_name <- lookupRdrNameInModuleForPlugins hsc_env mod_name
@@ -136,7 +142,7 @@ loadPlugin' occ_name plugin_name hsc_env mnwib mod_name
Just (name, mod_iface) ->
do { plugin_tycon <- forceLoadTyCon hsc_env plugin_name
- ; eith_plugin <- getValueSafely hsc_env mnwib name (mkTyConTy plugin_tycon)
+ ; eith_plugin <- getValueSafely hsc_env name (mkTyConTy plugin_tycon)
; case eith_plugin of
Left actual_type ->
throwGhcExceptionIO (CmdLineError $
@@ -147,7 +153,7 @@ loadPlugin' occ_name plugin_name hsc_env mnwib mod_name
, text "did not have the type"
, text "GHC.Plugins.Plugin"
, text "as required"])
- Right plugin -> return (plugin, mod_iface) } } }
+ Right (plugin, links, pkgs) -> return (plugin, mod_iface, links, pkgs) } } }
-- | Force the interfaces for the given modules to be loaded. The 'SDoc' parameter is used
@@ -192,23 +198,23 @@ forceLoadTyCon hsc_env con_name = do
-- * If the Name does not exist in the module
-- * If the link failed
-getValueSafely :: HscEnv -> Maybe ModuleNameWithIsBoot -> Name -> Type -> IO (Either Type a)
-getValueSafely hsc_env mnwib val_name expected_type = do
+getValueSafely :: HscEnv -> Name -> Type -> IO (Either Type (a, [Linkable], PkgsLoaded))
+getValueSafely hsc_env val_name expected_type = do
eith_hval <- case getValueSafelyHook hooks of
- Nothing -> getHValueSafely interp hsc_env mnwib val_name expected_type
- Just h -> h hsc_env mnwib val_name expected_type
+ Nothing -> getHValueSafely interp hsc_env val_name expected_type
+ Just h -> h hsc_env val_name expected_type
case eith_hval of
Left actual_type -> return (Left actual_type)
- Right hval -> do
+ Right (hval, links, pkgs) -> do
value <- lessUnsafeCoerce logger "getValueSafely" hval
- return (Right value)
+ return (Right (value, links, pkgs))
where
interp = hscInterp hsc_env
logger = hsc_logger hsc_env
hooks = hsc_hooks hsc_env
-getHValueSafely :: Interp -> HscEnv -> Maybe ModuleNameWithIsBoot -> Name -> Type -> IO (Either Type HValue)
-getHValueSafely interp hsc_env mnwib val_name expected_type = do
+getHValueSafely :: Interp -> HscEnv -> Name -> Type -> IO (Either Type (HValue, [Linkable], PkgsLoaded))
+getHValueSafely interp hsc_env val_name expected_type = do
forceLoadNameModuleInterface hsc_env (text "contains a name used in an invocation of getHValueSafely") val_name
-- Now look up the names for the value and type constructor in the type environment
mb_val_thing <- lookupType hsc_env val_name
@@ -221,13 +227,14 @@ getHValueSafely interp hsc_env mnwib val_name expected_type = do
then do
-- Link in the module that contains the value, if it has such a module
case nameModule_maybe val_name of
- Just mod -> do loadModule interp hsc_env mnwib mod
+ Just mod -> do loadModule interp hsc_env mod
return ()
Nothing -> return ()
-- Find the value that we just linked in and cast it given that we have proved it's type
hval <- do
- v <- loadName interp hsc_env mnwib val_name
- wormhole interp v
+ (v, links, pkgs) <- loadName interp hsc_env val_name
+ hv <- wormhole interp v
+ return (hv, links, pkgs)
return (Right hval)
else return (Left (idType id))
Just val_thing -> throwCmdLineErrorS dflags $ wrongTyThingError val_name val_thing
diff --git a/compiler/GHC/Tc/Gen/Splice.hs b/compiler/GHC/Tc/Gen/Splice.hs
index 7264f2232a..f2efb93f2d 100644
--- a/compiler/GHC/Tc/Gen/Splice.hs
+++ b/compiler/GHC/Tc/Gen/Splice.hs
@@ -1011,13 +1011,12 @@ runMeta' show_code ppr_hs run_and_convert expr
-- Compile and link it; might fail if linking fails
; src_span <- getSrcSpanM
- ; mnwib <- getMnwib
; traceTc "About to run (desugared)" (ppr ds_expr)
; either_hval <- tryM $ liftIO $
- GHC.Driver.Main.hscCompileCoreExpr hsc_env (src_span, Just mnwib) ds_expr
+ GHC.Driver.Main.hscCompileCoreExpr hsc_env src_span ds_expr
; case either_hval of {
Left exn -> fail_with_exn "compile and link" exn ;
- Right hval -> do
+ Right (hval, needed_mods, needed_pkgs) -> do
{ -- Coerce it to Q t, and run it
@@ -1031,6 +1030,7 @@ runMeta' show_code ppr_hs run_and_convert expr
--
-- See Note [Exceptions in TH]
let expr_span = getLocA expr
+ ; recordThNeededRuntimeDeps needed_mods needed_pkgs
; either_tval <- tryAllM $
setSrcSpan expr_span $ -- Set the span so that qLocation can
-- see where this splice is
diff --git a/compiler/GHC/Tc/Types.hs b/compiler/GHC/Tc/Types.hs
index e1f0400e44..b49bc718cd 100644
--- a/compiler/GHC/Tc/Types.hs
+++ b/compiler/GHC/Tc/Types.hs
@@ -175,6 +175,7 @@ import GHCi.RemoteTypes
import qualified Language.Haskell.TH as TH
import GHC.Driver.Env.KnotVars
+import GHC.Linker.Types
-- | A 'NameShape' is a substitution on 'Name's that can be used
-- to refine the identities of a hole while we are renaming interfaces
@@ -521,6 +522,10 @@ data TcGblEnv
--
-- Splices disable recompilation avoidance (see #481)
+ tcg_th_needed_deps :: TcRef ([Linkable], PkgsLoaded),
+ -- ^ The set of runtime dependencies required by this module
+ -- See Note [Object File Dependencies]
+
tcg_dfun_n :: TcRef OccSet,
-- ^ Allows us to choose unique DFun names.
diff --git a/compiler/GHC/Tc/Utils/Monad.hs b/compiler/GHC/Tc/Utils/Monad.hs
index ca2915e8fa..72670e6b06 100644
--- a/compiler/GHC/Tc/Utils/Monad.hs
+++ b/compiler/GHC/Tc/Utils/Monad.hs
@@ -58,7 +58,7 @@ module GHC.Tc.Utils.Monad(
getRdrEnvs, getImports,
getFixityEnv, extendFixityEnv, getRecFieldEnv,
getDeclaredDefaultTys,
- addDependentFiles, getMnwib,
+ addDependentFiles,
-- * Error management
getSrcSpanM, setSrcSpan, setSrcSpanA, addLocM, addLocMA, inGeneratedCode,
@@ -116,7 +116,7 @@ module GHC.Tc.Utils.Monad(
emitNamedTypeHole, IsExtraConstraint(..), emitAnonTypeHole,
-- * Template Haskell context
- recordThUse, recordThSpliceUse,
+ recordThUse, recordThSpliceUse, recordThNeededRuntimeDeps,
keepAlive, getStage, getStageAndBindLevel, setStage,
addModFinalizersWithLclEnv,
@@ -222,6 +222,8 @@ import {-# SOURCE #-} GHC.Tc.Utils.Env ( tcInitTidyEnv )
import qualified Data.Map as Map
import GHC.Driver.Env.KnotVars
+import GHC.Linker.Types
+import GHC.Types.Unique.DFM
{-
************************************************************************
@@ -263,6 +265,7 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
th_state_var <- newIORef Map.empty ;
th_remote_state_var <- newIORef Nothing ;
th_docs_var <- newIORef Map.empty ;
+ th_needed_deps_var <- newIORef ([], emptyUDFM) ;
next_wrapper_num <- newIORef emptyModuleEnv ;
let {
-- bangs to avoid leaking the env (#19356)
@@ -311,6 +314,7 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
tcg_ann_env = emptyAnnEnv,
tcg_th_used = th_var,
tcg_th_splice_used = th_splice_var,
+ tcg_th_needed_deps = th_needed_deps_var,
tcg_exports = [],
tcg_imports = emptyImportAvails,
tcg_used_gres = used_gre_var,
@@ -963,11 +967,6 @@ getSrcSpanM :: TcRn SrcSpan
-- Avoid clash with Name.getSrcLoc
getSrcSpanM = do { env <- getLclEnv; return (RealSrcSpan (tcl_loc env) Strict.Nothing) }
-getMnwib :: TcRn ModuleNameWithIsBoot
-getMnwib = do
- gbl_env <- getGblEnv
- return $ GWIB (moduleName $ tcg_mod gbl_env) (hscSourceToIsBoot (tcg_src gbl_env))
-
-- See Note [Error contexts in generated code]
inGeneratedCode :: TcRn Bool
inGeneratedCode = tcl_in_gen_code <$> getLclEnv
@@ -2010,6 +2009,15 @@ recordThUse = do { env <- getGblEnv; writeTcRef (tcg_th_used env) True }
recordThSpliceUse :: TcM ()
recordThSpliceUse = do { env <- getGblEnv; writeTcRef (tcg_th_splice_used env) True }
+recordThNeededRuntimeDeps :: [Linkable] -> PkgsLoaded -> TcM ()
+recordThNeededRuntimeDeps new_links new_pkgs
+ = do { env <- getGblEnv
+ ; updTcRef (tcg_th_needed_deps env) $ \(needed_links, needed_pkgs) ->
+ let links = new_links ++ needed_links
+ !pkgs = plusUDFM needed_pkgs new_pkgs
+ in (links, pkgs)
+ }
+
keepAlive :: Name -> TcRn () -- Record the name in the keep-alive set
keepAlive name
= do { env <- getGblEnv
diff --git a/compiler/GHC/Types/Basic.hs b/compiler/GHC/Types/Basic.hs
index 2ba185c25e..ccb660e130 100644
--- a/compiler/GHC/Types/Basic.hs
+++ b/compiler/GHC/Types/Basic.hs
@@ -1211,6 +1211,12 @@ instance Outputable (DefMethSpec ty) where
data SuccessFlag = Succeeded | Failed
+instance Semigroup SuccessFlag where
+ Failed <> _ = Failed
+ _ <> Failed = Failed
+ _ <> _ = Succeeded
+
+
instance Outputable SuccessFlag where
ppr Succeeded = text "Succeeded"
ppr Failed = text "Failed"
diff --git a/compiler/GHC/Types/Unique/DFM.hs b/compiler/GHC/Types/Unique/DFM.hs
index 4b0f5e545d..c8e63d333a 100644
--- a/compiler/GHC/Types/Unique/DFM.hs
+++ b/compiler/GHC/Types/Unique/DFM.hs
@@ -57,6 +57,7 @@ module GHC.Types.Unique.DFM (
listToUDFM, listToUDFM_Directly,
udfmMinusUFM, ufmMinusUDFM,
partitionUDFM,
+ udfmRestrictKeys,
anyUDFM, allUDFM,
pprUniqDFM, pprUDFM,
@@ -310,6 +311,9 @@ filterUDFM_Directly p (UDFM m i) = UDFM (M.filterWithKey p' m) i
where
p' k (TaggedVal v _) = p (getUnique k) v
+udfmRestrictKeys :: UniqDFM key elt -> UniqDFM key elt2 -> UniqDFM key elt
+udfmRestrictKeys (UDFM a i) (UDFM b _) = UDFM (M.restrictKeys a (M.keysSet b)) i
+
-- | Converts `UniqDFM` to a list, with elements in deterministic order.
-- It's O(n log n) while the corresponding function on `UniqFM` is O(n).
udfmToList :: UniqDFM key elt -> [(Unique, elt)]
diff --git a/compiler/GHC/Unit/Module/Deps.hs b/compiler/GHC/Unit/Module/Deps.hs
index 0bab72763b..0da8a06979 100644
--- a/compiler/GHC/Unit/Module/Deps.hs
+++ b/compiler/GHC/Unit/Module/Deps.hs
@@ -52,10 +52,13 @@ import Data.Bifunctor
data Dependencies = Deps
{ dep_direct_mods :: Set (UnitId, ModuleNameWithIsBoot)
-- ^ All home-package modules which are directly imported by this one.
+ -- This may include modules from other units when using multiple home units
, dep_direct_pkgs :: Set UnitId
-- ^ All packages directly imported by this module
-- I.e. packages to which this module's direct imports belong.
+ -- Does not include other home units when using multiple home units.
+ -- Modules from these units will go in `dep_direct_mods`
, dep_plugin_pkgs :: Set UnitId
-- ^ All units needed for plugins
diff --git a/compiler/GHC/Unit/Module/Env.hs b/compiler/GHC/Unit/Module/Env.hs
index a69c865aef..0c8016e17e 100644
--- a/compiler/GHC/Unit/Module/Env.hs
+++ b/compiler/GHC/Unit/Module/Env.hs
@@ -7,6 +7,7 @@ module GHC.Unit.Module.Env
, extendModuleEnvList_C, plusModuleEnv_C
, delModuleEnvList, delModuleEnv, plusModuleEnv, lookupModuleEnv
, lookupWithDefaultModuleEnv, mapModuleEnv, mkModuleEnv, emptyModuleEnv
+ , partitionModuleEnv
, moduleEnvKeys, moduleEnvElts, moduleEnvToList
, unitModuleEnv, isEmptyModuleEnv
, extendModuleEnvWith, filterModuleEnv
@@ -19,7 +20,8 @@ module GHC.Unit.Module.Env
, emptyModuleSet, mkModuleSet, moduleSetElts
, extendModuleSet, extendModuleSetList, delModuleSet
, elemModuleSet, intersectModuleSet, minusModuleSet, unionModuleSet
- , unitModuleSet
+ , unitModuleSet, isEmptyModuleSet
+ , unionManyModuleSets
-- * InstalledModuleEnv
, InstalledModuleEnv
@@ -56,6 +58,9 @@ import GHC.Utils.Outputable
-- | A map keyed off of 'Module's
newtype ModuleEnv elt = ModuleEnv (Map NDModule elt)
+instance Outputable a => Outputable (ModuleEnv a) where
+ ppr (ModuleEnv m) = ppr m
+
{-
Note [ModuleEnv performance and determinism]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -76,6 +81,9 @@ newtype NDModule = NDModule { unNDModule :: Module }
deriving Eq
-- A wrapper for Module with faster nondeterministic Ord.
-- Don't export, See [ModuleEnv performance and determinism]
+ --
+instance Outputable NDModule where
+ ppr (NDModule a) = ppr a
instance Ord NDModule where
compare (NDModule (Module p1 n1)) (NDModule (Module p2 n2)) =
@@ -130,6 +138,11 @@ lookupWithDefaultModuleEnv (ModuleEnv e) x m =
mapModuleEnv :: (a -> b) -> ModuleEnv a -> ModuleEnv b
mapModuleEnv f (ModuleEnv e) = ModuleEnv (Map.mapWithKey (\_ v -> f v) e)
+partitionModuleEnv :: (a -> Bool) -> ModuleEnv a -> (ModuleEnv a, ModuleEnv a)
+partitionModuleEnv f (ModuleEnv e) = (ModuleEnv a, ModuleEnv b)
+ where
+ (a,b) = Map.partition f e
+
mkModuleEnv :: [(Module, a)] -> ModuleEnv a
mkModuleEnv xs = ModuleEnv (Map.fromList [(NDModule k, v) | (k,v) <- xs])
@@ -170,6 +183,9 @@ extendModuleSetList s ms = foldl' (coerce . flip Set.insert) s ms
emptyModuleSet :: ModuleSet
emptyModuleSet = Set.empty
+isEmptyModuleSet :: ModuleSet -> Bool
+isEmptyModuleSet = Set.null
+
moduleSetElts :: ModuleSet -> [Module]
moduleSetElts = sort . coerce . Set.toList
@@ -188,6 +204,9 @@ delModuleSet = coerce (flip Set.delete)
unionModuleSet :: ModuleSet -> ModuleSet -> ModuleSet
unionModuleSet = coerce Set.union
+unionManyModuleSets :: [ModuleSet] -> ModuleSet
+unionManyModuleSets = coerce (Set.unions :: [Set NDModule] -> Set NDModule)
+
unitModuleSet :: Module -> ModuleSet
unitModuleSet = coerce Set.singleton
diff --git a/docs/users_guide/separate_compilation.rst b/docs/users_guide/separate_compilation.rst
index ec33cddcd4..c43dcc1f2c 100644
--- a/docs/users_guide/separate_compilation.rst
+++ b/docs/users_guide/separate_compilation.rst
@@ -705,6 +705,24 @@ beautiful sight!
You can read about :ghc-wiki:`how all this works <commentary/compiler/recompilation-avoidance>` in the GHC commentary.
+Recompilation for Template Haskell and Plugins
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Recompilation checking gets a bit more complicated when using Template Haskell or
+plugins. Both these features execute code at compile time and so if any of the
+executed code changes then it's necessary to recompile the module. Consider the
+top-level splice::
+
+ main = $(foo bar [| () |])
+
+When the module is compiled ``foo bar [| () |]`` will be evaluated and the resulting
+code placed into the program. The dependencies of the expression are calculated
+and stored during module compilation. When the interface file is written, additional
+dependencies are created on the object file dependencies of the expression. For instance,
+if ``foo`` is from module ``A`` and ``bar`` is from module ``B``, the module will
+now depend on ``A.o`` and ``B.o``, if either of these change then the module will
+be recompiled.
+
.. _mutual-recursion:
How to compile mutually recursive modules
diff --git a/ghc/Main.hs b/ghc/Main.hs
index cb701e24e2..3cb71b77e8 100644
--- a/ghc/Main.hs
+++ b/ghc/Main.hs
@@ -1056,7 +1056,7 @@ dumpUnitsSimple hsc_env = putMsg (hsc_logger hsc_env) (pprUnitsSimple (hsc_units
doFrontend :: ModuleName -> [(String, Maybe Phase)] -> Ghc ()
doFrontend modname srcs = do
hsc_env <- getSession
- frontend_plugin <- liftIO $ loadFrontendPlugin hsc_env modname
+ (frontend_plugin, _pkgs, _deps) <- liftIO $ loadFrontendPlugin hsc_env modname -- TODO do these need to recorded?
frontend frontend_plugin
(reverse $ frontendPluginOpts (hsc_dflags hsc_env)) srcs
diff --git a/testsuite/tests/driver/T20604/A.hs b/testsuite/tests/driver/T20604/A.hs
new file mode 100644
index 0000000000..60c4445820
--- /dev/null
+++ b/testsuite/tests/driver/T20604/A.hs
@@ -0,0 +1,4 @@
+{-# LANGUAGE TemplateHaskell #-}
+module A where
+
+x = $([| True |])
diff --git a/testsuite/tests/driver/T20604/A1.hs b/testsuite/tests/driver/T20604/A1.hs
new file mode 100644
index 0000000000..24009b444c
--- /dev/null
+++ b/testsuite/tests/driver/T20604/A1.hs
@@ -0,0 +1,3 @@
+module A1 where
+
+import A
diff --git a/testsuite/tests/driver/T20604/Makefile b/testsuite/tests/driver/T20604/Makefile
new file mode 100644
index 0000000000..53c3446611
--- /dev/null
+++ b/testsuite/tests/driver/T20604/Makefile
@@ -0,0 +1,16 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+clean:
+ rm -f *.o
+ rm -f *.hi
+ rm -f *.dyn_o
+ rm -f *.dyn_hi
+
+T20604: clean
+ '$(TEST_HC)' $(TEST_HC_OPTS) A1 -v0
+ echo "A1"
+ '$(TEST_HC)' $(TEST_HC_OPTS) --show-iface A1.hi | grep addDependentFile || :
+ echo "A"
+ '$(TEST_HC)' $(TEST_HC_OPTS) --show-iface A.hi | grep addDependentFile || :
diff --git a/testsuite/tests/driver/T20604/T20604.stdout b/testsuite/tests/driver/T20604/T20604.stdout
new file mode 100644
index 0000000000..864f215cb3
--- /dev/null
+++ b/testsuite/tests/driver/T20604/T20604.stdout
@@ -0,0 +1,11 @@
+A1
+A
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSghc-prim-0.8.0-ghc9.3.20220210.so" 9f60cabb62a3305daa26440cde34be2b
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSghc-bignum-1.3-ghc9.3.20220210.so" 5da53b9a870ae800132527a10de182ef
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSbase-4.16.0.0-ghc9.3.20220210.so" ed1808d3e2f7368d81345094adfea50f
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSfilepath-1.4.2.1-ghc9.3.20220210.so" 08b9514f12122be71c467bddf9edf99b
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSghc-boot-th-9.3-ghc9.3.20220210.so" 165417fb87110cd768173641e96854d7
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSarray-0.5.4.0-ghc9.3.20220210.so" 267bb3b9d1d59920b08f6d81ddb1f5f4
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSdeepseq-1.4.7.0-ghc9.3.20220210.so" 4d4f1682b257ae7674e1dc6b84f1f8ae
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHSpretty-1.1.3.6-ghc9.3.20220210.so" dd0fddaebe5d06357fe1748efefd1705
+addDependentFile "/home/matt/ghc-numa/_build/stage1/lib/../lib/x86_64-linux-ghc-9.3.20220210/libHStemplate-haskell-2.18.0.0-ghc9.3.20220210.so" 72cdf17fd485c6bb49e684730a0a55b6
diff --git a/testsuite/tests/driver/T20604/all.T b/testsuite/tests/driver/T20604/all.T
new file mode 100644
index 0000000000..290da3ec53
--- /dev/null
+++ b/testsuite/tests/driver/T20604/all.T
@@ -0,0 +1,13 @@
+def normalise_paths(s):
+ res = []
+ for line in s.splitlines():
+ lib_name = re.search(r'(HS[a-z-]+)-', line)
+ if lib_name:
+ res.append(lib_name.group(0))
+ else:
+ res.append(line)
+ return '\n'.join(res)
+
+
+test('T20604', [extra_files(['A.hs', 'A1.hs'])
+ , normalise_fun(normalise_paths)], makefile_test, [])