diff options
author | Matthew Pickering <matthewtpickering@gmail.com> | 2021-05-05 09:52:13 +0100 |
---|---|---|
committer | Matthew Pickering <matthewtpickering@gmail.com> | 2021-05-05 09:52:13 +0100 |
commit | 56f2f6789448cea7108f05a60c839763416b8521 (patch) | |
tree | fff1feee40356ec7640e08e1f3740fc2dbca3097 | |
parent | f0f6857bd7e3ce3c77e67d36448c3d19df8c722b (diff) | |
download | haskell-56f2f6789448cea7108f05a60c839763416b8521.tar.gz |
wip: unify -c --make
-rw-r--r-- | compiler/GHC/Driver/Dependencies.hs | 15 | ||||
-rw-r--r-- | compiler/GHC/Driver/Env.hs | 5 | ||||
-rw-r--r-- | compiler/GHC/Driver/Main.hs | 86 | ||||
-rw-r--r-- | compiler/GHC/Driver/Make.hs | 5 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline.hs | 41 | ||||
-rw-r--r-- | compiler/GHC/Iface/Load.hs | 3 | ||||
-rw-r--r-- | compiler/GHC/Iface/Recomp.hs | 2 | ||||
-rw-r--r-- | compiler/GHC/IfaceToCore.hs | 113 | ||||
-rw-r--r-- | compiler/GHC/Rename/Names.hs | 1 | ||||
-rw-r--r-- | compiler/GHC/Tc/Module.hs | 13 | ||||
-rw-r--r-- | compiler/GHC/Unit/External.hs | 16 | ||||
-rw-r--r-- | compiler/GHC/Unit/Home/ModInfo.hs | 2 |
12 files changed, 159 insertions, 143 deletions
diff --git a/compiler/GHC/Driver/Dependencies.hs b/compiler/GHC/Driver/Dependencies.hs index bbfc22730a..a913f00ba0 100644 --- a/compiler/GHC/Driver/Dependencies.hs +++ b/compiler/GHC/Driver/Dependencies.hs @@ -51,7 +51,7 @@ import GHC.Types.Unique.DSet import qualified GHC.Data.Maybe as Maybes import GHC.Linker.Types import GHC.Data.List.SetOps -import Data.List ( isSuffixOf, nub, isPrefixOf, intercalate ) +import Data.List ( isSuffixOf, nub, isPrefixOf, intercalate, sortBy ) import GHC.Runtime.Interpreter import qualified GHC.Data.ShortText as ST import qualified GHC.Unit as Packages @@ -68,15 +68,16 @@ import GHC.Platform import Data.IORef import GHC.Platform.Ways import GHC.Driver.Phases +import Data.Ord -- | Used in OneShot mode to work out what the HPT looks like -findHomeModules :: HscEnv -> HomeUnit -> [ModuleNameWithIsBoot] -> IO (ModuleNameEnv ModuleNameWithIsBoot) +findHomeModules :: HscEnv -> HomeUnit -> [ModuleNameWithIsBoot] -> IO (ModuleNameEnv (ModIface, ModuleNameWithIsBoot)) findHomeModules hsc_env home_unit mns = go mns mempty where go [] seen = return seen go (mn:mods) seen - | Just mn' <- lookupUFM seen (gwib_mod mn) + | Just (_, mn') <- lookupUFM seen (gwib_mod mn) -- Already seen the module before , gwib_isBoot mn' == gwib_isBoot mn = go mods seen | otherwise = do @@ -86,7 +87,7 @@ findHomeModules hsc_env home_unit mns = go mns mempty mb_iface <- computeInterface hsc_env (text "need mi_direct_deps for") (gwib_isBoot mn) mod -- loadInterface should work but if you load A.hi-boot before trying to load A.hi, then the A.hi-boot -- interface will be returned, which is not what you want. - --mb_iface <- loadInterface (text "need mi_direct_deps for") mod (ImportByUser (gwib_isBoot mn)) +-- mb_iface <- loadInterface (text "need mi_direct_deps for") mod (ImportByUser (gwib_isBoot mn)) case mb_iface of -- This case should **never** happen because there has to already be -- interface files for all the dependencies already in OneShot mode. @@ -94,10 +95,10 @@ findHomeModules hsc_env home_unit mns = go mns mempty Failed _err -> go mods seen Succeeded (imported_iface, _) -> let new_names = dep_direct_mods $ mi_deps imported_iface - comb m@(GWIB { gwib_isBoot = NotBoot }) _ = m - comb (GWIB { gwib_isBoot = IsBoot }) x = x + comb m@(_, (GWIB { gwib_isBoot = NotBoot })) _ = m + comb (_, (GWIB { gwib_isBoot = IsBoot })) x = x in go (new_names ++ mods) - (addToUFM_C comb seen (gwib_mod mn) mn) + (addToUFM_C comb seen (gwib_mod mn) (imported_iface, mn)) -- | Find all depedencies that we need to link, used for GHCi and -- interface file dependency calculation diff --git a/compiler/GHC/Driver/Env.hs b/compiler/GHC/Driver/Env.hs index 42abd7aab2..a92491e8e7 100644 --- a/compiler/GHC/Driver/Env.hs +++ b/compiler/GHC/Driver/Env.hs @@ -236,9 +236,6 @@ hptModulesBelow include_boot hsc_env mn = go mn Set.empty -- C.f Inst.hptInstances hptSomeThingsBelowUs :: (HomeModInfo -> [a]) -> Bool -> HscEnv -> [ModuleNameWithIsBoot] -> [a] hptSomeThingsBelowUs extract include_hi_boot hsc_env mod - | isOneShot (ghcMode (hsc_dflags hsc_env)) = [] - - | otherwise = let hpt = hsc_HPT hsc_env in [ thing @@ -297,7 +294,7 @@ lookupType hsc_env name = do then mkHomeModule (hsc_home_unit hsc_env) (moduleName (nameModule name)) else nameModule name - !ty = if isOneShot (ghcMode (hsc_dflags hsc_env)) + !ty = if False -- isOneShot (ghcMode (hsc_dflags hsc_env)) -- in one-shot, we don't use the HPT then lookupNameEnv pte name else case lookupHptByModule hpt mod of diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs index a6b3b2f7c3..75a8353a43 100644 --- a/compiler/GHC/Driver/Main.hs +++ b/compiler/GHC/Driver/Main.hs @@ -83,7 +83,6 @@ module GHC.Driver.Main , getHscEnv , hscSimpleIface' , oneShotMsg - , dumpIfaceStats , ioMsgMaybe , showModuleIndex , hscAddSptEntries @@ -118,7 +117,7 @@ import GHC.HsToCore import GHC.StgToByteCode ( byteCodeGen, stgExprToBCOs ) -import GHC.IfaceToCore ( typecheckIface ) +import GHC.IfaceToCore ( typecheckIface, initModDetails, dumpIfaceStats ) import GHC.Iface.Load ( ifaceStats, writeIface ) import GHC.Iface.Make @@ -881,7 +880,7 @@ hscIncrementalCompile always_do_basic_recompilation_check m_tc_result -- We didn't need to do any typechecking; the old interface -- file on disk was good enough. Left (iface, linkable) -> do - details <- liftIO $ initModDetails hsc_env mod_summary iface + details <- liftIO $ initModDetails hsc_env (ms_mod_name mod_summary) iface return (HscUpToDate (HomeModInfo iface details linkable), hsc_env') -- We finished type checking. (mb_old_hash is the hash of -- the interface that existed on disk; it's possible we had @@ -891,64 +890,8 @@ hscIncrementalCompile always_do_basic_recompilation_check m_tc_result status <- finish mod_summary tc_result mb_old_hash return (status, hsc_env) --- Knot tying! See Note [Knot-tying typecheckIface] --- See Note [ModDetails and --make mode] -initModDetails :: HscEnv -> ModSummary -> ModIface -> IO ModDetails -initModDetails hsc_env mod_summary iface = - fixIO $ \details' -> do - let act hpt = addToHpt hpt (ms_mod_name mod_summary) - (HomeModInfo iface details' Nothing) - let hsc_env' = hscUpdateHPT act hsc_env - -- NB: This result is actually not that useful - -- in one-shot mode, since we're not going to do - -- any further typechecking. It's much more useful - -- in make mode, since this HMI will go into the HPT. - genModDetails hsc_env' iface -{- -Note [ModDetails and --make mode] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An interface file consists of two parts - -* The `ModIface` which ends up getting written to disk. - The `ModIface` is a completely acyclic tree, which can be serialised - and de-serialised completely straightforwardly. The `ModIface` is - also the structure that is finger-printed for recompilation control. - -* The `ModDetails` which provides a more structured view that is suitable - for usage during compilation. The `ModDetails` is heavily cyclic: - An `Id` contains a `Type`, which mentions a `TyCon` that contains kind - that mentions other `TyCons`; the `Id` also includes an unfolding that - in turn mentions more `Id`s; And so on. - -The `ModIface` can be created from the `ModDetails` and the `ModDetails` from -a `ModIface`. - -During tidying, just before interfaces are written to disk, -the ModDetails is calculated and then converted into a ModIface (see GHC.Iface.Make.mkIface_). -Then when GHC needs to restart typechecking from a certain point it can read the -interface file, and regenerate the ModDetails from the ModIface (see GHC.IfaceToCore.typecheckIface). -The key part about the loading is that the ModDetails is regenerated lazily -from the ModIface, so that there's only a detailed in-memory representation -for declarations which are actually used from the interface. This mode is -also used when reading interface files from external packages. - -In the old --make mode implementation, the interface was written after compiling a module -but the in-memory ModDetails which was used to compute the ModIface was retained. -The result was that --make mode used much more memory than `-c` mode, because a large amount of -information about a module would be kept in the ModDetails but never used. - -The new idea is that even in `--make` mode, when there is an in-memory `ModDetails` -at hand, we re-create the `ModDetails` from the `ModIface`. Doing this means that -we only have to keep the `ModIface` decls in memory and then lazily load -detailed representations if needed. It turns out this makes a really big difference -to memory usage, halving maximum memory used in some cases. - -See !5492 and #13586 --} - -- 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 -- and good unfoldings and other info in the interface file. @@ -1155,14 +1098,6 @@ hscMaybeWriteIface logger dflags is_simple iface old_iface mod_location = do -- NoRecomp handlers -------------------------------------------------------------- --- NB: this must be knot-tied appropriately, see hscIncrementalCompile -genModDetails :: HscEnv -> ModIface -> IO ModDetails -genModDetails hsc_env old_iface - = do - new_details <- {-# SCC "tcRnIface" #-} - initIfaceLoad hsc_env (typecheckIface old_iface) - dumpIfaceStats hsc_env - return new_details -------------------------------------------------------------- -- Progress displayers. @@ -2273,23 +2208,6 @@ hscCompileCoreExpr' hsc_env srcspan ds_expr ; loadExpr (hscInterp hsc_env) hsc_env srcspan bcos } -{- ********************************************************************** -%* * - Statistics on reading interfaces -%* * -%********************************************************************* -} - -dumpIfaceStats :: HscEnv -> IO () -dumpIfaceStats hsc_env = do - eps <- hscEPS hsc_env - dumpIfSet logger dflags (dump_if_trace || dump_rn_stats) - "Interface statistics" - (ifaceStats eps) - where - dflags = hsc_dflags hsc_env - logger = hsc_logger hsc_env - dump_rn_stats = dopt Opt_D_dump_rn_stats dflags - dump_if_trace = dopt Opt_D_dump_if_trace dflags {- ********************************************************************** diff --git a/compiler/GHC/Driver/Make.hs b/compiler/GHC/Driver/Make.hs index 24ffdc1635..1f564cbf17 100644 --- a/compiler/GHC/Driver/Make.hs +++ b/compiler/GHC/Driver/Make.hs @@ -135,6 +135,7 @@ import System.FilePath import System.IO ( fixIO ) import GHC.Conc ( getNumProcessors, getNumCapabilities, setNumCapabilities ) +import GHC.Driver.Ppr label_self :: String -> IO () label_self thread_name = do @@ -937,6 +938,10 @@ buildCompGraph (scc:sccs) = case scc of data BuildModule = BuildModule_Unit {-# UNPACK #-} !InstantiatedUnit | BuildModule_Module {-# UNPACK #-} !ModuleWithIsBoot deriving (Eq, Ord) +instance Outputable BuildModule where + ppr (BuildModule_Unit u) = ppr u + ppr (BuildModule_Module m) = ppr m + mkBuildModule :: ModuleGraphNode -> BuildModule mkBuildModule = \case diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 08fb0c9889..8f8300e2ea 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -16,6 +16,7 @@ -- ----------------------------------------------------------------------------- +{-# LANGUAGE TupleSections #-} module GHC.Driver.Pipeline ( -- Run a series of compilation steps in a pipeline, for a -- collection of source files. @@ -83,7 +84,7 @@ import qualified GHC.LanguageExtensions as LangExt import GHC.Settings import GHC.Data.Bag ( unitBag ) -import GHC.Data.FastString ( mkFastString ) +import GHC.Data.FastString ( mkFastString, FastString ) import GHC.Data.StringBuffer ( hGetStringBuffer, hPutStringBuffer ) import GHC.Data.Maybe ( expectJust ) @@ -117,6 +118,8 @@ import Data.Version import Data.Either ( partitionEithers ) import Data.Time ( getCurrentTime ) +import GHC.Driver.Dependencies +import GHC.Types.Unique.FM -- --------------------------------------------------------------------------- -- Pre-process @@ -265,7 +268,7 @@ compileOne' m_tc_result mHscMessage final_iface <- mkFullIface hsc_env' partial_iface Nothing -- Reconstruct the `ModDetails` from the just-constructed `ModIface` -- See Note [ModDetails and --make mode] - hmi_details <- liftIO $ initModDetails hsc_env' summary final_iface + hmi_details <- liftIO $ initModDetails hsc_env' (ms_mod_name summary) final_iface liftIO $ hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash (ms_location summary) (hasStub, comp_bc, spt_entries) <- hscInteractive hsc_env' cgguts mod_location @@ -297,7 +300,7 @@ compileOne' m_tc_result mHscMessage o_time <- getModificationUTCTime object_filename let !linkable = LM o_time this_mod [DotO object_filename] -- See Note [ModDetails and --make mode] - details <- initModDetails hsc_env' summary iface + details <- initModDetails hsc_env' (ms_mod_name summary) iface return $! HomeModInfo iface details (Just linkable) where dflags0 = ms_hspp_opts summary @@ -1083,6 +1086,30 @@ llvmOptions dflags = ArchRISCV64 -> "lp64d" _ -> "" +-- | Populate the HPT with the transively discovered interfaces from the imports +-- given +preloadInterfaces :: HscEnv -> [(Maybe FastString, ModuleNameWithIsBoot)] -> IO (HscEnv -> HscEnv) +preloadInterfaces hsc_env imps = do + let fc = hsc_FC hsc_env + let dflags = hsc_dflags hsc_env + let units = hsc_units hsc_env + let home_unit = hsc_home_unit hsc_env + let find mod maybe_pkg = liftIO $ findImportedModule fc units home_unit dflags mod maybe_pkg + let do_one (mb_pkg, GWIB mod_name is_boot) = do + res <- find mod_name mb_pkg + case res of + Found _ m | isHomeModule home_unit m -> return $ Just $ GWIB mod_name is_boot + _ -> return Nothing + mk_hmi mod_iface mod_name = do + (gwib_mod mod_name,) . (\md -> HomeModInfo mod_iface md Nothing) <$> initModDetails hsc_env (gwib_mod mod_name) mod_iface + direct_home_mods <- mapMaybeM do_one imps + interfaces <- liftIO $ findHomeModules hsc_env (hsc_home_unit hsc_env) direct_home_mods + hmis <- liftIO $ mapM (uncurry mk_hmi) (eltsUFM interfaces) + let new_hpt = addListToHpt emptyHomePackageTable hmis + let upd = hscUpdateHPT (const new_hpt) + pprTraceM "preloadInterfaces" (ppr (fmap snd interfaces)) + return upd + -- ----------------------------------------------------------------------------- -- | Each phase in the pipeline returns the next phase to execute, and the -- name of the file in which the output was placed. @@ -1248,6 +1275,11 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn Right (src_imps,imps,L _ mod_name) -> return (Just buf, mod_name, imps, src_imps) + PipeState{hsc_env=hsc_env'} <- getPipeState + let mns = [(mb_pkg, GWIB (unLoc mn) NotBoot) | (mb_pkg, mn) <- imps] + ++ [(mb_pkg, GWIB (unLoc mn) IsBoot) | (mb_pkg, mn) <- src_imps] + upd_hsc <- liftIO $ preloadInterfaces hsc_env' mns + -- Take -o into account if present -- Very like -ohi, but we must *only* do this if we aren't linking -- (If we're linking then the -o applies to the linked thing, not to @@ -1265,7 +1297,6 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn o_mod <- liftIO $ modificationTimeIfExists o_file dyn_o_mod <- liftIO $ modificationTimeIfExists dyn_o_file - PipeState{hsc_env=hsc_env'} <- getPipeState -- Tell the finder cache about this module mod <- liftIO $ do @@ -1295,7 +1326,7 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn -- run the compiler! let msg hsc_env _ what _ = oneShotMsg hsc_env what (result, plugin_hsc_env) <- - liftIO $ hscIncrementalCompile True Nothing (Just msg) hsc_env' + liftIO $ hscIncrementalCompile True Nothing (Just msg) (upd_hsc hsc_env') mod_summary Nothing Nothing (1,1) -- In the rest of the pipeline use the loaded plugins diff --git a/compiler/GHC/Iface/Load.hs b/compiler/GHC/Iface/Load.hs index 03627641aa..7b8069abb6 100644 --- a/compiler/GHC/Iface/Load.hs +++ b/compiler/GHC/Iface/Load.hs @@ -940,8 +940,7 @@ findAndReadIface logger name_cache fc hooks unit_state home_unit dflags doc_str -- Found file, so read it let file_path = addBootSuffix_maybe hi_boot_file (ml_hi_file loc) -- See Note [Home module load error] - if isHomeInstalledModule home_unit mod && - not (isOneShot (ghcMode dflags)) + if isHomeInstalledModule home_unit mod then return (Failed (homeModError mod loc)) else do r <- read_file logger name_cache unit_state dflags wanted_mod file_path diff --git a/compiler/GHC/Iface/Recomp.hs b/compiler/GHC/Iface/Recomp.hs index 059619baa9..b2f3e00af9 100644 --- a/compiler/GHC/Iface/Recomp.hs +++ b/compiler/GHC/Iface/Recomp.hs @@ -282,7 +282,7 @@ checkVersions hsc_env mod_summary iface -- ; pprTraceM "home_mods" (ppr home_mods) -- ; pprTraceM "home_mods" (ppr (dep_mods (mi_deps iface))) -- ; pprTraceM "home_mods" (ppr (Set.difference (Set.fromList (eltsUFM home_mods)) (Set.fromList (dep_mods (mi_deps iface))))) - ; updateEps_ $ \eps -> eps { eps_is_boot = home_mods } + ; updateEps_ $ \eps -> eps { eps_is_boot = fmap snd $ home_mods } } ; recomp <- checkList [checkModUsage (homeUnitAsUnit home_unit) u | u <- mi_usages iface] diff --git a/compiler/GHC/IfaceToCore.hs b/compiler/GHC/IfaceToCore.hs index b3644633af..477ead3dfe 100644 --- a/compiler/GHC/IfaceToCore.hs +++ b/compiler/GHC/IfaceToCore.hs @@ -21,7 +21,8 @@ module GHC.IfaceToCore ( tcIfaceAnnotations, tcIfaceCompleteMatches, tcIfaceExpr, -- Desired by HERMIT (#7683) tcIfaceGlobal, - tcIfaceOneShot + tcIfaceOneShot, + initModDetails, genModDetails, dumpIfaceStats ) where #include "HsVersions.h" @@ -56,7 +57,7 @@ import GHC.Core import GHC.Core.Unify( RoughMatchTc(..) ) import GHC.Core.Utils import GHC.Core.Unfold.Make -import GHC.Core.Lint +import GHC.Core.Lint hiding (dumpIfSet) import GHC.Core.Make import GHC.Core.Class import GHC.Core.TyCon @@ -108,6 +109,7 @@ import qualified GHC.Data.BooleanFormula as BF import Control.Monad import GHC.Parser.Annotation +import System.IO {- This module takes @@ -558,27 +560,9 @@ tcHiBootIface hsc_src mod ; case read_result of { Succeeded (iface, _path) -> do { tc_iface <- initIfaceTcRn $ typecheckIface iface ; mkSelfBootInfo iface tc_iface } ; - Failed err -> - - -- There was no hi-boot file. But if there is circularity in - -- the module graph, there really should have been one. - -- Since we've read all the direct imports by now, - -- eps_is_boot will record if any of our imports mention the - -- current module, which either means a module loop (not - -- a SOURCE import) or that our hi-boot file has mysteriously - -- disappeared. - do { eps <- getEps - ; case lookupUFM (eps_is_boot eps) (moduleName mod) of - -- The typical case - Nothing -> return NoSelfBoot - -- error cases - Just (GWIB { gwib_isBoot = is_boot }) -> case is_boot of - IsBoot -> failWithTc (elaborate err) - -- The hi-boot file has mysteriously disappeared. - NotBoot -> failWithTc moduleLoop - -- Someone below us imported us! - -- This is a loop with no hi-boot in the way - }}}} + Failed err -> return NoSelfBoot } + + }} where need = text "Need the hi-boot interface for" <+> ppr mod <+> text "to compare against the Real Thing" @@ -613,6 +597,89 @@ mkSelfBootInfo iface mds isIfaceTyCon IfaceAxiom{} = False isIfaceTyCon IfacePatSyn{} = False +-- Knot tying! See Note [Knot-tying typecheckIface] +-- See Note [ModDetails and --make mode] +initModDetails :: HscEnv -> ModuleName -> ModIface -> IO ModDetails +initModDetails hsc_env mod_name iface = + fixIO $ \details' -> do + let act hpt = addToHpt hpt mod_name + (HomeModInfo iface details' Nothing) + let hsc_env' = hscUpdateHPT act hsc_env + -- NB: This result is actually not that useful + -- in one-shot mode, since we're not going to do + -- any further typechecking. It's much more useful + -- in make mode, since this HMI will go into the HPT. + genModDetails hsc_env' iface + +-- NB: this must be knot-tied appropriately, see hscIncrementalCompile +genModDetails :: HscEnv -> ModIface -> IO ModDetails +genModDetails hsc_env old_iface + = do + new_details <- {-# SCC "tcRnIface" #-} + initIfaceLoad hsc_env (typecheckIface old_iface) + dumpIfaceStats hsc_env + return new_details + +{- ********************************************************************** +%* * + Statistics on reading interfaces +%* * +%********************************************************************* -} + +dumpIfaceStats :: HscEnv -> IO () +dumpIfaceStats hsc_env = do + eps <- hscEPS hsc_env + dumpIfSet logger dflags (dump_if_trace || dump_rn_stats) + "Interface statistics" + (ifaceStats eps) + where + dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + dump_rn_stats = dopt Opt_D_dump_rn_stats dflags + dump_if_trace = dopt Opt_D_dump_if_trace dflags +{- +Note [ModDetails and --make mode] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An interface file consists of two parts + +* The `ModIface` which ends up getting written to disk. + The `ModIface` is a completely acyclic tree, which can be serialised + and de-serialised completely straightforwardly. The `ModIface` is + also the structure that is finger-printed for recompilation control. + +* The `ModDetails` which provides a more structured view that is suitable + for usage during compilation. The `ModDetails` is heavily cyclic: + An `Id` contains a `Type`, which mentions a `TyCon` that contains kind + that mentions other `TyCons`; the `Id` also includes an unfolding that + in turn mentions more `Id`s; And so on. + +The `ModIface` can be created from the `ModDetails` and the `ModDetails` from +a `ModIface`. + +During tidying, just before interfaces are written to disk, +the ModDetails is calculated and then converted into a ModIface (see GHC.Iface.Make.mkIface_). +Then when GHC needs to restart typechecking from a certain point it can read the +interface file, and regenerate the ModDetails from the ModIface (see GHC.IfaceToCore.typecheckIface). +The key part about the loading is that the ModDetails is regenerated lazily +from the ModIface, so that there's only a detailed in-memory representation +for declarations which are actually used from the interface. This mode is +also used when reading interface files from external packages. + +In the old --make mode implementation, the interface was written after compiling a module +but the in-memory ModDetails which was used to compute the ModIface was retained. +The result was that --make mode used much more memory than `-c` mode, because a large amount of +information about a module would be kept in the ModDetails but never used. + +The new idea is that even in `--make` mode, when there is an in-memory `ModDetails` +at hand, we re-create the `ModDetails` from the `ModIface`. Doing this means that +we only have to keep the `ModIface` decls in memory and then lazily load +detailed representations if needed. It turns out this makes a really big difference +to memory usage, halving maximum memory used in some cases. + +See !5492 and #13586 +-} + {- ************************************************************************ * * diff --git a/compiler/GHC/Rename/Names.hs b/compiler/GHC/Rename/Names.hs index 3037a4e408..b8adcd6a92 100644 --- a/compiler/GHC/Rename/Names.hs +++ b/compiler/GHC/Rename/Names.hs @@ -268,7 +268,6 @@ Running generateModules from #14693 with DEPTH=16, WIDTH=30 finishes in -} - -- | Given a located import declaration @decl@ from @this_mod@, -- calculate the following pieces of information: -- diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs index c31e7925d7..6fac7fe845 100644 --- a/compiler/GHC/Tc/Module.hs +++ b/compiler/GHC/Tc/Module.hs @@ -18,6 +18,7 @@ -- | Typechecking a whole module -- -- https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/type-checker +{-# LANGUAGE TupleSections #-} module GHC.Tc.Module ( tcRnStmt, tcRnExpr, TcRnExprMode(..), tcRnType, tcRnImportDecls, @@ -162,6 +163,7 @@ import GHC.Unit.Module.ModSummary import GHC.Unit.Module.ModIface import GHC.Unit.Module.ModDetails import GHC.Unit.Module.Deps +import GHC.Unit.Finder.Types import GHC.Data.FastString import GHC.Data.Maybe @@ -176,6 +178,9 @@ import qualified Data.Set as S import Control.DeepSeq import Control.Monad import GHC.Driver.Dependencies +import GHC.Unit.Finder +import GHC.Driver.Ppr +import GHC.Unit.Home.ModInfo #include "HsVersions.h" @@ -347,6 +352,12 @@ tcRnModuleTcRnM hsc_env mod_sum } } + +modNameBootWithPackage :: LImportDecl GhcPs -> (Maybe FastString, ModuleNameWithIsBoot) +modNameBootWithPackage (L _ decl) = + ( sl_fs <$> ideclPkgQual decl + , GWIB (unLoc $ ideclName decl) (ideclSource decl)) + implicitPreludeWarn :: SDoc implicitPreludeWarn = text "Module `Prelude' implicitly imported" @@ -385,7 +396,7 @@ tcRnImports hsc_env import_decls -- ; pprTraceM "home_mods" (ppr home_mods) -- ; pprTraceM "home_mods" (ppr (dep_mods (mi_deps iface))) -- ; pprTraceM "home_mods" (ppr (Set.difference (Set.fromList (eltsUFM home_mods)) (Set.fromList (dep_mods (mi_deps iface))))) - ; updateEps_ $ \eps -> eps { eps_is_boot = home_mods } + ; updateEps_ $ \eps -> eps { eps_is_boot = fmap snd home_mods } } -- Update the gbl env diff --git a/compiler/GHC/Unit/External.hs b/compiler/GHC/Unit/External.hs index 177a9db2ba..6e3e774932 100644 --- a/compiler/GHC/Unit/External.hs +++ b/compiler/GHC/Unit/External.hs @@ -62,8 +62,7 @@ initExternalUnitCache = ExternalUnitCache <$> newIORef initExternalPackageState initExternalPackageState :: ExternalPackageState initExternalPackageState = EPS - { eps_is_boot = emptyUFM - , eps_PIT = emptyPackageIfaceTable + { eps_PIT = emptyPackageIfaceTable , eps_free_holes = emptyInstalledModuleEnv , eps_PTE = emptyTypeEnv , eps_inst_env = emptyInstEnv @@ -89,19 +88,6 @@ initExternalPackageState = EPS -- their interface files data ExternalPackageState = EPS { - eps_is_boot :: !(ModuleNameEnv ModuleNameWithIsBoot), - -- ^ In OneShot mode (only), home-package modules - -- accumulate in the external package state, and are - -- sucked in lazily. For these home-pkg modules - -- (only) we need to record which are boot modules. - -- We set this field after loading all the - -- explicitly-imported interfaces, but before doing - -- anything else - -- - -- The 'ModuleName' part is not necessary, but it's useful for - -- debug prints, and it's convenient because this field comes - -- direct from 'GHC.Tc.Utils.imp_dep_mods' - eps_PIT :: !PackageIfaceTable, -- ^ The 'ModIface's for modules in external packages -- whose interfaces we have opened. diff --git a/compiler/GHC/Unit/Home/ModInfo.hs b/compiler/GHC/Unit/Home/ModInfo.hs index fd97689972..81b94eadfd 100644 --- a/compiler/GHC/Unit/Home/ModInfo.hs +++ b/compiler/GHC/Unit/Home/ModInfo.hs @@ -30,6 +30,8 @@ import GHC.Types.Unique import GHC.Types.Unique.DFM import GHC.Utils.Outputable +import GHC.Driver.Ppr +import GHC.Data.Maybe -- | Information about modules in the package being compiled data HomeModInfo = HomeModInfo |