Erik de Castro Lopo pushed to branch wip/fix-9809 at Glasgow Haskell Compiler / GHC
Commits:
-
ac870ad5
by Simon Jakobi at 2026-10-28T04:40:42-04:00
-
53087373
by Facundo Domínguez at 2026-10-28T04:41:32-04:00
-
af899dcc
by Erik de Castro Lopo at 2026-10-28T22:27:57+11:00
-
bf2d932e
by Moritz Angermann at 2026-10-28T22:27:57+11:00
29 changed files:
- + changelog.d/T28002
- + changelog.d/T28002-api
- + changelog.d/T9809-timer-manager-contention
- + changelog.d/ncg-x86-subword-popcnt
- compiler/GHC.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/Core/Opt/Monad.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Types/Annotations.hs
- compiler/GHC/Utils/Outputable.hs
- libraries/base/tests/.gitignore
- + libraries/base/tests/T9809.hs
- libraries/base/tests/all.T
- libraries/ghc-boot/GHC/Serialized.hs
- libraries/ghc-internal/src/GHC/Internal/Event/TimerManager.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/TH.hs
- libraries/ghci/GHCi/TH/Binary.hs
- testsuite/tests/annotations/should_run/Makefile
- + testsuite/tests/annotations/should_run/T16841.hs
- + testsuite/tests/annotations/should_run/T16841.stdout
- testsuite/tests/annotations/should_run/all.T
- + testsuite/tests/codeGen/should_gen_asm/T24813.asm
- + testsuite/tests/codeGen/should_gen_asm/T24813.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
- testsuite/tests/plugins/T26761Plugin.hs
- testsuite/tests/plugins/all.T
- testsuite/tests/rts/pause-resume/all.T
- testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.hs
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #28002
|
|
| 3 | +mrs: !16841
|
|
| 4 | +synopsis:
|
|
| 5 | + Use ``ShortByteString`` in the ``Serialized`` type instead of ``[Word8]``
|
|
| 6 | +description:
|
|
| 7 | + ``GHC.Serialized.Serialized`` now uses ``ByteString`` to store serialized data.
|
|
| 8 | + This makes in-memory representation more compact. |
| 1 | +section: ghc-lib
|
|
| 2 | +issues: #28002
|
|
| 3 | +mrs: !16841
|
|
| 4 | +synopsis:
|
|
| 5 | + Use ``ShortByteString`` in the ``Serialized`` type instead of ``[Word8]``
|
|
| 6 | +description:
|
|
| 7 | + Breaking change: All functions that previously handled annotations in ``[Word8]``
|
|
| 8 | + form now use ``ShortByteString``. The following definitions have modified types:
|
|
| 9 | + * ``GHC.findGlobalAnns``
|
|
| 10 | + * ``GHC.Tc.Gen.Splice.getAnnotationsByTypeRep``
|
|
| 11 | + * ``GHC.Core.Opt.Monad.getAnnotations``
|
|
| 12 | + * ``GHC.Core.Opt.Monad.getFirstAnnotations``
|
|
| 13 | + * ``GHC.Types.Annotations.deserializeAnns``
|
|
| 14 | + * ``GHC.Types.Annotations.findAnns``
|
|
| 15 | + * ``GHC.Types.Annotations.findAnnsByTypeRep``
|
|
| 16 | + * ``GHC.Serialized.Serialized``
|
|
| 17 | + * ``GHC.Serialized.toSerialized``
|
|
| 18 | + * ``GHC.Serialized.fromSerialized``
|
|
| 19 | + * ``GHC.Serialized.serializeWithData``
|
|
| 20 | + * ``GHC.Serialized.deserializeWithData``
|
|
| 21 | + * ``GHCi.Message.ReifyAnnotations`` |
| 1 | +section: ghc-internal
|
|
| 2 | +synopsis: Fix excessive GC, memory use and timer latency when many threads
|
|
| 3 | + register timeouts at once with ``threadDelay``, ``registerDelay`` or
|
|
| 4 | + ``System.Timeout.timeout``.
|
|
| 5 | +issues: #9809
|
|
| 6 | +mrs: !16863
|
|
| 7 | +description: {
|
|
| 8 | + Threads registering timeouts now publish only fully evaluated updates to
|
|
| 9 | + the timer manager's timeout queue, using a compare-and-swap loop, instead
|
|
| 10 | + of building chains of unevaluated updates that other threads then had to
|
|
| 11 | + evaluate or block on.
|
|
| 12 | +} |
| 1 | +section: compiler
|
|
| 2 | +synopsis: The x86 native code generator now emits shorter code for 8-bit and
|
|
| 3 | + 16-bit ``popCount`` when :ghc-flag:`-msse4.2` is enabled.
|
|
| 4 | +mrs: !16640
|
|
| 5 | +issues: #24813 |
| ... | ... | @@ -453,12 +453,12 @@ import qualified GHC.Unit.Home.Graph as HUG |
| 453 | 453 | import Control.Applicative ((<|>))
|
| 454 | 454 | import Control.Monad
|
| 455 | 455 | import Control.Monad.Catch as MC
|
| 456 | +import Data.ByteString.Short (ShortByteString)
|
|
| 456 | 457 | import Data.Foldable
|
| 457 | 458 | import Data.Function ((&))
|
| 458 | 459 | import Data.IORef
|
| 459 | 460 | import Data.List (isPrefixOf)
|
| 460 | 461 | import Data.Typeable ( Typeable )
|
| 461 | -import Data.Word ( Word8 )
|
|
| 462 | 462 | |
| 463 | 463 | import qualified Data.Map.Strict as Map
|
| 464 | 464 | import Data.Set (Set)
|
| ... | ... | @@ -1526,7 +1526,7 @@ lookupGlobalName :: GhcMonad m => Name -> m (Maybe TyThing) |
| 1526 | 1526 | lookupGlobalName name = withSession $ \hsc_env -> do
|
| 1527 | 1527 | liftIO $ lookupType hsc_env name
|
| 1528 | 1528 | |
| 1529 | -findGlobalAnns :: (GhcMonad m, Typeable a) => ([Word8] -> a) -> AnnTarget Name -> m [a]
|
|
| 1529 | +findGlobalAnns :: (GhcMonad m, Typeable a) => (ShortByteString -> a) -> AnnTarget Name -> m [a]
|
|
| 1530 | 1530 | findGlobalAnns deserialize target = withSession $ \hsc_env -> do
|
| 1531 | 1531 | ann_env <- liftIO $ prepareAnnotations hsc_env Nothing
|
| 1532 | 1532 | return (findAnns deserialize ann_env target)
|
| ... | ... | @@ -6739,20 +6739,23 @@ genPopCnt bid width dst src = do |
| 6739 | 6739 | |
| 6740 | 6740 | True -> do
|
| 6741 | 6741 | code_src <- getAnyReg src
|
| 6742 | - src_r <- getNewRegNat format
|
|
| 6742 | + -- Sub-word popcounts use 32-bit POPCNT, whose write zero-extends the
|
|
| 6743 | + -- result to the full register.
|
|
| 6744 | + src_r <- getNewRegNat (max II32 format)
|
|
| 6743 | 6745 | let dst_r = getRegisterReg platform (CmmLocal dst)
|
| 6746 | + -- A sub-word argument is a full Word# whose upper bits may be
|
|
| 6747 | + -- dirty, so zero-extend it unless it visibly already is.
|
|
| 6748 | + zx_src
|
|
| 6749 | + | CmmMachOp (MO_UU_Conv from _) [_] <- src, from <= width
|
|
| 6750 | + = nilOL
|
|
| 6751 | + | otherwise
|
|
| 6752 | + = unitOL (MOVZxL format (OpReg src_r) (OpReg src_r))
|
|
| 6744 | 6753 | return $ code_src src_r `appOL`
|
| 6745 | - (if width == W8 then
|
|
| 6746 | - -- The POPCNT instruction doesn't take a r/m8
|
|
| 6747 | - unitOL (MOVZxL II8 (OpReg src_r) (OpReg src_r)) `appOL`
|
|
| 6748 | - unitOL (POPCNT II16 (OpReg src_r) dst_r)
|
|
| 6749 | - else
|
|
| 6750 | - unitOL (POPCNT format (OpReg src_r) dst_r)) `appOL`
|
|
| 6751 | 6754 | (if width == W8 || width == W16 then
|
| 6752 | - -- We used a 16-bit destination register above,
|
|
| 6753 | - -- so zero-extend
|
|
| 6754 | - unitOL (MOVZxL II16 (OpReg dst_r) (OpReg dst_r))
|
|
| 6755 | - else nilOL)
|
|
| 6755 | + zx_src `appOL`
|
|
| 6756 | + unitOL (POPCNT II32 (OpReg src_r) dst_r)
|
|
| 6757 | + else
|
|
| 6758 | + unitOL (POPCNT format (OpReg src_r) dst_r))
|
|
| 6756 | 6759 | |
| 6757 | 6760 | False ->
|
| 6758 | 6761 | -- generate C call to hs_popcntN in ghc-prim
|
| ... | ... | @@ -68,9 +68,9 @@ import GHC.Unit.Module.ModGuts |
| 68 | 68 | import GHC.Unit.External
|
| 69 | 69 | |
| 70 | 70 | import Data.Bifunctor ( bimap )
|
| 71 | +import Data.ByteString.Short ( ShortByteString )
|
|
| 71 | 72 | import Data.Dynamic
|
| 72 | 73 | import Data.Maybe (listToMaybe)
|
| 73 | -import Data.Word
|
|
| 74 | 74 | import Control.Monad
|
| 75 | 75 | import Control.Applicative ( Alternative(..) )
|
| 76 | 76 | |
| ... | ... | @@ -323,14 +323,14 @@ get_eps = do |
| 323 | 323 | -- annotations.
|
| 324 | 324 | --
|
| 325 | 325 | -- See Note [Annotations]
|
| 326 | -getAnnotations :: Typeable a => ([Word8] -> a) -> ModGuts -> CoreM (ModuleEnv [a], NameEnv [a])
|
|
| 326 | +getAnnotations :: Typeable a => (ShortByteString -> a) -> ModGuts -> CoreM (ModuleEnv [a], NameEnv [a])
|
|
| 327 | 327 | getAnnotations deserialize guts = do
|
| 328 | 328 | hsc_env <- getHscEnv
|
| 329 | 329 | ann_env <- liftIO $ prepareAnnotations hsc_env (Just guts)
|
| 330 | 330 | return (deserializeAnns deserialize ann_env)
|
| 331 | 331 | |
| 332 | 332 | -- | Get at most one annotation of a given type per annotatable item.
|
| 333 | -getFirstAnnotations :: Typeable a => ([Word8] -> a) -> ModGuts -> CoreM (ModuleEnv a, NameEnv a)
|
|
| 333 | +getFirstAnnotations :: Typeable a => (ShortByteString -> a) -> ModGuts -> CoreM (ModuleEnv a, NameEnv a)
|
|
| 334 | 334 | getFirstAnnotations deserialize guts
|
| 335 | 335 | = bimap mod name <$> getAnnotations deserialize guts
|
| 336 | 336 | where
|
| ... | ... | @@ -155,10 +155,10 @@ import Control.DeepSeq |
| 155 | 155 | import Control.Monad
|
| 156 | 156 | import Data.Binary
|
| 157 | 157 | import Data.Binary.Get
|
| 158 | +import Data.ByteString.Short (ShortByteString)
|
|
| 158 | 159 | import Data.Containers.ListUtils ( nubOrd )
|
| 159 | 160 | import Data.List ( sortBy )
|
| 160 | 161 | import Data.Maybe
|
| 161 | -import qualified Data.ByteString as B
|
|
| 162 | 162 | import qualified Data.ByteString.Lazy as LB
|
| 163 | 163 | import Data.Dynamic ( fromDynamic, toDyn )
|
| 164 | 164 | import qualified Data.IntMap as IntMap
|
| ... | ... | @@ -1947,7 +1947,7 @@ handleTHMessage msg = case msg of |
| 1947 | 1947 | ReifyInstances n ts -> wrapTHResult $ runQinTcM $ TH.reifyInstances n ts
|
| 1948 | 1948 | ReifyRoles n -> wrapTHResult $ runQinTcM $ TH.reifyRoles n
|
| 1949 | 1949 | ReifyAnnotations lookup tyrep ->
|
| 1950 | - wrapTHResult $ (map B.pack <$> getAnnotationsByTypeRep lookup tyrep)
|
|
| 1950 | + wrapTHResult $ getAnnotationsByTypeRep lookup tyrep
|
|
| 1951 | 1951 | ReifyModule m -> wrapTHResult $ runQinTcM $ TH.reifyModule m
|
| 1952 | 1952 | ReifyConStrictness nm -> wrapTHResult $ runQinTcM $ TH.reifyConStrictness nm
|
| 1953 | 1953 | GetPackageRoot -> wrapTHResult $ runQinTcM $ TH.getPackageRoot
|
| ... | ... | @@ -1967,7 +1967,7 @@ handleTHMessage msg = case msg of |
| 1967 | 1967 | FailIfErrs -> wrapTHResult failIfErrsM
|
| 1968 | 1968 | _ -> panic ("handleTHMessage: unexpected message " ++ show msg)
|
| 1969 | 1969 | |
| 1970 | -getAnnotationsByTypeRep :: TH.AnnLookup -> TypeRep -> TcM [[Word8]]
|
|
| 1970 | +getAnnotationsByTypeRep :: TH.AnnLookup -> TypeRep -> TcM [ShortByteString]
|
|
| 1971 | 1971 | getAnnotationsByTypeRep th_name tyrep
|
| 1972 | 1972 | = do { name <- lookupThAnnLookup th_name
|
| 1973 | 1973 | ; topEnv <- getTopEnv
|
| ... | ... | @@ -27,9 +27,9 @@ import GHC.Utils.Outputable |
| 27 | 27 | import GHC.Serialized
|
| 28 | 28 | |
| 29 | 29 | import Control.Monad
|
| 30 | +import Data.ByteString.Short ( ShortByteString )
|
|
| 30 | 31 | import Data.Maybe
|
| 31 | 32 | import Data.Typeable
|
| 32 | -import Data.Word ( Word8 )
|
|
| 33 | 33 | import Control.DeepSeq
|
| 34 | 34 | |
| 35 | 35 | -- | Represents an annotation after it has been sufficiently desugared from
|
| ... | ... | @@ -112,14 +112,14 @@ plusAnnEnv a b = |
| 112 | 112 | -- | Find the annotations attached to the given target as 'Typeable'
|
| 113 | 113 | -- values of your choice. If no deserializer is specified,
|
| 114 | 114 | -- only transient annotations will be returned.
|
| 115 | -findAnns :: Typeable a => ([Word8] -> a) -> AnnEnv -> CoreAnnTarget -> [a]
|
|
| 115 | +findAnns :: Typeable a => (ShortByteString -> a) -> AnnEnv -> CoreAnnTarget -> [a]
|
|
| 116 | 116 | findAnns deserialize env
|
| 117 | 117 | = mapMaybe (fromSerialized deserialize) . findAnnPayloads env
|
| 118 | 118 | |
| 119 | 119 | -- | Find the annotations attached to the given target as 'Typeable'
|
| 120 | 120 | -- values of your choice. If no deserializer is specified,
|
| 121 | 121 | -- only transient annotations will be returned.
|
| 122 | -findAnnsByTypeRep :: AnnEnv -> CoreAnnTarget -> TypeRep -> [[Word8]]
|
|
| 122 | +findAnnsByTypeRep :: AnnEnv -> CoreAnnTarget -> TypeRep -> [ShortByteString]
|
|
| 123 | 123 | findAnnsByTypeRep env target tyrep
|
| 124 | 124 | = [ ws | Serialized tyrep' ws <- findAnnPayloads env target
|
| 125 | 125 | , tyrep' == tyrep ]
|
| ... | ... | @@ -134,7 +134,7 @@ findAnnPayloads env target = |
| 134 | 134 | -- | Deserialize all annotations of a given type. This happens lazily, that is
|
| 135 | 135 | -- no deserialization will take place until the [a] is actually demanded and
|
| 136 | 136 | -- the [a] can also be empty (the UniqFM is not filtered).
|
| 137 | -deserializeAnns :: Typeable a => ([Word8] -> a) -> AnnEnv -> (ModuleEnv [a], NameEnv [a])
|
|
| 137 | +deserializeAnns :: Typeable a => (ShortByteString -> a) -> AnnEnv -> (ModuleEnv [a], NameEnv [a])
|
|
| 138 | 138 | deserializeAnns deserialize env
|
| 139 | 139 | = ( mapModuleEnv deserAnns (ann_mod_env env)
|
| 140 | 140 | , mapNameEnv deserAnns (ann_name_env env)
|
| ... | ... | @@ -138,6 +138,7 @@ import GHC.Utils.Misc (lastMaybe, snocView) |
| 138 | 138 | |
| 139 | 139 | import Data.ByteString (ByteString)
|
| 140 | 140 | import qualified Data.ByteString as BS
|
| 141 | +import qualified Data.ByteString.Short as Short
|
|
| 141 | 142 | import Data.Char
|
| 142 | 143 | import qualified Data.Map as M
|
| 143 | 144 | import Data.Int
|
| ... | ... | @@ -1105,7 +1106,7 @@ instance Outputable a => Outputable (SCC a) where |
| 1105 | 1106 | ppr (CyclicSCC vs) = text "REC" $$ (nest 3 (vcat (map ppr vs)))
|
| 1106 | 1107 | |
| 1107 | 1108 | instance Outputable Serialized where
|
| 1108 | - ppr (Serialized the_type bytes) = int (length bytes) <+> text "of type" <+> text (show the_type)
|
|
| 1109 | + ppr (Serialized the_type bytes) = int (Short.length bytes) <+> text "of type" <+> text (show the_type)
|
|
| 1109 | 1110 | |
| 1110 | 1111 | instance Outputable Extension where
|
| 1111 | 1112 | ppr = text . show
|
| ... | ... | @@ -275,4 +275,5 @@ |
| 275 | 275 | /weak001
|
| 276 | 276 | /T9395
|
| 277 | 277 | /T9532
|
| 278 | +/T9809
|
|
| 278 | 279 | /T11555 |
| 1 | +-- #9809: when many threads registered timeouts at the same time, the
|
|
| 2 | +-- TimerManager published unevaluated timeout queues with atomicModifyIORef'.
|
|
| 3 | +-- The threads then built long chains of thunks and blocked on each other's
|
|
| 4 | +-- black holes, and the heap grew to hundreds of megabytes. all.T runs this
|
|
| 5 | +-- test with a heap limit that the old code exceeded.
|
|
| 6 | + |
|
| 7 | +import Control.Concurrent
|
|
| 8 | +import Control.Monad
|
|
| 9 | +import Data.IORef
|
|
| 10 | +import GHC.Conc (TVar, atomically, newTVarIO, readTVar, registerDelay, retry, writeTVar)
|
|
| 11 | + |
|
| 12 | +main :: IO ()
|
|
| 13 | +main = do
|
|
| 14 | + let n = 100000
|
|
| 15 | + ref <- newIORef []
|
|
| 16 | + registered <- newTVarIO (0 :: Int)
|
|
| 17 | + replicateM_ n . forkIO $ do
|
|
| 18 | + tv <- registerDelay 10
|
|
| 19 | + atomicModifyIORef' ref (\tvs -> (tv : tvs, ()))
|
|
| 20 | + atomically $ readTVar registered >>= writeTVar registered . (+ 1)
|
|
| 21 | + atomically $ do
|
|
| 22 | + r <- readTVar registered
|
|
| 23 | + when (r /= n) retry
|
|
| 24 | + readIORef ref >>= mapM_ waitFor
|
|
| 25 | + |
|
| 26 | +-- | Block until a delay has fired, so that the test also checks that the
|
|
| 27 | +-- TimerManager fires every timeout.
|
|
| 28 | +waitFor :: TVar Bool -> IO ()
|
|
| 29 | +waitFor tv = atomically $ readTVar tv >>= \fired -> unless fired retry |
| ... | ... | @@ -193,6 +193,12 @@ test('T7653', [when(opsys('mingw32'), skip), |
| 193 | 193 | omit_ways(prof_ways + ghci_ways)], compile_and_run, [''])
|
| 194 | 194 | test('T7787', normal, compile_and_run, [''])
|
| 195 | 195 | |
| 196 | +# Before the fix for #9809 this needed more than 256MB of heap.
|
|
| 197 | +test('T9809', [req_ghc_smp,
|
|
| 198 | + req_target_smp,
|
|
| 199 | + only_ways(['threaded1', 'threaded2', 'nonmoving_thr'])],
|
|
| 200 | + compile_and_run, ['-threaded -with-rtsopts="-N2 -M128m"'])
|
|
| 201 | + |
|
| 196 | 202 | test('topHandler01', [when(opsys('mingw32'), skip), js_broken(22261), req_process], compile_and_run, [''])
|
| 197 | 203 | test('topHandler02',
|
| 198 | 204 | [when(opsys('mingw32'), skip),
|
| ... | ... | @@ -19,144 +19,68 @@ module GHC.Serialized ( |
| 19 | 19 | ) where
|
| 20 | 20 | |
| 21 | 21 | import Prelude -- See note [Why do we import Prelude here?]
|
| 22 | -import Data.Bits
|
|
| 23 | -import Data.Word ( Word8 )
|
|
| 24 | -import Data.Data
|
|
| 25 | 22 | import Control.DeepSeq
|
| 23 | +import Data.Binary
|
|
| 24 | +import Data.Binary.Get
|
|
| 25 | +import Data.Binary.Put
|
|
| 26 | +import Data.ByteString.Short (ShortByteString)
|
|
| 27 | +import qualified Data.ByteString.Short as Short
|
|
| 28 | +import qualified Data.ByteString.Lazy as ByteString.Lazy
|
|
| 29 | +import Data.Data
|
|
| 26 | 30 | |
| 27 | 31 | |
| 28 | 32 | -- | Represents a serialized value of a particular type. Attempts can be made to deserialize it at certain types
|
| 29 | -data Serialized = Serialized TypeRep [Word8]
|
|
| 33 | +data Serialized = Serialized TypeRep ShortByteString
|
|
| 30 | 34 | |
| 31 | 35 | instance NFData Serialized where
|
| 32 | 36 | rnf (Serialized tr ws) = rnf tr `seq` rnf ws
|
| 33 | 37 | |
| 34 | 38 | -- | Put a Typeable value that we are able to actually turn into bytes into a 'Serialized' value ready for deserialization later
|
| 35 | -toSerialized :: forall a. Typeable a => (a -> [Word8]) -> a -> Serialized
|
|
| 39 | +toSerialized :: forall a. Typeable a => (a -> ShortByteString) -> a -> Serialized
|
|
| 36 | 40 | toSerialized serialize what = Serialized (typeOf what) (serialize what)
|
| 37 | 41 | |
| 38 | 42 | -- | If the 'Serialized' value contains something of the given type, then use the specified deserializer to return @Just@ that.
|
| 39 | 43 | -- Otherwise return @Nothing@.
|
| 40 | -fromSerialized :: forall a. Typeable a => ([Word8] -> a) -> Serialized -> Maybe a
|
|
| 44 | +fromSerialized :: forall a. Typeable a => (ShortByteString -> a) -> Serialized -> Maybe a
|
|
| 41 | 45 | fromSerialized deserialize (Serialized the_type bytes)
|
| 42 | 46 | | the_type == rep = Just (deserialize bytes)
|
| 43 | 47 | | otherwise = Nothing
|
| 44 | 48 | where rep = typeRep (Proxy :: Proxy a)
|
| 45 | 49 | |
| 46 | 50 | -- | Use a 'Data' instance to implement a serialization scheme dual to that of 'deserializeWithData'
|
| 47 | -serializeWithData :: Data a => a -> [Word8]
|
|
| 48 | -serializeWithData what = serializeWithData' what []
|
|
| 49 | - |
|
| 50 | -serializeWithData' :: Data a => a -> [Word8] -> [Word8]
|
|
| 51 | -serializeWithData' what = fst $ gfoldl (\(before, a_to_b) a -> (before . serializeWithData' a, a_to_b a))
|
|
| 52 | - (\x -> (serializeConstr (constrRep (toConstr what)), x))
|
|
| 53 | - what
|
|
| 51 | +serializeWithData :: Data a => a -> ShortByteString
|
|
| 52 | +serializeWithData = Short.toShort . ByteString.Lazy.toStrict . runPut . putData
|
|
| 54 | 53 | |
| 55 | 54 | -- | Use a 'Data' instance to implement a deserialization scheme dual to that of 'serializeWithData'
|
| 56 | -deserializeWithData :: Data a => [Word8] -> a
|
|
| 57 | -deserializeWithData = snd . deserializeWithData'
|
|
| 58 | - |
|
| 59 | -deserializeWithData' :: forall a. Data a => [Word8] -> ([Word8], a)
|
|
| 60 | -deserializeWithData' bytes = deserializeConstr bytes $ \constr_rep bytes ->
|
|
| 61 | - gunfold (\(bytes, b_to_r) -> let (bytes', b) = deserializeWithData' bytes in (bytes', b_to_r b))
|
|
| 62 | - (\x -> (bytes, x))
|
|
| 63 | - (repConstr (dataTypeOf (undefined :: a)) constr_rep)
|
|
| 64 | - |
|
| 65 | - |
|
| 66 | -serializeConstr :: ConstrRep -> [Word8] -> [Word8]
|
|
| 67 | -serializeConstr (AlgConstr ix) = serializeWord8 1 . serializeInt ix
|
|
| 68 | -serializeConstr (IntConstr i) = serializeWord8 2 . serializeInteger i
|
|
| 69 | -serializeConstr (FloatConstr r) = serializeWord8 3 . serializeRational r
|
|
| 70 | -serializeConstr (CharConstr c) = serializeWord8 4 . serializeChar c
|
|
| 71 | - |
|
| 72 | - |
|
| 73 | -deserializeConstr :: [Word8] -> (ConstrRep -> [Word8] -> a) -> a
|
|
| 74 | -deserializeConstr bytes k = deserializeWord8 bytes $ \constr_ix bytes ->
|
|
| 75 | - case constr_ix of
|
|
| 76 | - 1 -> deserializeInt bytes $ \ix -> k (AlgConstr ix)
|
|
| 77 | - 2 -> deserializeInteger bytes $ \i -> k (IntConstr i)
|
|
| 78 | - 3 -> deserializeRational bytes $ \r -> k (FloatConstr r)
|
|
| 79 | - 4 -> deserializeChar bytes $ \c -> k (CharConstr c)
|
|
| 80 | - x -> error $ "deserializeConstr: unrecognised serialized constructor type " ++ show x ++ " in context " ++ show bytes
|
|
| 81 | - |
|
| 82 | - |
|
| 83 | -serializeFixedWidthNum :: forall a. (Integral a, FiniteBits a) => a -> [Word8] -> [Word8]
|
|
| 84 | -serializeFixedWidthNum what = go (finiteBitSize what) what
|
|
| 85 | - where
|
|
| 86 | - go :: Int -> a -> [Word8] -> [Word8]
|
|
| 87 | - go size current rest
|
|
| 88 | - | size <= 0 = rest
|
|
| 89 | - | otherwise = fromIntegral (current .&. 255) : go (size - 8) (current `shiftR` 8) rest
|
|
| 90 | - |
|
| 91 | -deserializeFixedWidthNum :: forall a b. (Integral a, FiniteBits a) => [Word8] -> (a -> [Word8] -> b) -> b
|
|
| 92 | -deserializeFixedWidthNum bytes k = go (finiteBitSize (undefined :: a)) bytes k
|
|
| 93 | - where
|
|
| 94 | - go :: Int -> [Word8] -> (a -> [Word8] -> b) -> b
|
|
| 95 | - go size bytes k
|
|
| 96 | - | size <= 0 = k 0 bytes
|
|
| 97 | - | otherwise = case bytes of
|
|
| 98 | - (byte:bytes) -> go (size - 8) bytes (\x -> k ((x `shiftL` 8) .|. fromIntegral byte))
|
|
| 99 | - [] -> error "deserializeFixedWidthNum: unexpected end of stream"
|
|
| 100 | - |
|
| 101 | - |
|
| 102 | -serializeEnum :: (Enum a) => a -> [Word8] -> [Word8]
|
|
| 103 | -serializeEnum = serializeInt . fromEnum
|
|
| 104 | - |
|
| 105 | -deserializeEnum :: Enum a => [Word8] -> (a -> [Word8] -> b) -> b
|
|
| 106 | -deserializeEnum bytes k = deserializeInt bytes (k . toEnum)
|
|
| 107 | - |
|
| 108 | - |
|
| 109 | -serializeWord8 :: Word8 -> [Word8] -> [Word8]
|
|
| 110 | -serializeWord8 x = (x:)
|
|
| 111 | - |
|
| 112 | -deserializeWord8 :: [Word8] -> (Word8 -> [Word8] -> a) -> a
|
|
| 113 | -deserializeWord8 (byte:bytes) k = k byte bytes
|
|
| 114 | -deserializeWord8 [] _ = error "deserializeWord8: unexpected end of stream"
|
|
| 115 | - |
|
| 116 | - |
|
| 117 | -serializeInt :: Int -> [Word8] -> [Word8]
|
|
| 118 | -serializeInt = serializeFixedWidthNum
|
|
| 119 | - |
|
| 120 | -deserializeInt :: [Word8] -> (Int -> [Word8] -> a) -> a
|
|
| 121 | -deserializeInt = deserializeFixedWidthNum
|
|
| 122 | - |
|
| 123 | - |
|
| 124 | -serializeRational :: (Real a) => a -> [Word8] -> [Word8]
|
|
| 125 | -serializeRational = serializeString . show . toRational
|
|
| 126 | - |
|
| 127 | -deserializeRational :: (Fractional a) => [Word8] -> (a -> [Word8] -> b) -> b
|
|
| 128 | -deserializeRational bytes k = deserializeString bytes (k . fromRational . read)
|
|
| 129 | - |
|
| 130 | - |
|
| 131 | -serializeInteger :: Integer -> [Word8] -> [Word8]
|
|
| 132 | -serializeInteger = serializeString . show
|
|
| 133 | - |
|
| 134 | -deserializeInteger :: [Word8] -> (Integer -> [Word8] -> a) -> a
|
|
| 135 | -deserializeInteger bytes k = deserializeString bytes (k . read)
|
|
| 136 | - |
|
| 137 | - |
|
| 138 | -serializeChar :: Char -> [Word8] -> [Word8]
|
|
| 139 | -serializeChar = serializeString . show
|
|
| 140 | - |
|
| 141 | -deserializeChar :: [Word8] -> (Char -> [Word8] -> a) -> a
|
|
| 142 | -deserializeChar bytes k = deserializeString bytes (k . read)
|
|
| 143 | - |
|
| 144 | - |
|
| 145 | -serializeString :: String -> [Word8] -> [Word8]
|
|
| 146 | -serializeString = serializeList serializeEnum
|
|
| 147 | - |
|
| 148 | -deserializeString :: [Word8] -> (String -> [Word8] -> a) -> a
|
|
| 149 | -deserializeString = deserializeList deserializeEnum
|
|
| 150 | - |
|
| 151 | - |
|
| 152 | -serializeList :: (a -> [Word8] -> [Word8]) -> [a] -> [Word8] -> [Word8]
|
|
| 153 | -serializeList serialize_element xs = serializeInt (length xs) . foldr (.) id (map serialize_element xs)
|
|
| 154 | - |
|
| 155 | -deserializeList :: forall a b. (forall c. [Word8] -> (a -> [Word8] -> c) -> c)
|
|
| 156 | - -> [Word8] -> ([a] -> [Word8] -> b) -> b
|
|
| 157 | -deserializeList deserialize_element bytes k = deserializeInt bytes $ \len bytes -> go len bytes k
|
|
| 158 | - where
|
|
| 159 | - go :: Int -> [Word8] -> ([a] -> [Word8] -> b) -> b
|
|
| 160 | - go len bytes k
|
|
| 161 | - | len <= 0 = k [] bytes
|
|
| 162 | - | otherwise = deserialize_element bytes (\elt bytes -> go (len - 1) bytes (k . (elt:))) |
|
| 55 | +deserializeWithData :: Data a => ShortByteString -> a
|
|
| 56 | +deserializeWithData bs =
|
|
| 57 | + case runGetOrFail getData (ByteString.Lazy.fromStrict (Short.fromShort bs)) of
|
|
| 58 | + Right (_, _, x) -> x
|
|
| 59 | + Left (_, off, msg) ->
|
|
| 60 | + error ("deserializeWithData: " ++ msg ++ " at offset " ++ show off)
|
|
| 61 | + |
|
| 62 | +putData :: Data a => a -> Put
|
|
| 63 | +putData x = do
|
|
| 64 | + putConstrRep (constrRep (toConstr x))
|
|
| 65 | + sequence_ (gmapQ putData x)
|
|
| 66 | + |
|
| 67 | +getData :: forall a. Data a => Get a
|
|
| 68 | +getData = do
|
|
| 69 | + rep <- getConstrRep
|
|
| 70 | + fromConstrM getData (repConstr (dataTypeOf (undefined :: a)) rep)
|
|
| 71 | + |
|
| 72 | +putConstrRep :: ConstrRep -> Put
|
|
| 73 | +putConstrRep (AlgConstr ix) = putWord8 1 >> put ix
|
|
| 74 | +putConstrRep (IntConstr i) = putWord8 2 >> put i
|
|
| 75 | +putConstrRep (FloatConstr r) = putWord8 3 >> put r
|
|
| 76 | +putConstrRep (CharConstr c) = putWord8 4 >> put c
|
|
| 77 | + |
|
| 78 | +getConstrRep :: Get ConstrRep
|
|
| 79 | +getConstrRep = do
|
|
| 80 | + tag <- getWord8
|
|
| 81 | + case tag of
|
|
| 82 | + 1 -> AlgConstr <$> get
|
|
| 83 | + 2 -> IntConstr <$> get
|
|
| 84 | + 3 -> FloatConstr <$> get
|
|
| 85 | + 4 -> CharConstr <$> get
|
|
| 86 | + _ -> fail ("unrecognised constructor tag " ++ show tag) |
| ... | ... | @@ -13,8 +13,10 @@ import GHC.Internal.Types () |
| 13 | 13 | {-# LANGUAGE CPP #-}
|
| 14 | 14 | {-# LANGUAGE ExistentialQuantification #-}
|
| 15 | 15 | {-# LANGUAGE FlexibleInstances #-}
|
| 16 | +{-# LANGUAGE MagicHash #-}
|
|
| 16 | 17 | {-# LANGUAGE NoImplicitPrelude #-}
|
| 17 | 18 | {-# LANGUAGE Trustworthy #-}
|
| 19 | +{-# LANGUAGE UnboxedTuples #-}
|
|
| 18 | 20 | |
| 19 | 21 | -- TODO: use the new Windows IO manager
|
| 20 | 22 | module GHC.Internal.Event.TimerManager
|
| ... | ... | @@ -53,6 +55,9 @@ import GHC.Internal.Control.Exception (finally) |
| 53 | 55 | import GHC.Internal.Data.Foldable (sequence_)
|
| 54 | 56 | import GHC.Internal.Data.IORef (IORef, atomicModifyIORef', mkWeakIORef, newIORef, readIORef,
|
| 55 | 57 | writeIORef)
|
| 58 | +import GHC.Internal.IORef (IORef(..))
|
|
| 59 | +import GHC.Internal.Prim (casMutVar#, readMutVar#)
|
|
| 60 | +import GHC.Internal.STRef (STRef(..))
|
|
| 56 | 61 | import GHC.Internal.Err (errorWithoutStackTrace)
|
| 57 | 62 | import GHC.Internal.Clock (getMonotonicTimeNSec)
|
| 58 | 63 | import GHC.Internal.Conc.Signal (runHandlers)
|
| ... | ... | @@ -260,7 +265,7 @@ updateTimeout mgr (TK key) us = do |
| 260 | 265 | |
| 261 | 266 | editTimeouts :: TimerManager -> TimeoutEdit -> IO ()
|
| 262 | 267 | editTimeouts mgr g = do
|
| 263 | - wake <- atomicModifyIORef' (emTimeouts mgr) f
|
|
| 268 | + wake <- modifyTimeouts (emTimeouts mgr) f
|
|
| 264 | 269 | when wake (wakeManager mgr)
|
| 265 | 270 | where
|
| 266 | 271 | f q = (q', wake)
|
| ... | ... | @@ -276,4 +281,75 @@ editTimeouts mgr g = do |
| 276 | 281 | t0 /= t1
|
| 277 | 282 | _ -> True
|
| 278 | 283 | |
| 284 | +-- | Atomically replace the timeout queue with the first component of
|
|
| 285 | +-- @f old@ and return the second component.
|
|
| 286 | +--
|
|
| 287 | +-- Unlike 'atomicModifyIORef'', the new queue is evaluated /before/ it is
|
|
| 288 | +-- published, using a compare-and-swap loop. 'atomicModifyIORef'' installs
|
|
| 289 | +-- the new contents as a thunk and only evaluates it afterwards, so when many
|
|
| 290 | +-- threads register timeouts at once (#9809) each one builds its insert on
|
|
| 291 | +-- top of the previous thread's unevaluated insert, giving long thunk chains,
|
|
| 292 | +-- threads blocking on each other's half evaluated updates and lots of extra
|
|
| 293 | +-- GC work. Since 'Q.IntPSQ' is spine strict, WHNF means fully evaluated, and
|
|
| 294 | +-- other threads only ever see a fully evaluated queue.
|
|
| 295 | +--
|
|
| 296 | +-- The cost is that a thread whose compare-and-swap loses a race has to redo
|
|
| 297 | +-- its O(log n) update. The update runs in constant stack (see #7653).
|
|
| 298 | +--
|
|
| 299 | +-- Only the short edits made by registering threads use this. The timer
|
|
| 300 | +-- manager's own update in 'mkTimeout' (removing every expired timeout) can
|
|
| 301 | +-- take much longer, and under a stream of registrations from other
|
|
| 302 | +-- capabilities its compare-and-swap would almost never succeed, starving it
|
|
| 303 | +-- so that expired timeouts don't fire. It therefore keeps using
|
|
| 304 | +-- 'atomicModifyIORef'', which never retries: it publishes a single thunk and
|
|
| 305 | +-- evaluates it straight away. A registering thread that reads that thunk
|
|
| 306 | +-- evaluates it (or waits for the manager to) before publishing its own fully
|
|
| 307 | +-- evaluated queue, so thunks still cannot pile up.
|
|
| 308 | +--
|
|
| 309 | +-- 'casMutVar#' compares pointers, so the expected value must be exactly the
|
|
| 310 | +-- pointer read from the 'MutVar#'. See Note [Pointer equality in
|
|
| 311 | +-- modifyTimeouts] for why this function is OPAQUE.
|
|
| 312 | +{-# OPAQUE modifyTimeouts #-}
|
|
| 313 | +modifyTimeouts :: IORef TimeoutQueue -> (TimeoutQueue -> (TimeoutQueue, b)) -> IO b
|
|
| 314 | +modifyTimeouts (IORef (STRef ref)) f = IO modLoop
|
|
| 315 | + where
|
|
| 316 | + modLoop s0 = case readMutVar# ref s0 of
|
|
| 317 | + (# s1, oldVar #) -> case f oldVar of
|
|
| 318 | + (newVar, r) -> newVar `seq` case casMutVar# ref oldVar newVar s1 of
|
|
| 319 | + (# s2, 0#, _ #) -> (# s2, r #)
|
|
| 320 | + (# s2, _, _ #) -> modLoop s2
|
|
| 321 | + |
|
| 322 | +{- Note [Pointer equality in modifyTimeouts]
|
|
| 323 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 324 | +'casMutVar#' succeeds only if the expected value is the very pointer stored
|
|
| 325 | +in the 'MutVar#', so all the difficulties of 'reallyUnsafePtrEquality#'
|
|
| 326 | +apply to it. In 'modifyTimeouts' the expected value is @oldVar@, exactly as
|
|
| 327 | +'readMutVar#' returned it, and that must survive optimisation.
|
|
| 328 | + |
|
| 329 | +The danger is the optimiser seeing a @case@ on @oldVar@ in the same function
|
|
| 330 | +as the 'casMutVar#'. That would happen if 'modifyTimeouts' were inlined or
|
|
| 331 | +specialised at a call site, bringing in the body of @f@ (in 'editTimeouts',
|
|
| 332 | +@f@ inspects the queue to decide whether to wake the manager). The binder
|
|
| 333 | +swap (Note [Binder swap] in GHC.Core.Opt.OccurAnal) may then replace @oldVar@
|
|
| 334 | +by the case binder, which is the /evaluated/ queue. That is a different
|
|
| 335 | +pointer whenever the 'MutVar#' holds a thunk or an indirection, for example
|
|
| 336 | +the selector thunk that 'atomicModifyIORef'' in 'mkTimeout' leaves behind.
|
|
| 337 | +Case-of-known-constructor can likewise replace it by a freshly built
|
|
| 338 | +constructor. Either way the compare-and-swap would keep failing and the
|
|
| 339 | +registering thread would spin.
|
|
| 340 | + |
|
| 341 | +The OPAQUE pragma keeps @f@ an unknown function inside 'modifyTimeouts', so
|
|
| 342 | +there is never a @case@ on @oldVar@ to rewrite. OPAQUE rather than NOINLINE:
|
|
| 343 | +both stop inlining and specialisation, but NOINLINE still allows the
|
|
| 344 | +worker/wrapper transformation. Today that is harmless (the worker only
|
|
| 345 | +unboxes the 'IORef' and still takes @f@ as an argument), but OPAQUE states
|
|
| 346 | +the requirement directly: the body must be compiled as written and every call
|
|
| 347 | +must go to it. It also costs nothing, since 'editTimeouts' passes a closure
|
|
| 348 | +anyway.
|
|
| 349 | + |
|
| 350 | +An alternative is to hide the expected pointer in a box built by a NOINLINE
|
|
| 351 | +function before @f@ runs (like the @Ticket@ type of the atomic-primops
|
|
| 352 | +package), but that allocates on every attempt.
|
|
| 353 | +-}
|
|
| 354 | + |
|
| 279 | 355 | #endif |
| ... | ... | @@ -297,7 +297,7 @@ data THMessage a where |
| 297 | 297 | ReifyInstances :: TH.Name -> [TH.Type] -> THMessage (THResult [TH.Dec])
|
| 298 | 298 | ReifyRoles :: TH.Name -> THMessage (THResult [TH.Role])
|
| 299 | 299 | ReifyAnnotations :: TH.AnnLookup -> TypeRep
|
| 300 | - -> THMessage (THResult [ByteString])
|
|
| 300 | + -> THMessage (THResult [BS.ShortByteString])
|
|
| 301 | 301 | ReifyModule :: TH.Module -> THMessage (THResult TH.ModuleInfo)
|
| 302 | 302 | ReifyConStrictness :: TH.Name -> THMessage (THResult [TH.DecidedStrictness])
|
| 303 | 303 |
| ... | ... | @@ -100,7 +100,6 @@ import Control.Monad.IO.Class (MonadIO (..)) |
| 100 | 100 | import Data.Binary
|
| 101 | 101 | import Data.Binary.Put
|
| 102 | 102 | import Data.ByteString (ByteString)
|
| 103 | -import qualified Data.ByteString as B
|
|
| 104 | 103 | import qualified Data.ByteString.Lazy as LB
|
| 105 | 104 | import Data.Data
|
| 106 | 105 | import Data.Dynamic
|
| ... | ... | @@ -169,7 +168,7 @@ instance MonadIO GHCiQ where |
| 169 | 168 | -- serialize irrelevant annotations.
|
| 170 | 169 | reifyAnnotations :: forall a . Data a => TH.AnnLookup -> GHCiQ [a]
|
| 171 | 170 | reifyAnnotations lookup =
|
| 172 | - map (deserializeWithData . B.unpack) <$>
|
|
| 171 | + map deserializeWithData <$>
|
|
| 173 | 172 | ghcCmd (ReifyAnnotations lookup typerep)
|
| 174 | 173 | where typerep = typeOf (undefined :: a)
|
| 175 | 174 |
| ... | ... | @@ -9,7 +9,6 @@ module GHCi.TH.Binary () where |
| 9 | 9 | |
| 10 | 10 | import Prelude -- See note [Why do we import Prelude here?]
|
| 11 | 11 | import Data.Binary
|
| 12 | -import qualified Data.ByteString as B
|
|
| 13 | 12 | import qualified Data.ByteString.Internal as B
|
| 14 | 13 | import GHC.Serialized
|
| 15 | 14 | import qualified GHC.Boot.TH.Syntax as TH
|
| ... | ... | @@ -74,8 +73,8 @@ instance Binary TH.DocLoc |
| 74 | 73 | -- We need Binary TypeRep for serializing annotations
|
| 75 | 74 | |
| 76 | 75 | instance Binary Serialized where
|
| 77 | - put (Serialized tyrep wds) = put tyrep >> put (B.pack wds)
|
|
| 78 | - get = Serialized <$> get <*> (B.unpack <$> get)
|
|
| 76 | + put (Serialized tyrep wds) = put tyrep >> put wds
|
|
| 77 | + get = Serialized <$> get <*> get
|
|
| 79 | 78 | |
| 80 | 79 | instance Binary TH.Bytes where
|
| 81 | 80 | put (TH.Bytes ptr off sz) = put bs
|
| ... | ... | @@ -9,3 +9,7 @@ config : |
| 9 | 9 | echo "module Config where" >>$(CONFIG_HS)
|
| 10 | 10 | echo "cTop :: String" >> $(CONFIG_HS)
|
| 11 | 11 | echo 'cTop = "$(subst \,\\,$(shell '$(TEST_HC)' --print-libdir))"' >> $(CONFIG_HS)
|
| 12 | + |
|
| 13 | +T16841:
|
|
| 14 | + '$(TEST_HC)' $(TEST_HC_OPTS) -v0 -c T16841.hs
|
|
| 15 | + '$(TEST_HC)' $(TEST_HC_OPTS) --show-iface T16841.hi | grep 'annotated by' |
| 1 | +-- | Measures the overhead of serializing annotations of a few types.
|
|
| 2 | +module T16841 where
|
|
| 3 | + |
|
| 4 | +foo :: Int
|
|
| 5 | +foo = 42
|
|
| 6 | + |
|
| 7 | +{-# ANN foo (42 :: Int) #-}
|
|
| 8 | +{-# ANN foo (2^100 :: Integer) #-}
|
|
| 9 | +{-# ANN foo (3.25 :: Double) #-}
|
|
| 10 | +{-# ANN foo 'x' #-}
|
|
| 11 | +{-# ANN foo "helloworld" #-}
|
|
| 12 | +{-# ANN foo (Just True) #-}
|
|
| 13 | +{-# ANN foo ([1..100] :: [Int]) #-}
|
|
| 14 | +{-# ANN module (Left 7 :: Either Int String) #-}
|
|
| 15 | + |
| 1 | +Module target T16841 annotated by 15 of type Either Int [Char]
|
|
| 2 | +Named target foo annotated by 1509 of type [Int]
|
|
| 3 | +Named target foo annotated by 18 of type Maybe Bool
|
|
| 4 | +Named target foo annotated by 119 of type [Char]
|
|
| 5 | +Named target foo annotated by 2 of type Char
|
|
| 6 | +Named target foo annotated by 11 of type Double
|
|
| 7 | +Named target foo annotated by 24 of type Integer
|
|
| 8 | +Named target foo annotated by 6 of type Int
|
|
| 9 | + |
| ... | ... | @@ -16,6 +16,10 @@ test('annrun01', |
| 16 | 16 | multimod_compile_and_run,
|
| 17 | 17 | ['annrun01', '-package ghc ' + config.ghc_th_way_flags])
|
| 18 | 18 | |
| 19 | +# Skipping on windows because of platform-specific failures. See
|
|
| 20 | +# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16841#note_706770
|
|
| 21 | +test('T16841', [req_th, windows_skip, extra_files(['T16841.hs'])], makefile_test, ['T16841'])
|
|
| 22 | + |
|
| 19 | 23 | """"
|
| 20 | 24 | Helpful things to C+P:
|
| 21 | 25 |
| 1 | +popcnt8:
|
|
| 2 | + movzbl
|
|
| 3 | + popcnt
|
|
| 4 | + movq
|
|
| 5 | + jmp
|
|
| 6 | +popcnt16:
|
|
| 7 | + movzwl
|
|
| 8 | + popcnt
|
|
| 9 | + movq
|
|
| 10 | + jmp |
| 1 | +#include "Cmm.h"
|
|
| 2 | + |
|
| 3 | +// Popcount of a zero-extended sub-word value should be a single
|
|
| 4 | +// zero-extending load followed by one POPCNT (#24813).
|
|
| 5 | + |
|
| 6 | +popcnt8 (W_ p) {
|
|
| 7 | + W_ r;
|
|
| 8 | + (r) = prim %popcnt8(%zx64(bits8[p]));
|
|
| 9 | + return (r);
|
|
| 10 | +}
|
|
| 11 | + |
|
| 12 | +popcnt16 (W_ p) {
|
|
| 13 | + W_ r;
|
|
| 14 | + (r) = prim %popcnt16(%zx64(bits16[p]));
|
|
| 15 | + return (r);
|
|
| 16 | +} |
| ... | ... | @@ -27,6 +27,7 @@ test('T25233', [unless(arch('x86_64'), skip), |
| 27 | 27 | when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-O'])
|
| 28 | 28 | test('T25233b', [unless(arch('x86_64'), skip),
|
| 29 | 29 | when(unregisterised(), skip)], compile_grep_asm, ['cmm', True, ''])
|
| 30 | +test('T24813', is_amd64_codegen, compile_cmp_asm, ['cmm', '-msse4.2'])
|
|
| 30 | 31 | is_aarch64_codegen = [
|
| 31 | 32 | unless(arch('aarch64'), skip),
|
| 32 | 33 | when(unregisterised(), skip),
|
| ... | ... | @@ -7,6 +7,7 @@ import GHC |
| 7 | 7 | import GHC.Plugins
|
| 8 | 8 | import GHC.Tc.Types
|
| 9 | 9 | import GHC.Tc.Utils.Monad
|
| 10 | +import qualified Data.ByteString.Short as Short
|
|
| 10 | 11 | import qualified Data.Map as Map
|
| 11 | 12 | import Data.Word (Word8)
|
| 12 | 13 | import System.IO (hFlush, stdout)
|
| ... | ... | @@ -38,5 +39,5 @@ plugin = defaultPlugin { typeCheckResultAction = typecheckPlugin } |
| 38 | 39 | -- check that it is visible from other modules.
|
| 39 | 40 | let ann = Annotation
|
| 40 | 41 | (ModuleTarget $ tcg_mod gblEnv)
|
| 41 | - (toSerialized id [0])
|
|
| 42 | + (toSerialized id (Short.pack [0]))
|
|
| 42 | 43 | return gblEnv { tcg_anns = ann : tcg_anns gblEnv } |
| ... | ... | @@ -155,7 +155,7 @@ test('T14335', |
| 155 | 155 | -fexternal-interpreter -package simple-plugin ' + config.plugin_way_flags])
|
| 156 | 156 | |
| 157 | 157 | test('T26761', [extra_files(['T26761Plugin.hs', 'T26761a.hs'])],
|
| 158 | - multimod_compile, ['T26761', '-package ghc ' + config.plugin_way_flags ])
|
|
| 158 | + multimod_compile, ['T26761', '-package ghc -package bytestring ' + config.plugin_way_flags ])
|
|
| 159 | 159 | |
| 160 | 160 | test('plugin-recomp-pure',
|
| 161 | 161 | [extra_files(['plugin-recomp/', 'plugin-recomp-test.hs']),
|
| ... | ... | @@ -14,6 +14,8 @@ test('pause_resume_via_safe_ffi_concurrent', |
| 14 | 14 | [ only_ways(['threaded1', 'threaded2'])
|
| 15 | 15 | , req_c
|
| 16 | 16 | , extra_files(['pause_resume.c','pause_resume.h'])
|
| 17 | + # Internal watchdog is 60s; give the harness room on loaded Darwin CI.
|
|
| 18 | + , run_timeout_multiplier(2)
|
|
| 17 | 19 | ],
|
| 18 | 20 | multi_compile_and_run, ['pause_resume_via_safe_ffi_concurrent', [('pause_resume.c','')], ''])
|
| 19 | 21 | test('pause_and_use_rts_api',
|
| ... | ... | @@ -8,6 +8,7 @@ import Foreign.Marshal.Alloc |
| 8 | 8 | import Foreign.Ptr
|
| 9 | 9 | import Foreign.Storable
|
| 10 | 10 | import System.Exit
|
| 11 | +import System.IO (hFlush, stdout)
|
|
| 11 | 12 | import System.Timeout
|
| 12 | 13 | |
| 13 | 14 | foreign import ccall safe "pause_resume.h pauseAndResume"
|
| ... | ... | @@ -38,11 +39,17 @@ main = do |
| 38 | 39 | countPtr
|
| 39 | 40 | putMVar mvar ()
|
| 40 | 41 | |
| 41 | - -- Wait (at least 2n seconds to be safe) for all threads to finish.
|
|
| 42 | - result <- timeout (2 * n * 1000000) (mapM_ takeMVar mvars)
|
|
| 42 | + -- Each pause holds the RTS for about a second, so n concurrent
|
|
| 43 | + -- calls serialize to ~n seconds. 2n (10s) was enough on Linux but
|
|
| 44 | + -- a false deadlock on Darwin CI: rts_pause waits for every
|
|
| 45 | + -- capability, and the counter threads keep hitting the timer
|
|
| 46 | + -- manager via threadDelay. Keep a watchdog so a real hang still
|
|
| 47 | + -- fails, but give 60s of slack.
|
|
| 48 | + result <- timeout (60 * 1000000) (mapM_ takeMVar mvars)
|
|
| 43 | 49 | case result of
|
| 44 | 50 | Nothing -> do
|
| 45 | 51 | putStrLn "Not all rts_pause/rts_resume threads have finished. Assuming deadlocked and failing test."
|
| 52 | + hFlush stdout
|
|
| 46 | 53 | exitFailure
|
| 47 | 54 | Just () -> do
|
| 48 | 55 | putStrLn "All threads finished"
|