Wolfgang Jeltsch pushed to branch wip/sort-usages at Glasgow Haskell Compiler / GHC
Commits:
-
f49f411f
by Wolfgang Jeltsch at 2026-06-09T13:50:52+03:00
3 changed files:
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Unit/Module/Deps.hs
- testsuite/tests/driver/recomp016/recomp016.stdout
Changes:
| ... | ... | @@ -12,7 +12,6 @@ import GHC.Tc.Types |
| 12 | 12 | import GHC.Iface.Load
|
| 13 | 13 | |
| 14 | 14 | import GHC.Utils.Outputable
|
| 15 | -import GHC.Utils.Misc
|
|
| 16 | 15 | import GHC.Utils.Fingerprint
|
| 17 | 16 | import GHC.Utils.Panic
|
| 18 | 17 | import GHC.Utils.Monad
|
| ... | ... | @@ -33,7 +32,7 @@ import GHC.Data.Maybe |
| 33 | 32 | import GHC.Data.FastString
|
| 34 | 33 | |
| 35 | 34 | import Data.Containers.ListUtils (nubOrdOn)
|
| 36 | -import Data.List (sortBy, sortOn)
|
|
| 35 | +import Data.List (sortBy)
|
|
| 37 | 36 | import Data.Map (Map)
|
| 38 | 37 | import qualified Data.Map as Map
|
| 39 | 38 | import qualified Data.Set as Set
|
| ... | ... | @@ -70,8 +69,8 @@ data UsageConfig = UsageConfig |
| 70 | 69 | { uc_safe_implicit_imps_req :: !Bool -- ^ Are all implicit imports required to be safe for this Safe Haskell mode?
|
| 71 | 70 | }
|
| 72 | 71 | |
| 73 | --- | Build the list of 'Usage's that drives recompilation checking.
|
|
| 74 | --- The resulting list is deterministically sorted (see 'usageFingerprint').
|
|
| 72 | +-- | Build the list of 'Usage's that drive recompilation checking.
|
|
| 73 | +-- The resulting list is deterministically sorted.
|
|
| 75 | 74 | mkUsageInfo :: UsageConfig -> Plugins -> FinderCache -> UnitEnv
|
| 76 | 75 | -> Module -> ImportedMods -> [ImportUserSpec] -> NameSet
|
| 77 | 76 | -> [FilePath] -> [FilePath] -> [(Module, Fingerprint)] -> [LinkableUsage] -> PkgsLoaded
|
| ... | ... | @@ -102,13 +101,14 @@ mkUsageInfo uc plugins fc unit_env |
| 102 | 101 | }
|
| 103 | 102 | | (mod, hash) <- merged ]
|
| 104 | 103 | ++ object_usages
|
| 105 | - |
|
| 106 | - -- Sort all the Usages to ensure a deterministic ordering.
|
|
| 107 | - let sorted_usages = sortOn usageFingerprint usages
|
|
| 108 | - sorted_usages `seqList` return sorted_usages
|
|
| 109 | - -- seq the list of Usages returned: occasionally these
|
|
| 110 | - -- don't get evaluated for a while and we can end up hanging on to
|
|
| 111 | - -- the entire collection of Ifaces.
|
|
| 104 | + return (sortBy stableUsageCmp usages)
|
|
| 105 | + -- Note that the sorting serves two purposes:
|
|
| 106 | + -- * It makes the order of usages deterministic.
|
|
| 107 | + -- * It evaluates the spine of 'usages' and reduces all of its elements to
|
|
| 108 | + -- at least WHNF und thus has at least the effect of 'seqList'.
|
|
| 109 | + -- The latter is important because occasionally the returned list is not
|
|
| 110 | + -- evaluated for a while, so that with too much laziness here we could end
|
|
| 111 | + -- up hanging on to the entire collection of 'Iface's.
|
|
| 112 | 112 | |
| 113 | 113 | {- Note [Plugin dependencies]
|
| 114 | 114 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 1 | 1 | {-# LANGUAGE PatternSynonyms #-}
|
| 2 | 2 | {-# LANGUAGE DerivingVia #-}
|
| 3 | + |
|
| 4 | +{-# OPTIONS_GHC -Wwarn=incomplete-record-selectors #-}
|
|
| 5 | + |
|
| 3 | 6 | -- | Dependencies and Usage of a module
|
| 4 | 7 | module GHC.Unit.Module.Deps
|
| 5 | 8 | ( Dependencies(dep_direct_mods
|
| ... | ... | @@ -17,7 +20,7 @@ module GHC.Unit.Module.Deps |
| 17 | 20 | , noDependencies
|
| 18 | 21 | , pprDeps
|
| 19 | 22 | , Usage (..)
|
| 20 | - , usageFingerprint
|
|
| 23 | + , stableUsageCmp
|
|
| 21 | 24 | , HomeModImport (..)
|
| 22 | 25 | , HomeModImportedAvails (..)
|
| 23 | 26 | , ImportAvails (..)
|
| ... | ... | @@ -53,6 +56,7 @@ import GHC.Utils.Outputable |
| 53 | 56 | import Control.DeepSeq
|
| 54 | 57 | import Data.Bifunctor
|
| 55 | 58 | import qualified Data.Foldable as Foldable
|
| 59 | +import Data.Function (on)
|
|
| 56 | 60 | import Data.List (sortBy, sort, partition)
|
| 57 | 61 | import Data.Set (Set)
|
| 58 | 62 | import qualified Data.Set as Set
|
| ... | ... | @@ -498,16 +502,46 @@ instance Binary Usage where |
| 498 | 502 | |
| 499 | 503 | i -> error ("Binary.get(Usage): " ++ show i)
|
| 500 | 504 | |
| 501 | --- | Extract the distinguishing fingerprint carried by a particular 'Usage'
|
|
| 502 | --- constructor. Every constructor stores a hash capturing the bit of state
|
|
| 503 | --- that drives recompilation decisions, so we can sort on it directly.
|
|
| 504 | -usageFingerprint :: Usage -> Fingerprint
|
|
| 505 | -usageFingerprint UsagePackageModule{ usg_mod_hash = fp } = fp
|
|
| 506 | -usageFingerprint UsageHomeModule{ usg_mod_hash = fp } = fp
|
|
| 507 | -usageFingerprint UsageFile{ usg_file_hash = fp } = fp
|
|
| 508 | -usageFingerprint UsageDirectory{ usg_dir_hash = fp } = fp
|
|
| 509 | -usageFingerprint UsageHomeModuleBytecode{ usg_bytecode_hash = fp } = fp
|
|
| 510 | -usageFingerprint UsageMergedRequirement{ usg_mod_hash = fp } = fp
|
|
| 505 | +-- | Compares 'Usage's by constructor and, if the constructors are the same, by
|
|
| 506 | +-- identifying strings, to achieve a predictable ordering.
|
|
| 507 | +stableUsageCmp :: Usage -> Usage -> Ordering
|
|
| 508 | +stableUsageCmp
|
|
| 509 | + usage1@UsagePackageModule {}
|
|
| 510 | + usage2@UsagePackageModule {}
|
|
| 511 | + = (compare `on` usg_mod) usage1 usage2
|
|
| 512 | +stableUsageCmp
|
|
| 513 | + usage1@UsageHomeModule {}
|
|
| 514 | + usage2@UsageHomeModule {}
|
|
| 515 | + = (compare `on` Module <$> usg_unit_id <*> usg_mod_name) usage1 usage2
|
|
| 516 | +stableUsageCmp
|
|
| 517 | + usage1@UsageFile {}
|
|
| 518 | + usage2@UsageFile {}
|
|
| 519 | + = (lexicalCompareFS `on` usg_file_path) usage1 usage2
|
|
| 520 | +stableUsageCmp
|
|
| 521 | + usage1@UsageDirectory {}
|
|
| 522 | + usage2@UsageDirectory {}
|
|
| 523 | + = (lexicalCompareFS `on` usg_dir_path) usage1 usage2
|
|
| 524 | +stableUsageCmp
|
|
| 525 | + usage1@UsageHomeModuleBytecode {}
|
|
| 526 | + usage2@UsageHomeModuleBytecode {}
|
|
| 527 | + = (compare `on` Module <$> usg_unit_id <*> usg_mod_name) usage1 usage2
|
|
| 528 | +stableUsageCmp
|
|
| 529 | + usage1@UsageMergedRequirement {}
|
|
| 530 | + usage2@UsageMergedRequirement {}
|
|
| 531 | + = (compare `on` usg_mod) usage1 usage2
|
|
| 532 | +stableUsageCmp
|
|
| 533 | + usage1
|
|
| 534 | + usage2
|
|
| 535 | + = (compare `on` constructorIndex) usage1 usage2
|
|
| 536 | + where
|
|
| 537 | + |
|
| 538 | + constructorIndex :: Usage -> Int
|
|
| 539 | + constructorIndex UsagePackageModule {} = 0
|
|
| 540 | + constructorIndex UsageHomeModule {} = 1
|
|
| 541 | + constructorIndex UsageFile {} = 2
|
|
| 542 | + constructorIndex UsageDirectory {} = 3
|
|
| 543 | + constructorIndex UsageHomeModuleBytecode {} = 4
|
|
| 544 | + constructorIndex UsageMergedRequirement {} = 5
|
|
| 511 | 545 | |
| 512 | 546 | -- | Records the imports that we depend on from a home module,
|
| 513 | 547 | -- for recompilation checking.
|
| ... | ... | @@ -9,4 +9,4 @@ second run |
| 9 | 9 | [2 of 5] Compiling B ( B.hs, B.o ) [Source file changed]
|
| 10 | 10 | [3 of 5] Compiling C ( C.hs, C.o ) [B changed]
|
| 11 | 11 | [4 of 5] Compiling D ( D.hs, D.o ) [C changed]
|
| 12 | -[5 of 5] Compiling E ( E.hs, E.o ) [D changed] |
|
| 12 | +[5 of 5] Compiling E ( E.hs, E.o ) [B changed] |