Skip to content

Commit 0220797

Browse files
committed
Remove GetDependencyInformation in favour of GetModuleGraph.
Computing and storing GetDependencyInformation for each file essentially individually means that we perform downsweep on each file individually, wasting a lot of work and using an excessive amount of memory to store all these duplicated graphs individually. However, we already have the `GetModuleGraph` rule, which we need to compute before compiling files any way due to being depended on by `NeedsCompilation`, which needs to know if any reverse dependencies of the module we are compiling requires TH, which meant that each file already depends on the results of downsweep for the whole project. Instead, we can compute the whole graph once when we execute the `GetModuleGraph` rule and even use this inside `HscEnv.hsc_mod_graph` to avoid reconstructing the `ModuleGraph` on each invocation of `GhcSessionDeps`. There may be concerns about excessive build churn due to any change to the result of `GetModuleGraph` invalidating the result of `GhcSessionDeps` too often, but note that this only happens when something in the header of a module changes, and this could be solved easily be re-introducing a version of `GetDependencyInformation` with early cutoff that essentially returns the result of `GetModuleGraph` but includes the hash of only the `ModSummary`s in the downward dependency closure of the file.
1 parent 27f46d7 commit 0220797

File tree

6 files changed

+145
-106
lines changed

6 files changed

+145
-106
lines changed

Diff for: ghcide/src/Development/IDE/Core/Compile.hs

+26-37
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import GHC (Anchor (anchor),
136136
import qualified GHC as G
137137
import GHC.Hs (LEpaComment)
138138
import qualified GHC.Types.Error as Error
139+
import Development.IDE.Import.DependencyInformation
139140
#endif
140141

141142
#if MIN_VERSION_ghc(9,5,0)
@@ -1045,25 +1046,19 @@ handleGenerationErrors' dflags source action =
10451046
-- Add the current ModSummary to the graph, along with the
10461047
-- HomeModInfo's of all direct dependencies (by induction hypothesis all
10471048
-- transitive dependencies will be contained in envs)
1049+
mergeEnvs :: HscEnv -> ModuleGraph -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv
1050+
mergeEnvs env mg ms extraMods envs = do
10481051
#if MIN_VERSION_ghc(9,3,0)
1049-
mergeEnvs :: HscEnv -> (ModSummary, [NodeKey]) -> [HomeModInfo] -> [HscEnv] -> IO HscEnv
1050-
mergeEnvs env (ms, deps) extraMods envs = do
10511052
let im = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))
10521053
ifr = InstalledFound (ms_location ms) im
10531054
curFinderCache = Compat.extendInstalledModuleEnv Compat.emptyInstalledModuleEnv im ifr
1054-
-- Very important to force this as otherwise the hsc_mod_graph field is not
1055-
-- forced and ends up retaining a reference to all the old hsc_envs we have merged to get
1056-
-- this new one, which in turn leads to the EPS referencing the HPT.
1057-
module_graph_nodes =
1058-
nubOrdOn mkNodeKey (ModuleNode deps ms : concatMap (mgModSummaries' . hsc_mod_graph) envs)
1059-
10601055
newFinderCache <- concatFC curFinderCache (map hsc_FC envs)
1061-
liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $
1056+
return $! loadModulesHome extraMods $
10621057
let newHug = foldl' mergeHUG (hsc_HUG env) (map hsc_HUG envs) in
10631058
(hscUpdateHUG (const newHug) env){
10641059
hsc_FC = newFinderCache,
1065-
hsc_mod_graph = mkModuleGraph module_graph_nodes
1066-
})
1060+
hsc_mod_graph = mg
1061+
}
10671062

10681063
where
10691064
mergeHUG (UnitEnvGraph a) (UnitEnvGraph b) = UnitEnvGraph $ Map.unionWith mergeHUE a b
@@ -1082,30 +1077,16 @@ mergeEnvs env (ms, deps) extraMods envs = do
10821077
pure $ FinderCache fcModules' fcFiles'
10831078

10841079
#else
1085-
mergeEnvs :: HscEnv -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv
1086-
mergeEnvs env ms extraMods envs = do
10871080
prevFinderCache <- concatFC <$> mapM (readIORef . hsc_FC) envs
10881081
let im = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))
10891082
ifr = InstalledFound (ms_location ms) im
1090-
-- Very important to force this as otherwise the hsc_mod_graph field is not
1091-
-- forced and ends up retaining a reference to all the old hsc_envs we have merged to get
1092-
-- this new one, which in turn leads to the EPS referencing the HPT.
1093-
module_graph_nodes =
1094-
#if MIN_VERSION_ghc(9,2,0)
1095-
-- We don't do any instantiation for backpack at this point of time, so it is OK to use
1096-
-- 'extendModSummaryNoDeps'.
1097-
-- This may have to change in the future.
1098-
map extendModSummaryNoDeps $
1099-
#endif
1100-
nubOrdOn ms_mod (ms : concatMap (mgModSummaries . hsc_mod_graph) envs)
1101-
11021083
newFinderCache <- newIORef $! Compat.extendInstalledModuleEnv prevFinderCache im ifr
1103-
liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $
1084+
return $! loadModulesHome extraMods $
11041085
env{
11051086
hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs,
11061087
hsc_FC = newFinderCache,
1107-
hsc_mod_graph = mkModuleGraph module_graph_nodes
1108-
})
1088+
hsc_mod_graph = mg
1089+
}
11091090

11101091
where
11111092
mergeUDFM = plusUDFM_C combineModules
@@ -1504,8 +1485,8 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
15041485
let runtime_deps
15051486
| not (mi_used_th iface) = emptyModuleEnv
15061487
| otherwise = parseRuntimeDeps (md_anns details)
1507-
-- Perform the fine grained recompilation check for TH
1508-
maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) runtime_deps
1488+
-- Peform the fine grained recompilation check for TH
1489+
maybe_recomp <- checkLinkableDependencies session get_linkable_hashes runtime_deps
15091490
case maybe_recomp of
15101491
Just msg -> do_regenerate msg
15111492
Nothing
@@ -1542,13 +1523,21 @@ parseRuntimeDeps anns = mkModuleEnv $ mapMaybe go anns
15421523
-- the runtime dependencies of the module, to check if any of them are out of date
15431524
-- Hopefully 'runtime_deps' will be empty if the module didn't actually use TH
15441525
-- See Note [Recompilation avoidance in the presence of TH]
1545-
checkLinkableDependencies :: MonadIO m => ([NormalizedFilePath] -> m [BS.ByteString]) -> ModuleGraph -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired)
1546-
checkLinkableDependencies get_linkable_hashes graph runtime_deps = do
1547-
let hs_files = mapM go (moduleEnvToList runtime_deps)
1548-
go (mod, hash) = do
1549-
ms <- mgLookupModule graph mod
1550-
let hs = fromJust $ ml_hs_file $ ms_location ms
1551-
pure (toNormalizedFilePath' hs, hash)
1526+
checkLinkableDependencies :: MonadIO m => HscEnv -> ([NormalizedFilePath] -> m [BS.ByteString]) -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired)
1527+
checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps = do
1528+
#if MIN_VERSION_ghc(9,3,0)
1529+
moduleLocs <- liftIO $ readIORef (fcModuleCache $ hsc_FC hsc_env)
1530+
#else
1531+
moduleLocs <- liftIO $ readIORef (hsc_FC hsc_env)
1532+
#endif
1533+
let go (mod, hash) = do
1534+
ifr <- lookupInstalledModuleEnv moduleLocs $ Compat.installedModule (toUnitId $ moduleUnit mod) (moduleName mod)
1535+
case ifr of
1536+
InstalledFound loc _ -> do
1537+
hs <- ml_hs_file loc
1538+
pure (toNormalizedFilePath' hs,hash)
1539+
_ -> Nothing
1540+
hs_files = mapM go (moduleEnvToList runtime_deps)
15521541
case hs_files of
15531542
Nothing -> error "invalid module graph"
15541543
Just fs -> do

Diff for: ghcide/src/Development/IDE/Core/RuleTypes.hs

+2-12
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,6 @@ type instance RuleResult GetParsedModule = ParsedModule
6969
-- all comments included using Opt_KeepRawTokenStream
7070
type instance RuleResult GetParsedModuleWithComments = ParsedModule
7171

72-
-- | The dependency information produced by following the imports recursively.
73-
-- This rule will succeed even if there is an error, e.g., a module could not be located,
74-
-- a module could not be parsed or an import cycle.
75-
type instance RuleResult GetDependencyInformation = DependencyInformation
76-
7772
type instance RuleResult GetModuleGraph = DependencyInformation
7873

7974
data GetKnownTargets = GetKnownTargets
@@ -262,8 +257,8 @@ type instance RuleResult GhcSessionDeps = HscEnvEq
262257
-- | Resolve the imports in a module to the file path of a module in the same package
263258
type instance RuleResult GetLocatedImports = [(Located ModuleName, Maybe ArtifactsLocation)]
264259

265-
-- | This rule is used to report import cycles. It depends on GetDependencyInformation.
266-
-- We cannot report the cycles directly from GetDependencyInformation since
260+
-- | This rule is used to report import cycles. It depends on GetModuleGraph.
261+
-- We cannot report the cycles directly from GetModuleGraph since
267262
-- we can only report diagnostics for the current file.
268263
type instance RuleResult ReportImportCycles = ()
269264

@@ -401,11 +396,6 @@ data NeedsCompilation = NeedsCompilation
401396
instance Hashable NeedsCompilation
402397
instance NFData NeedsCompilation
403398

404-
data GetDependencyInformation = GetDependencyInformation
405-
deriving (Eq, Show, Typeable, Generic)
406-
instance Hashable GetDependencyInformation
407-
instance NFData GetDependencyInformation
408-
409399
data GetModuleGraph = GetModuleGraph
410400
deriving (Eq, Show, Typeable, Generic)
411401
instance Hashable GetModuleGraph

0 commit comments

Comments
 (0)