summaryrefslogtreecommitdiff
path: root/testsuite/tests/codeGen/should_run/cgrun068.hs
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests/codeGen/should_run/cgrun068.hs')
-rw-r--r--testsuite/tests/codeGen/should_run/cgrun068.hs386
1 files changed, 386 insertions, 0 deletions
diff --git a/testsuite/tests/codeGen/should_run/cgrun068.hs b/testsuite/tests/codeGen/should_run/cgrun068.hs
new file mode 100644
index 0000000000..f5096ad998
--- /dev/null
+++ b/testsuite/tests/codeGen/should_run/cgrun068.hs
@@ -0,0 +1,386 @@
+{-# LANGUAGE BangPatterns, GeneralizedNewtypeDeriving, MagicHash,
+ UnboxedTuples #-}
+
+-- !!! stress tests of copying/cloning primitive arrays
+
+-- Note: You can run this test manually with an argument
+-- (i.e. ./cgrun068 10000) if you want to run the stress test for
+-- longer.
+
+{-
+Test strategy
+=============
+
+We create an array of arrays of integers. Repeatedly we then either
+
+* allocate a new array in place of an old, or
+
+* copy a random segment of an array into another array (which might be
+ the source array).
+
+By running this process long enough we hope to trigger any bugs
+related to garbage collection or edge cases.
+
+We only test copyMutableArray# and cloneArray# as they are
+representative of all the primops.
+-}
+
+module Main ( main ) where
+
+import Debug.Trace (trace)
+
+import Control.Exception (assert)
+import Control.Monad
+import Control.Monad.State.Strict
+import GHC.Exts
+import GHC.ST hiding (liftST)
+import Prelude hiding (length, read)
+import qualified Prelude as P
+import qualified Prelude as P
+import System.Environment
+import System.Random
+
+main :: IO ()
+main = do
+ args <- getArgs
+ -- Number of copies to perform
+ let numMods = case args of
+ [] -> 100
+ [n] -> P.read n :: Int
+ putStr (test_copyMutableArray numMods ++ "\n" ++
+ test_cloneMutableArray numMods ++ "\n"
+ )
+
+-- Number of arrays
+numArrays :: Int
+numArrays = 100
+
+-- Maxmimum length of a sub-array
+maxLen :: Int
+maxLen = 1024
+
+-- Create an array of arrays, with each sub-array having random length
+-- and content.
+setup :: Rng s (MArray s (MArray s Int))
+setup = do
+ len <- rnd (1, numArrays)
+ marr <- liftST $ new_ len
+ let go i
+ | i >= len = return ()
+ | otherwise = do
+ n <- rnd (1, maxLen)
+ subarr <- liftST $ fromList [j*j | j <- [(0::Int)..n-1]]
+ liftST $ write marr i subarr
+ go (i+1)
+ go 0
+ return marr
+
+-- Replace one of the sub-arrays with a newly allocated array.
+allocate :: MArray s (MArray s Int) -> Rng s ()
+allocate marr = do
+ ix <- rnd (0, length marr - 1)
+ n <- rnd (1, maxLen)
+ subarr <- liftST $ fromList [j*j | j <- [(0::Int)..n-1]]
+ liftST $ write marr ix subarr
+
+type CopyFunction s a =
+ MArray s a -> Int -> MArray s a -> Int -> Int -> ST s ()
+
+-- Copy a random segment of an array onto another array, using the
+-- supplied copy function.
+copy :: MArray s (MArray s a) -> CopyFunction s a
+ -> Rng s (Int, Int, Int, Int, Int)
+copy marr f = do
+ six <- rnd (0, length marr - 1)
+ dix <- rnd (0, length marr - 1)
+ src <- liftST $ read marr six
+ dst <- liftST $ read marr dix
+ let srcLen = length src
+ srcOff <- rnd (0, srcLen - 1)
+ let dstLen = length dst
+ dstOff <- rnd (0, dstLen - 1)
+ n <- rnd (0, min (srcLen - srcOff) (dstLen - dstOff))
+ liftST $ f src srcOff dst dstOff n
+ return (six, dix, srcOff, dstOff, n)
+
+type CloneFunction s a = MArray s a -> Int -> Int -> ST s (MArray s a)
+
+-- Clone a random segment of an array, replacing another array, using
+-- the supplied clone function.
+clone :: MArray s (MArray s a) -> CloneFunction s a
+ -> Rng s (Int, Int, Int, Int)
+clone marr f = do
+ six <- rnd (0, length marr - 1)
+ dix <- rnd (0, length marr - 1)
+ src <- liftST $ read marr six
+ let srcLen = length src
+ -- N.B. The array length might be zero if we previously cloned
+ -- zero elements from some array.
+ srcOff <- rnd (0, max 0 (srcLen - 1))
+ n <- rnd (0, srcLen - srcOff)
+ dst <- liftST $ f src srcOff n
+ liftST $ write marr dix dst
+ return (six, dix, srcOff, n)
+
+------------------------------------------------------------------------
+-- copyMutableArray#
+
+-- Copy a slice of the source array into a destination array and check
+-- that the copy succeeded.
+test_copyMutableArray :: Int -> String
+test_copyMutableArray numMods = runST $ run $ do
+ marr <- local setup
+ marrRef <- setup
+ let go i
+ | i >= numMods = return "test_copyMutableArray: OK"
+ | otherwise = do
+ -- Either allocate or copy
+ alloc <- rnd (True, False)
+ if alloc then doAlloc else doCopy
+ go (i+1)
+
+ doAlloc = do
+ local $ allocate marr
+ allocate marrRef
+
+ doCopy = do
+ inp <- liftST $ asList marr
+ _ <- local $ copy marr copyMArray
+ (six, dix, srcOff, dstOff, n) <- copy marrRef copyMArraySlow
+ el <- liftST $ asList marr
+ elRef <- liftST $ asList marrRef
+ when (el /= elRef) $
+ fail inp el elRef six dix srcOff dstOff n
+ go 0
+ where
+ fail inp el elRef six dix srcOff dstOff n =
+ error $ "test_copyMutableArray: FAIL\n"
+ ++ " Input: " ++ unlinesShow inp
+ ++ " Copy: six: " ++ show six ++ " dix: " ++ show dix ++ " srcOff: "
+ ++ show srcOff ++ " dstOff: " ++ show dstOff ++ " n: " ++ show n ++ "\n"
+ ++ "Expected: " ++ unlinesShow elRef
+ ++ " Actual: " ++ unlinesShow el
+
+asList :: MArray s (MArray s a) -> ST s [[a]]
+asList marr = toListM =<< mapArrayM toListM marr
+
+unlinesShow :: Show a => [a] -> String
+unlinesShow = concatMap (\ x -> show x ++ "\n")
+
+------------------------------------------------------------------------
+-- cloneMutableArray#
+
+-- Copy a slice of the source array into a destination array and check
+-- that the copy succeeded.
+test_cloneMutableArray :: Int -> String
+test_cloneMutableArray numMods = runST $ run $ do
+ marr <- local setup
+ marrRef <- setup
+ let go i
+ | i >= numMods = return "test_cloneMutableArray: OK"
+ | otherwise = do
+ -- Either allocate or clone
+ alloc <- rnd (True, False)
+ if alloc then doAlloc else doClone
+ go (i+1)
+
+ doAlloc = do
+ local $ allocate marr
+ allocate marrRef
+
+ doClone = do
+ inp <- liftST $ asList marr
+ _ <- local $ clone marr cloneMArray
+ (six, dix, srcOff, n) <- clone marrRef cloneMArraySlow
+ el <- liftST $ asList marr
+ elRef <- liftST $ asList marrRef
+ when (el /= elRef) $
+ fail inp el elRef six dix srcOff n
+ go 0
+ where
+ fail inp el elRef six dix srcOff n =
+ error $ "test_cloneMutableArray: FAIL\n"
+ ++ " Input: " ++ unlinesShow inp
+ ++ " Clone: six: " ++ show six ++ " dix: " ++ show dix ++ " srcOff: "
+ ++ show srcOff ++ " n: " ++ show n ++ "\n"
+ ++ "Expected: " ++ unlinesShow elRef
+ ++ " Actual: " ++ unlinesShow el
+
+------------------------------------------------------------------------
+-- Convenience wrappers for Array# and MutableArray#
+
+data Array a = Array
+ { unArray :: Array# a
+ , lengthA :: {-# UNPACK #-} !Int}
+
+data MArray s a = MArray
+ { unMArray :: MutableArray# s a
+ , lengthM :: {-# UNPACK #-} !Int}
+
+class IArray a where
+ length :: a -> Int
+instance IArray (Array a) where
+ length = lengthA
+instance IArray (MArray s a) where
+ length = lengthM
+
+instance Eq a => Eq (Array a) where
+ arr1 == arr2 = toList arr1 == toList arr2
+
+new :: Int -> a -> ST s (MArray s a)
+new n@(I# n#) a =
+ assert (n >= 0) $
+ ST $ \s# -> case newArray# n# a s# of
+ (# s2#, marr# #) -> (# s2#, MArray marr# n #)
+
+new_ :: Int -> ST s (MArray s a)
+new_ n = new n (error "Undefined element")
+
+write :: MArray s a -> Int -> a -> ST s ()
+write marr i@(I# i#) a =
+ assert (i >= 0) $
+ assert (i < length marr) $
+ ST $ \ s# ->
+ case writeArray# (unMArray marr) i# a s# of
+ s2# -> (# s2#, () #)
+
+read :: MArray s a -> Int -> ST s a
+read marr i@(I# i#) =
+ assert (i >= 0) $
+ assert (i < length marr) $
+ ST $ \ s# ->
+ readArray# (unMArray marr) i# s#
+
+index :: Array a -> Int -> a
+index arr i@(I# i#) =
+ assert (i >= 0) $
+ assert (i < length arr) $
+ case indexArray# (unArray arr) i# of
+ (# a #) -> a
+
+unsafeFreeze :: MArray s a -> ST s (Array a)
+unsafeFreeze marr = ST $ \ s# ->
+ case unsafeFreezeArray# (unMArray marr) s# of
+ (# s2#, arr# #) -> (# s2#, Array arr# (length marr) #)
+
+toList :: Array a -> [a]
+toList arr = go 0
+ where
+ go i | i >= length arr = []
+ | otherwise = index arr i : go (i+1)
+
+fromList :: [e] -> ST s (MArray s e)
+fromList es = do
+ marr <- new_ n
+ let go !_ [] = return ()
+ go i (x:xs) = write marr i x >> go (i+1) xs
+ go 0 es
+ return marr
+ where
+ n = P.length es
+
+mapArrayM :: (a -> ST s b) -> MArray s a -> ST s (MArray s b)
+mapArrayM f src = do
+ dst <- new_ n
+ let go i
+ | i >= n = return dst
+ | otherwise = do
+ el <- read src i
+ el' <- f el
+ write dst i el'
+ go (i+1)
+ go 0
+ where
+ n = length src
+
+toListM :: MArray s e -> ST s [e]
+toListM marr =
+ sequence [read marr i | i <- [0..(length marr)-1]]
+
+------------------------------------------------------------------------
+-- Wrappers around copy/clone primops
+
+copyMArray :: MArray s a -> Int -> MArray s a -> Int -> Int -> ST s ()
+copyMArray src six@(I# six#) dst dix@(I# dix#) n@(I# n#) =
+ assert (six >= 0) $
+ assert (six + n <= length src) $
+ assert (dix >= 0) $
+ assert (dix + n <= length dst) $
+ ST $ \ s# ->
+ case copyMutableArray# (unMArray src) six# (unMArray dst) dix# n# s# of
+ s2# -> (# s2#, () #)
+
+cloneMArray :: MArray s a -> Int -> Int -> ST s (MArray s a)
+cloneMArray marr off@(I# off#) n@(I# n#) =
+ assert (off >= 0) $
+ assert (off + n <= length marr) $
+ ST $ \ s# ->
+ case cloneMutableArray# (unMArray marr) off# n# s# of
+ (# s2#, marr2 #) -> (# s2#, MArray marr2 n #)
+
+------------------------------------------------------------------------
+-- Manual versions of copy/clone primops. Used to validate the
+-- primops
+
+copyMArraySlow :: MArray s e -> Int -> MArray s e -> Int -> Int -> ST s ()
+copyMArraySlow !src !six !dst !dix n =
+ assert (six >= 0) $
+ assert (six + n <= length src) $
+ assert (dix >= 0) $
+ assert (dix + n <= length dst) $
+ if six < dix
+ then goB (six+n-1) (dix+n-1) 0 -- Copy backwards
+ else goF six dix 0 -- Copy forwards
+ where
+ goF !i !j c
+ | c >= n = return ()
+ | otherwise = do b <- read src i
+ write dst j b
+ goF (i+1) (j+1) (c+1)
+ goB !i !j c
+ | c >= n = return ()
+ | otherwise = do b <- read src i
+ write dst j b
+ goB (i-1) (j-1) (c+1)
+
+cloneMArraySlow :: MArray s a -> Int -> Int -> ST s (MArray s a)
+cloneMArraySlow !marr !off n =
+ assert (off >= 0) $
+ assert (off + n <= length marr) $ do
+ marr2 <- new_ n
+ let go !i !j c
+ | c >= n = return marr2
+ | otherwise = do
+ b <- read marr i
+ write marr2 j b
+ go (i+1) (j+1) (c+1)
+ go off 0 0
+
+------------------------------------------------------------------------
+-- Utilities for simplifying RNG passing
+
+newtype Rng s a = Rng { unRng :: StateT StdGen (ST s) a }
+ deriving Monad
+
+-- Same as 'randomR', but using the RNG state kept in the 'Rng' monad.
+rnd :: Random a => (a, a) -> Rng s a
+rnd r = Rng $ do
+ g <- get
+ let (x, g') = randomR r g
+ put g'
+ return x
+
+-- Run a sub-computation without affecting the RNG state.
+local :: Rng s a -> Rng s a
+local m = Rng $ do
+ g <- get
+ x <- unRng m
+ put g
+ return x
+
+liftST :: ST s a -> Rng s a
+liftST m = Rng $ lift m
+
+run :: Rng s a -> ST s a
+run = flip evalStateT (mkStdGen 13) . unRng
+