Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
098bba70
by Facundo Domínguez at 2026-09-17T14:57:41-04:00
-
e01799c7
by Facundo Domínguez at 2026-09-17T14:57:42-04:00
-
3875b257
by Facundo Domínguez at 2026-09-17T14:57:42-04:00
-
5236634a
by Andreas Klebinger at 2026-09-17T14:58:24-04:00
-
81ae48b5
by ARATA Mizuki at 2026-09-21T16:08:19-04:00
-
b70eb2c5
by Wolfgang Jeltsch at 2026-09-21T16:08:20-04:00
-
cbba15de
by Wolfgang Jeltsch at 2026-09-21T16:08:20-04:00
-
94826f17
by Wolfgang Jeltsch at 2026-09-21T16:08:20-04:00
30 changed files:
- + changelog.d/25763-cmdline-opt
- + compiler/GHC/Data/StringTrie.hs
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Data/Word64Set/Internal.hs
- compiler/GHC/Driver/CmdLine.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Unit/Env.hs
- compiler/ghc.cabal.in
- ghc/GHC/Driver/Session/Mode.hs
- rts/include/stg/Prim.h
- rts/prim/bitrev.c
- rts/prim/bswap.c
- testsuite/tests/MiniQuickCheck.hs
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/Makefile
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
- + testsuite/tests/driver/stringtrie.hs
- + testsuite/tests/driver/stringtrie.stdout
- + testsuite/tests/perf/compiler/T25763.stderr
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/primops/should_run/T27299.hs
- + testsuite/tests/primops/should_run/T27299.stdout
- testsuite/tests/primops/should_run/all.T
- + testsuite/tests/warnings/should_fail/WExpectedNoArgError.stderr
- testsuite/tests/warnings/should_fail/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Speed up command line parser with a trie map
|
|
| 3 | +description:
|
|
| 4 | + Parsing flags is now faster when there are many flags to parse.
|
|
| 5 | + |
|
| 6 | + There is a new trie implementation with String keys in ``GHC.Data.StringTrie``.
|
|
| 7 | + |
|
| 8 | + Added a new abstract type ``FlagSpecTrie`` to represent tries of flags.
|
|
| 9 | + |
|
| 10 | + ``GHC.Driver.Session`` gets ``flagsDynamic`` removed and ``flagsDynamicTrie``
|
|
| 11 | + added with type ``FlagSpecTrie``. ``processCmdLineP`` and ``parseDynamicFlagsFull``
|
|
| 12 | + now have a ``FlagSpecTrie`` parameter instead of a list.
|
|
| 13 | + |
|
| 14 | + ``GHC.Driver.Session.Mode`` gets a value ``mode_flags_trie`` of type ``FlagSpecTrie``.
|
|
| 15 | + |
|
| 16 | + ``GHC.Driver.CmdLine.processArgs`` now gets a ``FlagSpecTrie`` argument instead
|
|
| 17 | + of a list.
|
|
| 18 | +mrs: !16392
|
|
| 19 | +issues: #25763 |
| 1 | +module GHC.Data.StringTrie
|
|
| 2 | + ( StringTrie(..)
|
|
| 3 | + , empty
|
|
| 4 | + , insert
|
|
| 5 | + , fromList
|
|
| 6 | + , fromListWithSparseness
|
|
| 7 | + , lookup
|
|
| 8 | + ) where
|
|
| 9 | + |
|
| 10 | +import GHC.Prelude hiding (lookup)
|
|
| 11 | + |
|
| 12 | +import Data.Array (Array, accumArray, assocs)
|
|
| 13 | +import Data.Maybe (fromMaybe)
|
|
| 14 | +import qualified Data.IntMap.Lazy as M
|
|
| 15 | + |
|
| 16 | +-- | A trie mapping strings to values of type @a@.
|
|
| 17 | +--
|
|
| 18 | +-- See #25763 and Note [Optimising the processing of command-line arguments]
|
|
| 19 | +-- in "GHC.Driver.Session".
|
|
| 20 | +data StringTrie a
|
|
| 21 | + = -- | A value and other children under the same path.
|
|
| 22 | + --
|
|
| 23 | + -- The 'IntMap' can be empty.
|
|
| 24 | + STNode a (M.IntMap (StringTrie a))
|
|
| 25 | + -- | There is no value at this path, but there are children under it
|
|
| 26 | + --
|
|
| 27 | + -- The 'IntMap' must be non-empty, so 'STEmpty' remains the only way to
|
|
| 28 | + -- represent a trie with no values.
|
|
| 29 | + | STFork (M.IntMap (StringTrie a))
|
|
| 30 | + -- | A trie with no values
|
|
| 31 | + | STEmpty
|
|
| 32 | + deriving (Eq, Show)
|
|
| 33 | + |
|
| 34 | +-- | There are no key-value pairs in this trie.
|
|
| 35 | +empty :: StringTrie a
|
|
| 36 | +empty = STEmpty
|
|
| 37 | + |
|
| 38 | +-- | Inserts a key-value pair into the trie. When a value for the key is already
|
|
| 39 | +-- present, the new value replaces the old one.
|
|
| 40 | +insert :: forall a. String -> a -> StringTrie a -> StringTrie a
|
|
| 41 | +insert k0 v = go k0
|
|
| 42 | + where
|
|
| 43 | + go :: String -> StringTrie a -> StringTrie a
|
|
| 44 | + go (x:xs) STEmpty = STFork $ M.singleton (fromEnum x) (go xs STEmpty)
|
|
| 45 | + go "" STEmpty = STNode v M.empty
|
|
| 46 | + go "" (STFork children) = STNode v children
|
|
| 47 | + go (x:xs) (STFork children) =
|
|
| 48 | + STFork (M.insert (fromEnum x) child children)
|
|
| 49 | + where
|
|
| 50 | + child = go xs (fromMaybe empty $ M.lookup (fromEnum x) children)
|
|
| 51 | + |
|
| 52 | + go "" (STNode _ children) = STNode v children
|
|
| 53 | + go (x:xs) (STNode vs children) =
|
|
| 54 | + STNode vs (M.insert (fromEnum x) child children)
|
|
| 55 | + where
|
|
| 56 | + child = go xs (fromMaybe empty $ M.lookup (fromEnum x) children)
|
|
| 57 | + |
|
| 58 | +-- | Returns the values associated with prefixes of a query string as well as
|
|
| 59 | +-- the non-matching suffix. The matches are returned ordered by suffix length.
|
|
| 60 | +--
|
|
| 61 | +-- Examples:
|
|
| 62 | +--
|
|
| 63 | +-- > lookup "abc" (fromList [("ab", 2), ("abc", 3), ("b", 4)]) == [(3, ""), (2, "c")]
|
|
| 64 | +-- > lookup "abc" (fromList [("ab", 2), ("ab", 3)]) == [(3, "c")]
|
|
| 65 | +-- > lookup "ba" (fromList [("ab", 2), ("abc", 3)]) == []
|
|
| 66 | +--
|
|
| 67 | +-- More formally:
|
|
| 68 | +--
|
|
| 69 | +-- @(v, suffix)@ is in @lookup queryString t@ iff there is a string @k@ such that
|
|
| 70 | +-- * @(k, v)@ is in @t@, and
|
|
| 71 | +-- * @k ++ suffix == queryString@.
|
|
| 72 | +lookup
|
|
| 73 | + :: String
|
|
| 74 | + -> StringTrie a
|
|
| 75 | + -> [(a, String)]
|
|
| 76 | +lookup = go []
|
|
| 77 | + where
|
|
| 78 | + go :: [(a, String)] -> String -> StringTrie a -> [(a, String)]
|
|
| 79 | + --
|
|
| 80 | + -- In @lookup queryString t@ and @go acc unmatchedSuffix t'@,
|
|
| 81 | + --
|
|
| 82 | + -- unmatchedSuffix is a suffix of queryString, and t' is the subtrie
|
|
| 83 | + -- of t at path @queryString - unmatchedSuffix@.
|
|
| 84 | + --
|
|
| 85 | + -- (v, suffix) is in acc iff there is a k such that
|
|
| 86 | + -- * (k, v) is in t, and
|
|
| 87 | + -- * @k ++ suffix == queryString@, and
|
|
| 88 | + -- * @unmatchedSuffix /= suffix@.
|
|
| 89 | + go acc [] (STNode v _) = (v, "") : acc
|
|
| 90 | + go acc [] (STFork _) = acc
|
|
| 91 | + go acc _ STEmpty = acc
|
|
| 92 | + go acc unmatchedSuffix@(c:rest) (STNode v children) =
|
|
| 93 | + let acc' = (v, unmatchedSuffix) : acc
|
|
| 94 | + in maybe acc' (go acc' rest) (M.lookup (fromEnum c) children)
|
|
| 95 | + go acc (c:rest) (STFork children) =
|
|
| 96 | + maybe acc (go acc rest) (M.lookup (fromEnum c) children)
|
|
| 97 | + |
|
| 98 | +-- | Constructs a trie from a list of key-value pairs.
|
|
| 99 | +--
|
|
| 100 | +-- Later entries in the list will overwrite earlier ones with the same key.
|
|
| 101 | +--
|
|
| 102 | +-- Equivalent to @foldl (\t (k, v) -> insert k v t) empty@, but lazier: the
|
|
| 103 | +-- subtrie for each first character is only built when demanded.
|
|
| 104 | +fromList :: [(String, a)] -> StringTrie a
|
|
| 105 | +fromList = fromListWithSparseness 32
|
|
| 106 | + |
|
| 107 | +-- | Like 'fromList', but allows the caller to specify a threshold for when to use
|
|
| 108 | +-- an array to sort the keys.
|
|
| 109 | +fromListWithSparseness :: Int -> [(String, a)] -> StringTrie a
|
|
| 110 | +fromListWithSparseness sparseness = go
|
|
| 111 | + where
|
|
| 112 | + go [] = STEmpty
|
|
| 113 | + go xs =
|
|
| 114 | + let revxs = reverse xs
|
|
| 115 | + valuesWithEmptyKeys = filter (null . fst) revxs
|
|
| 116 | + children = M.map go (headBuckets sparseness revxs)
|
|
| 117 | + in case valuesWithEmptyKeys of
|
|
| 118 | + (_, v) : _ -> STNode v children
|
|
| 119 | + _ -> STFork children
|
|
| 120 | + |
|
| 121 | + -- Group the entries with a non-empty key by their first character (in reverse order).
|
|
| 122 | + --
|
|
| 123 | + -- Discards entries with an empty key. Uses an array to sort the entries, unless the
|
|
| 124 | + -- entries are too sparse.
|
|
| 125 | + headBuckets :: Int -> [(String, a)] -> M.IntMap [(String, a)]
|
|
| 126 | + headBuckets sparseness kvs
|
|
| 127 | + -- Entries are too sparse if they are too few or if they span too large a range
|
|
| 128 | + -- of characters.
|
|
| 129 | + | null (drop sparseness kvs) || any non_ascii kvs
|
|
| 130 | + = M.fromListWith (++) [ (fromEnum c, [(cs, v)]) | (c:cs, v) <- kvs ]
|
|
| 131 | + | otherwise
|
|
| 132 | + = M.fromDistinctAscList
|
|
| 133 | + [ (i, group) | (i, group) <- assocs (bucketsArr kvs), not (null group) ]
|
|
| 134 | + |
|
| 135 | + -- @(k, v)@ is in @bucketsArr kvs ! i@ iff @(tail k, v)@ is in @kvs@ and
|
|
| 136 | + -- @fromEnum (head k) == i@.
|
|
| 137 | + --
|
|
| 138 | + -- Note: Discard entries with an empty key.
|
|
| 139 | + bucketsArr :: [(String, a)] -> Array Int [(String, a)]
|
|
| 140 | + bucketsArr kvs =
|
|
| 141 | + accumArray
|
|
| 142 | + (flip (:))
|
|
| 143 | + []
|
|
| 144 | + (0, 0x7f)
|
|
| 145 | + [ (fromEnum c, (cs, v)) | (c:cs, v) <- kvs ]
|
|
| 146 | + |
|
| 147 | + non_ascii (c:_, _) = fromEnum c > 0x7f
|
|
| 148 | + non_ascii ([], _) = False |
| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | |
| 4 | 4 | {-# OPTIONS_HADDOCK not-home #-}
|
| 5 | 5 | {-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
|
| 6 | +-- Speeds up otherwise unoptimized ghcs by 5-25% (#27814)
|
|
| 7 | +{-# OPTIONS_GHC -O2 #-}
|
|
| 6 | 8 | |
| 7 | 9 | |
| 8 | 10 | -----------------------------------------------------------------------------
|
| 1 | 1 | |
| 2 | 2 | {-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
|
| 3 | +-- Speeds up otherwise unoptimized ghcs by 5-25% (#27814)
|
|
| 4 | +{-# OPTIONS_GHC -O2 #-}
|
|
| 3 | 5 | |
| 4 | 6 | -----------------------------------------------------------------------------
|
| 5 | 7 | -- |
|
| ... | ... | @@ -2,6 +2,8 @@ |
| 2 | 2 | {-# LANGUAGE TypeFamilies #-}
|
| 3 | 3 | |
| 4 | 4 | {-# OPTIONS_HADDOCK not-home #-}
|
| 5 | +-- Speeds up otherwise unoptimized ghcs by 5-25% (#27814)
|
|
| 6 | +{-# OPTIONS_GHC -O2 #-}
|
|
| 5 | 7 | |
| 6 | 8 | -----------------------------------------------------------------------------
|
| 7 | 9 | -- |
|
| 1 | +{-# LANGUAGE BangPatterns #-}
|
|
| 1 | 2 | -------------------------------------------------------------------------------
|
| 2 | 3 | --
|
| 3 | 4 | -- | Command-line parser
|
| 4 | 5 | --
|
| 5 | --- This is an abstract command-line parser used by DynFlags.
|
|
| 6 | +-- This is a command-line parser used by DynFlags.
|
|
| 6 | 7 | --
|
| 7 | 8 | -- (c) The University of Glasgow 2005
|
| 8 | 9 | --
|
| ... | ... | @@ -10,13 +11,26 @@ |
| 10 | 11 | |
| 11 | 12 | module GHC.Driver.CmdLine
|
| 12 | 13 | (
|
| 13 | - processArgs, parseResponseFile, OptKind(..), GhcFlagMode(..),
|
|
| 14 | - Flag(..), defFlag, defGhcFlag, defGhciFlag, defHiddenFlag, hoistFlag,
|
|
| 15 | - errorsToGhcException,
|
|
| 14 | + processArgs,
|
|
| 15 | + |
|
| 16 | + -- ** Flags
|
|
| 17 | + Flag(..), defFlag, defGhcFlag, defGhciFlag, defHiddenFlag,
|
|
| 18 | + OptKind(..), GhcFlagMode(..),
|
|
| 19 | + |
|
| 20 | + -- ** EwM monad
|
|
| 21 | + EwM, runEwM, addErr, addWarn, addFlagWarn, getArg, getCurLoc, liftEwM,
|
|
| 16 | 22 | |
| 17 | 23 | Err(..), Warn, warnsToMessages,
|
| 18 | 24 | |
| 19 | - EwM, runEwM, addErr, addWarn, addFlagWarn, getArg, getCurLoc, liftEwM
|
|
| 25 | + -- ** FlagSpecTrie
|
|
| 26 | + FlagSpecTrie, mkFlagSpecTrie, extendFlagSpecTrie,
|
|
| 27 | + |
|
| 28 | + -- ** State
|
|
| 29 | + CmdLineP(..), runCmdLineP,
|
|
| 30 | + getCmdLineState, putCmdLineState,
|
|
| 31 | + |
|
| 32 | + -- ** Utils
|
|
| 33 | + parseResponseFile, errorsToGhcException
|
|
| 20 | 34 | ) where
|
| 21 | 35 | |
| 22 | 36 | import GHC.Prelude
|
| ... | ... | @@ -24,6 +38,7 @@ import GHC.Prelude |
| 24 | 38 | import GHC.Utils.Misc
|
| 25 | 39 | import GHC.Utils.Panic
|
| 26 | 40 | import GHC.Data.Bag
|
| 41 | +import qualified GHC.Data.StringTrie as StringTrie
|
|
| 27 | 42 | import GHC.Types.SrcLoc
|
| 28 | 43 | import GHC.Types.Error
|
| 29 | 44 | import GHC.Utils.Error
|
| ... | ... | @@ -31,14 +46,16 @@ import GHC.Driver.Errors.Types |
| 31 | 46 | import GHC.Driver.Errors.Ppr () -- instance Diagnostic DriverMessage
|
| 32 | 47 | import GHC.Utils.Outputable (text)
|
| 33 | 48 | |
| 34 | -import Data.Function
|
|
| 35 | -import Data.List (sortBy, intercalate, stripPrefix)
|
|
| 49 | +import Data.List (intercalate)
|
|
| 36 | 50 | import Data.Word
|
| 37 | 51 | |
| 38 | 52 | import GHC.ResponseFile
|
| 39 | 53 | import Control.Exception (IOException, catch)
|
| 40 | 54 | import Control.Monad (ap)
|
| 41 | 55 | import Control.Monad.IO.Class
|
| 56 | +import Control.Monad.Trans.State (StateT)
|
|
| 57 | +import qualified Control.Monad.Trans.State as State
|
|
| 58 | +import Data.Functor.Identity (runIdentity)
|
|
| 42 | 59 | |
| 43 | 60 | --------------------------------------------------------
|
| 44 | 61 | -- The Flag and OptKind types
|
| ... | ... | @@ -62,24 +79,6 @@ defGhciFlag name optKind = Flag name optKind OnlyGhci |
| 62 | 79 | defHiddenFlag :: String -> OptKind m -> Flag m
|
| 63 | 80 | defHiddenFlag name optKind = Flag name optKind HiddenFlag
|
| 64 | 81 | |
| 65 | -hoistFlag :: forall m n. (forall a. m a -> n a) -> Flag m -> Flag n
|
|
| 66 | -hoistFlag f (Flag a b c) = Flag a (go b) c
|
|
| 67 | - where
|
|
| 68 | - go (NoArg k) = NoArg (go2 k)
|
|
| 69 | - go (HasArg k) = HasArg (\s -> go2 (k s))
|
|
| 70 | - go (SepArg k) = SepArg (\s -> go2 (k s))
|
|
| 71 | - go (Prefix k) = Prefix (\s -> go2 (k s))
|
|
| 72 | - go (OptPrefix k) = OptPrefix (\s -> go2 (k s))
|
|
| 73 | - go (OptIntSuffix k) = OptIntSuffix (\n -> go2 (k n))
|
|
| 74 | - go (IntSuffix k) = IntSuffix (\n -> go2 (k n))
|
|
| 75 | - go (Word64Suffix k) = Word64Suffix (\s -> go2 (k s))
|
|
| 76 | - go (FloatSuffix k) = FloatSuffix (\s -> go2 (k s))
|
|
| 77 | - go (PassFlag k) = PassFlag (\s -> go2 (k s))
|
|
| 78 | - go (AnySuffix k) = AnySuffix (\s -> go2 (k s))
|
|
| 79 | - |
|
| 80 | - go2 :: EwM m a -> EwM n a
|
|
| 81 | - go2 (EwM g) = EwM $ \loc es ws -> f (g loc es ws)
|
|
| 82 | - |
|
| 83 | 82 | -- | GHC flag modes describing when a flag has an effect.
|
| 84 | 83 | data GhcFlagMode
|
| 85 | 84 | = OnlyGhc -- ^ The flag only affects the non-interactive GHC
|
| ... | ... | @@ -159,6 +158,9 @@ getCurLoc = EwM (\(L loc _) es ws -> return (es, ws, loc)) |
| 159 | 158 | liftEwM :: Monad m => m a -> EwM m a
|
| 160 | 159 | liftEwM action = EwM (\_ es ws -> do { r <- action; return (es, ws, r) })
|
| 161 | 160 | |
| 161 | +hoistEwM :: (forall a. m a -> n a) -> EwM m b -> EwM n b
|
|
| 162 | +hoistEwM f (EwM g) = EwM $ \loc es ws -> f (g loc es ws)
|
|
| 163 | + |
|
| 162 | 164 | warnsToMessages :: DiagOpts -> [Warn] -> Messages DriverMessage
|
| 163 | 165 | warnsToMessages diag_opts = foldr
|
| 164 | 166 | (\(L loc w) ws -> addMessage (mkPlainMsgEnvelope diag_opts loc w) ws)
|
| ... | ... | @@ -168,20 +170,70 @@ warnsToMessages diag_opts = foldr |
| 168 | 170 | -- Processing arguments
|
| 169 | 171 | --------------------------------------------------------
|
| 170 | 172 | |
| 171 | -processArgs :: Monad m
|
|
| 172 | - => [Flag m] -- ^ cmdline parser spec
|
|
| 173 | - -> [Located String] -- ^ args
|
|
| 174 | - -> (FilePath -> EwM m [Located String]) -- ^ response file handler
|
|
| 175 | - -> m ( [Located String], -- spare args
|
|
| 173 | +newtype CmdLineP s a = CmdLineP (forall m. (Monad m) => StateT s m a)
|
|
| 174 | + deriving (Functor)
|
|
| 175 | + |
|
| 176 | +instance Monad (CmdLineP s) where
|
|
| 177 | + CmdLineP k >>= f = CmdLineP (k >>= \x -> case f x of CmdLineP g -> g)
|
|
| 178 | + return = pure
|
|
| 179 | + |
|
| 180 | +instance Applicative (CmdLineP s) where
|
|
| 181 | + pure x = CmdLineP (pure x)
|
|
| 182 | + (<*>) = ap
|
|
| 183 | + |
|
| 184 | +getCmdLineState :: CmdLineP s s
|
|
| 185 | +getCmdLineState = CmdLineP State.get
|
|
| 186 | + |
|
| 187 | +putCmdLineState :: s -> CmdLineP s ()
|
|
| 188 | +putCmdLineState x = CmdLineP (State.put x)
|
|
| 189 | + |
|
| 190 | +runCmdLineP :: CmdLineP s a -> s -> (a, s)
|
|
| 191 | +runCmdLineP (CmdLineP k) s0 = runIdentity $ State.runStateT k s0
|
|
| 192 | + |
|
| 193 | +-- | Flag specifications as a trie map
|
|
| 194 | +--
|
|
| 195 | +-- No two flags with the same 'flagName' are allowed.
|
|
| 196 | +--
|
|
| 197 | +-- See Note [Optimising the processing of command-line arguments]
|
|
| 198 | +-- in GHC.Driver.Session.
|
|
| 199 | +newtype FlagSpecTrie s =
|
|
| 200 | + -- | The key of each 'Flag' @f@ is @flagName f@.
|
|
| 201 | + FlagSpecTrie { unFlagSpecTrie :: StringTrie.StringTrie (Flag (CmdLineP s)) }
|
|
| 202 | + |
|
| 203 | +-- | Construct a 'FlagSpecTrie' from a list of flags.
|
|
| 204 | +--
|
|
| 205 | +-- Precondition: No two flags in the list have the same flag name.
|
|
| 206 | +mkFlagSpecTrie :: [Flag (CmdLineP s)] -> FlagSpecTrie s
|
|
| 207 | +mkFlagSpecTrie flags =
|
|
| 208 | + FlagSpecTrie $ StringTrie.fromList [ (flagName f, f) | !f <- flags ]
|
|
| 209 | +{-# INLINE mkFlagSpecTrie #-}
|
|
| 210 | + |
|
| 211 | +-- | Add flags to an existing 'FlagSpecTrie'.
|
|
| 212 | +--
|
|
| 213 | +-- Precondition: No two flags in the trie plus the list have the same flag name.
|
|
| 214 | +extendFlagSpecTrie :: FlagSpecTrie s -> [Flag (CmdLineP s)] -> FlagSpecTrie s
|
|
| 215 | +extendFlagSpecTrie (FlagSpecTrie t) flags =
|
|
| 216 | + FlagSpecTrie $ foldl' (\acc f -> StringTrie.insert (flagName f) f acc) t flags
|
|
| 217 | +{-# INLINE extendFlagSpecTrie #-}
|
|
| 218 | + |
|
| 219 | +processArgs :: forall s m. Monad m
|
|
| 220 | + => FlagSpecTrie s -- ^ cmdline parser spec
|
|
| 221 | + -> [Located String] -- ^ args
|
|
| 222 | + -> (FilePath -> EwM (StateT s m) [Located String]) -- ^ response file handler
|
|
| 223 | + -> StateT s m
|
|
| 224 | + ( [Located String], -- spare args
|
|
| 176 | 225 | [Err], -- errors
|
| 177 | 226 | Warns ) -- warnings
|
| 178 | 227 | processArgs spec args handleRespFile = do
|
| 179 | 228 | (errs, warns, spare) <- runEwM action
|
| 180 | 229 | return (spare, bagToList errs, warns)
|
| 181 | 230 | where
|
| 231 | + getCmdLineP :: CmdLineP s a -> StateT s m a
|
|
| 232 | + getCmdLineP (CmdLineP k) = k
|
|
| 233 | + |
|
| 182 | 234 | action = process args []
|
| 183 | 235 | |
| 184 | - -- process :: [Located String] -> [Located String] -> EwM m [Located String]
|
|
| 236 | + process :: [Located String] -> [Located String] -> EwM (StateT s m) [Located String]
|
|
| 185 | 237 | process [] spare = return (reverse spare)
|
| 186 | 238 | |
| 187 | 239 | process (L _ ('@' : resp_file) : args) spare = do
|
| ... | ... | @@ -189,7 +241,7 @@ processArgs spec args handleRespFile = do |
| 189 | 241 | process (resp_args ++ args) spare
|
| 190 | 242 | |
| 191 | 243 | process (locArg@(L _ ('-' : arg)) : args) spare =
|
| 192 | - case findArg spec arg of
|
|
| 244 | + case findArg (unFlagSpecTrie spec) arg of
|
|
| 193 | 245 | Just (rest, opt_kind) ->
|
| 194 | 246 | case processOneArg opt_kind rest arg args of
|
| 195 | 247 | Left err ->
|
| ... | ... | @@ -198,7 +250,7 @@ processArgs spec args handleRespFile = do |
| 198 | 250 | |
| 199 | 251 | Right (action,rest) ->
|
| 200 | 252 | let b = process rest spare
|
| 201 | - in (setArg locArg $ action) >> b
|
|
| 253 | + in setArg locArg (hoistEwM getCmdLineP action) >> b
|
|
| 202 | 254 | |
| 203 | 255 | Nothing -> process args (locArg : spare)
|
| 204 | 256 | |
| ... | ... | @@ -246,31 +298,38 @@ processOneArg opt_kind rest arg args |
| 246 | 298 | OptPrefix f -> Right (f rest_no_eq, args)
|
| 247 | 299 | AnySuffix f -> Right (f dash_arg, args)
|
| 248 | 300 | |
| 249 | -findArg :: [Flag m] -> String -> Maybe (String, OptKind m)
|
|
| 250 | -findArg spec arg =
|
|
| 251 | - case sortBy (compare `on` (length . fst)) -- prefer longest matching flag
|
|
| 252 | - [ (removeSpaces rest, optKind)
|
|
| 253 | - | flag <- spec,
|
|
| 254 | - let optKind = flagOptKind flag,
|
|
| 255 | - Just rest <- [stripPrefix (flagName flag) arg],
|
|
| 256 | - arg_ok optKind rest arg ]
|
|
| 257 | - of
|
|
| 258 | - [] -> Nothing
|
|
| 259 | - (one:_) -> Just one
|
|
| 260 | - |
|
| 261 | -arg_ok :: OptKind t -> [Char] -> String -> Bool
|
|
| 262 | -arg_ok (NoArg _) rest _ = null rest
|
|
| 263 | -arg_ok (HasArg _) _ _ = True
|
|
| 264 | -arg_ok (SepArg _) rest _ = null rest
|
|
| 265 | -arg_ok (Prefix _) _ _ = True -- Missing argument checked for in processOneArg t
|
|
| 266 | - -- to improve error message (#12625)
|
|
| 267 | -arg_ok (OptIntSuffix _) _ _ = True
|
|
| 268 | -arg_ok (IntSuffix _) _ _ = True
|
|
| 269 | -arg_ok (Word64Suffix _) _ _ = True
|
|
| 270 | -arg_ok (FloatSuffix _) _ _ = True
|
|
| 271 | -arg_ok (OptPrefix _) _ _ = True
|
|
| 272 | -arg_ok (PassFlag _) rest _ = null rest
|
|
| 273 | -arg_ok (AnySuffix _) _ _ = True
|
|
| 301 | +-- | Find a flag in the trie whose name is a prefix of the given string.
|
|
| 302 | +--
|
|
| 303 | +-- The unmatched suffix of the string is returned along with the 'OptKind'.
|
|
| 304 | +--
|
|
| 305 | +-- A flag is only returned if the suffix is valid for the 'OptKind' of the flag.
|
|
| 306 | +-- For example, a flag with 'NoArg' will only be returned if the suffix is
|
|
| 307 | +-- empty. More concretely, @-Wallx@ will produce an invalid suffix @"x"@ since
|
|
| 308 | +-- @-Wall@ has @OptKind@ 'NoArg'.
|
|
| 309 | +--
|
|
| 310 | +-- If more than one flag name is a prefix of the string, the longest match with
|
|
| 311 | +-- a valid suffix is returned.
|
|
| 312 | +findArg :: StringTrie.StringTrie (Flag m) -> String -> Maybe (String, OptKind m)
|
|
| 313 | +findArg trie arg =
|
|
| 314 | + case filter (uncurry valid_flag_suffix) $ StringTrie.lookup arg trie of
|
|
| 315 | + [] -> Nothing
|
|
| 316 | + (flag, rest) : _ -> Just (removeSpaces rest, flagOptKind flag)
|
|
| 317 | + where
|
|
| 318 | + valid_flag_suffix :: Flag m -> String -> Bool
|
|
| 319 | + valid_flag_suffix flag rest =
|
|
| 320 | + case flagOptKind flag of
|
|
| 321 | + NoArg _ -> null rest
|
|
| 322 | + HasArg _ -> True
|
|
| 323 | + SepArg _ -> null rest
|
|
| 324 | + Prefix _ -> True -- Missing argument checked for in processOneArg t
|
|
| 325 | + -- to improve error message (#12625)
|
|
| 326 | + OptIntSuffix _ -> True
|
|
| 327 | + IntSuffix _ -> True
|
|
| 328 | + Word64Suffix _ -> True
|
|
| 329 | + FloatSuffix _ -> True
|
|
| 330 | + OptPrefix _ -> True
|
|
| 331 | + PassFlag _ -> null rest
|
|
| 332 | + AnySuffix _ -> True
|
|
| 274 | 333 | |
| 275 | 334 | -- | Parse an Int
|
| 276 | 335 | --
|
| ... | ... | @@ -6,6 +6,8 @@ |
| 6 | 6 | {-# LANGUAGE BlockArguments #-}
|
| 7 | 7 | {-# LANGUAGE ViewPatterns #-}
|
| 8 | 8 | |
| 9 | +{-# OPTIONS_GHC -Wno-invalid-haddock #-}
|
|
| 10 | + |
|
| 9 | 11 | -- | See Note [The ModuleGraph]
|
| 10 | 12 | module GHC.Driver.Downsweep
|
| 11 | 13 | ( downsweep
|
| ... | ... | @@ -54,7 +56,6 @@ import GHC.Runtime.Context |
| 54 | 56 | import Language.Haskell.Syntax.ImpExp
|
| 55 | 57 | import GHC.Types.UnresolvedImport
|
| 56 | 58 | |
| 57 | -import GHC.Data.Graph.Directed
|
|
| 58 | 59 | import GHC.Data.FastString
|
| 59 | 60 | import GHC.Data.Maybe ( expectJust )
|
| 60 | 61 | import qualified GHC.Data.Maybe as M
|
| ... | ... | @@ -71,12 +72,15 @@ import GHC.Utils.Logger |
| 71 | 72 | import GHC.Utils.Fingerprint
|
| 72 | 73 | import GHC.Utils.TmpFs
|
| 73 | 74 | import GHC.Utils.Constants
|
| 75 | +import GHC.Utils.Monad (concatMapM)
|
|
| 76 | +import GHC.Utils.Monad.State.Strict
|
|
| 74 | 77 | |
| 75 | 78 | import GHC.Types.Error
|
| 76 | 79 | import GHC.Types.Target
|
| 77 | 80 | import GHC.Types.SourceFile
|
| 78 | 81 | import GHC.Types.SourceError
|
| 79 | 82 | import GHC.Types.SrcLoc
|
| 83 | +import GHC.Types.Unique.Set
|
|
| 80 | 84 | import GHC.Types.Unique.Map
|
| 81 | 85 | import GHC.Types.PkgQual
|
| 82 | 86 | import GHC.Types.Basic
|
| ... | ... | @@ -91,9 +95,12 @@ import GHC.Unit.Module.Graph |
| 91 | 95 | import GHC.Unit.Module.Deps
|
| 92 | 96 | import qualified GHC.Unit.Home.Graph as HUG
|
| 93 | 97 | import GHC.Unit.Module.Stage
|
| 98 | +import GHC.Unit.External.Index (UnitAbiHash)
|
|
| 94 | 99 | |
| 95 | 100 | import Data.Either ( partitionEithers, lefts )
|
| 101 | +import Data.Map (Map)
|
|
| 96 | 102 | import qualified Data.Map as Map
|
| 103 | +import Data.Set (Set)
|
|
| 97 | 104 | import qualified Data.Set as Set
|
| 98 | 105 | |
| 99 | 106 | import Control.Concurrent.MVar
|
| ... | ... | @@ -101,7 +108,7 @@ import Control.Monad |
| 101 | 108 | import Control.Monad.Trans.Except ( ExceptT(..), runExceptT, throwE )
|
| 102 | 109 | import qualified Control.Monad.Catch as MC
|
| 103 | 110 | import Data.Maybe
|
| 104 | -import Data.List (partition)
|
|
| 111 | +import Data.List (sort, partition)
|
|
| 105 | 112 | import Data.Time
|
| 106 | 113 | import Data.List (unfoldr)
|
| 107 | 114 | import Data.Bifunctor (first, bimap)
|
| ... | ... | @@ -933,52 +940,175 @@ rootSummariesParallel n_jobs hsc_env diag_wrapper msg get_summary = do |
| 933 | 940 | -- * Check/validate properties and error out
|
| 934 | 941 | --------------------------------------------------------------------------------
|
| 935 | 942 | |
| 936 | --- | This function checks then important property that if both p and q are home units
|
|
| 937 | --- then any dependency of p, which transitively depends on q is also a home unit.
|
|
| 943 | +-- | Checks whether the given 'UnitEnv' has the closure property.
|
|
| 938 | 944 | --
|
| 939 | --- See Note [Multiple Home Units], section 'Closure Property'.
|
|
| 940 | -checkHomeUnitsClosed :: UnitEnv -> [DriverMessages]
|
|
| 941 | -checkHomeUnitsClosed ue
|
|
| 942 | - | Set.null bad_unit_ids = []
|
|
| 943 | - | otherwise = [singleMessage $ mkPlainErrorMsgEnvelope rootLoc $ DriverHomePackagesNotClosed (Set.toList bad_unit_ids)]
|
|
| 945 | +-- See the section “Closure Property” in @Note [Multiple Home Units]@ for the
|
|
| 946 | +-- definition of the closure property and
|
|
| 947 | +-- @Note [Home unit closure property check]@ below for a discussion of the
|
|
| 948 | +-- algorithm used for this check, its justification, and a potential
|
|
| 949 | +-- alternative.
|
|
| 950 | +checkHomeUnitsClosed :: UnitEnv -> [DriverMessages]
|
|
| 951 | +checkHomeUnitsClosed unit_env
|
|
| 952 | + | null offenders = []
|
|
| 953 | + | otherwise = [
|
|
| 954 | + singleMessage $
|
|
| 955 | + mkPlainErrorMsgEnvelope error_source_span $
|
|
| 956 | + DriverHomePackagesNotClosed (sort offenders)
|
|
| 957 | + ]
|
|
| 944 | 958 | where
|
| 945 | - home_id_set = HUG.allUnits $ ue_home_unit_graph ue
|
|
| 946 | - bad_unit_ids = upwards_closure Set.\\ home_id_set {- Remove all home units reached, keep only bad nodes -}
|
|
| 947 | - rootLoc = mkGeneralSrcSpan (fsLit "<command line>")
|
|
| 948 | 959 | |
| 949 | - downwards_closure :: Graph (Node UnitId UnitId)
|
|
| 950 | - downwards_closure = graphFromEdgedVerticesUniq graphNodes
|
|
| 960 | + -- | The 'UnitId' and 'HomeUnitEnv' of each home unit.
|
|
| 961 | + home_unit_data :: [(UnitId, HomeUnitEnv)]
|
|
| 962 | + home_unit_data = HUG.unitEnv_assocs (ue_home_unit_graph unit_env)
|
|
| 963 | + |
|
| 964 | + -- | The 'UnitId's of all home units.
|
|
| 965 | + home_units :: UniqSet UnitId
|
|
| 966 | + home_units = mkUniqSet (map fst home_unit_data)
|
|
| 967 | + |
|
| 968 | + -- | All offending dependencies. A dependency of a unit /u/ on a unit /v/ is
|
|
| 969 | + -- offending exactly if /u/ is an external unit reachable from a home unit
|
|
| 970 | + -- and /v/ is a home unit. Each such dependency is represented in this list
|
|
| 971 | + -- by the pair of the 'UnitId' of /u/ and the 'UnitId' of /v/.
|
|
| 972 | + offenders :: [(UnitId, UnitId)]
|
|
| 973 | + offenders
|
|
| 974 | + = evalState (collect (map (homeUnitEnv_units . snd) home_unit_data))
|
|
| 975 | + emptyUniqMap
|
|
| 976 | + where
|
|
| 951 | 977 | |
| 952 | - inverse_closure = graphReachability $ transposeG downwards_closure
|
|
| 978 | + -- | Collects offending dependencies.
|
|
| 979 | + collect :: [UnitState]
|
|
| 980 | + -- ^ The 'UnitState's of the home units from which to traverse
|
|
| 981 | + -- the dependency graph.
|
|
| 982 | + -> State (UniqMap UnitId (Set UnitAbiHash)) [(UnitId, UnitId)]
|
|
| 983 | + -- ^ A stateful computation that collects offending dependencies
|
|
| 984 | + -- that have not yet been found. It uses its state, which is an
|
|
| 985 | + -- efficent representation of a set of 'GlobalUnitKey's, to
|
|
| 986 | + -- keep track of which units have already been considered as
|
|
| 987 | + -- sources of offending dependencies.
|
|
| 988 | + collect = concatMapM $ \ unit_state ->
|
|
| 989 | + collect_for_home_unit
|
|
| 990 | + (unitInfoMap unit_state)
|
|
| 991 | + (map (toUnitId . fst) $ explicitUnits $ unit_state)
|
|
| 992 | + where
|
|
| 953 | 993 | |
| 954 | - upwards_closure = Set.fromList $ map node_key $ allReachableMany inverse_closure [DigraphNode uid uid [] | uid <- Set.toList home_id_set]
|
|
| 994 | + -- | Collects offending dependencies that are reachable from a particular
|
|
| 995 | + -- home unit.
|
|
| 996 | + collect_for_home_unit
|
|
| 997 | + :: UnitInfoMap
|
|
| 998 | + -- ^ The 'UnitInfoMap' of the home unit.
|
|
| 999 | + -> [UnitId]
|
|
| 1000 | + -- ^ The 'UnitId's of the units from which to traverse the dependency
|
|
| 1001 | + -- graph.
|
|
| 1002 | + -> State (UniqMap UnitId (Set UnitAbiHash)) [(UnitId, UnitId)]
|
|
| 1003 | + -- ^ A stateful computation that collects offending dependencies that
|
|
| 1004 | + -- have not yet been found. It uses its state, which is an efficent
|
|
| 1005 | + -- representation of a set of 'GlobalUnitKey's, to keep track of
|
|
| 1006 | + -- which units have already been considered as sources of offending
|
|
| 1007 | + -- dependencies.
|
|
| 1008 | + collect_for_home_unit _ []
|
|
| 1009 | + = return []
|
|
| 1010 | + collect_for_home_unit unit_info_map (current_unit : remaining_units) = do
|
|
| 1011 | + let
|
|
| 1012 | + |
|
| 1013 | + -- | The 'UnitInfo' of the current unit.
|
|
| 1014 | + unit_info :: UnitInfo
|
|
| 1015 | + unit_info
|
|
| 1016 | + = fromMaybe (pprPanic unit_not_found_msg (ppr current_unit)) $
|
|
| 1017 | + lookupUniqMap unit_info_map current_unit
|
|
| 1018 | + where
|
|
| 1019 | + |
|
| 1020 | + -- | The message that says that a unit was not found.
|
|
| 1021 | + unit_not_found_msg :: String
|
|
| 1022 | + unit_not_found_msg = "Unit not found during closure property check"
|
|
| 1023 | + |
|
| 1024 | + -- | The ABI hash of the current unit.
|
|
| 1025 | + unit_abi_hash :: UnitAbiHash
|
|
| 1026 | + unit_abi_hash = unitAbiHash unit_info
|
|
| 1027 | + |
|
| 1028 | + has_been_processed <- gets $ maybe False (Set.member unit_abi_hash) .
|
|
| 1029 | + (`lookupUniqMap` current_unit)
|
|
| 1030 | + if has_been_processed
|
|
| 1031 | + then collect_for_home_unit unit_info_map remaining_units
|
|
| 1032 | + else do
|
|
| 1033 | + modify $ \ processed -> addToUniqMap_C Set.union
|
|
| 1034 | + processed
|
|
| 1035 | + current_unit
|
|
| 1036 | + (Set.singleton unit_abi_hash)
|
|
| 1037 | + let
|
|
| 1038 | + |
|
| 1039 | + -- | The 'UnitId's of the units that the current unit depends on.
|
|
| 1040 | + needed_units :: [UnitId]
|
|
| 1041 | + needed_units = unitDepends unit_info
|
|
| 1042 | + |
|
| 1043 | + -- | The offending dependencies of the current unit.
|
|
| 1044 | + current_offenders :: [(UnitId, UnitId)]
|
|
| 1045 | + current_offenders
|
|
| 1046 | + | current_unit `elementOfUniqSet` home_units
|
|
| 1047 | + = []
|
|
| 1048 | + | otherwise
|
|
| 1049 | + = map ((,) current_unit) $
|
|
| 1050 | + nonDetEltsUniqSet $
|
|
| 1051 | + mkUniqSet needed_units `intersectUniqSets` home_units
|
|
| 1052 | + |
|
| 1053 | + remaining_offenders <- collect_for_home_unit unit_info_map $
|
|
| 1054 | + needed_units ++ remaining_units
|
|
| 1055 | + return $ current_offenders ++ remaining_offenders
|
|
| 1056 | + |
|
| 1057 | + -- | A fake source span used for reporting violations of the closure property.
|
|
| 1058 | + error_source_span :: SrcSpan
|
|
| 1059 | + error_source_span = mkGeneralSrcSpan (fsLit "<command line>")
|
|
| 955 | 1060 | |
| 956 | - all_unit_direct_deps :: UniqMap UnitId (Set.Set UnitId)
|
|
| 957 | - all_unit_direct_deps
|
|
| 958 | - = HUG.unitEnv_foldWithKey go emptyUniqMap $ ue_home_unit_graph ue
|
|
| 959 | - where
|
|
| 960 | - go rest this this_uis =
|
|
| 961 | - plusUniqMap_C Set.union
|
|
| 962 | - (addToUniqMap_C Set.union external_depends this (Set.fromList $ this_deps))
|
|
| 963 | - rest
|
|
| 964 | - where
|
|
| 965 | - external_depends = mapUniqMap (Set.fromList . unitDepends) (unitInfoMap this_units)
|
|
| 966 | - this_units = homeUnitEnv_units this_uis
|
|
| 967 | - this_deps = [ toUnitId unit | (unit,Just _) <- explicitUnits this_units]
|
|
| 968 | - |
|
| 969 | - graphNodes :: [Node UnitId UnitId]
|
|
| 970 | - graphNodes = go Set.empty home_id_set
|
|
| 971 | - where
|
|
| 972 | - go done todo
|
|
| 973 | - = case Set.minView todo of
|
|
| 974 | - Nothing -> []
|
|
| 975 | - Just (uid, todo')
|
|
| 976 | - | Set.member uid done -> go done todo'
|
|
| 977 | - | otherwise -> case lookupUniqMap all_unit_direct_deps uid of
|
|
| 978 | - Nothing -> pprPanic "uid not found" (ppr (uid, all_unit_direct_deps))
|
|
| 979 | - Just depends ->
|
|
| 980 | - let todo'' = (depends Set.\\ done) `Set.union` todo'
|
|
| 981 | - in DigraphNode uid uid (Set.toList depends) : go (Set.insert uid done) todo''
|
|
| 1061 | +{-
|
|
| 1062 | + |
|
| 1063 | +Note [Home unit closure property check]
|
|
| 1064 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1065 | + |
|
| 1066 | +Per the definition in Note [Multiple Home Units], a unit environment has the
|
|
| 1067 | +closure property exactly if there are no paths /h/₁ →* /e/ →* /h/₂ in the
|
|
| 1068 | +dependency graph, where /h/₁ and /h/₂ are home units and /e/ is an external
|
|
| 1069 | +unit. However, the algorithm used by 'checkHomeUnitsClosed' searches for
|
|
| 1070 | +so-called offending dependencies, which are dependencies /e/ → /h/₂ that are
|
|
| 1071 | +part of a path /h/₁ →* /e/ → /h/₂ in the dependency graph. To see that this is a
|
|
| 1072 | +viable approach, consider the following:
|
|
| 1073 | + |
|
| 1074 | + * A path /h/₁ →* /e/ → /h/₂ is also a path /h/₁ →* /e/ →* /h/₂.
|
|
| 1075 | + |
|
| 1076 | + * For each path /h/₁ →* /e/ →* /h/₂, there exists a path /h/₁ →* /e/′ → /h/₂′,
|
|
| 1077 | + where /e/′ is an external unit and /h/₂′ is a home unit. Such a path can be
|
|
| 1078 | + constructed by taking as /h/₂′ the first home unit on the path /e/ →* /h/₂
|
|
| 1079 | + and as /e/′ the, necessarily external, unit preceding it.
|
|
| 1080 | + |
|
| 1081 | +Concretely, the algorithm picks one home unit after the other, determines what
|
|
| 1082 | +units it directly depends on, and, starting from them, follows unit dependencies
|
|
| 1083 | +to search for offending dependencies. It does not follow dependencies that have
|
|
| 1084 | +been followed before, possibly when processing another home unit. To achieve
|
|
| 1085 | +this, the algorithm tracks, across home units, from which units it has already
|
|
| 1086 | +followed dependencies. For this tracking, it identifies each unit by a 'UnitId'
|
|
| 1087 | +and an ABI hash. Using only a 'UnitId' would not work, because 'UnitId's are not
|
|
| 1088 | +always globally unique. Also using only an ABI hash is not an option, because an
|
|
| 1089 | +ABI hash is not necessarily an ABI hash: it can also be the string @"inline"@.
|
|
| 1090 | + |
|
| 1091 | +The correctness of this algorithm rests on the, likely correct, assumption that,
|
|
| 1092 | +among the units mentioned in the 'UnitState' of a particular home unit, any unit
|
|
| 1093 | +can be uniquely identified by its 'UnitId' and thus 'UnitId' clashes can only
|
|
| 1094 | +occur across the 'UnitState's of different home units.
|
|
| 1095 | + |
|
| 1096 | +An alternative approach to finding offending dependencies would be to follow
|
|
| 1097 | +dependencies starting from all units that /any/ home unit directly depends on
|
|
| 1098 | +instead of considering the different home units separately. A corresponding
|
|
| 1099 | +algorithm could in principle find the dependencies of a particular unit
|
|
| 1100 | +independently of any home unit by fetching the 'UnitInfo' of that unit from the
|
|
| 1101 | +'GlobalUnitInfoMap'. However, for such a lookup the algorithm would need not
|
|
| 1102 | +only the 'UnitId' but also the ABI hash of the unit in question. Therefore,
|
|
| 1103 | +whenever following a dependency of a unit /u/ on a unit /v/, it would have to
|
|
| 1104 | +determine the ABI hash of /v/, so that it could later look up /v/’s
|
|
| 1105 | +dependencies. The ABI hashes of all units that /u/ depends on should be
|
|
| 1106 | +available in the 'unitAbiDepends' field of /u/’s 'UnitInfo'. However, at the
|
|
| 1107 | +time of writing, 'unitAbiDepends' never contained anything other than the empty
|
|
| 1108 | +list during GHC test runs, which indicated that this alternative solution was
|
|
| 1109 | +impossible to realize.
|
|
| 1110 | + |
|
| 1111 | +-}
|
|
| 982 | 1112 | |
| 983 | 1113 | --------------------------------------------------------------------------------
|
| 984 | 1114 | -- * Enable Code Gen for Template Haskell
|
| ... | ... | @@ -1763,7 +1893,7 @@ data NodeRes v |
| 1763 | 1893 | --
|
| 1764 | 1894 | -- See also Note [Downsweep Control Flow and Caching]
|
| 1765 | 1895 | dfsBuild :: (Ord k, Monad m)
|
| 1766 | - => Maybe (Map.Map k (NodeRes v))
|
|
| 1896 | + => Maybe (Map k (NodeRes v))
|
|
| 1767 | 1897 | -- ^ Base map, existing results. We won't re-expand any of the nodes
|
| 1768 | 1898 | -- already present in this map.
|
| 1769 | 1899 | -> [n]
|
| ... | ... | @@ -1773,7 +1903,7 @@ dfsBuild :: (Ord k, Monad m) |
| 1773 | 1903 | -> (n -> m (NodeRes (v,[n])))
|
| 1774 | 1904 | -- ^ Expand this node into its payload result and into the list of
|
| 1775 | 1905 | -- children nodes to visit next.
|
| 1776 | - -> m (Map.Map k (NodeRes v))
|
|
| 1906 | + -> m (Map k (NodeRes v))
|
|
| 1777 | 1907 | -- ^ The result accumulates the payload of expanding the root nodes
|
| 1778 | 1908 | -- and all nodes transitively reachable from those roots.
|
| 1779 | 1909 | dfsBuild base_map roots key expand = go roots (fromMaybe Map.empty base_map)
|
| ... | ... | @@ -235,10 +235,17 @@ instance Diagnostic DriverMessage where |
| 235 | 235 | "but no output will be generated.") $$
|
| 236 | 236 | (text "There is no module named" <+>
|
| 237 | 237 | quotes (ppr mod_name) <> text "."))
|
| 238 | - DriverHomePackagesNotClosed needed_unit_ids
|
|
| 239 | - -> mkSimpleDecorated $ vcat ([text "Home units are not closed."
|
|
| 240 | - , text "It is necessary to also load the following units:" ]
|
|
| 241 | - ++ map (\uid -> text "-" <+> ppr uid) needed_unit_ids)
|
|
| 238 | + DriverHomePackagesNotClosed offending_dependencies
|
|
| 239 | + -> mkSimpleDecorated $
|
|
| 240 | + hang (text "Some units are not loaded but depend on loaded units.")
|
|
| 241 | + 4
|
|
| 242 | + (vcat (map pprDependency offending_dependencies))
|
|
| 243 | + where
|
|
| 244 | + |
|
| 245 | + pprDependency :: (UnitId, UnitId) -> SDoc
|
|
| 246 | + pprDependency (external_unit, home_unit)
|
|
| 247 | + = ppr external_unit <+> arrow <+> ppr home_unit
|
|
| 248 | + |
|
| 242 | 249 | DriverInterfaceError reason -> diagnosticMessage (ifaceDiagnosticOpts opts) reason
|
| 243 | 250 | |
| 244 | 251 | DriverInconsistentDynFlags msg
|
| ... | ... | @@ -369,7 +369,7 @@ data DriverMessage where |
| 369 | 369 | |
| 370 | 370 | DriverRedirectedNoMain :: !ModuleName -> DriverMessage
|
| 371 | 371 | |
| 372 | - DriverHomePackagesNotClosed :: ![UnitId] -> DriverMessage
|
|
| 372 | + DriverHomePackagesNotClosed :: ![(UnitId, UnitId)] -> DriverMessage
|
|
| 373 | 373 | |
| 374 | 374 | DriverInterfaceError :: !IfaceMessage -> DriverMessage
|
| 375 | 375 |
| ... | ... | @@ -181,7 +181,8 @@ module GHC.Driver.Session ( |
| 181 | 181 | -- ** Available DynFlags
|
| 182 | 182 | allNonDeprecatedFlags,
|
| 183 | 183 | flagsAll,
|
| 184 | - flagsDynamic,
|
|
| 184 | + flagsAllTrie,
|
|
| 185 | + flagsDynamicTrie,
|
|
| 185 | 186 | flagsPackage,
|
| 186 | 187 | flagsForCompletion,
|
| 187 | 188 | |
| ... | ... | @@ -294,8 +295,7 @@ import System.Semaphore ( getSemaphoreProtocolVersion, semaphoreVersion ) |
| 294 | 295 | import Data.IORef
|
| 295 | 296 | import Control.Arrow ((&&&))
|
| 296 | 297 | import Control.Monad
|
| 297 | -import Control.Monad.Trans.State as State
|
|
| 298 | -import Data.Functor.Identity
|
|
| 298 | +import Control.Monad.Trans.State (runStateT)
|
|
| 299 | 299 | |
| 300 | 300 | import Data.Ord
|
| 301 | 301 | import Data.Char
|
| ... | ... | @@ -391,6 +391,37 @@ import Language.Haskell.Syntax.Text |
| 391 | 391 | --
|
| 392 | 392 | -- See #4437 and #8176.
|
| 393 | 393 | |
| 394 | +-- Note [Optimising the processing of command-line arguments]
|
|
| 395 | +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 396 | +-- For each command line argument we need to search all the possible flag names to
|
|
| 397 | +-- find the correct command-line argument processor (of type `CmdLineP DynFlags`)
|
|
| 398 | +-- to complete parsing of the argument and modify the `DynFlags`.
|
|
| 399 | +--
|
|
| 400 | +-- GHC has a LOT of flags, and occasionally, users give a LOT of command line
|
|
| 401 | +-- arguments. So in some cases it pays to optimize the search for a command-line
|
|
| 402 | +-- argument processor (#25763).
|
|
| 403 | +--
|
|
| 404 | +-- An additional complication is that flag specs need to be looked up with a
|
|
| 405 | +-- string that may contain the flag key as a prefix.
|
|
| 406 | +--
|
|
| 407 | +-- In general, the `=` sign is optional, and anything after the name of the flag,
|
|
| 408 | +-- if it doesn't start with `=`, is considered the argument. E.g.
|
|
| 409 | +-- `-odirsome/path`, or `-pgmFprogram`, or `-package-dbDiff`, or `-optc-DFOO`,
|
|
| 410 | +-- or `-Isome/path`, or `-fpluginLiquidHaskell`, or `-fplugin-optLiquidHaskell:--save`.
|
|
| 411 | +--
|
|
| 412 | +-- To achieve this:
|
|
| 413 | +--
|
|
| 414 | +-- * We build global lookup tables
|
|
| 415 | +-- * GHC.Driver.Session.flagsAllTrie :: FlagSpecTrie DynFlags
|
|
| 416 | +-- * GHC.Driver.Session.flagsDynamicTrie :: FlagSpecTrie DynFlags
|
|
| 417 | +-- * GHC.Driver.Session.Mode.mode_flags_trie :: FlagSpecTrie ModeMS
|
|
| 418 | +--
|
|
| 419 | +-- These are implemented by a trie indexed by Strings, see GHC.Data.StringTrie.
|
|
| 420 | +--
|
|
| 421 | +-- * As we process each command line argument, one by one, we
|
|
| 422 | +-- look up in the `FlagSpecTrie` to find which processor
|
|
| 423 | +-- to use. See `GHC.Driver.CmdLine.processArgs`.
|
|
| 424 | + |
|
| 394 | 425 | -- -----------------------------------------------------------------------------
|
| 395 | 426 | -- DynFlags
|
| 396 | 427 | |
| ... | ... | @@ -837,7 +868,7 @@ parseDynamicFlagsCmdLine :: MonadIO m => Logger -> DynFlags -> [Located String] |
| 837 | 868 | -> m (DynFlags, [Located String], Messages DriverMessage)
|
| 838 | 869 | -- ^ Updated 'DynFlags', left-over arguments, and
|
| 839 | 870 | -- list of warnings.
|
| 840 | -parseDynamicFlagsCmdLine = parseDynamicFlagsFull flagsAll True
|
|
| 871 | +parseDynamicFlagsCmdLine = parseDynamicFlagsFull flagsAllTrie True
|
|
| 841 | 872 | |
| 842 | 873 | |
| 843 | 874 | -- | Like 'parseDynamicFlagsCmdLine' but does not allow the package flags
|
| ... | ... | @@ -847,42 +878,19 @@ parseDynamicFilePragma :: MonadIO m => Logger -> DynFlags -> [Located String] |
| 847 | 878 | -> m (DynFlags, [Located String], Messages DriverMessage)
|
| 848 | 879 | -- ^ Updated 'DynFlags', left-over arguments, and
|
| 849 | 880 | -- list of warnings.
|
| 850 | -parseDynamicFilePragma = parseDynamicFlagsFull flagsDynamic False
|
|
| 851 | - |
|
| 852 | -newtype CmdLineP s a = CmdLineP (forall m. (Monad m) => StateT s m a)
|
|
| 853 | - deriving (Functor)
|
|
| 854 | - |
|
| 855 | -instance Monad (CmdLineP s) where
|
|
| 856 | - CmdLineP k >>= f = CmdLineP (k >>= \x -> case f x of CmdLineP g -> g)
|
|
| 857 | - return = pure
|
|
| 858 | - |
|
| 859 | -instance Applicative (CmdLineP s) where
|
|
| 860 | - pure x = CmdLineP (pure x)
|
|
| 861 | - (<*>) = ap
|
|
| 862 | - |
|
| 863 | -getCmdLineState :: CmdLineP s s
|
|
| 864 | -getCmdLineState = CmdLineP State.get
|
|
| 865 | - |
|
| 866 | -putCmdLineState :: s -> CmdLineP s ()
|
|
| 867 | -putCmdLineState x = CmdLineP (State.put x)
|
|
| 868 | - |
|
| 869 | -runCmdLineP :: CmdLineP s a -> s -> (a, s)
|
|
| 870 | -runCmdLineP (CmdLineP k) s0 = runIdentity $ runStateT k s0
|
|
| 881 | +parseDynamicFilePragma = parseDynamicFlagsFull flagsDynamicTrie False
|
|
| 871 | 882 | |
| 872 | 883 | -- | A helper to parse a set of flags from a list of command-line arguments, handling
|
| 873 | 884 | -- response files.
|
| 874 | 885 | processCmdLineP
|
| 875 | 886 | :: forall s m. MonadIO m
|
| 876 | - => [Flag (CmdLineP s)] -- ^ valid flags to match against
|
|
| 887 | + => FlagSpecTrie s -- ^ valid flags to match against
|
|
| 877 | 888 | -> s -- ^ current state
|
| 878 | 889 | -> [Located String] -- ^ arguments to parse
|
| 879 | 890 | -> m (([Located String], [Err], [Warn]), s)
|
| 880 | 891 | -- ^ (leftovers, errors, warnings)
|
| 881 | 892 | processCmdLineP activeFlags s0 args =
|
| 882 | - runStateT (processArgs (map (hoistFlag getCmdLineP) activeFlags) args parseResponseFile) s0
|
|
| 883 | - where
|
|
| 884 | - getCmdLineP :: CmdLineP s a -> StateT s m a
|
|
| 885 | - getCmdLineP (CmdLineP k) = k
|
|
| 893 | + runStateT (processArgs activeFlags args parseResponseFile) s0
|
|
| 886 | 894 | |
| 887 | 895 | -- | Parses the dynamically set flags for GHC. This is the most general form of
|
| 888 | 896 | -- the dynamic flag parser that the other methods simply wrap. It allows
|
| ... | ... | @@ -890,7 +898,7 @@ processCmdLineP activeFlags s0 args = |
| 890 | 898 | -- arguments from the command line or from a file pragma.
|
| 891 | 899 | parseDynamicFlagsFull
|
| 892 | 900 | :: forall m. MonadIO m
|
| 893 | - => [Flag (CmdLineP DynFlags)] -- ^ valid flags to match against
|
|
| 901 | + => FlagSpecTrie DynFlags -- ^ valid flags to match against
|
|
| 894 | 902 | -> Bool -- ^ are the arguments from the command line?
|
| 895 | 903 | -> Logger -- ^ logger
|
| 896 | 904 | -> DynFlags -- ^ current dynamic flags
|
| ... | ... | @@ -1012,14 +1020,22 @@ allFlagsDeps keepDeprecated = [ '-':flagName flag |
| 1012 | 1020 | flagsAll :: [Flag (CmdLineP DynFlags)]
|
| 1013 | 1021 | flagsAll = map snd flagsAllDeps
|
| 1014 | 1022 | |
| 1023 | +-- | Same as 'flagsAll' but in trie form for fast lookup.
|
|
| 1024 | +--
|
|
| 1025 | +-- See Note [Optimising the processing of command-line arguments]
|
|
| 1026 | +flagsAllTrie :: FlagSpecTrie DynFlags
|
|
| 1027 | +flagsAllTrie = extendFlagSpecTrie flagsDynamicTrie $ map snd package_flags_deps
|
|
| 1028 | + |
|
| 1015 | 1029 | -- All dynamic flags present in GHC with deprecation information.
|
| 1016 | 1030 | flagsAllDeps :: [(Deprecation, Flag (CmdLineP DynFlags))]
|
| 1017 | 1031 | flagsAllDeps = package_flags_deps ++ dynamic_flags_deps
|
| 1018 | 1032 | |
| 1019 | 1033 | |
| 1020 | 1034 | -- All dynamic flags, minus package flags, present in GHC.
|
| 1021 | -flagsDynamic :: [Flag (CmdLineP DynFlags)]
|
|
| 1022 | -flagsDynamic = map snd dynamic_flags_deps
|
|
| 1035 | +--
|
|
| 1036 | +-- See Note [Optimising the processing of command-line arguments].
|
|
| 1037 | +flagsDynamicTrie :: FlagSpecTrie DynFlags
|
|
| 1038 | +flagsDynamicTrie = mkFlagSpecTrie $ map snd dynamic_flags_deps
|
|
| 1023 | 1039 | |
| 1024 | 1040 | -- ALl package flags present in GHC.
|
| 1025 | 1041 | flagsPackage :: [Flag (CmdLineP DynFlags)]
|
| ... | ... | @@ -2013,8 +2029,7 @@ dynamic_flags_deps = [ |
| 2013 | 2029 | ++
|
| 2014 | 2030 | |
| 2015 | 2031 | ------ Warning flags -------------------------------------------------
|
| 2016 | - [ make_ord_flag defFlag "W" (NoArg (setWarningGroup W_extra))
|
|
| 2017 | - , make_ord_flag defFlag "Werror"
|
|
| 2032 | + [ make_ord_flag defFlag "Werror"
|
|
| 2018 | 2033 | (NoArg (do { setGeneralFlag Opt_WarnIsError
|
| 2019 | 2034 | ; setFatalWarningGroup W_everything }))
|
| 2020 | 2035 | , make_ord_flag defFlag "Wwarn"
|
| ... | ... | @@ -2040,7 +2055,7 @@ dynamic_flags_deps = [ |
| 2040 | 2055 | , (NotDeprecated, customOrUnrecognisedWarning "Werror=" setCustomWErrorFlag)
|
| 2041 | 2056 | , (NotDeprecated, customOrUnrecognisedWarning "Wwarn=" unSetCustomFatalWarningFlag)
|
| 2042 | 2057 | , (NotDeprecated, customOrUnrecognisedWarning "Wno-error=" unSetCustomFatalWarningFlag)
|
| 2043 | - , (NotDeprecated, customOrUnrecognisedWarning "W" setCustomWarningFlag)
|
|
| 2058 | + , flagW
|
|
| 2044 | 2059 | , (Deprecated, customOrUnrecognisedWarning "fwarn-" setCustomWarningFlag)
|
| 2045 | 2060 | , (Deprecated, customOrUnrecognisedWarning "fno-warn-" unSetCustomWarningFlag)
|
| 2046 | 2061 | ]
|
| ... | ... | @@ -2076,19 +2091,35 @@ warningControls set unset set_werror unset_fatal xs = |
| 2076 | 2091 | ++ map (mkFlag turnOn "fwarn-" set . hideFlag) xs
|
| 2077 | 2092 | ++ map (mkFlag turnOff "fno-warn-" unset . hideFlag) xs
|
| 2078 | 2093 | |
| 2094 | +-- | The "W" flag is special in that it can mean enable extra warnings ('W_extra')
|
|
| 2095 | +-- or enable a custom warning, if the suffix is non-empty.
|
|
| 2096 | +flagW :: (Deprecation, Flag (CmdLineP DynFlags))
|
|
| 2097 | +flagW =
|
|
| 2098 | + make_ord_flag defFlag "W" $ OptPrefix $ \suffix ->
|
|
| 2099 | + if null suffix then do
|
|
| 2100 | + -- Reject "-W=" instead of taking it to mean "-W".
|
|
| 2101 | + arg <- getArg
|
|
| 2102 | + if arg == "-W" then
|
|
| 2103 | + setWarningGroup W_extra
|
|
| 2104 | + else
|
|
| 2105 | + addErr ("missing argument for flag: " ++ arg)
|
|
| 2106 | + else
|
|
| 2107 | + customOrUnrecognisedWarningAction "W" setCustomWarningFlag suffix
|
|
| 2108 | + |
|
| 2079 | 2109 | -- | This is where we handle unrecognised warning flags. If the flag is valid as
|
| 2080 | 2110 | -- an extended warning category, we call the supplied action. Otherwise, issue a
|
| 2081 | 2111 | -- warning if -Wunrecognised-warning-flags is set. See #11429 for context.
|
| 2082 | 2112 | -- See Note [Warning categories] in GHC.Unit.Module.Warnings.
|
| 2083 | 2113 | customOrUnrecognisedWarning :: String -> (WarningCategory -> DynP ()) -> Flag (CmdLineP DynFlags)
|
| 2084 | -customOrUnrecognisedWarning prefix custom = defHiddenFlag prefix (Prefix action)
|
|
| 2085 | - where
|
|
| 2086 | - action :: String -> DynP ()
|
|
| 2087 | - action flag
|
|
| 2114 | +customOrUnrecognisedWarning prefix custom =
|
|
| 2115 | + defHiddenFlag prefix $ Prefix $ customOrUnrecognisedWarningAction prefix custom
|
|
| 2116 | + |
|
| 2117 | +customOrUnrecognisedWarningAction :: String -> (WarningCategory -> DynP ()) -> String -> DynP ()
|
|
| 2118 | +customOrUnrecognisedWarningAction prefix custom flag
|
|
| 2088 | 2119 | | validWarningCategory cat = custom cat
|
| 2089 | 2120 | | otherwise = unrecognised flag
|
| 2090 | - where
|
|
| 2091 | - cat = mkWarningCategory (packHText flag)
|
|
| 2121 | + where
|
|
| 2122 | + cat = mkWarningCategory (packHText flag)
|
|
| 2092 | 2123 | |
| 2093 | 2124 | unrecognised flag = do
|
| 2094 | 2125 | -- #23402 and #12056
|
| ... | ... | @@ -443,13 +443,14 @@ The flow: |
| 443 | 443 | Closure Property
|
| 444 | 444 | ----------------
|
| 445 | 445 | |
| 446 | -You must perform a clean cut of the dependency graph.
|
|
| 446 | +A unit environment must have the closure property:
|
|
| 447 | 447 | |
| 448 | -> Any dependency which is not a home unit must not (transitively) depend on a home unit.
|
|
| 448 | + No used external unit depends on a home unit.
|
|
| 449 | 449 | |
| 450 | -For example, if you have three packages p, q and r, then if p depends on q which
|
|
| 451 | -depends on r then it is illegal to load both p and r as home units but not q,
|
|
| 452 | -because q is a dependency of the home unit p which depends on another home unit r.
|
|
| 450 | +More concretely, a unit environment has the closure property exactly if no home
|
|
| 451 | +unit directly or indirectly depends on an external unit that directly or
|
|
| 452 | +indirectly depends on a home unit. 'GHC.Driver.Downsweep.checkHomeUnitsClosed'
|
|
| 453 | +checks whether a given unit environment indeed has this property.
|
|
| 453 | 454 | |
| 454 | 455 | Offsetting Paths
|
| 455 | 456 | ----------------
|
| ... | ... | @@ -474,6 +474,7 @@ Library |
| 474 | 474 | GHC.Data.Stream
|
| 475 | 475 | GHC.Data.Strict
|
| 476 | 476 | GHC.Data.StringBuffer
|
| 477 | + GHC.Data.StringTrie
|
|
| 477 | 478 | GHC.Data.TrieMap
|
| 478 | 479 | GHC.Data.Unboxed
|
| 479 | 480 | GHC.Data.UnionFind
|
| ... | ... | @@ -180,7 +180,7 @@ parseModeFlags :: [Located String] |
| 180 | 180 | [Warn])
|
| 181 | 181 | parseModeFlags args = do
|
| 182 | 182 | ((leftover, errs1, warns), (mModeFlag, units, errs2, flags')) <-
|
| 183 | - processCmdLineP mode_flags (Nothing, [], [], []) args
|
|
| 183 | + processCmdLineP mode_flags_trie (Nothing, [], [], []) args
|
|
| 184 | 184 | let mode = case mModeFlag of
|
| 185 | 185 | Nothing -> doMakeMode
|
| 186 | 186 | Just (m, _) -> m
|
| ... | ... | @@ -191,10 +191,14 @@ parseModeFlags args = do |
| 191 | 191 | |
| 192 | 192 | return (mode, units, flags' ++ leftover, warns)
|
| 193 | 193 | |
| 194 | -type ModeM = CmdLineP (Maybe (Mode, String), [String], [String], [Located String])
|
|
| 194 | +type ModeMS = (Maybe (Mode, String), [String], [String], [Located String])
|
|
| 195 | +type ModeM = CmdLineP ModeMS
|
|
| 195 | 196 | -- mode flags sometimes give rise to new DynFlags (eg. -C, see below)
|
| 196 | 197 | -- so we collect the new ones and return them.
|
| 197 | 198 | |
| 199 | +mode_flags_trie :: FlagSpecTrie ModeMS
|
|
| 200 | +mode_flags_trie = mkFlagSpecTrie mode_flags
|
|
| 201 | + |
|
| 198 | 202 | mode_flags :: [Flag ModeM]
|
| 199 | 203 | mode_flags =
|
| 200 | 204 | [ ------- help / version ----------------------------------------------
|
| ... | ... | @@ -59,16 +59,16 @@ StgWord hs_xchg32(StgWord x, StgWord val); |
| 59 | 59 | StgWord64 hs_xchg64(StgWord x, StgWord64 val);
|
| 60 | 60 | |
| 61 | 61 | /* rts/prim/bswap.c */
|
| 62 | -StgWord16 hs_bswap16(StgWord16 x);
|
|
| 63 | -StgWord32 hs_bswap32(StgWord32 x);
|
|
| 62 | +StgWord hs_bswap16(StgWord x);
|
|
| 63 | +StgWord hs_bswap32(StgWord x);
|
|
| 64 | 64 | StgWord64 hs_bswap64(StgWord64 x);
|
| 65 | 65 | |
| 66 | 66 | /* rts/prim/bitrev.c
|
| 67 | 67 | This was done as part of issue #16164.
|
| 68 | 68 | See Note [Bit reversal primop] for more details about the implementation.*/
|
| 69 | 69 | StgWord hs_bitrev8(StgWord x);
|
| 70 | -StgWord16 hs_bitrev16(StgWord16 x);
|
|
| 71 | -StgWord32 hs_bitrev32(StgWord32 x);
|
|
| 70 | +StgWord hs_bitrev16(StgWord x);
|
|
| 71 | +StgWord hs_bitrev32(StgWord x);
|
|
| 72 | 72 | StgWord64 hs_bitrev64(StgWord64 x);
|
| 73 | 73 | |
| 74 | 74 | /* rts/prim/longlong.c */
|
| ... | ... | @@ -36,8 +36,8 @@ hs_bitrev8(StgWord x) |
| 36 | 36 | return x;
|
| 37 | 37 | }
|
| 38 | 38 | |
| 39 | -StgWord16
|
|
| 40 | -hs_bitrev16(StgWord16 x)
|
|
| 39 | +StgWord
|
|
| 40 | +hs_bitrev16(StgWord x)
|
|
| 41 | 41 | {
|
| 42 | 42 | x = ((x >> 1) & 0x5555) | ((x & 0x5555) << 1 );
|
| 43 | 43 | x = ((x >> 2) & 0x3333) | ((x & 0x3333) << 2 );
|
| ... | ... | @@ -46,14 +46,14 @@ hs_bitrev16(StgWord16 x) |
| 46 | 46 | return x;
|
| 47 | 47 | }
|
| 48 | 48 | |
| 49 | -StgWord32
|
|
| 50 | -hs_bitrev32(StgWord32 x)
|
|
| 49 | +StgWord
|
|
| 50 | +hs_bitrev32(StgWord x)
|
|
| 51 | 51 | {
|
| 52 | - x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1 );
|
|
| 53 | - x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2 );
|
|
| 54 | - x = ((x >> 4) & 0x0F0F0F0F) | ((x & 0x0F0F0F0F) << 4 );
|
|
| 55 | - x = ((x >> 8) & 0x00FF00FF) | ((x & 0x00FF00FF) << 8 );
|
|
| 56 | - x = ( x >> 16 ) | ( x << 16 );
|
|
| 52 | + x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1 );
|
|
| 53 | + x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2 );
|
|
| 54 | + x = ((x >> 4) & 0x0F0F0F0F) | ((x & 0x0F0F0F0F) << 4 );
|
|
| 55 | + x = ((x >> 8) & 0x00FF00FF) | ((x & 0x00FF00FF) << 8 );
|
|
| 56 | + x = ((x >> 16) & 0x0000FFFF) | ((x & 0x0000FFFF) << 16);
|
|
| 57 | 57 | return x;
|
| 58 | 58 | }
|
| 59 | 59 |
| 1 | 1 | #include "Rts.h"
|
| 2 | 2 | |
| 3 | -StgWord16
|
|
| 4 | -hs_bswap16(StgWord16 x)
|
|
| 3 | +StgWord
|
|
| 4 | +hs_bswap16(StgWord x)
|
|
| 5 | 5 | {
|
| 6 | - return ((x >> 8) | (x << 8));
|
|
| 6 | + x = (StgWord16)x;
|
|
| 7 | + return (StgWord16)((x >> 8) | (x << 8));
|
|
| 7 | 8 | }
|
| 8 | 9 | |
| 9 | -StgWord32
|
|
| 10 | -hs_bswap32(StgWord32 x)
|
|
| 10 | +StgWord
|
|
| 11 | +hs_bswap32(StgWord x)
|
|
| 11 | 12 | {
|
| 12 | - return ((x >> 24) | ((x >> 8) & 0xff00) |
|
|
| 13 | - (x << 24) | ((x & 0xff00) << 8));
|
|
| 13 | + x = (StgWord32)x;
|
|
| 14 | + return (StgWord32)((x >> 24) | ((x >> 8) & 0xff00) |
|
|
| 15 | + (x << 24) | ((x & 0xff00) << 8));
|
|
| 14 | 16 | }
|
| 15 | 17 | |
| 16 | 18 | StgWord64
|
| ... | ... | @@ -28,6 +28,7 @@ module MiniQuickCheck |
| 28 | 28 | , propertyCompare
|
| 29 | 29 | , propertyAnd
|
| 30 | 30 | , propertyTrue
|
| 31 | + , propertyEqWithContext
|
|
| 31 | 32 | , getCheck
|
| 32 | 33 | |
| 33 | 34 | -- * QuickCheck test tree
|
| ... | ... | @@ -51,11 +52,15 @@ module MiniQuickCheck |
| 51 | 52 | , nonZero
|
| 52 | 53 | , BoundedShiftAmount(..)
|
| 53 | 54 | , BoundedBy(..)
|
| 55 | + , BoundedList(..)
|
|
| 56 | + , BoundedChar(..)
|
|
| 54 | 57 | ) where
|
| 55 | 58 | |
| 56 | 59 | -- base
|
| 57 | 60 | import Control.Exception
|
| 58 | 61 | ( SomeException, displayException, evaluate, try )
|
| 62 | +import Control.Monad
|
|
| 63 | + ( replicateM )
|
|
| 59 | 64 | import Control.Monad.IO.Class
|
| 60 | 65 | ( liftIO )
|
| 61 | 66 | import Data.Bits
|
| ... | ... | @@ -64,8 +69,6 @@ import Data.Bits |
| 64 | 69 | )
|
| 65 | 70 | import Data.Int
|
| 66 | 71 | ( Int8, Int16, Int32, Int64 )
|
| 67 | -import Data.IORef
|
|
| 68 | - ( newIORef, atomicModifyIORef' )
|
|
| 69 | 72 | import Data.Kind
|
| 70 | 73 | ( Type )
|
| 71 | 74 | import Data.List
|
| ... | ... | @@ -84,6 +87,8 @@ import System.Exit |
| 84 | 87 | ( die, exitFailure )
|
| 85 | 88 | import Text.Read
|
| 86 | 89 | ( readMaybe )
|
| 90 | +import System.IO.Unsafe
|
|
| 91 | + ( unsafePerformIO )
|
|
| 87 | 92 | |
| 88 | 93 | -- transformers
|
| 89 | 94 | import Control.Monad.Trans.Reader
|
| ... | ... | @@ -141,6 +146,15 @@ propertyCompare s f a b = PropertyBinaryOp (f a b) s (show a) (show b) |
| 141 | 146 | (===) = propertyCompare "==" (==)
|
| 142 | 147 | infix 4 ===
|
| 143 | 148 | |
| 149 | +-- | Prints the given message when the two values are not equal.
|
|
| 150 | +propertyEqWithContext :: (Eq a, Show a) => String -> a -> a -> PropertyCheck
|
|
| 151 | +propertyEqWithContext msg =
|
|
| 152 | + propertyCompare
|
|
| 153 | + (intercalate "\n" $
|
|
| 154 | + "is not the expected" : map (" " ++) (lines msg ++ ["expected:"])
|
|
| 155 | + )
|
|
| 156 | + (==)
|
|
| 157 | + |
|
| 144 | 158 | -- | Conjunction of two property checks.
|
| 145 | 159 | propertyAnd :: PropertyCheck -> PropertyCheck -> PropertyCheck
|
| 146 | 160 | propertyAnd = PropertyAnd
|
| ... | ... | @@ -424,3 +438,28 @@ instance |
| 424 | 438 | where
|
| 425 | 439 | n :: a
|
| 426 | 440 | n = fromIntegral $ natVal @n Proxy
|
| 441 | + |
|
| 442 | +type BoundedList :: Nat -> Type -> Type
|
|
| 443 | +newtype BoundedList n a = BoundedList { getBoundedList :: [a] }
|
|
| 444 | + deriving (Eq, Ord, Show)
|
|
| 445 | + |
|
| 446 | +instance
|
|
| 447 | + forall n a
|
|
| 448 | + . ( KnownNat n, Arbitrary a )
|
|
| 449 | + => Arbitrary ( BoundedList n a ) where
|
|
| 450 | + arbitrary = do
|
|
| 451 | + x <- arbitrary
|
|
| 452 | + let n = (x :: Word64) `mod` fromIntegral (natVal @n Proxy + 1)
|
|
| 453 | + BoundedList <$> replicateM (fromIntegral n) arbitrary
|
|
| 454 | + |
|
| 455 | +instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b) where
|
|
| 456 | + arbitrary = (,) <$> arbitrary <*> arbitrary
|
|
| 457 | + |
|
| 458 | +type BoundedChar :: Nat -> Type
|
|
| 459 | +newtype BoundedChar n = BoundedChar { getBoundedChar :: Char }
|
|
| 460 | + deriving (Eq, Ord, Show)
|
|
| 461 | + |
|
| 462 | +instance KnownNat n => Arbitrary (BoundedChar n) where
|
|
| 463 | + arbitrary = do
|
|
| 464 | + x <- getBoundedBy @_ @n <$> arbitrary
|
|
| 465 | + pure $ BoundedChar (['a'..] !! abs x) |
| ... | ... | @@ -99,6 +99,8 @@ test('driver200', [extra_files(['A200.hs', 'B200/', 'D200.hs'])], makefile_test, |
| 99 | 99 | |
| 100 | 100 | test('T2566', normalise_fun(normalise_errmsg), makefile_test, [])
|
| 101 | 101 | |
| 102 | +test('stringtrie', [mini_quickcheck], compile_and_run, ['-package ghc'])
|
|
| 103 | + |
|
| 102 | 104 | test('pragma001', normal, compile, [''])
|
| 103 | 105 | test('pragma002', normal, compile, [''])
|
| 104 | 106 |
| ... | ... | @@ -3,39 +3,47 @@ include $(TOP)/mk/boilerplate.mk |
| 3 | 3 | include $(TOP)/mk/test.mk
|
| 4 | 4 | |
| 5 | 5 | SETUP=../Setup -v0
|
| 6 | +CONFIGURE=configure \
|
|
| 7 | + $(CABAL_MINIMAL_BUILD) \
|
|
| 8 | + --with-ghc='$(TEST_HC)' \
|
|
| 9 | + --with-hc-pkg='$(GHC_PKG)' \
|
|
| 10 | + --ghc-options='$(TEST_HC_OPTS)' \
|
|
| 11 | + --package-db=../tmp.d
|
|
| 12 | +TEST_BUILD='$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp
|
|
| 13 | +ONLY_BASE=-hide-all-packages -package base
|
|
| 6 | 14 | |
| 7 | -mhu-closure: clean
|
|
| 8 | - $(MAKE) -s --no-print-directory clean
|
|
| 15 | +mhu-closure: clean pkg-database
|
|
| 16 | + ! $(TEST_BUILD) -unit @unitP
|
|
| 17 | + ! $(TEST_BUILD) -unit @unitP -unit @unitQ
|
|
| 18 | + ! $(TEST_BUILD) -unit @unitP -unit @unitR
|
|
| 19 | + ! $(TEST_BUILD) -unit @unitP -unit @unitR1
|
|
| 20 | + $(TEST_BUILD) -unit @unitP -unit @unitQ -unit @unitR
|
|
| 21 | + ! $(TEST_BUILD) -unit @unitP -unit @unitQ -unit @unitR1
|
|
| 22 | + $(TEST_BUILD) $(ONLY_BASE) -unit @unitP
|
|
| 23 | + $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ
|
|
| 24 | + ! $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitR
|
|
| 25 | + ! $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitR1
|
|
| 26 | + $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ -unit @unitR
|
|
| 27 | + $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ -unit @unitR1
|
|
| 28 | + |
|
| 29 | +.PHONY: pkg-database
|
|
| 30 | +pkg-database:
|
|
| 9 | 31 | '$(GHC_PKG)' init tmp.d
|
| 10 | 32 | '$(TEST_HC)' $(TEST_HC_OPTS) -v0 --make Setup
|
| 11 | - cd p && $(SETUP) clean
|
|
| 12 | - cd p && $(SETUP) configure $(CABAL_MINIMAL_BUILD) --ipid=p-0.1.0.0 --with-ghc='$(TEST_HC)' --with-hc-pkg='$(GHC_PKG)' --ghc-options='$(TEST_HC_OPTS)' --package-db=../tmp.d
|
|
| 33 | + cd p && $(SETUP) $(CONFIGURE) --ipid=p-0.1.0.0
|
|
| 13 | 34 | cd p && $(SETUP) build
|
| 14 | 35 | cd p && $(SETUP) register --inplace
|
| 15 | - cd q && $(SETUP) configure $(CABAL_MINIMAL_BUILD) --ipid=q-0.1.0.0 --with-ghc='$(TEST_HC)' --with-hc-pkg='$(GHC_PKG)' --ghc-options='$(TEST_HC_OPTS)' --package-db=../tmp.d
|
|
| 36 | + cd q && $(SETUP) $(CONFIGURE) --ipid=q-0.1.0.0
|
|
| 16 | 37 | cd q && $(SETUP) build
|
| 17 | 38 | cd q && $(SETUP) register --inplace
|
| 18 | - cd r && $(SETUP) configure $(CABAL_MINIMAL_BUILD) --ipid=r-0.1.0.0 --with-ghc='$(TEST_HC)' --with-hc-pkg='$(GHC_PKG)' --ghc-options='$(TEST_HC_OPTS)' --package-db=../tmp.d
|
|
| 39 | + cd r && $(SETUP) $(CONFIGURE) --ipid=r-0.1.0.0
|
|
| 19 | 40 | cd r && $(SETUP) build
|
| 20 | 41 | cd r && $(SETUP) register --inplace
|
| 21 | - # This should work
|
|
| 22 | - '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP
|
|
| 23 | - # So should this
|
|
| 24 | - '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP -unit @unitQ
|
|
| 25 | - # So should this
|
|
| 26 | - '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP -unit @unitQ -unit @unitR
|
|
| 27 | - # This should error with a closure message
|
|
| 28 | - ! '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP -unit @unitR
|
|
| 29 | - # This should work, even though r1 is not in the package db
|
|
| 30 | - '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP -unit @unitQ -unit @unitR1
|
|
| 31 | - # This should fail, even though r1 is not in the package db
|
|
| 32 | - ! '$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp -unit @unitP -unit @unitR1
|
|
| 33 | 42 | |
| 43 | +.PHONY: clean
|
|
| 44 | +clean:
|
|
| 45 | + $(RM) -r tmp.d Setup$(exeext) */dist* *.hi *.o */*.hi */*.o
|
|
| 34 | 46 | |
| 35 | 47 | ifeq "$(CLEANUP)" "1"
|
| 36 | 48 | $(MAKE) -s --no-print-directory clean
|
| 37 | 49 | endif |
| 38 | - |
|
| 39 | -clean :
|
|
| 40 | - $(RM) -r tmp*.d inst-* *.o *.hi */*.o */*.hi */Setup$(exeext) */dist Setup$(exeext)
|
|
| 41 | - |
| 1 | 1 | |
| 2 | 2 | <command line>: error: [GHC-03271]
|
| 3 | - Home units are not closed.
|
|
| 4 | - It is necessary to also load the following units:
|
|
| 5 | - - q-0.1.0.0
|
|
| 3 | + Some units are not loaded but depend on loaded units.
|
|
| 4 | + q-0.1.0.0 -> p-0.1.0.0
|
|
| 6 | 5 | |
| 7 | 6 | <command line>: error: [GHC-03271]
|
| 8 | - Home units are not closed.
|
|
| 9 | - It is necessary to also load the following units:
|
|
| 10 | - - q-0.1.0.0 |
|
| 7 | + Some units are not loaded but depend on loaded units.
|
|
| 8 | + r-0.1.0.0 -> q-0.1.0.0
|
|
| 9 | + |
|
| 10 | +<command line>: error: [GHC-03271]
|
|
| 11 | + Some units are not loaded but depend on loaded units.
|
|
| 12 | + q-0.1.0.0 -> p-0.1.0.0
|
|
| 13 | + |
|
| 14 | +<command line>: error: [GHC-03271]
|
|
| 15 | + Some units are not loaded but depend on loaded units.
|
|
| 16 | + q-0.1.0.0 -> p-0.1.0.0
|
|
| 17 | + |
|
| 18 | +<command line>: error: [GHC-03271]
|
|
| 19 | + Some units are not loaded but depend on loaded units.
|
|
| 20 | + r-0.1.0.0 -> q-0.1.0.0
|
|
| 21 | + |
|
| 22 | +<command line>: error: [GHC-03271]
|
|
| 23 | + Some units are not loaded but depend on loaded units.
|
|
| 24 | + q-0.1.0.0 -> p-0.1.0.0
|
|
| 25 | + |
|
| 26 | +<command line>: error: [GHC-03271]
|
|
| 27 | + Some units are not loaded but depend on loaded units.
|
|
| 28 | + q-0.1.0.0 -> p-0.1.0.0 |
| 1 | +[1 of 3] Compiling P[p-0.1.0.0]
|
|
| 2 | +[2 of 3] Compiling Q[q-0.1.0.0]
|
|
| 3 | +[3 of 3] Compiling R[r-0.1.0.0]
|
|
| 1 | 4 | [1 of 1] Compiling P
|
| 2 | 5 | [1 of 2] Compiling P[p-0.1.0.0]
|
| 3 | 6 | [2 of 2] Compiling Q[q-0.1.0.0]
|
| 1 | +{-# LANGUAGE DataKinds #-}
|
|
| 2 | +{-# LANGUAGE TypeApplications #-}
|
|
| 3 | +{-# LANGUAGE TypeFamilies #-}
|
|
| 4 | + |
|
| 5 | +import Data.Bifunctor (first)
|
|
| 6 | +import Data.Coerce
|
|
| 7 | +import Data.Function (on)
|
|
| 8 | +import Data.List (nubBy, sortOn, stripPrefix)
|
|
| 9 | +import GHC.Data.StringTrie as StringTrie
|
|
| 10 | +import MiniQuickCheck
|
|
| 11 | + |
|
| 12 | +main :: IO ()
|
|
| 13 | +main = do
|
|
| 14 | + runTestsMain (Iterations 1000) tests
|
|
| 15 | + -- runTests (Iterations 100) 9856592474076851907 tests
|
|
| 16 | + |
|
| 17 | +tests :: Test
|
|
| 18 | +tests = Group "StringTrie"
|
|
| 19 | + [ Property "lookup_after_insert" prop_lookup_after_insert
|
|
| 20 | + , Property "fromList == foldl (flip $ uncurry insert) empty" prop_fromList_foldl
|
|
| 21 | + ]
|
|
| 22 | + |
|
| 23 | +type BString4 = BoundedList 4 (BoundedChar 4)
|
|
| 24 | + |
|
| 25 | +-- | @lookup qs (insert k v t)@ returns the values of matching keys ordered by
|
|
| 26 | +-- suffix length.
|
|
| 27 | +prop_lookup_after_insert
|
|
| 28 | + -- | query string as a bounded list
|
|
| 29 | + :: BoundedList 6 (BoundedChar 3)
|
|
| 30 | + -- | length of the key to insert in @t@
|
|
| 31 | + -> BoundedBy Int 3
|
|
| 32 | + -- | value to insert in @t@
|
|
| 33 | + -> BoundedBy Int 9
|
|
| 34 | + -- | list of key-value pairs to build the trie @t@
|
|
| 35 | + -> BoundedList 6 (BString4, Int `BoundedBy` 9)
|
|
| 36 | + -> PropertyCheck
|
|
| 37 | +prop_lookup_after_insert bqs i0 v0 bkvs =
|
|
| 38 | + let kvs = coerce bkvs :: [(String, Int)]
|
|
| 39 | + t = fromList kvs
|
|
| 40 | + qs = coerce @_ @String bqs
|
|
| 41 | + i = min (abs (getBoundedBy i0)) (length qs)
|
|
| 42 | + k = take i qs
|
|
| 43 | + v = getBoundedBy v0
|
|
| 44 | + -- (v1, sfx1) pairs where (k1, v1) is in t and k1 is a prefix of qs
|
|
| 45 | + expected =
|
|
| 46 | + [ (v1, sfx1)
|
|
| 47 | + | kv@(k1, v1) <- nubBy ((==) `on` fst) $ (k, v) : reverse kvs
|
|
| 48 | + , Just sfx1 <- [stripPrefix k1 qs]
|
|
| 49 | + ]
|
|
| 50 | + in
|
|
| 51 | + propertyEqWithContext
|
|
| 52 | + (unlines
|
|
| 53 | + [ "k = " ++ show k
|
|
| 54 | + , "v = " ++ show v
|
|
| 55 | + , "qs = " ++ show qs
|
|
| 56 | + , "kvs = " ++ show kvs
|
|
| 57 | + ]
|
|
| 58 | + )
|
|
| 59 | + (StringTrie.lookup qs (insert k v t))
|
|
| 60 | + (sortOn (length . snd) expected)
|
|
| 61 | + |
|
| 62 | +-- | @fromList xs == foldl (flip $ uncurry insert) empty xs@
|
|
| 63 | +prop_fromList_foldl
|
|
| 64 | + -- | list of key-value pairs to build the trie @t@
|
|
| 65 | + :: Int `BoundedBy` 7
|
|
| 66 | + -> BoundedList 6 (BString4, Int `BoundedBy` 9)
|
|
| 67 | + -> PropertyCheck
|
|
| 68 | +prop_fromList_foldl (BoundedBy sparseness) bkvs =
|
|
| 69 | + let kvs = map (first shiftString) $ coerce bkvs :: [(String, Int)]
|
|
| 70 | + in propertyEqWithContext
|
|
| 71 | + (unlines ["kvs = " ++ show kvs])
|
|
| 72 | + (fromListWithSparseness (sparseness - 1) kvs)
|
|
| 73 | + (foldl (flip $ uncurry insert) empty kvs)
|
|
| 74 | + where
|
|
| 75 | + -- Bring characters next to the end of the ASCII range to test the
|
|
| 76 | + -- array-based implementation of fromList.
|
|
| 77 | + shiftString = map shiftChar
|
|
| 78 | + shiftChar = charPlus (126 - fromEnum 'a')
|
|
| 79 | + charPlus x c = toEnum (x + fromEnum c) |
| 1 | +Group StringTrie
|
|
| 2 | + Running lookup_after_insert
|
|
| 3 | + Passed 1000 iterations
|
|
| 4 | + Running fromList == foldl (flip $ uncurry insert) empty
|
|
| 5 | + Passed 1000 iterations |
| 1 | +[1 of 1] Compiling Main ( t25763.hs, nothing ) |
| ... | ... | @@ -874,6 +874,16 @@ test ('T24984', |
| 874 | 874 | compile,
|
| 875 | 875 | ['-O'])
|
| 876 | 876 | |
| 877 | +# Performance regression test for the parser of command line arguments
|
|
| 878 | +test(
|
|
| 879 | + 'T25763',
|
|
| 880 | + [
|
|
| 881 | + collect_compiler_runtime(5),
|
|
| 882 | + pre_cmd(f'seq 400000 | sed "s/.*/-XCPP/" > t25763.txt && echo "main = pure ()" > t25763.hs'),
|
|
| 883 | + ],
|
|
| 884 | + multimod_compile,
|
|
| 885 | + ['t25763', '@t25763.txt -c -fno-code'])
|
|
| 886 | + |
|
| 877 | 887 | test('interpreter_steplocal',
|
| 878 | 888 | [ collect_compiler_stats('bytes allocated', 5)
|
| 879 | 889 | ],
|
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +import GHC.Exts
|
|
| 3 | +import GHC.Word
|
|
| 4 | +import Numeric
|
|
| 5 | + |
|
| 6 | +main :: IO ()
|
|
| 7 | +main = do
|
|
| 8 | + putStrLn $ showHex (W# (bitReverse8# 0x55##)) ""
|
|
| 9 | + putStrLn $ showHex (W# (bitReverse8# 0xAA##)) ""
|
|
| 10 | + putStrLn $ showHex (W# (bitReverse16# 0x5555##)) ""
|
|
| 11 | + putStrLn $ showHex (W# (bitReverse16# 0xAAAA##)) ""
|
|
| 12 | + putStrLn $ showHex (W# (bitReverse32# 0x5555_5555##)) ""
|
|
| 13 | + putStrLn $ showHex (W# (bitReverse32# 0xAAAA_AAAA##)) ""
|
|
| 14 | + putStrLn $ showHex (W# (byteSwap16# 0xABCD##)) ""
|
|
| 15 | + putStrLn $ showHex (W# (byteSwap32# 0xABCD_EF12##)) ""
|
|
| 16 | + putStrLn $ showHex (W# (noinline bitReverse8# 0x55##)) ""
|
|
| 17 | + putStrLn $ showHex (W# (noinline bitReverse8# 0xAA##)) ""
|
|
| 18 | + putStrLn $ showHex (W# (noinline bitReverse16# 0x5555##)) ""
|
|
| 19 | + putStrLn $ showHex (W# (noinline bitReverse16# 0xAAAA##)) ""
|
|
| 20 | + putStrLn $ showHex (W# (noinline bitReverse32# 0x5555_5555##)) ""
|
|
| 21 | + putStrLn $ showHex (W# (noinline bitReverse32# 0xAAAA_AAAA##)) ""
|
|
| 22 | + putStrLn $ showHex (W# (noinline byteSwap16# 0xABCD##)) ""
|
|
| 23 | + putStrLn $ showHex (W# (noinline byteSwap32# 0xABCD_EF12##)) "" |
| 1 | +aa
|
|
| 2 | +55
|
|
| 3 | +aaaa
|
|
| 4 | +5555
|
|
| 5 | +aaaaaaaa
|
|
| 6 | +55555555
|
|
| 7 | +cdab
|
|
| 8 | +12efcdab
|
|
| 9 | +aa
|
|
| 10 | +55
|
|
| 11 | +aaaa
|
|
| 12 | +5555
|
|
| 13 | +aaaaaaaa
|
|
| 14 | +55555555
|
|
| 15 | +cdab
|
|
| 16 | +12efcdab |
| ... | ... | @@ -91,3 +91,4 @@ test('T24496' |
| 91 | 91 | ]
|
| 92 | 92 | , compile_and_run, ['-O'])
|
| 93 | 93 | test('T6026', normal, compile_and_run, [''])
|
| 94 | +test('T27299', normal, compile_and_run, ['']) |
| 1 | +ghc: on the commandline: missing argument for flag: -W=
|
|
| 2 | +Usage: For basic information, try the `--help' option. |
| ... | ... | @@ -15,6 +15,7 @@ test('CaretDiagnostics1', |
| 15 | 15 | [normalise_whitespace_fun(normalise_whitespace_carefully)],
|
| 16 | 16 | compile_fail,
|
| 17 | 17 | ['-fdiagnostics-show-caret -ferror-spans'])
|
| 18 | +test('WExpectedNoArgError', normal, compile_fail, ['-W='])
|
|
| 18 | 19 | test('CaretDiagnostics2', normal, compile_fail, ['-fdiagnostics-show-caret'])
|
| 19 | 20 | test('Colour', normal, compile_fail, ['-fdiagnostics-color=always'])
|
| 20 | 21 | test('T20263', normal, compile_fail, ['-Wunicode-bidirectional-format-characters -Werror'])
|