summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Pickering <matthewtpickering@gmail.com>2021-05-05 09:52:13 +0100
committerMatthew Pickering <matthewtpickering@gmail.com>2021-05-05 09:52:13 +0100
commit56f2f6789448cea7108f05a60c839763416b8521 (patch)
treefff1feee40356ec7640e08e1f3740fc2dbca3097
parentf0f6857bd7e3ce3c77e67d36448c3d19df8c722b (diff)
downloadhaskell-56f2f6789448cea7108f05a60c839763416b8521.tar.gz
wip: unify -c --make
-rw-r--r--compiler/GHC/Driver/Dependencies.hs15
-rw-r--r--compiler/GHC/Driver/Env.hs5
-rw-r--r--compiler/GHC/Driver/Main.hs86
-rw-r--r--compiler/GHC/Driver/Make.hs5
-rw-r--r--compiler/GHC/Driver/Pipeline.hs41
-rw-r--r--compiler/GHC/Iface/Load.hs3
-rw-r--r--compiler/GHC/Iface/Recomp.hs2
-rw-r--r--compiler/GHC/IfaceToCore.hs113
-rw-r--r--compiler/GHC/Rename/Names.hs1
-rw-r--r--compiler/GHC/Tc/Module.hs13
-rw-r--r--compiler/GHC/Unit/External.hs16
-rw-r--r--compiler/GHC/Unit/Home/ModInfo.hs2
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