Skip to content

Commit 6ec2635

Browse files
committed
Implement sharing of module graphs
1 parent 77cfd42 commit 6ec2635

File tree

5 files changed

+103
-82
lines changed

5 files changed

+103
-82
lines changed

‎ghcide/src/Development/IDE/Core/Compile.hs‎

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ import GHC (Anchor (anchor),
131131
importqualifiedGHCasG
132132
importGHC.Hs (LEpaComment)
133133
importqualifiedGHC.Types.ErrorasError
134+
importDevelopment.IDE.Import.DependencyInformation
134135
#endif
135136

136137
--| Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'.
@@ -1006,25 +1007,19 @@ loadModulesHome mod_infos e =
10061007
-- Add the current ModSummary to the graph, along with the
10071008
-- HomeModInfo's of all direct dependencies (by induction hypothesis all
10081009
-- transitive dependencies will be contained in envs)
1010+
mergeEnvs::HscEnv->ModuleGraph->ModSummary-> [HomeModInfo] -> [HscEnv] ->IOHscEnv
1011+
mergeEnvs env mg ms extraMods envs =do
10091012
#if MIN_VERSION_ghc(9,3,0)
1010-
mergeEnvs::HscEnv-> (ModSummary, [NodeKey]) -> [HomeModInfo] -> [HscEnv] ->IOHscEnv
1011-
mergeEnvs env (ms, deps) extraMods envs =do
10121013
let im =Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))
10131014
ifr =InstalledFound (ms_location ms) im
10141015
curFinderCache =Compat.extendInstalledModuleEnv Compat.emptyInstalledModuleEnv im ifr
1015-
-- Very important to force this as otherwise the hsc_mod_graph field is not
1016-
-- forced and ends up retaining a reference to all the old hsc_envs we have merged to get
1017-
-- this new one, which in turn leads to the EPS referencing the HPT.
1018-
module_graph_nodes =
1019-
nubOrdOn mkNodeKey (ModuleNode deps ms :concatMap (mgModSummaries' . hsc_mod_graph) envs)
1020-
10211016
newFinderCache <- concatFC curFinderCache (map hsc_FC envs)
1022-
liftRnf rwhnf module_graph_nodes `seq` (return$ loadModulesHome extraMods $
1017+
return$! loadModulesHome extraMods $
10231018
let newHug = foldl' mergeHUG (hsc_HUG env) (map hsc_HUG envs) in
10241019
(hscUpdateHUG (const newHug) env){
10251020
hsc_FC = newFinderCache,
1026-
hsc_mod_graph =mkModuleGraph module_graph_nodes
1027-
})
1021+
hsc_mod_graph =mg
1022+
}
10281023

10291024
where
10301025
mergeHUG (UnitEnvGraph a) (UnitEnvGraph b) =UnitEnvGraph$Map.unionWith mergeHUE a b
@@ -1043,30 +1038,16 @@ mergeEnvs env (ms, deps) extraMods envs = do
10431038
pure$FinderCache fcModules' fcFiles'
10441039

10451040
#else
1046-
mergeEnvs::HscEnv->ModSummary-> [HomeModInfo] -> [HscEnv] ->IOHscEnv
1047-
mergeEnvs env ms extraMods envs =do
10481041
prevFinderCache <- concatFC <$>mapM (readIORef . hsc_FC) envs
10491042
let im =Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))
10501043
ifr =InstalledFound (ms_location ms) im
1051-
-- Very important to force this as otherwise the hsc_mod_graph field is not
1052-
-- forced and ends up retaining a reference to all the old hsc_envs we have merged to get
1053-
-- this new one, which in turn leads to the EPS referencing the HPT.
1054-
module_graph_nodes =
1055-
#if MIN_VERSION_ghc(9,2,0)
1056-
-- We don't do any instantiation for backpack at this point of time, so it is OK to use
1057-
-- 'extendModSummaryNoDeps'.
1058-
-- This may have to change in the future.
1059-
map extendModSummaryNoDeps $
1060-
#endif
1061-
nubOrdOn ms_mod (ms :concatMap (mgModSummaries . hsc_mod_graph) envs)
1062-
10631044
newFinderCache <- newIORef $!Compat.extendInstalledModuleEnv prevFinderCache im ifr
1064-
liftRnf rwhnf module_graph_nodes `seq` (return$ loadModulesHome extraMods $
1045+
return$! loadModulesHome extraMods $
10651046
env{
10661047
hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs,
10671048
hsc_FC = newFinderCache,
1068-
hsc_mod_graph =mkModuleGraph module_graph_nodes
1069-
})
1049+
hsc_mod_graph =mg
1050+
}
10701051

10711052
where
10721053
mergeUDFM = plusUDFM_C combineModules
@@ -1460,8 +1441,8 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
14601441
Just (old_hir, _)
14611442
| isNothing linkableNeeded || isJust (hirCoreFp old_hir)
14621443
->do
1463-
--Perform the fine grained recompilation check for TH
1464-
maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) (hirRuntimeModules old_hir)
1444+
--Peform the fine grained recompilation check for TH
1445+
maybe_recomp <- checkLinkableDependencies session get_linkable_hashes (hirRuntimeModules old_hir)
14651446
case maybe_recomp of
14661447
Just msg -> do_regenerate msg
14671448
Nothing->return ([], Just old_hir)
@@ -1472,8 +1453,8 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
14721453
let runtime_deps
14731454
|not (mi_used_th iface) = emptyModuleEnv
14741455
|otherwise= parseRuntimeDeps (md_anns details)
1475-
--Perform the fine grained recompilation check for TH
1476-
maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) runtime_deps
1456+
--Peform the fine grained recompilation check for TH
1457+
maybe_recomp <- checkLinkableDependencies session get_linkable_hashes runtime_deps
14771458
case maybe_recomp of
14781459
Just msg -> do_regenerate msg
14791460
Nothing
@@ -1510,13 +1491,21 @@ parseRuntimeDeps anns = mkModuleEnv $ mapMaybe go anns
15101491
-- the runtime dependencies of the module, to check if any of them are out of date
15111492
-- Hopefully 'runtime_deps' will be empty if the module didn't actually use TH
15121493
-- See Note [Recompilation avoidance in the presence of TH]
1513-
checkLinkableDependencies::MonadIOm=> ([NormalizedFilePath] ->m [BS.ByteString]) ->ModuleGraph->ModuleEnvBS.ByteString->m (MaybeRecompileRequired)
1514-
checkLinkableDependencies get_linkable_hashes graph runtime_deps =do
1515-
let hs_files =mapM go (moduleEnvToList runtime_deps)
1516-
go (mod, hash) =do
1517-
ms <- mgLookupModule graph mod
1518-
let hs = fromJust $ ml_hs_file $ ms_location ms
1519-
pure (toNormalizedFilePath' hs, hash)
1494+
checkLinkableDependencies::MonadIOm=>HscEnv-> ([NormalizedFilePath] ->m [BS.ByteString]) ->ModuleEnvBS.ByteString->m (MaybeRecompileRequired)
1495+
checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps =do
1496+
#if MIN_VERSION_ghc(9,3,0)
1497+
moduleLocs <- liftIO $ readIORef (fcModuleCache $ hsc_FC hsc_env)
1498+
#else
1499+
moduleLocs <- liftIO $ readIORef (hsc_FC hsc_env)
1500+
#endif
1501+
let go (mod, hash) =do
1502+
ifr <- lookupInstalledModuleEnv moduleLocs $Compat.installedModule (toUnitId $ moduleUnit mod) (moduleName mod)
1503+
case ifr of
1504+
InstalledFound loc _ ->do
1505+
hs <- ml_hs_file loc
1506+
pure (toNormalizedFilePath' hs,hash)
1507+
_ ->Nothing
1508+
hs_files =mapM go (moduleEnvToList runtime_deps)
15201509
case hs_files of
15211510
Nothing->error"invalid module graph"
15221511
Just fs ->do

‎ghcide/src/Development/IDE/Core/Rules.hs‎

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ module Development.IDE.Core.Rules(
1414
IdeState, GetParsedModule(..), TransitiveDependencies(..),
1515
Priority(..), GhcSessionIO(..), GetClientSettings(..),
1616
-- * Functions
17+
--
18+
--
19+
--
20+
--
1721
priorityTypeCheck,
1822
priorityGenerateCore,
1923
priorityFilesOfInterest,
@@ -23,7 +27,6 @@ module Development.IDE.Core.Rules(
2327
defineEarlyCutOffNoFile,
2428
mainRule,
2529
RulesConfig(..),
26-
getDependencies,
2730
getParsedModule,
2831
getParsedModuleWithComments,
2932
getClientConfigAction,
@@ -155,6 +158,7 @@ import qualified Development.IDE.Types.Shake as Shake
155158
importDevelopment.IDE.GHC.CoreFile
156159
importData.Time.Clock.POSIX (posixSecondsToUTCTime)
157160
importControl.Monad.IO.Unlift
161+
importqualifiedData.IntMapasIM
158162
#if MIN_VERSION_ghc(9,3,0)
159163
importGHC.Unit.Module.Graph
160164
importGHC.Unit.Env
@@ -204,12 +208,6 @@ toIdeResult = either (, Nothing) (([],) . Just)
204208
------------------------------------------------------------
205209
-- Exposed API
206210
------------------------------------------------------------
207-
--| Get all transitive file dependencies of a given module.
208-
-- Does not include the file itself.
209-
getDependencies::NormalizedFilePath->Action (Maybe [NormalizedFilePath])
210-
getDependencies file =
211-
fmap transitiveModuleDeps . (`transitiveDeps` file) <$> useNoFile_ GetModuleGraph
212-
213211
getSourceFileSource::NormalizedFilePath->ActionBS.ByteString
214212
getSourceFileSource nfp =do
215213
(_, msource) <- getFileContents nfp
@@ -417,17 +415,17 @@ type RawDepM a = StateT (RawDependencyInformation, IntMap ArtifactsLocation) Act
417415
execRawDepM::Monadm=>StateT (RawDependencyInformation, IntMapa1) ma2->m (RawDependencyInformation, IntMapa1)
418416
execRawDepM act =
419417
execStateT act
420-
( RawDependencyInformationIntMap.empty emptyPathIdMap IntMap.emptyIntMap.empty
418+
( RawDependencyInformationIntMap.empty emptyPathIdMap IntMap.empty
421419
, IntMap.empty
422420
)
423421

424422
--| Given a target file path, construct the raw dependency results by following
425423
-- imports recursively.
426-
rawDependencyInformation:: [NormalizedFilePath] ->ActionRawDependencyInformation
424+
rawDependencyInformation:: [NormalizedFilePath] ->Action(RawDependencyInformation, BootIdMap)
427425
rawDependencyInformation fs =do
428426
(rdi, ss) <- execRawDepM (goPlural fs)
429427
let bm =IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss
430-
return (rdi{rawBootMap = bm })
428+
return (rdi, bm)
431429
where
432430
goPlural ff =do
433431
mss <- lift $ (fmap.fmap) msrModSummary <$> uses GetModSummaryWithoutTimestamps ff
@@ -446,9 +444,9 @@ rawDependencyInformation fs = do
446444
fId <- getFreshFid al
447445
-- Record this module and its location
448446
whenJust msum $\ms ->
449-
modifyRawDepInfo (\rd -> rd{rawModuleNameMap=IntMap.insert (getFilePathId fId)
450-
(ShowableModuleName (moduleName $ ms_mod ms))
451-
(rawModuleNameMap rd)})
447+
modifyRawDepInfo (\rd -> rd{rawModuleMap=IntMap.insert (getFilePathId fId)
448+
(ShowableModule$ ms_mod ms)
449+
(rawModuleMap rd)})
452450
-- Adding an edge to the bootmap so we can make sure to
453451
-- insert boot nodes before the real files.
454452
addBootMap al fId
@@ -670,8 +668,30 @@ knownFilesRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake recorde
670668
getModuleGraphRule::Recorder (WithPriorityLog) ->Rules()
671669
getModuleGraphRule recorder = defineNoFile (cmapWithPrio LogShake recorder) $\GetModuleGraph->do
672670
fs <- toKnownFiles <$> useNoFile_ GetKnownTargets
673-
rawDepInfo <- rawDependencyInformation (HashSet.toList fs)
674-
pure$ processDependencyInformation rawDepInfo
671+
(rawDepInfo, bm) <- rawDependencyInformation (HashSet.toList fs)
672+
let (all_fs, _all_ids) =unzip$HM.toList $ pathToIdMap $ rawPathIdMap rawDepInfo
673+
mss <-map (fmap msrModSummary) <$> uses GetModSummaryWithoutTimestamps all_fs
674+
#if MIN_VERSION_ghc(9,3,0)
675+
let deps =map (\i ->IM.lookup (getFilePathId i) (rawImports rawDepInfo)) _all_ids
676+
nodeKeys =IM.fromList $ catMaybes $zipWith (\fi mms -> (getFilePathId fi,) .NodeKey_Module. msKey <$> mms) _all_ids mss
677+
mns = catMaybes $zipWith go mss deps
678+
go (Just ms) (Just (Right (ModuleImports xs))) =Just$ModuleNode this_dep_keys ms
679+
where this_dep_ids = mapMaybe snd xs
680+
this_dep_keys = mapMaybe (\fi ->IM.lookup (getFilePathId fi) nodeKeys) this_dep_ids
681+
go (Just ms) _ =Just$ModuleNode[] ms
682+
go _ _ =Nothing
683+
mg = mkModuleGraph mns
684+
#else
685+
let mg = mkModuleGraph $
686+
#if MIN_VERSION_ghc(9,2,0)
687+
-- We don't do any instantiation for backpack at this point of time, so it is OK to use
688+
-- 'extendModSummaryNoDeps'.
689+
-- This may have to change in the future.
690+
map extendModSummaryNoDeps $
691+
#endif
692+
(catMaybes mss)
693+
#endif
694+
pure$ processDependencyInformation rawDepInfo bm mg
675695

676696
-- This is factored out so it can be directly called from the GetModIface
677697
-- rule. Directly calling this rule means that on the initial load we can
@@ -772,19 +792,8 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do
772792
depSessions <-map hscEnv <$> uses_ (GhcSessionDeps_ fullModSummary) deps
773793
ifaces <- uses_ GetModIface deps
774794
let inLoadOrder =map (\HiFileResult{..} ->HomeModInfo hirModIface hirModDetails Nothing) ifaces
775-
#if MIN_VERSION_ghc(9,3,0)
776-
-- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph
777-
-- also points to all the direct descendants of the current module. To get the keys for the descendants
778-
-- we must get their `ModSummary`s
779-
!final_deps <-do
780-
dep_mss <-map msrModSummary <$> uses_ GetModSummaryWithoutTimestamps deps
781-
-- Don't want to retain references to the entire ModSummary when just the key will do
782-
return$!!map (NodeKey_Module. msKey) dep_mss
783-
let moduleNode = (ms, final_deps)
784-
#else
785-
let moduleNode = ms
786-
#endif
787-
session' <- liftIO $ mergeEnvs hsc moduleNode inLoadOrder depSessions
795+
mg <- depModuleGraph <$> useNoFile_ GetModuleGraph
796+
session' <- liftIO $ mergeEnvs hsc mg ms inLoadOrder depSessions
788797

789798
Just<$> liftIO (newHscEnvEqWithImportPaths (envImportPaths env) session' [])
790799

‎ghcide/src/Development/IDE/GHC/Orphans.hs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import Data.String (IsString (fromString))
4040
importData.Text (unpack)
4141
#if MIN_VERSION_ghc(9,0,0)
4242
importGHC.ByteCode.Types
43+
importGHC (ModuleGraph)
4344
#else
4445
importByteCodeTypes
4546
#endif
@@ -207,6 +208,9 @@ instance Hashable OccName where hashWithSalt s n = hashWithSalt s (getKey $ getU
207208

208209
instanceShowHomeModInfowhereshow=show. mi_module . hm_iface
209210

211+
instanceShowModuleGraphwhereshow _ ="ModuleGraph{..}"
212+
instanceNFDataModuleGraphwhere rnf = rwhnf
213+
210214
instanceNFDataHomeModInfowhere
211215
rnf (HomeModInfo iface dets link) = rwhnf iface `seq` rnf dets `seq` rnf link
212216

‎ghcide/src/Development/IDE/Import/DependencyInformation.hs‎

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ module Development.IDE.Import.DependencyInformation
1010
, TransitiveDependencies(..)
1111
, FilePathId(..)
1212
, NamedModuleDep(..)
13-
, ShowableModuleName(..)
14-
, PathIdMap
13+
, ShowableModule(..)
14+
, ShowableModuleEnv(..)
15+
, PathIdMap (..)
1516
, emptyPathIdMap
1617
, getPathId
1718
, lookupPathToId
@@ -23,7 +24,7 @@ module Development.IDE.Import.DependencyInformation
2324
, transitiveDeps
2425
, transitiveReverseDependencies
2526
, immediateReverseDependencies
26-
27+
, lookupModuleFile
2728
, BootIdMap
2829
, insertBootId
2930
) where
@@ -53,6 +54,7 @@ import Development.IDE.Types.Diagnostics
5354
importDevelopment.IDE.Types.Location
5455

5556
importGHC
57+
importDevelopment.IDE.GHC.Compat
5658

5759
--| The imports for a given module.
5860
newtypeModuleImports=ModuleImports
@@ -128,15 +130,14 @@ data RawDependencyInformation = RawDependencyInformation
128130
-- corresponding hs file. It is used when topologically sorting as we
129131
-- need to add edges between .hs-boot and .hs so that the .hs files
130132
-- appear later in the sort.
131-
, rawBootMap::!BootIdMap
132-
, rawModuleNameMap::!(FilePathIdMapShowableModuleName)
133+
, rawModuleMap::!(FilePathIdMapShowableModule)
133134
}derivingShow
134135

135136
dataDependencyInformation=
136137
DependencyInformation
137138
{depErrorNodes::!(FilePathIdMap (NonEmptyNodeError))
138139
--^ Nodes that cannot be processed correctly.
139-
, depModuleNames::!(FilePathIdMapShowableModuleName)
140+
, depModules::!(FilePathIdMapShowableModule)
140141
, depModuleDeps::!(FilePathIdMapFilePathIdSet)
141142
--^ For a non-error node, this contains the set of module immediate dependencies
142143
-- in the same package.
@@ -146,13 +147,24 @@ data DependencyInformation =
146147
--^ Map from FilePath to FilePathId
147148
, depBootMap::!BootIdMap
148149
--^ Map from hs-boot file to the corresponding hs file
150+
, depModuleFiles::!(ShowableModuleEnvFilePathId)
151+
--^ Map from Module to the corresponding non-boot hs file
152+
, depModuleGraph::!ModuleGraph
149153
}deriving (Show, Generic)
150154

151-
newtypeShowableModuleName=
152-
ShowableModuleName{showableModuleName::ModuleName}
155+
newtypeShowableModule=
156+
ShowableModule{showableModule::Module}
153157
derivingNFData
154158

155-
instanceShowShowableModuleNamewhereshow= moduleNameString . showableModuleName
159+
newtypeShowableModuleEnva=
160+
ShowableModuleEnv{showableModuleEnv::ModuleEnva}
161+
162+
instanceShowa=>Show (ShowableModuleEnva) where
163+
show (ShowableModuleEnv x) =show (moduleEnvToList x)
164+
instanceNFDataa=>NFData (ShowableModuleEnva) where
165+
rnf = rwhnf
166+
167+
instanceShowShowableModulewhereshow= moduleNameString . moduleName . showableModule
156168

157169
reachableModules::DependencyInformation-> [NormalizedFilePath]
158170
reachableModules DependencyInformation{..} =
@@ -215,15 +227,17 @@ instance Semigroup NodeResult where
215227
SuccessNode _ <>ErrorNode errs =ErrorNode errs
216228
SuccessNode a <>SuccessNode _ =SuccessNode a
217229

218-
processDependencyInformation::RawDependencyInformation->DependencyInformation
219-
processDependencyInformation RawDependencyInformation{..} =
230+
processDependencyInformation::RawDependencyInformation->BootIdMap->ModuleGraph->DependencyInformation
231+
processDependencyInformation RawDependencyInformation{..} rawBootMap mg =
220232
DependencyInformation
221233
{depErrorNodes =IntMap.fromList errorNodes
222234
, depModuleDeps = moduleDeps
223235
, depReverseModuleDeps = reverseModuleDeps
224-
, depModuleNames=rawModuleNameMap
236+
, depModules=rawModuleMap
225237
, depPathIdMap = rawPathIdMap
226238
, depBootMap = rawBootMap
239+
, depModuleFiles =ShowableModuleEnv reverseModuleMap
240+
, depModuleGraph = mg
227241
}
228242
where resultGraph = buildResultGraph rawImports
229243
(errorNodes, successNodes) = partitionNodeResults $IntMap.toList resultGraph
@@ -240,6 +254,7 @@ processDependencyInformation RawDependencyInformation{..} =
240254
foldr (\(p, cs) res ->
241255
let new =IntMap.fromList (map (, IntSet.singleton (coerce p)) (coerce cs))
242256
inIntMap.unionWith IntSet.union new res ) IntMap.empty successEdges
257+
reverseModuleMap = mkModuleEnv $map (\(i,sm) -> (showableModule sm, FilePathId i)) $IntMap.toList rawModuleMap
243258

244259

245260
--| Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
@@ -351,6 +366,10 @@ transitiveDeps DependencyInformation{..} file = do
351366

352367
vs = topSort g
353368

369+
lookupModuleFile::Module->DependencyInformation->MaybeNormalizedFilePath
370+
lookupModuleFile modDependencyInformation{..}
371+
= idToPath depPathIdMap <$> lookupModuleEnv (showableModuleEnv depModuleFiles) mod
372+
354373
newtypeTransitiveDependencies=TransitiveDependencies
355374
{transitiveModuleDeps:: [NormalizedFilePath]
356375
--^ Transitive module dependencies in topological order.

0 commit comments

Comments
(0)