summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/iface/FlagChecker.hs67
-rw-r--r--compiler/iface/LoadIface.hs2
-rw-r--r--compiler/iface/MkIface.hs45
-rw-r--r--compiler/main/DynFlags.hs73
-rw-r--r--compiler/main/HscTypes.hs15
5 files changed, 183 insertions, 19 deletions
diff --git a/compiler/iface/FlagChecker.hs b/compiler/iface/FlagChecker.hs
index eb99c743e6..f81b2652e8 100644
--- a/compiler/iface/FlagChecker.hs
+++ b/compiler/iface/FlagChecker.hs
@@ -4,6 +4,8 @@
-- interface file as part of the recompilation checking infrastructure.
module FlagChecker (
fingerprintDynFlags
+ , fingerprintOptFlags
+ , fingerprintHpcFlags
) where
import GhcPrelude
@@ -53,25 +55,45 @@ fingerprintDynFlags dflags@DynFlags{..} this_mod nameio =
-- -fprof-auto etc.
prof = if gopt Opt_SccProfilingOn dflags then fromEnum profAuto else 0
- -- -O, see https://ghc.haskell.org/trac/ghc/ticket/10923
- opt = if hscTarget == HscInterpreted ||
- hscTarget == HscNothing
- then 0
- else optLevel
+ flags = (mainis, safeHs, lang, cpp, paths, prof)
+ in -- pprTrace "flags" (ppr flags) $
+ computeFingerprint nameio flags
+
+-- Fingerprint the optimisation info. We keep this separate from the rest of
+-- the flags because GHCi users (especially) may wish to ignore changes in
+-- optimisation level or optimisation flags so as to use as many pre-existing
+-- object files as they can.
+-- See Note [Ignoring some flag changes]
+fingerprintOptFlags :: DynFlags
+ -> (BinHandle -> Name -> IO ())
+ -> IO Fingerprint
+fingerprintOptFlags DynFlags{..} nameio =
+ let
+ -- See https://ghc.haskell.org/trac/ghc/ticket/10923
+ -- We used to fingerprint the optimisation level, but as Joachim
+ -- Breitner pointed out in comment 9 on that ticket, it's better
+ -- to ignore that and just look at the individual optimisation flags.
+ opt_flags = map fromEnum $ filter (`EnumSet.member` optimisationFlags)
+ (EnumSet.toList generalFlags)
+
+ in computeFingerprint nameio opt_flags
+
+-- Fingerprint the HPC info. We keep this separate from the rest of
+-- the flags because GHCi users (especially) may wish to use an object
+-- file compiled for HPC when not actually using HPC.
+-- See Note [Ignoring some flag changes]
+fingerprintHpcFlags :: DynFlags
+ -> (BinHandle -> Name -> IO ())
+ -> IO Fingerprint
+fingerprintHpcFlags dflags@DynFlags{..} nameio =
+ let
-- -fhpc, see https://ghc.haskell.org/trac/ghc/ticket/11798
-- hpcDir is output-only, so we should recompile if it changes
hpc = if gopt Opt_Hpc dflags then Just hpcDir else Nothing
- -- -fignore-asserts, which affects how `Control.Exception.assert` works
- ignore_asserts = gopt Opt_IgnoreAsserts dflags
-
- -- Nesting just to avoid ever more Binary tuple instances
- flags = (mainis, safeHs, lang, cpp, paths,
- (prof, opt, hpc, ignore_asserts))
+ in computeFingerprint nameio hpc
- in -- pprTrace "flags" (ppr flags) $
- computeFingerprint nameio flags
{- Note [path flags and recompilation]
@@ -102,3 +124,22 @@ recompilation check; here we explain why.
The only path-related flag left is -hcsuf.
-}
+
+{- Note [Ignoring some flag changes]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Normally, --make tries to reuse only compilation products that are
+the same as those that would have been produced compiling from
+scratch. Sometimes, however, users would like to be more aggressive
+about recompilation avoidance. This is particularly likely when
+developing using GHCi (see #13604). Currently, we allow users to
+ignore optimisation changes using -fignore-optim-changes, and to
+ignore HPC option changes using -fignore-hpc-changes. If there's a
+demand for it, we could also allow changes to -fprof-auto-* flags
+(although we can't allow -prof flags to differ). The key thing about
+these options is that we can still successfully link a library or
+executable when some of its components differ in these ways.
+
+The way we accomplish this is to leave the optimization and HPC
+options out of the flag hash, hashing them separately.
+-}
diff --git a/compiler/iface/LoadIface.hs b/compiler/iface/LoadIface.hs
index b91d984b9a..b9a77598da 100644
--- a/compiler/iface/LoadIface.hs
+++ b/compiler/iface/LoadIface.hs
@@ -1020,6 +1020,8 @@ pprModIface iface
, nest 2 (text "export-list hash:" <+> ppr (mi_exp_hash iface))
, nest 2 (text "orphan hash:" <+> ppr (mi_orphan_hash iface))
, nest 2 (text "flag hash:" <+> ppr (mi_flag_hash iface))
+ , nest 2 (text "opt_hash:" <+> ppr (mi_opt_hash iface))
+ , nest 2 (text "hpc_hash:" <+> ppr (mi_hpc_hash iface))
, nest 2 (text "sig of:" <+> ppr (mi_sig_of iface))
, nest 2 (text "used TH splices:" <+> ppr (mi_used_th iface))
, nest 2 (text "where")
diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs
index a12cff2226..6d696d991c 100644
--- a/compiler/iface/MkIface.hs
+++ b/compiler/iface/MkIface.hs
@@ -4,6 +4,7 @@
-}
{-# LANGUAGE CPP, NondecreasingIndentation #-}
+{-# LANGUAGE MultiWayIf #-}
-- | Module for constructing @ModIface@ values (interface files),
-- writing them to disk and comparing two versions to see if
@@ -279,6 +280,8 @@ mkIface_ hsc_env maybe_old_fingerprint
mi_iface_hash = fingerprint0,
mi_mod_hash = fingerprint0,
mi_flag_hash = fingerprint0,
+ mi_opt_hash = fingerprint0,
+ mi_hpc_hash = fingerprint0,
mi_exp_hash = fingerprint0,
mi_used_th = used_th,
mi_orphan_hash = fingerprint0,
@@ -660,6 +663,10 @@ addFingerprints hsc_env mb_old_fingerprint iface0 new_decls
-- the abi hash and one that should
flag_hash <- fingerprintDynFlags dflags this_mod putNameLiterally
+ opt_hash <- fingerprintOptFlags dflags putNameLiterally
+
+ hpc_hash <- fingerprintHpcFlags dflags putNameLiterally
+
-- the ABI hash depends on:
-- - decls
-- - export list
@@ -695,6 +702,8 @@ addFingerprints hsc_env mb_old_fingerprint iface0 new_decls
mi_exp_hash = export_hash,
mi_orphan_hash = orphan_hash,
mi_flag_hash = flag_hash,
+ mi_opt_hash = opt_hash,
+ mi_hpc_hash = hpc_hash,
mi_orphan = not ( all ifRuleAuto orph_rules
-- See Note [Orphans and auto-generated rules]
&& null orph_insts
@@ -1200,6 +1209,10 @@ checkVersions hsc_env mod_summary iface
then return (RecompBecause "-this-unit-id changed", Nothing) else do {
; recomp <- checkFlagHash hsc_env iface
; if recompileRequired recomp then return (recomp, Nothing) else do {
+ ; recomp <- checkOptimHash hsc_env iface
+ ; if recompileRequired recomp then return (recomp, Nothing) else do {
+ ; recomp <- checkHpcHash hsc_env iface
+ ; if recompileRequired recomp then return (recomp, Nothing) else do {
; recomp <- checkMergedSignatures mod_summary iface
; if recompileRequired recomp then return (recomp, Nothing) else do {
; recomp <- checkHsig mod_summary iface
@@ -1223,7 +1236,7 @@ checkVersions hsc_env mod_summary iface
; updateEps_ $ \eps -> eps { eps_is_boot = mod_deps }
; recomp <- checkList [checkModUsage this_pkg u | u <- mi_usages iface]
; return (recomp, Just iface)
- }}}}}}
+ }}}}}}}}
where
this_pkg = thisPackage (hsc_dflags hsc_env)
-- This is a bit of a hack really
@@ -1255,6 +1268,36 @@ checkFlagHash hsc_env iface = do
(text " Module flags have changed")
old_hash new_hash
+-- | Check the optimisation flags haven't changed
+checkOptimHash :: HscEnv -> ModIface -> IfG RecompileRequired
+checkOptimHash hsc_env iface = do
+ let old_hash = mi_opt_hash iface
+ new_hash <- liftIO $ fingerprintOptFlags (hsc_dflags hsc_env)
+ putNameLiterally
+ if | old_hash == new_hash
+ -> up_to_date (text "Optimisation flags unchanged")
+ | gopt Opt_IgnoreOptimChanges (hsc_dflags hsc_env)
+ -> up_to_date (text "Optimisation flags changed; ignoring")
+ | otherwise
+ -> out_of_date_hash "Optimisation flags changed"
+ (text " Optimisation flags have changed")
+ old_hash new_hash
+
+-- | Check the HPC flags haven't changed
+checkHpcHash :: HscEnv -> ModIface -> IfG RecompileRequired
+checkHpcHash hsc_env iface = do
+ let old_hash = mi_hpc_hash iface
+ new_hash <- liftIO $ fingerprintHpcFlags (hsc_dflags hsc_env)
+ putNameLiterally
+ if | old_hash == new_hash
+ -> up_to_date (text "HPC flags unchanged")
+ | gopt Opt_IgnoreHpcChanges (hsc_dflags hsc_env)
+ -> up_to_date (text "HPC flags changed; ignoring")
+ | otherwise
+ -> out_of_date_hash "HPC flags changed"
+ (text " HPC flags have changed")
+ old_hash new_hash
+
-- Check that the set of signatures we are merging in match.
-- If the -unit-id flags change, this can change too.
checkMergedSignatures :: ModSummary -> ModIface -> IfG RecompileRequired
diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs
index 74c0a22cbf..4b95bfaab0 100644
--- a/compiler/main/DynFlags.hs
+++ b/compiler/main/DynFlags.hs
@@ -60,6 +60,7 @@ module DynFlags (
makeDynFlagsConsistent,
shouldUseColor,
positionIndependent,
+ optimisationFlags,
Way(..), mkBuildTag, wayRTSOnly, addWay', updateWays,
wayGeneralFlags, wayUnsetGeneralFlags,
@@ -400,6 +401,7 @@ data DumpFlag
| Opt_D_no_debug_output
deriving (Eq, Show, Enum)
+
-- | Enumerates the simple on-or-off dynamic flags
data GeneralFlag
-- See Note [Updating flag description in the User's Guide]
@@ -478,6 +480,10 @@ data GeneralFlag
| Opt_AlignmentSanitisation
| Opt_CatchBottoms
+ -- PreInlining is on by default. The option is there just to see how
+ -- bad things get if you turn it off!
+ | Opt_SimplPreInlining
+
-- Interface files
| Opt_IgnoreInterfacePragmas
| Opt_OmitInterfacePragmas
@@ -491,6 +497,8 @@ data GeneralFlag
-- misc opts
| Opt_Pp
| Opt_ForceRecomp
+ | Opt_IgnoreOptimChanges
+ | Opt_IgnoreHpcChanges
| Opt_ExcessPrecision
| Opt_EagerBlackHoling
| Opt_NoHsMain
@@ -535,10 +543,6 @@ data GeneralFlag
| Opt_VersionMacros
| Opt_WholeArchiveHsLibs
- -- PreInlining is on by default. The option is there just to see how
- -- bad things get if you turn it off!
- | Opt_SimplPreInlining
-
-- output style opts
| Opt_ErrorSpans -- Include full span info in error messages,
-- instead of just the start position.
@@ -593,6 +597,65 @@ data GeneralFlag
| Opt_G_NoOptCoercion
deriving (Eq, Show, Enum)
+-- Check whether a flag should be considered an "optimisation flag"
+-- for purposes of recompilation avoidance (see
+-- Note [Ignoring some flag changes] in FlagChecker). Being listed here is
+-- not a guarantee that the flag has no other effect. We could, and
+-- perhaps should, separate out the flags that have some minor impact on
+-- program semantics and/or error behavior (e.g., assertions), but
+-- then we'd need to go to extra trouble (and an additional flag)
+-- to allow users to ignore the optimisation level even though that
+-- means ignoring some change.
+optimisationFlags :: EnumSet GeneralFlag
+optimisationFlags = EnumSet.fromList
+ [ Opt_CallArity
+ , Opt_Strictness
+ , Opt_LateDmdAnal
+ , Opt_KillAbsence
+ , Opt_KillOneShot
+ , Opt_FullLaziness
+ , Opt_FloatIn
+ , Opt_Specialise
+ , Opt_SpecialiseAggressively
+ , Opt_CrossModuleSpecialise
+ , Opt_StaticArgumentTransformation
+ , Opt_CSE
+ , Opt_StgCSE
+ , Opt_LiberateCase
+ , Opt_SpecConstr
+ , Opt_SpecConstrKeen
+ , Opt_DoLambdaEtaExpansion
+ , Opt_IgnoreAsserts
+ , Opt_DoEtaReduction
+ , Opt_CaseMerge
+ , Opt_CaseFolding
+ , Opt_UnboxStrictFields
+ , Opt_UnboxSmallStrictFields
+ , Opt_DictsCheap
+ , Opt_EnableRewriteRules
+ , Opt_Vectorise
+ , Opt_VectorisationAvoidance
+ , Opt_RegsGraph
+ , Opt_RegsIterative
+ , Opt_PedanticBottoms
+ , Opt_LlvmTBAA
+ , Opt_LlvmPassVectorsInRegisters
+ , Opt_LlvmFillUndefWithGarbage
+ , Opt_IrrefutableTuples
+ , Opt_CmmSink
+ , Opt_CmmElimCommonBlocks
+ , Opt_OmitYields
+ , Opt_FunToThunk
+ , Opt_DictsStrict
+ , Opt_DmdTxDictSel
+ , Opt_Loopification
+ , Opt_CprAnal
+ , Opt_WorkerWrapper
+ , Opt_SolveConstantDicts
+ , Opt_CatchBottoms
+ , Opt_IgnoreAsserts
+ ]
+
-- | Used when outputting warnings: if a reason is given, it is
-- displayed. If a warning isn't controlled by a flag, this is made
-- explicit at the point of use.
@@ -3754,6 +3817,8 @@ fFlagsDeps = [
flagSpec "flat-cache" Opt_FlatCache,
flagSpec "float-in" Opt_FloatIn,
flagSpec "force-recomp" Opt_ForceRecomp,
+ flagSpec "ignore-optim-changes" Opt_IgnoreOptimChanges,
+ flagSpec "ignore-hpc-changes" Opt_IgnoreHpcChanges,
flagSpec "full-laziness" Opt_FullLaziness,
flagSpec "fun-to-thunk" Opt_FunToThunk,
flagSpec "gen-manifest" Opt_GenManifest,
diff --git a/compiler/main/HscTypes.hs b/compiler/main/HscTypes.hs
index b7baa57dff..fa0eed2344 100644
--- a/compiler/main/HscTypes.hs
+++ b/compiler/main/HscTypes.hs
@@ -857,7 +857,10 @@ data ModIface
mi_iface_hash :: !Fingerprint, -- ^ Hash of the whole interface
mi_mod_hash :: !Fingerprint, -- ^ Hash of the ABI only
mi_flag_hash :: !Fingerprint, -- ^ Hash of the important flags
- -- used when compiling this module
+ -- used when compiling the module,
+ -- excluding optimisation flags
+ mi_opt_hash :: !Fingerprint, -- ^ Hash of optimisation flags
+ mi_hpc_hash :: !Fingerprint, -- ^ Hash of hpc flags
mi_orphan :: !WhetherHasOrphans, -- ^ Whether this module has orphans
mi_finsts :: !WhetherHasFamInst,
@@ -1018,6 +1021,8 @@ instance Binary ModIface where
mi_iface_hash= iface_hash,
mi_mod_hash = mod_hash,
mi_flag_hash = flag_hash,
+ mi_opt_hash = opt_hash,
+ mi_hpc_hash = hpc_hash,
mi_orphan = orphan,
mi_finsts = hasFamInsts,
mi_deps = deps,
@@ -1044,6 +1049,8 @@ instance Binary ModIface where
put_ bh iface_hash
put_ bh mod_hash
put_ bh flag_hash
+ put_ bh opt_hash
+ put_ bh hpc_hash
put_ bh orphan
put_ bh hasFamInsts
lazyPut bh deps
@@ -1072,6 +1079,8 @@ instance Binary ModIface where
iface_hash <- get bh
mod_hash <- get bh
flag_hash <- get bh
+ opt_hash <- get bh
+ hpc_hash <- get bh
orphan <- get bh
hasFamInsts <- get bh
deps <- lazyGet bh
@@ -1099,6 +1108,8 @@ instance Binary ModIface where
mi_iface_hash = iface_hash,
mi_mod_hash = mod_hash,
mi_flag_hash = flag_hash,
+ mi_opt_hash = opt_hash,
+ mi_hpc_hash = hpc_hash,
mi_orphan = orphan,
mi_finsts = hasFamInsts,
mi_deps = deps,
@@ -1136,6 +1147,8 @@ emptyModIface mod
mi_iface_hash = fingerprint0,
mi_mod_hash = fingerprint0,
mi_flag_hash = fingerprint0,
+ mi_opt_hash = fingerprint0,
+ mi_hpc_hash = fingerprint0,
mi_orphan = False,
mi_finsts = False,
mi_hsc_src = HsSrcFile,