@@ -35,10 +35,10 @@ module Development.IDE.Core.Compile
35
35
36
36
import Control.Concurrent.Extra
37
37
import Control.Concurrent.STM.Stats hiding (orElse )
38
- import Control.DeepSeq (force , liftRnf , rnf , rwhnf )
38
+ import Control.DeepSeq (force , liftRnf , rnf , rwhnf , NFData ( .. ) )
39
39
import Control.Exception (evaluate )
40
40
import Control.Exception.Safe
41
- import Control.Lens hiding (List )
41
+ import Control.Lens hiding (List , (<.>) )
42
42
import Control.Monad.Except
43
43
import Control.Monad.Extra
44
44
import Control.Monad.Trans.Except
@@ -62,7 +62,7 @@ import Data.Maybe
62
62
import qualified Data.Text as T
63
63
import Data.Time (UTCTime (.. ),
64
64
getCurrentTime )
65
- import Data.Time.Clock.POSIX (posixSecondsToUTCTime )
65
+ import Data.Time.Clock.POSIX (posixSecondsToUTCTime , utcTimeToPOSIXSeconds )
66
66
import Data.Tuple.Extra (dupe )
67
67
import Data.Unique as Unique
68
68
import Debug.Trace
@@ -84,6 +84,7 @@ import Development.IDE.Spans.Common
84
84
import Development.IDE.Types.Diagnostics
85
85
import Development.IDE.Types.Location
86
86
import Development.IDE.Types.Options
87
+ import Development.IDE.GHC.CoreFile
87
88
import GHC (ForeignHValue ,
88
89
GetDocsFailure (.. ),
89
90
mgModSummaries ,
@@ -105,13 +106,23 @@ import ErrUtils
105
106
106
107
#if MIN_VERSION_ghc(9,0,1)
107
108
import GHC.Tc.Gen.Splice
109
+
110
+ #if MIN_VERSION_ghc(9,2,1)
111
+ import GHC.Types.HpcInfo
112
+ import GHC.Types.ForeignStubs
113
+ import GHC.Types.TypeEnv
114
+ #else
115
+ import GHC.Driver.Types
116
+ #endif
117
+
108
118
#else
109
119
import TcSplice
120
+ import HscTypes
110
121
#endif
111
122
112
- #if MIN_VERSION_ghc(9,2,0)
113
123
import Development.IDE.GHC.Compat.Util (emptyUDFM , fsLit ,
114
124
plusUDFM_C )
125
+ #if MIN_VERSION_ghc(9,2,0)
115
126
import GHC (Anchor (anchor ),
116
127
EpaComment (EpaComment ),
117
128
EpaCommentTok (EpaBlockComment , EpaLineComment ),
@@ -381,7 +392,7 @@ mkHiFileResultCompile session' tcm simplified_guts ltype = catchErrs $ do
381
392
382
393
let genLinkable = case ltype of
383
394
ObjectLinkable -> generateObjectCode
384
- BCOLinkable -> generateByteCode
395
+ BCOLinkable -> generateByteCode WriteCoreFile
385
396
386
397
(linkable, details, diags) <-
387
398
if mg_hsc_src simplified_guts == HsBootFile
@@ -483,8 +494,10 @@ generateObjectCode session summary guts = do
483
494
484
495
pure (map snd warnings, linkable)
485
496
486
- generateByteCode :: HscEnv -> ModSummary -> CgGuts -> IO (IdeResult Linkable )
487
- generateByteCode hscEnv summary guts = do
497
+ data WriteCoreFile = WriteCoreFile | CoreFileExists ! UTCTime
498
+
499
+ generateByteCode :: WriteCoreFile -> HscEnv -> ModSummary -> CgGuts -> IO (IdeResult Linkable )
500
+ generateByteCode write_core hscEnv summary guts = do
488
501
fmap (either (, Nothing ) (second Just )) $
489
502
catchSrcErrors (hsc_dflags hscEnv) " bytecode" $ do
490
503
(warnings, (_, bytecode, sptEntries)) <-
@@ -499,7 +512,14 @@ generateByteCode hscEnv summary guts = do
499
512
summary'
500
513
#endif
501
514
let unlinked = BCOs bytecode sptEntries
502
- time <- liftIO getCurrentTime
515
+ time <- case write_core of
516
+ CoreFileExists time -> pure time
517
+ WriteCoreFile -> liftIO $ do
518
+ let core_fp = ml_core_file $ ms_location summary
519
+ core_file = codeGutsToCoreFile guts
520
+ atomicFileWrite core_fp $ \ fp ->
521
+ writeBinCoreFile fp core_file
522
+ getModificationTime core_fp
503
523
let linkable = LM time (ms_mod summary) [unlinked]
504
524
505
525
pure (map snd warnings, linkable)
@@ -1124,6 +1144,17 @@ data RecompilationInfo m
1124
1144
, regenerate :: Maybe LinkableType -> m ([FileDiagnostic ], Maybe HiFileResult ) -- ^ Action to regenerate an interface
1125
1145
}
1126
1146
1147
+ -- | Either a regular GHC linkable or a core file that
1148
+ -- can be later turned into a proper linkable
1149
+ data IdeLinkable = GhcLinkable ! Linkable | CoreLinkable ! UTCTime ! CoreFile
1150
+
1151
+ instance NFData IdeLinkable where
1152
+ rnf (GhcLinkable lb) = rnf lb
1153
+ rnf (CoreLinkable time _) = rnf time
1154
+
1155
+ ml_core_file :: ModLocation -> FilePath
1156
+ ml_core_file ml = ml_hi_file ml <.> " core"
1157
+
1127
1158
-- | Retuns an up-to-date module interface, regenerating if needed.
1128
1159
-- Assumes file exists.
1129
1160
-- Requires the 'HscEnv' to be set up with dependencies
@@ -1141,14 +1172,22 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
1141
1172
mb_old_version = snd <$> old_value
1142
1173
1143
1174
obj_file = ml_obj_file (ms_location ms)
1175
+ core_file = ml_core_file (ms_location ms)
1176
+ iface_file = ml_hi_file (ms_location ms)
1144
1177
1145
1178
! mod = ms_mod ms
1146
1179
1147
1180
mb_dest_version <- case mb_old_version of
1148
1181
Just ver -> pure $ Just ver
1149
- Nothing -> get_file_version $ toNormalizedFilePath' $ case linkableNeeded of
1150
- Just ObjectLinkable -> ml_obj_file (ms_location ms)
1151
- _ -> ml_hi_file (ms_location ms)
1182
+ Nothing -> liftIO $ do
1183
+ let file = case linkableNeeded of
1184
+ Just ObjectLinkable -> obj_file
1185
+ Just BCOLinkable -> core_file
1186
+ Nothing -> iface_file
1187
+ exists <- doesFileExist file
1188
+ if exists
1189
+ then Just . ModificationTime . utcTimeToPOSIXSeconds <$> getModificationTime file
1190
+ else pure Nothing
1152
1191
1153
1192
-- The source is modified if it is newer than the destination
1154
1193
let sourceMod = case mb_dest_version of
@@ -1162,42 +1201,46 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
1162
1201
<- liftIO $ checkOldIface sessionWithMsDynFlags ms sourceMod mb_old_iface
1163
1202
1164
1203
1165
- let
1166
- (recomp_obj_reqd, mb_linkable) = case linkableNeeded of
1167
- Nothing -> (UpToDate , Nothing )
1168
- Just linkableType -> case old_value of
1169
- -- We don't have an old result
1170
- Nothing -> recompMaybeBecause " missing"
1171
- -- We have an old result
1172
- Just (old_hir, old_file_version) ->
1173
- case hm_linkable $ hirHomeMod old_hir of
1174
- Nothing -> recompMaybeBecause " missing [not needed before]"
1175
- Just old_lb
1176
- | Just True <- mi_used_th <$> mb_checked_iface -- No need to recompile if TH wasn't used
1177
- , old_file_version /= source_version -> recompMaybeBecause " out of date"
1178
-
1179
- -- Check if it is the correct type
1180
- -- Ideally we could use object-code in case we already have
1181
- -- it when we are generating bytecode, but this is difficult because something
1182
- -- below us may be bytecode, and object code can't depend on bytecode
1183
- | ObjectLinkable <- linkableType, isObjectLinkable old_lb
1184
- -> (UpToDate , Just old_lb)
1185
-
1186
- | BCOLinkable <- linkableType , not (isObjectLinkable old_lb)
1187
- -> (UpToDate , Just old_lb)
1188
-
1189
- | otherwise -> recompMaybeBecause " missing [wrong type]"
1190
- where
1191
- recompMaybeBecause msg = case linkableType of
1192
- BCOLinkable -> (RecompBecause (" bytecode " ++ msg), Nothing )
1193
- ObjectLinkable -> case mb_dest_version of -- The destination file should be the object code
1194
- Nothing -> (RecompBecause (" object code " ++ msg), Nothing )
1195
- Just disk_obj_version@ (ModificationTime t) ->
1196
- -- If we make it this far, assume that the object code on disk is up to date
1197
- -- This assertion works because of the sourceMod check
1198
- assert (disk_obj_version >= source_version)
1199
- (UpToDate , Just $ LM (posixSecondsToUTCTime t) mod [DotO obj_file])
1200
- Just (VFSVersion _) -> error " object code in vfs"
1204
+ (recomp_obj_reqd, mb_linkable) <- case linkableNeeded of
1205
+ Nothing -> pure (UpToDate , Nothing )
1206
+ Just linkableType -> case old_value of
1207
+ -- We don't have an old result
1208
+ Nothing -> recompMaybeBecause " missing"
1209
+ -- We have an old result
1210
+ Just (old_hir, old_file_version) ->
1211
+ case hm_linkable $ hirHomeMod old_hir of
1212
+ Nothing -> recompMaybeBecause " missing [not needed before]"
1213
+ Just old_lb
1214
+ | Just True <- mi_used_th <$> mb_checked_iface -- No need to recompile if TH wasn't used
1215
+ , old_file_version /= source_version -> recompMaybeBecause " out of date"
1216
+
1217
+ -- Check if it is the correct type
1218
+ -- Ideally we could use object-code in case we already have
1219
+ -- it when we are generating bytecode, but this is difficult because something
1220
+ -- below us may be bytecode, and object code can't depend on bytecode
1221
+ | ObjectLinkable <- linkableType, isObjectLinkable old_lb
1222
+ -> pure (UpToDate , Just $ GhcLinkable old_lb)
1223
+
1224
+ | BCOLinkable <- linkableType , not (isObjectLinkable old_lb)
1225
+ -> pure (UpToDate , Just $ GhcLinkable old_lb)
1226
+
1227
+ where
1228
+ recompMaybeBecause msg =
1229
+ case mb_dest_version of -- The destination file should be the object code or the core file
1230
+ Nothing -> pure (RecompBecause msg', Nothing )
1231
+ Just disk_obj_version@ (ModificationTime t) ->
1232
+ if (disk_obj_version >= source_version)
1233
+ then case linkableType of
1234
+ ObjectLinkable -> pure (UpToDate , Just $ GhcLinkable $ LM (posixSecondsToUTCTime t) mod [DotO obj_file])
1235
+ BCOLinkable -> liftIO $ do
1236
+ core <- readBinCoreFile (mkUpdater $ hsc_NC session) core_file
1237
+ pure (UpToDate , Just $ CoreLinkable (posixSecondsToUTCTime t) core)
1238
+ else pure (RecompBecause msg', Nothing )
1239
+ Just (VFSVersion _) -> pure (RecompBecause msg', Nothing )
1240
+ where
1241
+ msg' = case linkableType of
1242
+ BCOLinkable -> " bytecode " ++ msg
1243
+ ObjectLinkable -> " Object code " ++ msg
1201
1244
1202
1245
let do_regenerate _reason = withTrace " regenerate interface" $ \ setTag -> do
1203
1246
setTag " Module" $ moduleNameString $ moduleName mod
@@ -1217,12 +1260,12 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
1217
1260
-> do_regenerate msg
1218
1261
| otherwise -> return ([] , Just old_hir)
1219
1262
Nothing -> do
1220
- hmi <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags iface lb
1263
+ (warns, hmi) <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags ms iface lb
1221
1264
-- parse the runtime dependencies from the annotations
1222
1265
let runtime_deps
1223
1266
| not (mi_used_th iface) = emptyModuleEnv
1224
1267
| otherwise = parseRuntimeDeps (md_anns (hm_details hmi))
1225
- return ([] , Just $ mkHiFileResult ms hmi runtime_deps)
1268
+ return (warns , Just $ mkHiFileResult ms hmi runtime_deps)
1226
1269
(_, _reason) -> do_regenerate _reason
1227
1270
1228
1271
-- | ModDepTime is stored as an annotation in the iface to
@@ -1269,12 +1312,34 @@ showReason UpToDate = "UpToDate"
1269
1312
showReason MustCompile = " MustCompile"
1270
1313
showReason (RecompBecause s) = s
1271
1314
1272
- mkDetailsFromIface :: HscEnv -> ModIface -> Maybe Linkable -> IO HomeModInfo
1273
- mkDetailsFromIface session iface linkable = do
1315
+ mkDetailsFromIface :: HscEnv -> ModSummary -> ModIface -> Maybe IdeLinkable -> IO ([ FileDiagnostic ], HomeModInfo )
1316
+ mkDetailsFromIface session ms iface ide_linkable = do
1274
1317
details <- liftIO $ fixIO $ \ details -> do
1275
- let hsc' = session { hsc_HPT = addToHpt (hsc_HPT session) (moduleName $ mi_module iface) (HomeModInfo iface details linkable ) }
1318
+ let hsc' = session { hsc_HPT = addToHpt (hsc_HPT session) (moduleName $ mi_module iface) (HomeModInfo iface details Nothing ) }
1276
1319
initIfaceLoad hsc' (typecheckIface iface)
1277
- return (HomeModInfo iface details linkable)
1320
+ (warns, linkable) <- liftIO $ case ide_linkable of
1321
+ Nothing -> pure ([] , Nothing )
1322
+ Just (GhcLinkable lb) -> pure ([] , Just lb)
1323
+ Just (CoreLinkable t core_file) -> do
1324
+ cgi_guts <- coreFileToCgGuts session iface details core_file
1325
+ generateByteCode (CoreFileExists t) session ms cgi_guts
1326
+
1327
+ return (warns, HomeModInfo iface details linkable)
1328
+
1329
+ coreFileToCgGuts :: HscEnv -> ModIface -> ModDetails -> CoreFile -> IO CgGuts
1330
+ coreFileToCgGuts session iface details core_file = do
1331
+ let act hpt = addToHpt hpt (moduleName this_mod)
1332
+ (HomeModInfo iface details Nothing )
1333
+ this_mod = mi_module iface
1334
+ types_var <- newIORef (md_types details)
1335
+ let kv = Just (this_mod, types_var)
1336
+ hsc_env' = session { hsc_HPT = act (hsc_HPT session)
1337
+ , hsc_type_env_var = kv }
1338
+ core_binds <- initIfaceCheck (text " l" ) hsc_env' $ typecheckCoreFile this_mod types_var core_file
1339
+ -- Implicit binds aren't saved, so we need to regenerate them ourselves.
1340
+ let implicit_binds = concatMap getImplicitBinds tyCons
1341
+ tyCons = typeEnvTyCons (md_types details)
1342
+ pure $ CgGuts this_mod tyCons (implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False ) Nothing []
1278
1343
1279
1344
-- | Non-interactive, batch version of 'InteractiveEval.getDocs'.
1280
1345
-- The interactive paths create problems in ghc-lib builds
0 commit comments