Skip to content

Commit aa7a9c1

Browse files
committed
Add notes and documentation for recompilation avoidance scheme
1 parent 570d4d8 commit aa7a9c1

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

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

+69-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,15 @@ captureSplicesAndDeps env k = do
192192

193193
-- We want to record exactly which linkables/modules the typechecker needed at runtime
194194
-- This is useful for recompilation checking.
195-
-- From hscCompileCoreExpr'
195+
-- See Note [Recompilation avoidance in the presence of TH]
196+
--
197+
-- From hscCompileCoreExpr' in GHC
198+
-- To update, copy hscCompileCoreExpr' (the implementation of
199+
-- hscCompileCoreExprHook) verbatim, and add code to extract all the free
200+
-- names in the compiled bytecode, recording the modules that those names
201+
-- come from in the IORef,, as these are the modules on whose implementation
202+
-- we depend.
203+
--
196204
-- Only compute direct dependencies instead of transitive dependencies.
197205
-- It is much cheaper to store the direct dependencies, we can compute
198206
-- the transitive ones when required.
@@ -336,6 +344,7 @@ tcRnModule hsc_env keep_lbls pmod = do
336344

337345
-- The linkables we depend on at runtime are the transitive closure of 'mods'
338346
-- restricted to the home package
347+
-- See Note [Recompilation avoidance in the presence of TH]
339348
mod_env = filterModuleEnv (\m _ -> elementOfUniqSet (moduleName m) mods_transitive) keep_lbls -- Could use restrictKeys if the constructors were exported
340349

341350
-- Serialize mod_env so we can read it from the interface
@@ -1050,6 +1059,63 @@ loadHieFile :: Compat.NameCacheUpdater -> FilePath -> IO GHC.HieFile
10501059
loadHieFile ncu f = do
10511060
GHC.hie_file_result <$> GHC.readHieFile ncu f
10521061

1062+
1063+
{- Note [Recompilation avoidance in the presence of TH]
1064+
1065+
Most versions of GHC we currently support don't have a working implementation of
1066+
code unloading for object code, and no version of GHC supports this on certain
1067+
platforms like Windows. This makes it completely infeasible for interactive use,
1068+
as symbols from previous compiles will shadow over all future compiles.
1069+
1070+
This means that we need to use bytecode when generating code for Template
1071+
Haskell. Unfortunately, we can't serialize bytecode, so we will always need
1072+
to recompile when the IDE starts. However, we can put in place a much tighter
1073+
recompilation avoidance scheme for subsequent compiles:
1074+
1075+
1. If the source file changes, then we always need to recompile
1076+
a. For files of interest, we will get explicit `textDocument/change` events
1077+
that will let us invalidate our build products
1078+
b. For files we read from disk, we can detect source file changes by
1079+
comparing the `mtime` of the source file with the build product (.hi/.o) file
1080+
on disk.
1081+
2. If GHC's recompilation avoidance scheme based on interface file hashes says
1082+
that we need to recompile, the we need to recompile.
1083+
3. If the file in question requires code generation then, we need to recompile
1084+
if we don't have the appropriate kind of build products.
1085+
a. If we already have the build products in memory, and the conditions 1 and
1086+
2 above hold, then we don't need to recompile
1087+
b. If we are generating object code, then we can also search for it on
1088+
disk and ensure it is up to date. Notably, we did _not_ previously re-use
1089+
old bytecode from memory when `hls-graph`/`shake` decided to rebuild the
1090+
`HiFileResult` for some reason
1091+
1092+
4. If the file in question used Template Haskell on the previous compile, then
1093+
we need to recompile if any `Linkable` in its transitive closure changed. This
1094+
sounds bad, but it is possible to make some improvements.
1095+
In particular, we only need to recompile if any of the `Linkable`s actually used during the previous compile change.
1096+
1097+
How can we tell if a `Linkable` was actually used while running some TH?
1098+
1099+
GHC provides a `hscCompileCoreExprHook` which lets us intercept bytecode as
1100+
it is being compiled and linked. We can inspect the bytecode to see which
1101+
`Linkable` dependencies it requires, and record this for use in
1102+
recompilation checking.
1103+
We record all the home package modules of the free names that occur in the
1104+
bytecode. The `Linkable`s required are then the transitive closure of these
1105+
modules in the home-package environment. This is the same scheme as used by
1106+
GHC to find the correct things to link in before running bytecode.
1107+
1108+
This works fine if we already have previous build products in memory, but
1109+
what if we are reading an interface from disk? Well, we can smuggle in the
1110+
necessary information (linkable `Module`s required as well as the time they
1111+
were generated) using `Annotation`s, which provide a somewhat general purpose
1112+
way to serialise arbitrary information along with interface files.
1113+
1114+
Then when deciding whether to recompile, we need to check that the versions
1115+
of the linkables used during a previous compile match whatever is currently
1116+
in the HPT.
1117+
-}
1118+
10531119
data RecompilationInfo m
10541120
= RecompilationInfo
10551121
{ source_version :: FileVersion
@@ -1061,6 +1127,7 @@ data RecompilationInfo m
10611127
-- | Retuns an up-to-date module interface, regenerating if needed.
10621128
-- Assumes file exists.
10631129
-- Requires the 'HscEnv' to be set up with dependencies
1130+
-- See Note [Recompilation avoidance in the presence of TH]
10641131
loadInterface
10651132
:: (MonadIO m, MonadMask m)
10661133
=> HscEnv
@@ -1183,6 +1250,7 @@ parseRuntimeDeps anns = mkModuleEnv $ mapMaybe go anns
11831250
-- | checkLinkableDependencies compares the linkables in the home package to
11841251
-- the runtime dependencies of the module, to check if any of them are out of date
11851252
-- Hopefully 'runtime_deps' will be empty if the module didn't actually use TH
1253+
-- See Note [Recompilation avoidance in the presence of TH]
11861254
checkLinkableDependencies :: HomePackageTable -> ModuleEnv UTCTime -> Maybe RecompileRequired
11871255
checkLinkableDependencies hpt runtime_deps
11881256
| isEmptyModuleEnv out_of_date = Nothing -- Nothing out of date, so don't recompile

0 commit comments

Comments
 (0)