diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,88 @@
+## 0.8.8 [2024.06.20]
+* Fix infinite loops in the implementations of `storeLoadBarrier`,
+  `loadLoadBarrier`, and `writeBarrier` when building with GHC 9.10 or later.
+
+## 0.8.7 [2024.04.20]
+* Fix typos in the `foreign import`s introduced in `atomic-primops-0.8.5` and
+  `atomic-primops-0.8.6`, which would lead to linker errors when building
+  executables with GHC 9.10.
+
+## 0.8.6 [2024.04.16]
+* Use `prim`, not `ccall`, for the `foreign import`s used when building the
+  library with GHC 9.10 or later. This fixes a GHC 9.10-specific build issue.
+
+## 0.8.5 [2024.02.17]
+* Allow building with GHC 9.10.
+
+## 0.8.4 [2020.10.03]
+* Allow building with `base-4.15` (GHC 9.0).
+
+## 0.8.3 [2019.05.02]
+* Allow the tests to build with `base-4.13` (GHC 8.8).
+* Require GHC 7.10 or later.
+
+## 0.8.2 [2018.03.08]
+* Allow building with `base-4.11`.
+
+## 0.8.1.1 [2017.12.10]
+* Bundle `testing/Fetch.hs` with the package tarball
+
+## 0.8.1
+* Simplify `Setup.hs` to support `Cabal-2.0`/GHC 8.2
+* Properly link `store_load_barrier` and friends against the GHC RTS on Windows
+  when using GHC 8.2 or later
+
+## 0.8.0.4
+* Internal changes to support forthcoming GHC 8.0
+
+## 0.8
+* Implements additional fetch primops available in GHC 7.10
+
+## 0.7
+* This release adds support for GHC 7.10 and its expanded library of (now inline) primops.
+
+## 0.6.1
+* This is a good version to use for GHC 7.8.3.  It includes portability and bug fixes
+  and adds atomicModifyIORefCAS.
+
+## 0.6.0.5
+* fix for GHC 7.8
+
+## 0.6.0.1
+* minor ghc 7.8 fix
+
+## 0.6
+* add atomicModifyIORefCAS, and bump due to prev bugfixes
+
+## 0.5.0.2
+* IMPORTANT BUGFIXES - don't use earlier versions.  They have been marked deprecated.
+
+## 0.5
+* Nix Data.Atomics.Counter.Foreign and the bits-atomic dependency.
+
+## 0.4.1
+* Add advance support for GHC 7.8
+
+## 0.4
+* Further internal changes, duplicate 'cas' routine well as barriers.
+* Add `fetchAddByteArrayInt`
+* Add an `Unboxed` counter variant that uses movable "ByteArray"s on the GHC heap.
+
+## 0.3
+* Major internal change.  Duplicate the barrier code from the GHC RTS and thus
+  enable support for executables that are NOT built with '-threaded'.
+
+## 0.2.2.1
+* Minor, add warning.
+
+## 0.2.2
+* Add more counters
+
+## 0.2
+* Critical bugfix and add Counter.
+
+## 0.1.0.2
+* disable profiling
+
+## 0.1.0.0
+* initial release
diff --git a/Data/Atomics.hs b/Data/Atomics.hs
--- a/Data/Atomics.hs
+++ b/Data/Atomics.hs
@@ -1,5 +1,9 @@
-{-# LANGUAGE  MagicHash, UnboxedTuples, ScopedTypeVariables, BangPatterns, CPP #-}
+{-# LANGUAGE MagicHash, UnboxedTuples, ScopedTypeVariables, BangPatterns, CPP #-}
 {-# LANGUAGE ForeignFunctionInterface #-}
+#if __GLASGOW_HASKELL__ >= 909
+{-# LANGUAGE GHCForeignImportPrim #-}
+{-# LANGUAGE UnliftedFFITypes #-}
+#endif
 
 -- | Provides atomic memory operations on IORefs and Mutable Arrays.
 --
@@ -10,58 +14,60 @@
 --   (`Tickets`).  Currently, the user cannot coin new tickets, rather a `Ticket`
 --   provides evidence of a past observation, and grants permission to make a future
 --   change.
-module Data.Atomics 
+module Data.Atomics
  (
    -- * Types for atomic operations
    Ticket, peekTicket, -- CASResult(..),
 
    -- * Atomic operations on IORefs
-   readForCAS, casIORef, casIORef2, 
+   readForCAS, casIORef, casIORef2,
    atomicModifyIORefCAS, atomicModifyIORefCAS_,
-   
+
    -- * Atomic operations on mutable arrays
-   casArrayElem, casArrayElem2, readArrayElem, 
+   casArrayElem, casArrayElem2, readArrayElem,
 
    -- * Atomic operations on byte arrays
-   casByteArrayInt, fetchAddByteArrayInt,
-      
+   casByteArrayInt,
+   fetchAddIntArray,
+   fetchSubIntArray,
+   fetchAndIntArray,
+   fetchNandIntArray,
+   fetchOrIntArray,
+   fetchXorIntArray,
+   -- -- ** Reading and writing with barriers
+   -- atomicReadIntArray,
+   -- atomicWriteIntArray,
+
    -- * Atomic operations on raw MutVars
    -- | A lower-level version of the IORef interface.
    readMutVarForCAS, casMutVar, casMutVar2,
 
    -- * Memory barriers
-   storeLoadBarrier, loadLoadBarrier, writeBarrier
+   storeLoadBarrier, loadLoadBarrier, writeBarrier,
+
+   -- * Deprecated Functions
+   fetchAddByteArrayInt
  ) where
 
-import Control.Monad.ST (stToIO)
 import Control.Exception (evaluate)
 import Data.Primitive.Array (MutableArray(MutableArray))
 import Data.Primitive.ByteArray (MutableByteArray(MutableByteArray))
 import Data.Atomics.Internal
-import Data.Int -- TEMPORARY
-import Debug.Trace
 
-import Data.IORef 
+import Data.IORef
 import GHC.IORef hiding (atomicModifyIORef)
 import GHC.STRef
-import GHC.ST
-#if MIN_VERSION_base(4,7,0)
-import GHC.Prim hiding ((==#))
+import GHC.Exts hiding ((==#))
 import qualified GHC.PrimopWrappers as GPW
-#else
-import GHC.Prim
-#endif
-import GHC.Arr 
-import GHC.Base (Int(I#))
 import GHC.IO (IO(IO))
-import GHC.Word (Word(W#))
+-- import GHC.Word (Word(W#))
 
 #ifdef DEBUG_ATOMICS
 #warning "Activating DEBUG_ATOMICS... NOINLINE's and more"
 {-# NOINLINE seal #-}
 
 {-# NOINLINE casIORef #-}
-{-# NOINLINE casArrayElem2 #-}   
+{-# NOINLINE casArrayElem2 #-}
 {-# NOINLINE readArrayElem #-}
 {-# NOINLINE readForCAS #-}
 {-# NOINLINE casArrayElem #-}
@@ -69,9 +75,16 @@
 {-# NOINLINE readMutVarForCAS #-}
 {-# NOINLINE casMutVar #-}
 {-# NOINLINE casMutVar2 #-}
+{-# NOINLINE casByteArrayInt #-}
+{-# NOINLINE fetchAddIntArray #-}
+{-# NOINLINE fetchSubIntArray #-}
+{-# NOINLINE fetchAndIntArray #-}
+{-# NOINLINE fetchNandIntArray #-}
+{-# NOINLINE fetchOrIntArray #-}
+{-# NOINLINE fetchXorIntArray #-}
 #else
 {-# INLINE casIORef #-}
-{-# INLINE casArrayElem2 #-}   
+{-# INLINE casArrayElem2 #-}
 {-# INLINE readArrayElem #-}
 {-# INLINE readForCAS #-}
 {-# INLINE casArrayElem #-}
@@ -79,24 +92,28 @@
 {-# INLINE readMutVarForCAS #-}
 {-# INLINE casMutVar #-}
 {-# INLINE casMutVar2 #-}
+{-# INLINE fetchAddIntArray #-}
+{-# INLINE fetchSubIntArray #-}
+{-# INLINE fetchAndIntArray #-}
+{-# INLINE fetchNandIntArray #-}
+{-# INLINE fetchOrIntArray #-}
+{-# INLINE fetchXorIntArray #-}
 #endif
 
 
 -- GHC 7.8 changed some primops
-#if MIN_VERSION_base(4,7,0)
 (==#) :: Int# -> Int# -> Bool
 (==#) x y = case x GPW.==# y of { 0# -> False; _ -> True }
-#endif
 
 --------------------------------------------------------------------------------
 
 -- | Compare-and-swap.  Follows the same rules as `casIORef`, returning the ticket for
 --   then next operation.
--- 
+--
 --   By convention this is WHNF strict in the "new" value provided.
 casArrayElem :: MutableArray RealWorld a -> Int -> Ticket a -> a -> IO (Bool, Ticket a)
 -- casArrayElem (MutableArray arr#) (I# i#) old new = IO$ \s1# ->
---  case casArray# arr# i# old new s1# of 
+--  case casArray# arr# i# old new s1# of
 --    (# s2#, x#, res #) -> (# s2#, (x# ==# 0#, res) #)
 casArrayElem arr i old !new = casArrayElem2 arr i old (seal new)
 
@@ -104,7 +121,7 @@
 -- arbitrary, lifted, Haskell value.
 casArrayElem2 :: MutableArray RealWorld a -> Int -> Ticket a -> Ticket a -> IO (Bool, Ticket a)
 casArrayElem2 (MutableArray arr#) (I# i#) old new = IO$ \s1# ->
- case casArrayTicketed# arr# i# old new s1# of 
+ case casArrayTicketed# arr# i# old new s1# of
    (# s2#, x#, res #) -> (# s2#, (x# ==# 0#, res) #)
 
 -- | Ordinary processor load instruction (non-atomic, not implying any memory barriers).
@@ -122,6 +139,8 @@
 -- Further, this version always returns the /old value/, that was read from the array during
 -- the CAS operation.  That is, it follows the normal protocol for CAS operations
 -- (and matches the underlying instruction on most architectures).
+--
+-- Implies a full memory barrier.
 casByteArrayInt ::  MutableByteArray RealWorld -> Int -> Int -> Int -> IO Int
 casByteArrayInt (MutableByteArray mba#) (I# ix#) (I# old#) (I# new#) =
   IO$ \s1# ->
@@ -136,27 +155,114 @@
   (# s2#, (I# res) #)
   -- I don't know if a let will mak any difference here... hopefully not.
 
+
+--------------------------------------------------------------------------------
+-- Fetch-and-* family of functions:
+
+-- | Atomically add to a word of memory within a `MutableByteArray`, returning
+-- the value *before* the operation. Implies a full memory barrier.
+fetchAddIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be added
+                     -> IO Int -- ^ The value *before* the addition
+fetchAddIntArray (MutableByteArray mba#) (I# offset#) (I# incr#) = IO $ \ s1# ->
+  let (# s2#, res #) = fetchAddIntArray# mba# offset# incr# s1# in
+  (# s2#, (I# res) #)
+
+
+-- | Atomically subtract to a word of memory within a `MutableByteArray`,
+-- returning the value *before* the operation. Implies a full memory barrier.
+fetchSubIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be subtracted
+                     -> IO Int -- ^ The value *before* the addition
+fetchSubIntArray = doAtomicRMW fetchSubIntArray#
+
+-- | Atomically bitwise AND to a word of memory within a `MutableByteArray`,
+-- returning the value *before* the operation. Implies a full memory barrier.
+fetchAndIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be AND-ed
+                     -> IO Int -- ^ The value *before* the addition
+fetchAndIntArray = doAtomicRMW fetchAndIntArray#
+
+-- | Atomically bitwise NAND to a word of memory within a `MutableByteArray`,
+-- returning the value *before* the operation. Implies a full memory barrier.
+fetchNandIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be NAND-ed
+                     -> IO Int -- ^ The value *before* the addition
+fetchNandIntArray = doAtomicRMW fetchNandIntArray#
+
+-- | Atomically bitwise OR to a word of memory within a `MutableByteArray`,
+-- returning the value *before* the operation. Implies a full memory barrier.
+fetchOrIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be OR-ed
+                     -> IO Int -- ^ The value *before* the addition
+fetchOrIntArray = doAtomicRMW fetchOrIntArray#
+
+-- | Atomically bitwise XOR to a word of memory within a `MutableByteArray`,
+-- returning the value *before* the operation. Implies a full memory barrier.
+fetchXorIntArray :: MutableByteArray RealWorld
+                     -> Int    -- ^ The offset into the array
+                     -> Int    -- ^ The value to be XOR-ed
+                     -> IO Int -- ^ The value *before* the addition
+fetchXorIntArray = doAtomicRMW fetchXorIntArray#
+
+
+-- Internals for our fetch* family of functions, with CAS loop fallbacks for
+-- GHC < 7.10:
+{-# INLINE doAtomicRMW #-}
+doAtomicRMW :: (MutableByteArray# RealWorld -> Int# -> Int# -> State# RealWorld -> (# State# RealWorld, Int# #)) --  primop
+            -> MutableByteArray RealWorld -> Int -> Int -> IO Int      --  exported function
+doAtomicRMW atomicOp# =
+  \(MutableByteArray mba#) (I# offset#) (I# val#) ->
+    IO $ \ s1# ->
+      let (# s2#, res #) = atomicOp# mba# offset# val# s1# in
+      (# s2#, (I# res) #)
+
+
 {-# DEPRECATED fetchAddByteArrayInt "Replaced by fetchAddIntArray which returns the OLD value" #-}
 -- | Atomically add to a word of memory within a `MutableByteArray`.
--- 
+--
 --   This function returns the NEW value of the location after the increment.
 --   Thus, it is a bit misnamed, and in other contexts might be called "add-and-fetch",
 --   such as in GCC's `__sync_add_and_fetch`.
 fetchAddByteArrayInt ::  MutableByteArray RealWorld -> Int -> Int -> IO Int
-fetchAddByteArrayInt (MutableByteArray mba#) (I# offset#) (I# incr#) = IO $ \ s1# -> 
+fetchAddByteArrayInt (MutableByteArray mba#) (I# offset#) (I# incr#) = IO $ \ s1# ->
   let (# s2#, res #) = fetchAddIntArray# mba# offset# incr# s1# in
--- fetchAddIntArray# changed behavior in 7.10 to return the OLD value, so we
--- need this to maintain forwards compatibility until removed:
-#if MIN_VERSION_base(4,8,0)
   (# s2#, (I# (res +# incr#)) #)
-#else
-  (# s2#, (I# res) #)
-#endif
 
+
 --------------------------------------------------------------------------------
+{- WIP. Having trouble writing good tests for these, and not sure how useful
+ - these are. See #43 discussion
+ -
+ - Also remember to add these to the INLINE / NOINLINE section when exported
 
+
+
+-- | Given an array and an offset in Int units, read an element. The index is
+-- assumed to be in bounds. Implies a full memory barrier.
+atomicReadIntArray :: MutableByteArray RealWorld -> Int -> IO Int
+atomicReadIntArray (MutableByteArray mba#) (I# ix#) = IO $ \ s# ->
+    case atomicReadIntArray# mba# ix# s# of
+        (# s2#, n# #) -> (# s2#, I# n# #)
+
+-- | Given an array and an offset in Int units, write an element. The index is
+-- assumed to be in bounds. Implies a full memory barrier.
+atomicWriteIntArray :: MutableByteArray RealWorld -> Int -> Int -> IO ()
+atomicWriteIntArray (MutableByteArray mba#) (I# ix#) (I# n#) = IO $ \ s# ->
+    case atomicWriteIntArray# mba# ix# n# s# of
+        s2# -> (# s2#, () #)
+
+-}
+
+--------------------------------------------------------------------------------
+
 -- | Ordinary processor load instruction (non-atomic, not implying any memory barriers).
--- 
+--
 --   The difference between this function and `readIORef`, is that it returns a /ticket/,
 --   for use in future compare-and-swap operations.
 readForCAS :: IORef a -> IO ( Ticket a )
@@ -171,8 +277,8 @@
 -- The reason for the difference is the ticket API.  This function always returns the
 -- ticket that you should use in your next CAS attempt.  In case of success, this ticket
 -- corresponds to the `new` value which you yourself installed in the `IORef`, whereas
--- in the case of failure it represents the preexisting value currently in the IORef. 
--- 
+-- in the case of failure it represents the preexisting value currently in the IORef.
+--
 -- Note \"compare\" here means pointer equality in the sense of
 -- 'GHC.Prim.reallyUnsafePtrEquality#'.  However, the ticket API absolves
 -- the user of this module from needing to worry about the pointer equality of their
@@ -186,15 +292,15 @@
          -> Ticket a -- ^ A ticket for the 'old' value
          -> a        -- ^ The 'new' value to replace 'current' if @old == current@
          -> IO (Bool, Ticket a) -- ^ Success flag, plus ticket for the NEXT operation.
-casIORef (IORef (STRef var)) old !new = casMutVar var old new 
+casIORef (IORef (STRef var)) old !new = casMutVar var old new
 
 -- | This variant takes two tickets, i.e. the 'new' value is a ticket rather than an
 -- arbitrary, lifted, Haskell value.
-casIORef2 :: IORef a 
+casIORef2 :: IORef a
          -> Ticket a -- ^ A ticket for the 'old' value
          -> Ticket a -- ^ A ticket for the 'new' value
          -> IO (Bool, Ticket a)
-casIORef2 (IORef (STRef var)) old new = casMutVar2 var old new 
+casIORef2 (IORef (STRef var)) old new = casMutVar2 var old new
 
 
 --------------------------------------------------------------------------------
@@ -204,12 +310,12 @@
 {-# NOINLINE peekTicket #-}
 -- At least this function MUST remain NOINLINE.  Issue5 is an example of a bug that
 -- ensues otherwise.
-peekTicket :: Ticket a -> a 
+peekTicket :: Ticket a -> a
 peekTicket = unsafeCoerce#
 
 -- Not exposing this for now.  Presently the idea is that you must read from the
 -- mutable data structure itself to get a ticket.
-seal :: a -> Ticket a 
+seal :: a -> Ticket a
 seal = unsafeCoerce#
 
 -- | Like `readForCAS`, but for `MutVar#`.
@@ -220,17 +326,17 @@
 --
 --   By convention this is WHNF strict in the "new" value provided.
 casMutVar :: MutVar# RealWorld a -> Ticket a -> a -> IO (Bool, Ticket a)
-casMutVar mv tick !new = 
-  -- trace ("TEMPDBG: Inside casMutVar.. ") $ 
+casMutVar mv tick !new =
+  -- trace ("TEMPDBG: Inside casMutVar.. ") $
   casMutVar2 mv tick (seal new)
 
 -- | This variant takes two tickets, i.e. the 'new' value is a ticket rather than an
 -- arbitrary, lifted, Haskell value.
 casMutVar2 :: MutVar# RealWorld a -> Ticket a -> Ticket a -> IO (Bool, Ticket a)
-casMutVar2 mv tick new = IO$ \st -> 
-  case casMutVarTicketed# mv tick new st of 
-    (# st, flag, tick' #) ->
-      (# st, (flag ==# 0#, tick') #)
+casMutVar2 mv tick new = IO$ \st ->
+  case casMutVarTicketed# mv tick new st of
+    (# st', flag, tick' #) ->
+      (# st', (flag ==# 0#, tick') #)
 --      (# st, if flag ==# 0# then Succeed tick' else Fail tick' #)
 --      if flag ==# 0#    then       else (# st, Fail (W# tick')  #)
 
@@ -239,30 +345,58 @@
 --------------------------------------------------------------------------------
 
 -- | Memory barrier implemented by the GHC rts (see SMP.h).
-storeLoadBarrier :: IO ()
+-- storeLoadBarrier :: IO ()
 
 -- | Memory barrier implemented by the GHC rts (see SMP.h).
-loadLoadBarrier :: IO ()
+-- loadLoadBarrier :: IO ()
 
 -- | Memory barrier implemented by the GHC rts (see SMP.h).
+-- writeBarrier :: IO ()
+
+#if __GLASGOW_HASKELL__ >= 909
+
+foreign import prim "hs_atomic_primops_store_load_barrier" storeLoadBarrier#
+  :: State# RealWorld -> State# RealWorld
+
+-- | A memory barrier that prevents future loads occurring before preceding
+-- stores.
+storeLoadBarrier :: IO ()
+storeLoadBarrier = IO $ \s -> case storeLoadBarrier# s of s' -> (# s', () #)
+
+foreign import prim "hs_atomic_primops_load_load_barrier" loadLoadBarrier#
+  :: State# RealWorld -> State# RealWorld
+
+-- | A memory barrier that prevents future loads occurring before earlier loads.
+loadLoadBarrier :: IO ()
+loadLoadBarrier = IO $ \s -> case loadLoadBarrier# s of s' -> (# s', () #)
+
+foreign import prim "hs_atomic_primops_write_barrier" writeBarrier#
+  :: State# RealWorld -> State# RealWorld
+
+-- | A memory barrier that prevents future stores occurring before preceding
+-- stores.
 writeBarrier :: IO ()
+writeBarrier = IO $ \s -> case writeBarrier# s of s' -> (# s', () #)
 
--- GHC 7.8 consistently exposes these symbols while linking:
-#if MIN_VERSION_base(4,7,0) && !defined(_WIN32) && !defined(_WIN64)
+#elif !(defined(mingw32_HOST_OS) && __GLASGOW_HASKELL__ < 802)
+
+-- | Memory barrier implemented by the GHC rts (see SMP.h).
 foreign import ccall  unsafe "store_load_barrier" storeLoadBarrier
-  :: IO () 
+  :: IO ()
 
+-- | Memory barrier implemented by the GHC rts (see SMP.h).
 foreign import ccall unsafe "load_load_barrier" loadLoadBarrier
   :: IO ()
 
+-- | Memory barrier implemented by the GHC rts (see SMP.h).
 foreign import ccall unsafe "write_barrier" writeBarrier
   :: IO ()
-
 #else
--- GHC 7.6 did not consistently expose them (e.g. in the non-threaded RTS),
--- so rather we grab this functionality from RtsDup.c:
+#warning "importing store_load_barrier and friends from the package's C code."
+
+-- Workaround for Trac #12846, which affects old GHCs on Windows
 foreign import ccall  unsafe "DUP_store_load_barrier" storeLoadBarrier
-  :: IO () 
+  :: IO ()
 
 foreign import ccall unsafe "DUP_load_load_barrier" loadLoadBarrier
   :: IO ()
@@ -271,18 +405,17 @@
   :: IO ()
 #endif
 
-
 --------------------------------------------------------------------------------
 
 
--- | A drop-in replacement for `atomicModifyIORefCAS` that
+-- | A drop-in replacement for `atomicModifyIORef` that
 --   optimistically attempts to compute the new value and CAS it into
 --   place without introducing new thunks or locking anything.  Note
 --   that this is more STRICT than its standard counterpart and will only
 --   place evaluated (WHNF) values in the IORef.
--- 
---   The upside is that sometimes we see a performance benefit.  
---   The downside is that this version is speculative -- when it 
+--
+--   The upside is that sometimes we see a performance benefit.
+--   The downside is that this version is speculative -- when it
 --   retries, it must reexecute the compution.
 atomicModifyIORefCAS :: IORef a      -- ^ Mutable location to modify
                      -> (a -> (a,b)) -- ^ Computation runs one or more times (speculation)
@@ -291,13 +424,13 @@
    -- TODO: Should handle contention in a better way...
    tick <- readForCAS ref
    loop tick effort
-  where 
+  where
    effort = 30 :: Int -- TODO: Tune this.
-   loop old 0     = atomicModifyIORef ref fn -- Fall back to the regular version.
-   loop old tries = do 
+   loop _   0     = atomicModifyIORef ref fn -- Fall back to the regular version.
+   loop old tries = do
      (new,result) <- evaluate $ fn $ peekTicket old
      (b,tick) <- casIORef ref old new
-     if b 
+     if b
       then return result
       else loop tick (tries-1)
 
@@ -310,15 +443,13 @@
 atomicModifyIORefCAS_ ref fn = do
    tick <- readForCAS ref
    loop tick effort
-  where 
+  where
    effort = 30 :: Int -- TODO: Tune this.
-   loop old 0     = atomicModifyIORef_ ref fn
-   loop old tries = do 
+   loop _   0     = atomicModifyIORef ref (\ x -> (fn x, ()))
+   loop old tries = do
      new <- evaluate $ fn $ peekTicket old
      (b,val) <- casIORef ref old new
-     if b 
+     if b
       then return ()
       else loop val (tries-1)
-   atomicModifyIORef_ ref fn = atomicModifyIORef ref (\ x -> (fn x, ()))
 -- </duplicated code>
-
diff --git a/Data/Atomics/Counter.hs b/Data/Atomics/Counter.hs
--- a/Data/Atomics/Counter.hs
+++ b/Data/Atomics/Counter.hs
@@ -32,25 +32,14 @@
  where
 
 
-import GHC.Ptr
-import Data.Atomics          (casByteArrayInt)
--- import Data.Atomics.Internal (casIntArray#, fetchAddIntArray#)
 import Data.Atomics.Internal
-#if MIN_VERSION_base(4,7,0)
 import GHC.Base  hiding ((==#))
-import GHC.Prim hiding ((==#))
 import qualified GHC.PrimopWrappers as GPW
-#else
-import GHC.Base
-import GHC.Prim
-#endif
 
 
 -- GHC 7.8 changed some primops
-#if MIN_VERSION_base(4,7,0)
 (==#) :: Int# -> Int# -> Bool
 (==#) x y = case x GPW.==# y of { 0# -> False; _ -> True }
-#endif
 
 
 
@@ -83,23 +72,23 @@
 {-# INLINE newRawCounter #-}
 newRawCounter :: IO AtomicCounter  
 newRawCounter = IO $ \s ->
-  case newByteArray# size s of { (# s, arr #) ->
-  (# s, AtomicCounter arr #) }
+  case newByteArray# size s of { (# s', arr #) ->
+  (# s', AtomicCounter arr #) }
   where !(I# size) = SIZEOF_HSINT
 
 {-# INLINE readCounter #-}
 -- | Equivalent to `readCounterForCAS` followed by `peekCTicket`.        
 readCounter :: AtomicCounter -> IO Int
 readCounter (AtomicCounter arr) = IO $ \s ->
-  case readIntArray# arr 0# s of { (# s, i #) ->
-  (# s, I# i #) }
+  case readIntArray# arr 0# s of { (# s', i #) ->
+  (# s', I# i #) }
 
 {-# INLINE writeCounter #-}
 -- | Make a non-atomic write to the counter.  No memory-barrier.
 writeCounter :: AtomicCounter -> Int -> IO ()
 writeCounter (AtomicCounter arr) (I# i) = IO $ \s ->
-  case writeIntArray# arr 0# i s of { s ->
-  (# s, () #) }
+  case writeIntArray# arr 0# i s of { s' ->
+  (# s', () #) }
 
 {-# INLINE readCounterForCAS #-}
 -- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque
@@ -127,9 +116,9 @@
     False -> (# s2#, (False, I# res# ) #) -- Failure
     True  -> (# s2#, (True , newBox ) #) -- Success
 
-{-# INLINE sameCTicket #-}
-sameCTicket :: CTicket -> CTicket -> Bool
-sameCTicket = (==)
+-- {-# INLINE sameCTicket #-}
+-- sameCTicket :: CTicket -> CTicket -> Bool
+-- sameCTicket = (==)
 
 {-# INLINE incrCounter #-}
 -- | Increment the counter by a given amount.  Returns the value AFTER the increment
@@ -142,17 +131,11 @@
 incrCounter :: Int -> AtomicCounter -> IO Int
 incrCounter (I# incr#) (AtomicCounter mba#) = IO $ \ s1# -> 
   let (# s2#, res #) = fetchAddIntArray# mba# 0# incr# s1# in
--- fetchAddIntArray# changed behavior in 7.10 to return the OLD value, so we
--- need this to maintain forwards compatibility:
-#if MIN_VERSION_base(4,8,0)
   (# s2#, (I# (res +# incr#)) #)
-#else
-  (# s2#, (I# res) #)
-#endif
 
 {-# INLINE incrCounter_ #-}
 -- | An alternate version for when you don't care about the old value.
 incrCounter_ :: Int -> AtomicCounter -> IO ()
 incrCounter_ (I# incr#) (AtomicCounter mba#) = IO $ \ s1# -> 
-  let (# s2#, res #) = fetchAddIntArray# mba# 0# incr# s1# in
+  let (# s2#, _ #) = fetchAddIntArray# mba# 0# incr# s1# in
   (# s2#, () #)
diff --git a/Data/Atomics/Internal.hs b/Data/Atomics/Internal.hs
--- a/Data/Atomics/Internal.hs
+++ b/Data/Atomics/Internal.hs
@@ -4,32 +4,20 @@
 #define CASTFUN
 
 -- | This module provides only the raw primops (and necessary types) for atomic
--- operations.  
-module Data.Atomics.Internal 
+-- operations.
+module Data.Atomics.Internal
    (
-    casIntArray#, fetchAddIntArray#, 
-    readForCAS#, casMutVarTicketed#, casArrayTicketed#, 
+    casIntArray#, fetchAddIntArray#,
+    readForCAS#, casMutVarTicketed#, casArrayTicketed#,
     Ticket,
     -- * Very unsafe, not to be used
     ptrEq
    )
-  where 
-
-import GHC.Base (Int(I#))
-import GHC.Word (Word(W#))
-import GHC.Prim (RealWorld, Int#, Word#, State#, MutableArray#, MutVar#,
-                 MutableByteArray#, 
-                 unsafeCoerce#, reallyUnsafePtrEquality#) 
+  where
 
-#if MIN_VERSION_base(4,7,0)
-import GHC.Prim (casArray#, casIntArray#, fetchAddIntArray#, Any, readMutVar#, casMutVar#)
-#elif MIN_VERSION_base(4,6,0)
--- Any is only supported in the FFI in the way we need in GHC 7.6+
-import GHC.Prim (readMutVar#, casMutVar#, Any)
-#else
-#error "Need to figure out how to emulate Any () in GHC <= 7.4 !"
--- type Any a = Word#
-#endif    
+import GHC.Exts (Int(I#), Any, RealWorld, Int#, State#, MutableArray#, MutVar#,
+                 unsafeCoerce#, reallyUnsafePtrEquality#,
+                 casArray#, casIntArray#, fetchAddIntArray#, readMutVar#, casMutVar#)
 
 #ifdef DEBUG_ATOMICS
 {-# NOINLINE readForCAS# #-}
@@ -45,18 +33,12 @@
 -- CAS and friends
 --------------------------------------------------------------------------------
 
--- | Unsafe, machine-level atomic compare and swap on an element within an Array.  
-casArrayTicketed# :: MutableArray# RealWorld a -> Int# -> Ticket a -> Ticket a 
+-- | Unsafe, machine-level atomic compare and swap on an element within an Array.
+casArrayTicketed# :: MutableArray# RealWorld a -> Int# -> Ticket a -> Ticket a
           -> State# RealWorld -> (# State# RealWorld, Int#, Ticket a #)
 -- WARNING: cast of a function -- need to verify these are safe or eta expand.
-casArrayTicketed# = unsafeCoerce#
-#if MIN_VERSION_base(4,7,0)
-   -- In GHC 7.8 onward we just want to expose the existing primop with a different type:
-   casArray#
-#else
-   casArrayTypeErased#
-#endif
-    
+casArrayTicketed# = unsafeCoerce# casArray#
+
 -- | When performing compare-and-swaps, the /ticket/ encapsulates proof
 -- that a thread observed a specific previous value of a mutable
 -- variable.  It is provided in lieu of the "old" value to
@@ -98,46 +80,4 @@
 casMutVarTicketed# :: MutVar# RealWorld a -> Ticket a -> Ticket a ->
                State# RealWorld -> (# State# RealWorld, Int#, Ticket a #)
 -- WARNING: cast of a function -- need to verify these are safe or eta expand:
-casMutVarTicketed# =
-#if MIN_VERSION_base(4,7,0) 
-  unsafeCoerce# casMutVar#
-#else
-  unsafeCoerce# casMutVar_TypeErased#
-#endif
-
---------------------------------------------------------------------------------
--- Type-erased versions that call the raw foreign primops:
---------------------------------------------------------------------------------
--- Due to limitations of the "foreign import prim" mechanism, we can't use the
--- polymorphic signature for the below functions.  So we lie to the type system
--- instead.
-
-#if MIN_VERSION_base(4,7,0) 
-#else
-
-foreign import prim "stg_casArrayzh" casArrayTypeErased#
-  :: MutableArray# RealWorld () -> Int# -> Any () -> Any () -> 
-     State# RealWorld  -> (# State# RealWorld, Int#, Any () #) 
---   out_of_line = True
---   has_side_effects = True
-
--- | This alternate version of casMutVar returns an opaque "ticket" for
---   future CAS operations.
-foreign import prim "stg_casMutVar2zh" casMutVar_TypeErased#
-  :: MutVar# RealWorld () -> Any () -> Any () ->
-     State# RealWorld -> (# State# RealWorld, Int#, Any () #)
-
--- foreign import prim "stg_readMutVar2zh" readMutVar_TypeErased#
---   :: MutVar# RealWorld () -> 
---      State# RealWorld -> (# State# RealWorld, Any () #)
-  -- with has_side_effects = True
-  --      commutable = False
-
-foreign import prim "stg_casByteArrayIntzh" casIntArray#
-  :: MutableByteArray# s -> Int# -> Int# -> Int# ->
-     State# s -> (# State# s, Int# #) 
-
-foreign import prim "stg_fetchAddByteArrayIntzh" fetchAddIntArray#
-  :: MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #) 
-
-#endif
+casMutVarTicketed# = unsafeCoerce# casMutVar#
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,29 +1,2 @@
-
-import Control.Monad (when)
-import Language.Haskell.TH
-import Distribution.Simple                (defaultMainWithHooks, simpleUserHooks, UserHooks(postConf), Args)
-import Distribution.Simple.Utils          (cabalVersion)
-import Distribution.Simple.LocalBuildInfo
-import Distribution.Simple.Setup          (ConfigFlags)
-import Distribution.Version               (Version(..))
-import Distribution.PackageDescription    (PackageDescription)
-import Debug.Trace
-
--- I couldn't figure out a way to do this check from the cabal file, so we drop down
--- here to do it instead:
-checkGoodVersion :: IO ()
-checkGoodVersion =
-  if   cabalVersion >= Version [1,17,0] []
-  then putStrLn (" [Setup.hs] This version of Cabal is ok for profiling: "++show cabalVersion)
-  else error (" [Setup.hs] This package should not be used in profiling mode with cabal version "++
-                        show (versionBranch cabalVersion)++" < 1.17.0\n"++
-                        " It will break, see cabal issue #1284")
-
-main :: IO ()
-main = do
-  let myPostConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()
-      myPostConf _args confFlags _descr lbi =
-        when (withProfLib lbi)
-          checkGoodVersion
-      hooks = simpleUserHooks { postConf = myPostConf }
-  defaultMainWithHooks hooks
+import Distribution.Simple
+main = defaultMain
diff --git a/atomic-primops.cabal b/atomic-primops.cabal
--- a/atomic-primops.cabal
+++ b/atomic-primops.cabal
@@ -1,45 +1,20 @@
+Cabal-version:       3.0
 Name:                atomic-primops
-Version:             0.7
-License:             BSD3
+Version:             0.8.8
+License:             BSD-3-Clause
 License-file:        LICENSE
 Author:              Ryan Newton
 Maintainer:          rrnewton@gmail.com
 Category:            Data
 -- Portability:         non-portabile (x86_64)
-Build-type:          Custom
--- TODO: This requirement needs to be bumped to 1.17 when the latest cabal is
--- released.  This package triggers issue #1284:
--- Cabal-version:       >=1.18
-Cabal-version:       >=1.10
--- Egad!  Requiring cabal version 1.18 triggers a seemingly spurious failure on Mac OS:
--- http://tester-lin.soic.indiana.edu:8080/job/Haskell-LockFree_master/JENKINS_GHC=7.6.3,label=macos/346/console
--- So for now I'm backing off about the cabal requirement.  Setup.hs will catch it later IF an early version
--- of cabal is used WITH profiling mode.
-
+Build-type:          Simple
+tested-with:         GHC == 8.4.3, GHC == 8.2.2, GHC == 8.0.2, GHC == 7.10.3
 HomePage: https://github.com/rrnewton/haskell-lockfree/wiki
 Bug-Reports: https://github.com/rrnewton/haskell-lockfree/issues
 
--- Version History:
--- 0.1.0.0 -- initial release
--- 0.1.0.2 -- disable profiling
--- 0.2 -- Critical bugfix and add Counter.
--- 0.2.2 -- Add more counters
--- 0.2.2.1 -- Minor, add warning.
--- 0.3 -- Major internal change.  Duplicate GHC RTS barriers and support non -threaded.
--- 0.4 -- Duplicate 'cas' as well as barriers.  Add fetchAdd on ByteArray, Counter.Unboxed.
--- 0.4.1 -- Add advance support for GHC 7.8
--- 0.5 -- Nix Data.Atomics.Counter.Foreign and the bits-atomic dependency.
--- 0.5.0.2 -- IMPORTANT Bugfix release.
--- 0.6 -- add atomicModifyIORefCAS, and bump due to prev bugfixes
--- 0.6.0.1 -- minor ghc 7.8 fix
--- 0.6.0.5 -- fix for GHC 7.8
--- 0.6.1   -- several bug fixes, mainly re: platform portability
--- 0.7     -- support for GHC 7.10 and several new primops
-
 Synopsis: A safe approach to CAS and other atomic ops in Haskell.
 
 Description:
-
   After GHC 7.4 a new `casMutVar#` primop became available, but it's
   difficult to use safely, because pointer equality is a highly
   unstable property in Haskell.  This library provides a safer method
@@ -50,46 +25,16 @@
   interest, specifically, compare and swap inside an array.
  .
   Note that as of GHC 7.8, the relevant primops have been included in GHC itself.
-  This library is engineered to work pre- and post-GHC-7.8, while exposing the 
+  This library is engineered to work pre- and post-GHC-7.8, while exposing the
   same interface.
- .
- Changes in 0.3:
- .
- * Major internal change.  Duplicate the barrier code from the GHC RTS and thus 
-   enable support for executables that are NOT built with '-threaded'.
- .
- Changes in 0.4:
- . 
- * Further internal changes, duplicate 'cas' routine well as barriers.  
- .
- * Add `fetchAddByteArrayInt`
- .
- * Add an `Unboxed` counter variant that uses movable "ByteArray"s on the GHC heap.
- .
- Changes in 0.5:
- . 
- * Remove dependency on bits-atomic unless a flag is turned on.
- . 
- Changes in 0.5.0.2:
- .
- * IMPORTANT BUGFIXES - don't use earlier versions.  They have been marked deprecated.
- .
- Changes in 0.6.1
- .
- * This is a good version to use for GHC 7.8.3.  It includes portability and bug fixes 
-   and adds atomicModifyIORefCAS.
- .
- Changes in 0.7: 
- .
- * This release adds support for GHC 7.10 and its expanded library of (now inline) primops.
 
-
-Extra-Source-Files:  DEVLOG.md,
-                     testing/Test.hs, testing/test-atomic-primops.cabal, testing/ghci-test.hs
-                     testing/Makefile, testing/CommonTesting.hs, testing/Counter.hs, testing/hello.hs
+Extra-Source-Files:  DEVLOG.md
+                     testing/Test.hs testing/test-atomic-primops.cabal testing/ghci-test.hs
+                     testing/Makefile testing/CommonTesting.hs testing/Counter.hs testing/CounterCommon.hs testing/hello.hs testing/Fetch.hs
                      testing/Issue28.hs
                      testing/TemplateHaskellSplices.hs
                      testing/Raw781_test.hs
+extra-doc-files:     CHANGELOG.md
 
 Flag debug
     Description: Enable extra internal checks.
@@ -101,39 +46,23 @@
                      Data.Atomics.Internal
                      Data.Atomics.Counter
   ghc-options: -O2 -funbox-strict-fields
-
-  -- casMutVar# had a bug in GHC 7.2, thus we require GHC 7.4 or greater
-  -- (base 4.5 or greater). We also need the "Any" kind.
-  build-depends:     base >= 4.6.0.0 && < 4.9, ghc-prim, primitive
+  ghc-options: -Wall
 
-  -- TODO: Try to push support back to 7.0, but make it default to an implementation
-  -- other than Unboxed.
+  build-depends:     base >= 4.8 && < 5
+                   , ghc-prim
+                   , primitive
 
-  -- Ah, but if we don't USE casMutVar# in this package we are ok:
-  -- build-depends:     base >= 4.3, ghc-prim, primitive
+  if impl(ghc >= 9.9)
+    cmm-sources: cbits/atomics.cmm
 
-  if impl(ghc < 7.7) {
-     Include-Dirs:     cbits
-     C-Sources:        cbits/primops.cmm
-     -- Duplicate RTS functionality for GHC 7.6:
-     C-Sources:        cbits/RtsDup.c
+  if os(windows) {
+    Include-Dirs:     cbits
+    C-Sources:        cbits/RtsDup.c
   }
-  CC-Options:       -Wall 
-
-  -- if( cabal-version < 1.17 ) {
-  --   ghc-prof-options: ERROR_DO_NOT_BUILD_THIS_WITH_PROFILING_YET__SEE_CABAL_ISSUE_1284
-  -- }
+  CC-Options:       -Wall
 
   if flag(debug)
-    cpp-options: -DDEBUG_ATOMICS 
-
-
--- -- [2013.04.08] This isn't working presently:
--- -- I'm having problems with building it along with the library; see DEVLOG.
--- -- Switching to a separate package in ./testing/ 
--- Test-Suite test-atomic-primops
---     type:       exitcode-stdio-1.0
---     ...
+    cpp-options: -DDEBUG_ATOMICS
 
 Source-Repository head
     Type:         git
diff --git a/cbits/atomics.cmm b/cbits/atomics.cmm
new file mode 100644
--- /dev/null
+++ b/cbits/atomics.cmm
@@ -0,0 +1,20 @@
+#include "Cmm.h"
+
+// These approximate GHC's old barrier operations in terms of the new C11-style
+// ordered atomic fences.
+
+hs_atomic_primops_store_load_barrier() {
+  prim %fence_seq_cst();
+  return ();
+}
+
+hs_atomic_primops_load_load_barrier() {
+  prim %fence_acquire();
+  return ();
+}
+
+hs_atomic_primops_write_barrier() {
+  prim %fence_release();
+  return ();
+}
+
diff --git a/cbits/primops.cmm b/cbits/primops.cmm
deleted file mode 100644
--- a/cbits/primops.cmm
+++ /dev/null
@@ -1,128 +0,0 @@
-
-// ============================================================
-// NOTE: We only use this file for GHC < 7.8.
-// ============================================================
-
-#include "Cmm.h"
-
-#warning "Duplicating functionality from the GHC RTS..."
-#define WHICH_CAS       DUP_cas
-#define WHICH_SLBARRIER DUP_store_load_barrier
-#define WHICH_LLBARRIER DUP_load_load_barrier
-#define WHICH_WBARRIER  DUP_write_barrier
-
-// These versions are linked directly from the RTS:
-/* #define WHICH_CAS       cas */
-/* #define WHICH_SLBARRIER store_load_barrier */
-/* #define WHICH_LLBARRIER load_load_barrier */
-/* #define WHICH_WBARRIER  write_barrier */
-
-// ================================================================================
-
-add1Op
-/* Int# -> Int# */
-{
-    W_ num;
-    num = R1 + 1;
-    RET_P(num);
-}
-
-
-stg_casArrayzh
-/* MutableArray# s a -> Int# -> a -> a -> State# s -> (# State# s, Int#, a #) */
-{
-    W_ arr, p, ind, old, new, h, len;
-    arr = R1; // anything else?
-    ind = R2;
-    old = R3;
-    new = R4;
-
-    p = arr + SIZEOF_StgMutArrPtrs + WDS(ind);
-    (h) = foreign "C" WHICH_CAS(p, old, new) [];
-    if (h != old) {
-        // Failure, return what was there instead of 'old':
-        RET_NP(1,h);
-    } else {
-        // Compare and Swap Succeeded:
-	SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS);
-	len = StgMutArrPtrs_ptrs(arr);
-	// The write barrier.  We must write a byte into the mark table:
-	I8[arr + SIZEOF_StgMutArrPtrs + WDS(len) + (ind >> MUT_ARR_PTRS_CARD_BITS )] = 1;
-        RET_NP(0,new);
-    }
-}
-
-
-stg_casByteArrayIntzh
-/* MutableByteArray# s -> Int# -> Int# -> Int# -> State# s -> (# State# s, Int# #) */
-{
-    W_ arr, p, ind, old, new, h, len;
-    arr = R1; 
-    ind = R2;
-    old = R3;
-    new = R4;
-
-    p = arr + SIZEOF_StgArrWords + WDS(ind);
-    (h) = foreign "C" WHICH_CAS(p, old, new) [];
-
-    RET_N(h);
-}
-
-stg_fetchAddByteArrayIntzh
-/* MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #) */
-{
-    W_ arr, p, ind, incr, h, len;
-    arr  = R1; 
-    ind  = R2;
-    incr = R3;
-
-    p = arr + SIZEOF_StgArrWords + WDS(ind);
-    (h) = foreign "C" atomic_inc_with(incr, p) [];
-
-    RET_N(h);
-}
-
-// One difference from casMutVar# is that this version returns the NEW
-// pointer in the case of success, NOT the old one.
-stg_casMutVar2zh
- /* MutVar# s a -> Word# -> a -> State# s -> (# State#, Int#, a #) */
-{
-    W_ mv, old, new, h, addr;
-    // Calling convention: Up to 8 registers contain arguments.
-    mv  = R1;
-    old = R2;
-    new = R3;
-    addr = mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var;
-
-    // The "cas" function from the C runtime abstracts over
-    // platform/architecture differences.  It returns the old value.
-    (h) = foreign "C" WHICH_CAS(addr, old, new) [];
-    if (h != old) {
-        // Failure:
-        RET_NP(1, h);
-    }
-    else 
-    {
-        // Success means a mutation and thus GC write barrier:
-        if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) {
-           foreign "C" dirty_MUT_VAR(BaseReg "ptr", mv "ptr") [];
-        }
-	// Return the NEW value as the ticket for next time.
-        RET_NP(0,new);
-    }
-}
-
-
-// Takes a single input argument in R1:
-stg_readMutVar2zh
-/*  MutVar# RealWorld a -> State# RealWorld -> (# State# RealWorld, Word#, a #) */
-{
-    W_ mv, res;
-    mv  = R1;
-    // Do the actual read:
-    res = W_[mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var];
-    RET_NP(res, res);
-}
-/* emitPrimOp [res] ReadMutVarOp [mutv] _ */
-/*    = stmtC (CmmAssign (CmmLocal res) (cmmLoadIndexW mutv fixedHdrSize gcWord)) */
-
diff --git a/testing/CommonTesting.hs b/testing/CommonTesting.hs
--- a/testing/CommonTesting.hs
+++ b/testing/CommonTesting.hs
@@ -13,7 +13,7 @@
 import System.CPUTime
 import System.Mem.StableName (makeStableName, hashStableName)
 import System.Environment (getEnvironment)
-import System.IO        (stdout, stderr, hPutStrLn, hFlush)
+import System.IO        (stderr, hPutStrLn, hFlush)
 import Debug.Trace      (trace)
 
 -- import Test.Framework.TH (defaultMainGenerator)
@@ -84,8 +84,8 @@
     start <- getCPUTime
     v <- a
     end   <- getCPUTime
-    let diff = (fromIntegral (end - start)) / (10^12)
-    printf "SELFTIMED: %0.3f sec\n" (diff :: Double)
+    let diff = (fromIntegral (end - start)) / (10^(12::Int))
+    _ <- printf "SELFTIMED: %0.3f sec\n" (diff :: Double)
     return v
 
 
@@ -106,7 +106,7 @@
 --
 --  INLINE should not affect recursive functions.  But here it seems to have a
 --  deleterious effect!
-nTimes 0 !c = return ()
+nTimes 0  _  = return ()
 nTimes !n !c = c >> nTimes (n-1) c
 
 
diff --git a/testing/Counter.hs b/testing/Counter.hs
--- a/testing/Counter.hs
+++ b/testing/Counter.hs
@@ -1,148 +1,14 @@
+{-# LANGUAGE CPP #-}
 module Counter (tests) where
-
--- This was formerly CounterCommon and #include-ed to test the different
--- counter implementations which have been removed. 
--- TODO clean up remnants of that approach.
 import qualified Data.Atomics.Counter as C
 
-import Control.Monad
-import GHC.Conc
-import System.CPUTime
-import Test.Framework.Providers.HUnit (testCase)
-import Test.HUnit (Assertion, assertEqual, assertBool)
-import Text.Printf
-import Data.IORef  
-
-import CommonTesting (numElems, forkJoin, timeit, nTimes)
+#include "CounterCommon.hs"
 
+name :: String
 name = "Unboxed"
 
+default_seq_tries, default_conc_tries :: Int
 default_seq_tries  = 10 * numElems
 -- Things are MUCH slower with contention:
 default_conc_tries = numElems
 
---------------------------------------------------------------------------------
--- Test the basics
-
-case_basic1 = do 
-  r <- C.newCounter 0
-  ret <- C.incrCounter 10 r
-  assertEqual "incrCounter returns the NEW value" 10 ret
-
-case_basic2 = do 
-  r <- C.newCounter 0
-  t <- C.readCounterForCAS r
-  (True,newt) <- C.casCounter r t 10
-  assertEqual "casCounter returns new val/ticket on success" 10 (C.peekCTicket newt)
-
-case_basic3 = do 
-  r <- C.newCounter 0
-  t <- C.readCounterForCAS r
-  _ <- C.incrCounter 1 r
-  (False,oldt) <- C.casCounter r t 10
-  assertEqual "casCounter returns read val on failure" 1 (C.peekCTicket oldt)
-
-case_basic4 = do 
-  let tries = numElems `quot` 100
-  r <- C.newCounter 0
-  nTimes tries $ do
-    t <- C.readCounterForCAS r
-    (True,_) <- C.casCounter r t (C.peekCTicket t + 1)
-    return ()
-  cnt <- C.readCounter r
-  assertEqual "Every CAS should succeed on one thread" tries cnt 
-
---------------------------------------------------------------------------------
--- Repeated increments
-
-incrloop tries = do r <- C.newCounter 0; nTimes tries $ void$ C.incrCounter 1 r
-                    C.readCounter r
-case_incrloop = do 
-   cnt <- incrloop default_seq_tries
-   assertEqual "incrloop sum" default_seq_tries cnt
-
--- | Here we do a loop to test the unboxing of results from incrCounter:
---   As of now [2013.07.19], it is successfully unboxing the results 
---   for Data.Atomics.Counter.Unboxed.
-incrloop4B tries = do
-  putStrLn " [incrloop4B] A test where we use the result of each incr."
-  r <- C.newCounter 1
-  loop r tries 1
- where
-   loop :: C.AtomicCounter -> Int -> Int -> IO ()
-   loop r 0 _ = do v <- C.readCounter r
-                   putStrLn$"Final value: "++show v
-                   return ()
-   loop r tries last = do
-     n <- C.incrCounter last r
-     if n == 2
-       then loop r (tries-1) 2
-       else loop r (tries-1) 1
-
--- | Here we let the counter overflow, which seems to be causing problems.
-overflowTest tries = do
-  putStrLn " [incrloop4B] A test where we use the result of each incr."
-  r <- C.newCounter 1
-  loop r tries 1
- where
-   loop :: C.AtomicCounter -> Int -> Int -> IO ()
-   loop r 0 _ = do v <- C.readCounter r
-                   putStrLn$"Final value: "++show v
-                   return ()
-   loop r tries last = do
-     putStrLn$ " [incrloop4B] Looping with tries left "++show tries 
-     n <- C.incrCounter last r
-     -- This is HANGING afer passing 2,147,483,648.  (using Unboxed)
-     -- Is there some defect wrt overflow?
-     putStrLn$ " [incrloop4B] Done incr, received "++show n
-     loop r (tries-1) n
-
---------------------------------------------------------------------------------
--- Parallel repeated increments
-
-
-{-# INLINE parIncrloop #-} 
-parIncrloop new incr iters = do
-  numcap <- getNumCapabilities
-  let (each,left) = iters `quotRem` numcap
-  putStrLn$ "Concurrently incrementing counter from all "++show numcap++" threads, incrs per thread: "++show each
-  r <- new 0
-  forkJoin numcap $ \ ix -> do
-    let mine = if ix==0 then each+left else each
-    nTimes mine $ void $ incr 1 r
-  C.readCounter r
-
-case_parincrloop = do 
-  cnt <- parIncrloop C.newCounter C.incrCounter default_conc_tries
-  assertEqual "incrloop sum" default_conc_tries cnt
-
--- | Use CAS instead of the real incr so we can compare the overhead.
-case_parincrloop_wCAS = do 
-  cnt <- parIncrloop C.newCounter fakeIncr default_conc_tries
-  assertEqual "incrloop sum" default_conc_tries cnt
- where
-  fakeIncr delt r = do tick <- C.readCounterForCAS r
-                       loop r delt tick
-  loop r delt tick = do x <- C.casCounter r tick (C.peekCTicket tick + delt)
-                        case x of 
-                          (True, newtick) -> return (C.peekCTicket newtick)
-                          (False,newtick) -> loop r delt newtick
-                   
-
---------------------------------------------------------------------------------
-
-tests = 
- [
-   testCase (name++"basic1_incrCounter") $ case_basic1
- , testCase (name++"basic2_casCounter") $ case_basic2
- , testCase (name++"basic3_casCounter") $ case_basic3
- , testCase (name++"basic4_casCounter") $ case_basic4
-   ----------------------------------------
- , testCase (name++"_single_thread_repeat_incr") $ timeit case_incrloop
- , testCase (name++"_incr_with_result_feedback") $ timeit (incrloop4B default_seq_tries)
-   ----------------------------------------
-
-   -- Parallel versions:
- , testCase (name++"_concurrent_repeat_incr") $ void$ timeit case_parincrloop
- , testCase (name++"_concurrent_repeat_incrCAS") $ void$ timeit case_parincrloop_wCAS
- ]
diff --git a/testing/CounterCommon.hs b/testing/CounterCommon.hs
new file mode 100644
--- /dev/null
+++ b/testing/CounterCommon.hs
@@ -0,0 +1,152 @@
+-- Common tests to the different counter implementations. N.B. #included from
+-- other projects via soft links!
+
+import Control.Monad
+import GHC.Conc
+import Test.Framework.Providers.HUnit (testCase)
+import Test.Framework(Test)
+import Test.HUnit (assertEqual)
+
+import CommonTesting (numElems, forkJoin, timeit, nTimes)
+
+--------------------------------------------------------------------------------
+-- Test the basics
+
+case_basic1 :: IO ()
+case_basic1 = do 
+  r <- C.newCounter 0
+  ret <- C.incrCounter 10 r
+  assertEqual "incrCounter returns the NEW value" 10 ret
+
+case_basic2 :: IO ()
+case_basic2 = do 
+  r <- C.newCounter 0
+  t <- C.readCounterForCAS r
+  (True,newt) <- C.casCounter r t 10
+  assertEqual "casCounter returns new val/ticket on success" 10 (C.peekCTicket newt)
+
+case_basic3 :: IO ()
+case_basic3 = do 
+  r <- C.newCounter 0
+  t <- C.readCounterForCAS r
+  _ <- C.incrCounter 1 r
+  (False,oldt) <- C.casCounter r t 10
+  assertEqual "casCounter returns read val on failure" 1 (C.peekCTicket oldt)
+
+case_basic4 :: IO ()
+case_basic4 = do 
+  let tries = numElems `quot` 100
+  r <- C.newCounter 0
+  nTimes tries $ do
+    t <- C.readCounterForCAS r
+    (True,_) <- C.casCounter r t (C.peekCTicket t + 1)
+    return ()
+  cnt <- C.readCounter r
+  assertEqual "Every CAS should succeed on one thread" tries cnt 
+
+--------------------------------------------------------------------------------
+-- Repeated increments
+
+incrloop :: Int -> IO Int
+incrloop tries = do r <- C.newCounter 0; nTimes tries $ void$ C.incrCounter 1 r
+                    C.readCounter r
+
+case_incrloop :: IO ()
+case_incrloop = do 
+   cnt <- incrloop default_seq_tries
+   assertEqual "incrloop sum" default_seq_tries cnt
+
+-- | Here we do a loop to test the unboxing of results from incrCounter:
+--   As of now [2013.07.19], it is successfully unboxing the results 
+--   for Data.Atomics.Counter.Unboxed.
+incrloop4B :: Int -> IO ()
+incrloop4B tries = do
+  putStrLn " [incrloop4B] A test where we use the result of each incr."
+  r <- C.newCounter 1
+  loop r tries 1
+ where
+   loop :: C.AtomicCounter -> Int -> Int -> IO ()
+   loop r 0 _ = do v <- C.readCounter r
+                   putStrLn$"Final value: "++show v
+                   return ()
+   loop r i l = do
+     n <- C.incrCounter l r
+     if n == 2
+       then loop r (i-1) 2
+       else loop r (i-1) 1
+
+-- | Here we let the counter overflow, which seems to be causing problems.
+-- NOTE 2/3/2015: THIS APPEARS TO BE WORKING NOW -Brandon 
+overflowTest :: Int -> IO ()
+overflowTest tries = do
+  putStrLn " [incrloop4B] A test where we use the result of each incr."
+  r <- C.newCounter 1
+  loop r tries 1
+ where
+   loop :: C.AtomicCounter -> Int -> Int -> IO ()
+   loop r 0 _ = do v <- C.readCounter r
+                   putStrLn$"Final value: "++show v
+                   return ()
+   loop r i l = do
+     --putStrLn$ " [incrloop4B] Looping with tries left "++show i 
+     n <- C.incrCounter l r
+     -- This is HANGING afer passing 2,147,483,648.  (using Unboxed)
+     -- Is there some defect wrt overflow?
+     --putStrLn$ " [incrloop4B] Done incr, received "++show n
+     loop r (i-1) n
+
+--------------------------------------------------------------------------------
+-- Parallel repeated increments
+
+
+{-# INLINE parIncrloop #-} 
+parIncrloop :: (Int -> IO C.AtomicCounter)
+            -> (Int -> C.AtomicCounter -> IO Int) -> Int -> IO Int
+parIncrloop new incr iters = do
+  numcap <- getNumCapabilities
+  let (each,left) = iters `quotRem` numcap
+  putStrLn$ "Concurrently incrementing counter from all "++show numcap++" threads, incrs per thread: "++show each
+  r <- new 0
+  void $ forkJoin numcap $ \ ix -> do
+    let mine = if ix==0 then each+left else each
+    nTimes mine $ void $ incr 1 r
+  C.readCounter r
+
+case_parincrloop :: IO ()
+case_parincrloop = do 
+  cnt <- parIncrloop C.newCounter C.incrCounter default_conc_tries
+  assertEqual "incrloop sum" default_conc_tries cnt
+
+-- | Use CAS instead of the real incr so we can compare the overhead.
+case_parincrloop_wCAS :: IO ()
+case_parincrloop_wCAS = do 
+  cnt <- parIncrloop C.newCounter fakeIncr default_conc_tries
+  assertEqual "incrloop sum" default_conc_tries cnt
+ where
+  fakeIncr delt r = do tick <- C.readCounterForCAS r
+                       loop r delt tick
+  loop r delt tick = do x <- C.casCounter r tick (C.peekCTicket tick + delt)
+                        case x of 
+                          (True, newtick) -> return (C.peekCTicket newtick)
+                          (False,newtick) -> loop r delt newtick
+                   
+
+--------------------------------------------------------------------------------
+
+tests :: [Test]
+tests = 
+ [
+   testCase (name++"_basic1_incrCounter") $ case_basic1
+ , testCase (name++"_basic2_casCounter") $ case_basic2
+ , testCase (name++"_basic3_casCounter") $ case_basic3
+ , testCase (name++"_basic4_casCounter") $ case_basic4
+   ----------------------------------------
+ , testCase (name++"_single_thread_repeat_incr") $ timeit case_incrloop
+ , testCase (name++"_incr_with_result_feedback") $ timeit (incrloop4B default_seq_tries)
+ , testCase (name++"_overflow_test") $ timeit (overflowTest 100000)
+   ----------------------------------------
+
+   -- Parallel versions:
+ , testCase (name++"_concurrent_repeat_incr") $ void$ timeit case_parincrloop
+ , testCase (name++"_concurrent_repeat_incrCAS") $ void$ timeit case_parincrloop_wCAS
+ ]
diff --git a/testing/Fetch.hs b/testing/Fetch.hs
new file mode 100644
--- /dev/null
+++ b/testing/Fetch.hs
@@ -0,0 +1,218 @@
+module Fetch (tests) where
+
+-- tests for our fetch-and-* family of functions.
+import Control.Monad
+import System.Random
+import Test.Framework.Providers.HUnit (testCase)
+import Test.Framework (Test)
+import Test.HUnit (assertEqual,assertBool)
+import Data.Primitive
+import Data.List
+import Data.Bits
+import Data.Atomics
+import Control.Monad.Primitive
+import Control.Concurrent
+
+tests :: [Test]
+tests = [
+      testCase "Fetch-and-* operations return previous value" case_return_previous
+    , testCase "Fetch-and-* operations behave like their corresponding bitwise operators" case_like_bitwise
+    , testCase "fetchAndIntArray and fetchOrIntArray are atomic"  $ fetchAndOrTest  10000000
+    , testCase "fetchNandIntArray atomic"                         $ fetchNandTest   1000000
+    , testCase "fetchAddIntArray and fetchSubIntArray are atomic" $ fetchAddSubTest 10000000
+    , testCase "fetchXorIntArray is atomic"                       $ fetchXorTest    10000000
+    ] 
+
+nand :: Bits a => a -> a -> a
+nand x y = complement (x .&. y)
+
+fetchOps :: [( String
+            ,  MutableByteArray RealWorld -> Int -> Int -> IO Int
+            ,  Int -> Int -> Int )]
+fetchOps = [
+   ("Add",  fetchAddIntArray,  (+)),
+   ("Sub",  fetchSubIntArray,  (-)),
+   ("And",  fetchAndIntArray,  (.&.)),
+   ("Nand", fetchNandIntArray, nand),
+   ("Or",   fetchOrIntArray,   (.|.)),
+   ("Xor",  fetchXorIntArray,  xor)
+   ]
+
+
+-- Test all operations at once, somewhat randomly, ensuring they behave like
+-- their corresponding bitwise operator; we compose a few operations before
+-- inspecting the intermediate result, and spread them randomly around a small
+-- array.
+-- TODO use quickcheck if we want
+case_like_bitwise :: IO ()
+case_like_bitwise = do
+    let opGroupSize = 5
+    let grp n = go n []
+          where go _ stck [] = [stck]
+                go 0 stck xs = stck : go n [] xs
+                go i stck (x:xs) = go (i-1) (x:stck) xs
+    -- Inf list of different short sequences of bitwise operations:
+    let opGroups = grp opGroupSize $ cycle $ concat $ permutations fetchOps
+    
+    let size = 4
+    randIxs <- randomRs (0, size-1) <$> newStdGen 
+    randArgs <- grp opGroupSize . randoms <$> newStdGen
+    
+    a <- newByteArray (sizeOf (undefined::Int) * size)
+    forM_ [0.. size-1] $ \ix-> writeByteArray a ix (0::Int)
+
+    forM_ (take 1000000 $ zip randIxs $ zipWith zip opGroups randArgs) $
+        \ (ix, opsArgs)-> do
+            assertEqual "test not b0rken" (length opsArgs) opGroupSize
+            
+            let doOpGroups pureLHS [] = return pureLHS
+                doOpGroups pureLHS (((_,atomicOp,op), v) : rest) = do
+                    atomicOp a ix v >> doOpGroups (pureLHS `op` v) rest
+                  
+            vInitial <- readByteArray a ix
+            vFinalPure <- doOpGroups vInitial opsArgs
+            vFinal <- readByteArray a ix
+
+            let nmsArgs = map (\ ((nm,_,_),v) -> (nm,v)) opsArgs
+            assertEqual ("sequence on initial value "++(show vInitial)
+                          ++" of ops with RHS args: "++(show nmsArgs)
+                          ++" gives same result in both pure and atomic op"
+                        ) vFinal vFinalPure
+
+              
+            
+-- check all operations return the value before the operation was applied;
+-- basic smoke test, with each op tested individually.
+case_return_previous :: IO ()
+case_return_previous = do
+    let l = length fetchOps
+    a <- newByteArray (sizeOf (undefined::Int) * l)
+    let randomInts = take l . randoms <$> newStdGen :: IO [Int]
+    initial <- randomInts
+    forM_ (zip [0..] initial) $ \(ix, v)-> writeByteArray a ix v
+
+    args <- randomInts
+    forM_ (zip4 [0..] initial args fetchOps) $ \(ix, pre, v, (nm,atomicOp,op))-> do
+        pre' <- atomicOp a ix v
+        assertEqual (fetchStr nm "returned previous value") pre pre'
+        let post = pre `op` v
+        post' <- readByteArray a ix
+        assertEqual (fetchStrArgVal nm v pre "operation was seen correctly on read") post post'
+
+fetchStr :: String -> String -> String
+fetchStr nm = (("fetch"++nm++"IntArray: ")++)
+fetchStrArgVal :: (Show a, Show a1) => String -> a -> a1 -> String -> String
+fetchStrArgVal nm v initial = (("fetch"++nm++"IntArray, with arg "++(show v)++" on value "++(show initial)++": ")++)
+
+-- ----------------------------------------------------------------------------
+-- Tests of atomicity:
+
+
+-- Concurrently run a sequence of AND and OR simultaneously on separate parts
+-- of the bit range of an Int.
+fetchAndOrTest :: Int -> IO ()
+fetchAndOrTest iters = do
+    out0 <- newEmptyMVar
+    out1 <- newEmptyMVar
+    mba <- newByteArray (sizeOf (undefined :: Int))
+    let andLowersBit , orRaisesBit :: Int -> Int
+        andLowersBit = clearBit (complement 0)
+        orRaisesBit = setBit 0
+    writeByteArray mba 0 (0 :: Int)
+    -- thread 1 toggles bit 0, thread 2 toggles bit 1; then we verify results
+    -- in the main thread.
+    let go v b = do
+            -- Avoid stack overflow on GHC 7.6:
+            let replicateMrev l 0 = putMVar v l
+                replicateMrev l iter = do
+                       low <- fetchOrIntArray mba 0 (orRaisesBit b)
+                       high <- fetchAndIntArray mba 0 (andLowersBit b)
+                       replicateMrev ((low,high):l) (iter-1)
+             in replicateMrev [] iters
+    void $ forkIO $ go out0 0
+    void $ forkIO $ go out1 1
+    res0 <- takeMVar out0
+    res1 <- takeMVar out1
+    let check b = all ( \(low,high)-> (not $ testBit low b) && testBit high b)
+
+    assertBool "fetchAndOrTest not broken" $ length (res0++res1) == iters*2
+    assertBool "fetchAndOrTest thread1" $ check 0 res0
+    assertBool "fetchAndOrTest thread2" $ check 1 res1
+
+-- Nand of 1 is a bit complement. Concurrently run two threads running an even
+-- number of complements in this way and verify the final value is unchanged.
+-- TODO think of a more clever test
+fetchNandTest :: Int -> IO ()
+fetchNandTest iters = do
+    let nandComplements = complement 0
+        dblComplement mba = replicateM_ (2 * iters) $
+            fetchNandIntArray mba 0 nandComplements
+    randomInts <- take 10 . randoms <$> newStdGen :: IO [Int]
+    forM_ randomInts $ \ initial -> do
+        final <- race initial dblComplement dblComplement 
+        assertEqual "fetchNandTest" initial final
+
+
+-- ----------------------------------------------------------------------------
+-- Code below copied with minor modifications from GHC
+-- testsuite/tests/concurrent/should_run/AtomicPrimops.hs @ f293931
+-- ----------------------------------------------------------------------------
+
+
+-- | Test fetchAddIntArray# by having two threads concurrenctly
+-- increment a counter and then checking the sum at the end.
+fetchAddSubTest :: Int -> IO ()
+fetchAddSubTest iters = do
+    tot <- race 0
+        (\ mba -> work fetchAddIntArray mba iters 2)
+        (\ mba -> work fetchSubIntArray mba iters 1)
+    assertEqual "fetchAddSubTest" iters tot
+  where
+    work :: (MutableByteArray RealWorld -> Int -> Int -> IO Int) -> MutableByteArray RealWorld -> Int -> Int
+         -> IO ()
+    work _ _    0 _ = return ()
+    work op mba n val = op mba 0 val >> work op mba (n-1) val
+
+-- | Test fetchXorIntArray# by having two threads concurrenctly XORing
+-- and then checking the result at the end. Works since XOR is
+-- commutative.
+--
+-- Covers the code paths for AND, NAND, and OR as well.
+fetchXorTest :: Int -> IO ()
+fetchXorTest iters = do
+    res <- race n0
+        (\ mba -> work mba iters t1pat)
+        (\ mba -> work mba iters t2pat)
+    assertEqual "fetchXorTest" expected res
+  where
+    work :: MutableByteArray RealWorld -> Int -> Int -> IO ()
+    work _   0 _ = return ()
+    work mba n val = fetchXorIntArray mba 0 val >> work mba (n-1) val
+
+    -- Initial value is a large prime and the two patterns are 1010...
+    -- and 0101...
+    (n0, t1pat, t2pat)
+        -- TODO: If we want to silence warnings from here, use CPP conditional
+        --       on arch x86_64
+        | sizeOf (undefined :: Int) == 8 =
+            (0x00000000ffffffff, 0x5555555555555555, 0x9999999999999999)
+        | otherwise = (0x0000ffff, 0x55555555, 0x99999999)
+    expected
+        | sizeOf (undefined :: Int) == 8 = 4294967295
+        | otherwise = 65535
+
+-- | Create two threads that mutate the byte array passed to them
+-- concurrently. The array is one word large.
+race :: Int                    -- ^ Initial value of array element
+     -> (MutableByteArray RealWorld -> IO ())  -- ^ Thread 1 action
+     -> (MutableByteArray RealWorld -> IO ())  -- ^ Thread 2 action
+     -> IO Int                 -- ^ Final value of array element
+race n0 thread1 thread2 = do
+    done1 <- newEmptyMVar
+    done2 <- newEmptyMVar
+    mba <- newByteArray (sizeOf (undefined :: Int))
+    writeByteArray mba 0 n0
+    void $ forkIO $ thread1 mba >> putMVar done1 ()
+    void $ forkIO $ thread2 mba >> putMVar done2 ()
+    mapM_ takeMVar [done1, done2]
+    readByteArray mba 0
diff --git a/testing/Issue28.hs b/testing/Issue28.hs
--- a/testing/Issue28.hs
+++ b/testing/Issue28.hs
@@ -1,11 +1,12 @@
 
 module Issue28 (main) where
 
-import Control.Monad
+-- import Control.Monad
 import Data.IORef
 import Data.Atomics
 -- import Data.Atomics.Internal (ptrEq)
 
+main :: IO ()
 main = do
   putStrLn "Issue28: Conducting the simplest possible read-then-CAS test."
   r <- newIORef "hi"
@@ -18,4 +19,3 @@
   -- unless (b1 == True) $ error "Test failed"  
 
   putStrLn$  "Issue28: test passed "++show t1
-  return ()
diff --git a/testing/Test.hs b/testing/Test.hs
--- a/testing/Test.hs
+++ b/testing/Test.hs
@@ -9,7 +9,7 @@
 import Control.Monad
 -- import Control.Monad.ST (stToIO)
 import Control.Exception (evaluate)
-import Data.IORef (modifyIORef')
+import Data.IORef
 import Data.Int
 import Data.Primitive.Array
 import Data.Word
@@ -18,32 +18,41 @@
 import Text.Printf
 import GHC.Conc
 import GHC.STRef
-import GHC.IORef
+import GHC.IORef (IORef(..))
+#if MIN_VERSION_base(4,10,0)
+import GHC.Stats (getRTSStats, RTSStats(..))
+#else
 import GHC.Stats (getGCStats, GCStats(..))
+#endif
 import System.Random (randomIO, randomRIO)
 import Test.HUnit (Assertion, assertEqual, assertBool)
-import Test.Framework  (defaultMain)
+import Test.Framework  (defaultMain,testGroup,mutuallyExclusive)
 import Test.Framework.Providers.HUnit (testCase)
 import System.Mem (performGC)
 
 ----------------------------------------
 import Data.Atomics as A
-import Data.Atomics (casArrayElem, readArrayElem)
 
 import qualified Issue28
 
-import CommonTesting 
+import CommonTesting
 import qualified Counter
+import qualified Fetch
 
 ------------------------------------------------------------------------
 
 expect_false_positive_on_GC :: Bool
 expect_false_positive_on_GC = False
 
-getGCCount :: IO Int64 
-getGCCount | expect_false_positive_on_GC = 
+getGCCount :: IO Int64
+getGCCount | expect_false_positive_on_GC =
+#if MIN_VERSION_base(4,10,0)
+               do RTSStats{gcs} <- getRTSStats
+                  return (fromIntegral gcs)
+#else
                do GCStats{numGcs} <- getGCStats
                   return numGcs
+#endif
            | otherwise = return 0
 
 main :: IO ()
@@ -53,8 +62,12 @@
        -- numcap <- getNumProcessors
        let numcap = 4
        when (numCapabilities /= numcap) $ setNumCapabilities numcap
-       
-       defaultMain $ 
+
+       defaultMain $
+        -- Make these run sequentially (hopefully), so we don't interfere with
+        -- concurrent tests. TODO I guess: figure out how to run tests that
+        -- don't fork in parallel, but forking tests sequentially
+        return $ mutuallyExclusive $ testGroup "All tests" $
          [ testCase "casTicket1"              case_casTicket1
          , testCase "issue28_standalone"      case_issue28_standalone
          , testCase "issue28_copied "         case_issue28_copied
@@ -89,6 +102,7 @@
          , iters   <- [10000]]
 
          ++ Counter.tests
+         ++ Fetch.tests
 
 setify :: [Int] -> [Int]
 setify = S.toList . S.fromList
@@ -98,7 +112,7 @@
 mynum :: Int
 mynum = 33
 
--- Expected output: 
+-- Expected output:
 {---------------------------------------
     Perform a CAS within a MutableArray#
       1st try should succeed: (True,33)
@@ -108,24 +122,24 @@
     Done.
 -}
 case_casmutarray1 :: IO ()
-case_casmutarray1 = do 
+case_casmutarray1 = do
  putStrLn "Perform a CAS within a MutableArray#"
  arr <- newArray 5 mynum
 
  writeArray arr 4 33
  putStrLn "Wrote array elements..."
- 
- tick <- readArrayElem arr 4
+
+ tick <- A.readArrayElem arr 4
  putStrLn$ "(Peeking at array gave: "++show (peekTicket tick)++")"
 
- (res1,tick2) <- casArrayElem arr 4 tick 44
- (res2,_)     <- casArrayElem arr 4 tick 44
+ (res1,_tick2) <- A.casArrayElem arr 4 tick 44
+ (res2,_)     <- A.casArrayElem arr 4 tick 44
 -- res  <- stToIO$ casArrayST arr 4 mynum 44
--- res2 <- stToIO$ casArrayST arr 4 mynum 44 
+-- res2 <- stToIO$ casArrayST arr 4 mynum 44
 
  putStrLn "Printing array:"
  forM_ [0..4] $ \ i -> do
-   x <- readArray arr i 
+   x <- readArray arr i
    putStr ("  "++show x)
 
  assertBool "1st try should succeed: " res1
@@ -133,82 +147,75 @@
 
 
 -- case_casbytearray1 :: IO ()
--- case_casbytearray1 = do 
+-- case_casbytearray1 = do
 --  putStrLn "Perform a CAS within a MutableByteArray#"
 
 -- | This test uses a number of producer and consumer threads which push and pop
 -- elements from random positions in an array.
 test_random_array_comm :: Int -> Int -> Int -> IO ()
-test_random_array_comm threads size iters = do 
+test_random_array_comm threads size iters = do
   arr <- newArray size Nothing
-  tick0 <- readArrayElem arr 0
+  tick0 <- A.readArrayElem arr 0
   for_ 1 size $ \ i -> do
-    t2 <- readArrayElem arr i
+    t2 <- A.readArrayElem arr i
     assertEqual "All initial Nothings in the array should be ticket-equal:" tick0 t2
 
-  ls <- forkJoin threads $ \tid -> do 
+  ls <- forkJoin threads $ \_tid -> do
     localAcc <- newIORef 0
     for_ 0 iters $ \iter -> do
       -- Randomly pick a position:
       ix <- randomRIO (0,size-1) :: IO Int
       -- Randomly either produce or consume:
       b <- randomIO :: IO Bool
-      if b then do 
-        (b,newtick) <- casArrayElem arr ix tick0 (Just iter)
-        return ()
+      if b then do
+        void $ A.casArrayElem arr ix tick0 (Just iter)
        else do -- Consume:
-        tick <- readArrayElem arr ix
+        tick <- A.readArrayElem arr ix
         case peekTicket tick of
-          Just _  -> do (b,x) <- casArrayElem arr ix tick (peekTicket tick0) -- Set back to Nothing.
-                        when b $ modifyIORef' localAcc (+1)
+          Just _  -> do (success,_) <- A.casArrayElem arr ix tick (peekTicket tick0) -- Set back to Nothing.
+                        when success $ modifyIORef' localAcc (+1)
 --                        print (peekTicket x)
           Nothing -> return ()
         return ()
     readIORef localAcc
-    
+
   let successes = sum ls
       -- Pidgeonhole principle.
       -- min_success =
-  printf "Communication through random array positions (threads/size/iters %s).\n" (show (threads,size,iters))
-  printf "Successes: %d (expected 1/4 of total iterations on all threads)\n" successes
-  printf "Per-thread successes: %s\n" (show ls)
+  _ <- printf "Communication through random array positions (threads/size/iters %s).\n" (show (threads,size,iters))
+  _ <- printf "Successes: %d (expected 1/4 of total iterations on all threads)\n" successes
+  _ <- printf "Per-thread successes: %s\n" (show ls)
   assertBool "Number of successes: " (successes <= (threads * iters) `quot` 2 && successes >= 0)
   for_ 0 size $ \ i -> do
-    x <- readArray arr i
---    putStr (show x ++ " ")
+    _x <- readArray arr i
+--    putStr (show _x ++ " ")
     return ()
   putStrLn ""
   return ()
-  
-   
+
+
 ----------------------------------------------------------------------------------------------------
 -- Simple, non-parameterized tests
  ----------------------------------------------------------------------------------------------------
 
-{-# NOINLINE zer #-}
-zer :: Int
-zer = 0
-default_iters :: Int
-default_iters = 100000
-
 case_casTicket1 :: IO ()
 case_casTicket1 = do
   dbgPrint 1 "\nUsing new 'ticket' based compare and swap:"
 
-  IORef (STRef mutvar) <- newIORef (3::Int)  
+  IORef (STRef mutvar) <- newIORef (3::Int)
   tick <- A.readMutVarForCAS mutvar
   dbgPrint 1$"YAY, read the IORef, ticket "++show tick
   dbgPrint 1$"     and the value was:  "++show (peekTicket tick)
 
-  (True,tick2) <- A.casMutVar mutvar tick 99 
+  (True,tick2) <- A.casMutVar mutvar tick 99
   dbgPrint 1$"Hoorah!  Attempted compare and swap..."
 --  dbgPrint 1$"         Result was: "++show (True,tick2)
 
   dbgPrint 1$"Ok, next take a look at a SECOND CAS attempt, to see if the ticket from the first works..."
   res2 <- A.casMutVar mutvar tick2 12345678
   dbgPrint 1$"Result was: "++show res2
-  
---  res <- A.casMutVar mutvar tick 99 
+
+--  res <- A.casMutVar mutvar tick 99
   res3 <- A.readMutVarForCAS mutvar
   dbgPrint 1$"To check contents, did a SECOND read: "++show res3
 
@@ -218,10 +225,10 @@
 case_issue28_standalone = Issue28.main
 
 case_issue28_copied :: Assertion
-case_issue28_copied = do 
+case_issue28_copied = do
   r  <- newIORef "hi"
   t0 <- readForCAS r
-  (True,t1) <- casIORef r t0 "bye"
+  (True,_t1) <- casIORef r t0 "bye"
   return ()
 
 ---- toddaaro's tests -----
@@ -249,9 +256,9 @@
   dbgPrint 1$ "  Creating a single 'ticket' based variable to mutate twice."
   x <- newIORef (0::Int)
   tick1 <- A.readForCAS(x)
-  res1 <- A.casIORef x tick1 5
+  void $ A.casIORef x tick1 5
   tick2 <- A.readForCAS(x)
-  res2 <- A.casIORef x tick2 120
+  void $ A.casIORef x tick2 120
   valf <- readIORef x
   assertBool "Does the value after the first mutate equal 5?" (peekTicket tick2 == 5)
   assertBool "Does the value after the second mutate equal 120?" (valf == 120)
@@ -264,23 +271,23 @@
   dbgPrint 1$ "   Creating 120 threads and having each increment a counter value."
   counter <- newIORef (0::Int)
 --  let work :: Int -> IORef Int -> IO (Int,StableName Int,Int,StableName Int,Int)
-  let work :: Int -> IORef Int -> IO (Int,Int,Int,Int,Int)
-      work ix counter = do
+  let work :: Int -> IO (Int,Int,Int,Int,Int)
+      work ix = do
         tick <- A.readForCAS(counter)
         let nxt = peekTicket tick + 1
         (b,was) <- A.casIORef counter tick nxt
-        if b then do 
+        if b then do
           putStr $ show (peekTicket was) ++ "_"
           assertEqual "Check that the value written was the one we put in." nxt (peekTicket was)
           return (ix, unsafeName tick, unsafeName was, peekTicket tick, nxt)
-         else do 
-          when (peekTicket was == peekTicket tick) $ 
+         else do
+          when (peekTicket was == peekTicket tick) $
              putStrLn ("(Spoofed by boxing, old val was indeed "++show was++")")
           putStr "!"
 --          putStrLn $ "("++ show ix ++ ": Fail when putting "++show nxt
 --                     ++", was already "++show (peekTicket was) ++")"
-          work ix counter
-  arr <- forkJoin 120 (\i -> work i counter) 
+          work ix
+  arr <- forkJoin 120 work
   ans <- readIORef counter
 
   let dups = [ n | (_,_,_,_,n) <- arr] \\ [1..120]
@@ -301,18 +308,18 @@
 ----------------------------------------------------------------------------------------------------
 
 ----------------------------------------------------------------------------------------------------
--- Adapted Old tests from original CAS library:  
+-- Adapted Old tests from original CAS library:
 
 
 -- | First test: Run a simple CAS a small number of times.
 test_succeed_once :: (Show a, Num a, Eq a) => a -> Assertion
-test_succeed_once n = 
+test_succeed_once initialVal =
   do
      performGC -- We *ASSUME* GC does not happen below.
      performGC -- We *ASSUME* GC does not happen below.
      checkGCStats
-     gc1 <- getGCCount 
-     r <- newIORef n
+     gc1 <- getGCCount
+     r <- newIORef initialVal
      bitls <- newIORef []
      tick1 <- A.readForCAS r
      let loop 0 = return ()
@@ -321,24 +328,24 @@
           atomicModifyIORef bitls (\x -> (res:x, ()))
 --          putStrLn$ "  CAS result: " ++ show res
           loop (n-1)
-     loop 10
+     loop (10::Int)
 
      x <- readIORef r
      assertEqual "Finished with loop, read cell: " 100 x
-     
-     writeIORef r 111     
+
+     writeIORef r 111
      y <- readIORef r
      assertEqual "Wrote and read again read: " 111 y
 
      ls <- readIORef bitls
      let rev = (reverse ls)
-         tickets = map snd rev
+      -- tickets = map snd rev
          (hd:tl) = map fst rev
 
      gc2 <- getGCCount
      if gc1 /= gc2
        then putStrLn " [skipped] test couldn't be assessed properly due to GC."
-       else do      
+       else do
   --     print scrubbed
        assertBool "Only first succeeds" (all (/= hd) tl)
        assertBool "All but first fail" (all (== head tl) (tail tl))
@@ -355,18 +362,18 @@
 -- time.  Adding a second thread can only foil the K attempts of the first thread by
 -- itself succeeding (leaving the total at or above K).  Likewise for the third
 -- thread and so on.
--- 
+--
 -- Conversely, for N threads each aiming to complete K operations,
 -- there should be at most N*N*K total operations required.
 test_all_hammer_one :: (Show a, Num a, Eq a) => Int -> Int -> a -> Assertion
 test_all_hammer_one threads iters seed = do
   ref <- newIORef seed
-  logs::[[Bool]] <- forkJoin threads $ \_ -> 
+  logs::[[Bool]] <- forkJoin threads $ \_ ->
     do checkGCStats
        let loop 0 _ _ !acc = return (reverse acc)
            loop n !ticket !expected !acc = do
             -- This line will result in boxing/unboxing and using extra memory locations:
---            let bumped = expected + 1 
+--            let bumped = expected + 1
             bumped <- evaluate$ expected + 1
             (res,tick) <- casIORef ref ticket bumped
             case res of
@@ -377,10 +384,10 @@
               False -> do
                 let v = peekTicket tick
                 when (iters < 30) $
-                  dbgPrint 1 $ 
+                  dbgPrint 1 $
                             "  Fizzled CAS with ticket: "++show ticket ++" containing "++show v++
                             ", expected: "++ show expected ++
-                            " (#"++show (unsafeName expected)++"): " 
+                            " (#"++show (unsafeName expected)++"): "
                             ++ " found " ++ show v ++ " (#"++show (unsafeName v)++", ticket "++show tick++")"
                 loop (n-1) tick v      (False:acc)
 
@@ -402,6 +409,149 @@
              (total_success >= expected_success)
 
 
+
+------------------------------------------------------------------------
+-- Reads and Writes with full barriers:
+{-
+ - WIP
+
+import Data.Atomics (atomicReadIntArray, atomicWriteIntArray)
+import Data.Primitive
+import Control.Concurrent
+import Data.List(sort)
+
+-- TODO DEBUGGING: for required NoBuffering
+import System.IO
+
+
+test_atomic_read_write_sanity :: IO ()
+test_atomic_read_write_sanity = do
+    mba <- newByteArray (sizeOf (undefined :: Int))
+    atomicWriteIntArray mba 0 0
+    x <- atomicReadIntArray mba 0
+    atomicWriteIntArray mba 0 1
+    y <- atomicReadIntArray mba 0
+    assertEqual "test_atomic_read_write_sanity x" x 0
+    assertEqual "test_atomic_read_write_sanity y" y 1
+
+-- These don't really adequately test that we have a *full* barrier, but only
+-- store/store and load/load I think. TODO something better
+test_atomic_read_write_barriers1, test_atomic_read_write_barriers2 :: Int -> IO ()
+
+-- NOTE: We don't observe failure here on x86 with non-atomic reads/writes, but
+-- maybe it will for other architectures. Otherwise this can be removed.
+test_atomic_read_write_barriers1 iters = do
+    let theWrite mba = atomicWriteIntArray mba 0
+        theRead mba = atomicReadIntArray mba 0
+    {- NOTE: We would like this to fail (but it seems to work on x86)
+    let theWrite mba = writeByteArray mba 0
+        theRead mba = readByteArray mba 0
+     -}
+    -- For kicks, a bunch of padding to ensure these are on different cache-lines:
+    mba0 <- newByteArray (sizeOf (undefined :: Int) * 32)
+    mba1 <- newByteArray (sizeOf (undefined :: Int) * 32)
+    writeByteArray mba0 0 (0 :: Int)
+    writeByteArray mba1 0 (1 :: Int)
+    -- One thread increments mba0, then mba1 and repeats. The other repeatedly
+    -- loops reading mba0 and mba1, checking that the value from the first is
+    -- always <= the second:
+    readerWait <- newEmptyMVar
+    void $ forkIO $
+        let go :: Int -> IO ()
+            go n = unless (n > iters) $ do
+                    theWrite mba0 n
+                    theWrite mba1 (n+1)
+                    go (n+1)
+         in go 1
+    void $ forkIO $
+        let go = do x <- theRead mba0
+                    y <- theRead mba1
+                    assertBool "test_atomic_read_write_barriers" $
+                        (x <= y)
+                    when (x < iters) go
+         in go
+-- Peterson's lock: http://en.wikipedia.org/wiki/Peterson%27s_algorithm
+--
+-- TODO DEBUGGING see https://github.com/rrnewton/haskell-lockfree/issues/43#issuecomment-71294801
+--                for a discussion of issues to be resolved here.
+test_atomic_read_write_barriers2 iters = do
+
+    hSetBuffering stdout NoBuffering  -- TODO DEBUGGING (THIS APPEARS NECESSARY FOR PUTSTR TRICK BELOW TO WORK, TOO)
+
+    let theWrite mba = atomicWriteIntArray mba 0
+        theRead mba = atomicReadIntArray mba 0
+    {- NOTE: WE WANT TO MAKE SURE THESE FAIL, BUT THEY DON'T !!
+    let theWrite mba (v::Int) = writeByteArray mba 0 v
+        theRead mba = readByteArray mba 0 :: IO Int
+     -}
+    let true = 1 :: Int
+        false = 0 :: Int
+    -- For kicks, a bunch of padding to ensure these are on different cache-lines:
+    flag0 <- newByteArray (sizeOf (undefined :: Int) * 32)
+    flag1 <- newByteArray (sizeOf (undefined :: Int) * 32)
+    turn <- newByteArray (sizeOf (undefined :: Int) * 32)
+    writeByteArray flag0 0 false
+    writeByteArray flag1 0 false
+
+    -- We use our lock to get an atomic counter:
+    counter <- newByteArray (sizeOf (undefined :: Int) * 32)
+    writeByteArray counter 0 (0::Int)
+
+    let petersonIncr flagA flagB turnVal = do
+            theWrite flagA true
+            theWrite turn turnVal
+            let busyWait = do
+                  flagBVal <- theRead flagB
+                  turnVal' <- theRead turn
+                  if turnVal == 1 then putStr "x"  else putStr "+" -- TODO DEBUGGING (THIS APPEARS NECESSARY, AND MUST HAPPEN HERE)
+                  -- putStrLn ""                                      -- TODO DEBUGGING this works too (BUT NOT FOR 1MIL?)
+                  -- void $ newEmptyMVar                              -- TODO DEBUGGING does some heap alloc help? NOPE
+                  -- yield                                             -- TODO DEBUGGING neither this nor -fno-omit-yields seem to help
+                  when (flagBVal == true && turnVal' == 1) busyWait
+            busyWait
+            -- start critical section --
+            old <- theRead counter
+            theWrite counter (old+1)
+            -- exit critical section --
+            theWrite flagA false
+            return old
+
+    out1 <- newEmptyMVar
+    out2 <- newEmptyMVar
+    void $ forkIO $
+        (replicateM iters $ petersonIncr flag0 flag1 1)
+          >>= putMVar out1
+    void $ forkIO $
+        (replicateM iters $ petersonIncr flag1 flag0 0)
+          >>= putMVar out2
+
+    -- make sure we got some interleaving, and that output was correct:
+    res1 <- takeMVar out1
+    res2 <- takeMVar out2
+
+    let numGaps gaps _ [] = gaps
+        numGaps gaps prev (x:xs)
+            | prev+1 == x = numGaps gaps x xs
+            | otherwise   = numGaps (gaps+1) x xs
+    -- TODO DEBUGGING FYI:
+    print $ numGaps (0::Int) (-1::Int) res1
+    print $ numGaps (0::Int) (-1::Int) res2
+    -- ------------------
+
+    -- if this fails, fix the test or call with more iters
+    assertBool "test_atomic_read_write_barriers2 had enough interleaving to be legit" $
+           numGaps (0::Int) (-1::Int) res1 > 10000
+        && numGaps (0::Int) (-1::Int) res2 > 10000
+
+    -- braindead merge check:
+    let ok = sort res1 == res1
+              &&  sort res2 == res2
+              &&  sort (res1++res2) == [0..iters*2-1]
+
+    assertBool "test_atomic_read_write_barriers2" ok
+
+ -}
+
 ----------------------------------------------------------------------------------------------------
 {-
 
@@ -409,9 +559,9 @@
 -- This tests repeated atomicModifyIORefCAS operations.
 
 testCAS3 :: Int -> IORef ElemTy -> IO [()]
-testCAS3 iters ref = 
+testCAS3 iters ref =
   forkJoin numCapabilities (loop iters)
- where 
+ where
    loop 0  = return ()
    loop n  = do
     -- let bumped = expected+1 -- Must do this only once, should be NOINLINE
@@ -427,7 +577,7 @@
 #endif
     loop (n-1)
 
-----------------------------------------------------------------------------------------------------       
+----------------------------------------------------------------------------------------------------
 -- This version uses a non-scalar type for CAS.  It instead
 -- manipulates the tail pointers of a simple linked-list.
 
@@ -440,7 +590,7 @@
 
 -- testCAS4 :: CASable ref Int => List ref -> IO [Bool]
 testCAS4 :: CASable ref Int => Int -> ref (List ref) -> IO ()
-testCAS4 iters ref = do 
+testCAS4 iters ref = do
   forkJoin numCapabilities $ do
      -- From each thread, attempt to extend the list 'iters' times:
      ref' <- readCASable ref
@@ -449,11 +599,11 @@
      return ()
 
   return ()
- where 
+ where
   loop 0 _ _ = return ()
   loop n new (Cons _ tl) = do
     tl' <- readCASable tl
-    case tl' of 
+    case tl' of
       Null -> do (b,v) <- cas tl tl' new
          if b then loop (n-1) v
               else loop v
@@ -471,14 +621,14 @@
   else error$ "Test "++ msg ++ " failed to have the right CAS success pattern: " ++ show ls
 
 checkOutput2 :: String -> Int -> [[Bool]] -> ElemTy -> IO ()
-checkOutput2 msg iters ls fin = do 
+checkOutput2 msg iters ls fin = do
   let totalAttempts = sum $ map length ls
   putStrLn$ "Final value "++show fin++", Total successes "++ show (length $ filter id $ concat ls)
   when (fin < fromIntegral iters) $
-    error$ "ERROR in "++ show msg ++ " expected at least "++show iters++" successful CAS's.." 
+    error$ "ERROR in "++ show msg ++ " expected at least "++show iters++" successful CAS's.."
 
 checkOutput3 :: String -> Int -> [[Bool]] -> ElemTy -> IO ()
-checkOutput3 msg iters ls fin = do 
+checkOutput3 msg iters ls fin = do
 
   return ()
 
@@ -489,12 +639,12 @@
 
 
 -- test x = do
---   a <- newStablePtr x 
---   b <- newStablePtr x 
---   printf "First call, word %d IntPtr %d\n" 
+--   a <- newStablePtr x
+--   b <- newStablePtr x
+--   printf "First call, word %d IntPtr %d\n"
 --   (unsafeCoerce a :: Word)
 --   ((fromIntegral$ ptrToIntPtr $ castStablePtrToPtr a) :: Int)
---   printf "Second call, word %d IntPtr %d\n" 
+--   printf "Second call, word %d IntPtr %d\n"
 --   (unsafeCoerce b :: Word)
 --   ((fromIntegral$ ptrToIntPtr $ castStablePtrToPtr b) :: Int)
 
diff --git a/testing/test-atomic-primops.cabal b/testing/test-atomic-primops.cabal
--- a/testing/test-atomic-primops.cabal
+++ b/testing/test-atomic-primops.cabal
@@ -4,9 +4,10 @@
 Name:                test-atomic-primops
 Version:             0.6.0.5
 Build-type:          Simple
-Cabal-version:       >=1.8
-
+Cabal-version:       >=1.18
 -- This is generally controled by the continuous integration script at a more granular level:
+tested-with:         GHC == 8.4.3, GHC == 8.2.2, GHC == 8.0.2, GHC == 7.10.3
+
 Flag opt
     Description: Enable GHC optimization.
     Default: False
@@ -19,66 +20,88 @@
 Test-Suite test-atomic-primops
     type:       exitcode-stdio-1.0
     main-is:    Test.hs
-    ghc-options: -rtsopts -main-is Test.main
+    other-modules:
+        CommonTesting
+        Counter
+        Fetch
+        Issue28
+    ghc-options: -rtsopts -main-is Test.main -Wall
 
     if flag(opt)
        ghc-options: -O2 -funbox-strict-fields
     if flag(threaded)
-       ghc-options: -threaded 
+       ghc-options: -threaded
        ghc-options: -rtsopts -with-rtsopts=-N4
 
     -- Set it to always run with some parallelism.
-    build-depends: base, ghc-prim, primitive, containers, random, atomic-primops >= 0.6.0.5,
+    build-depends: base >= 4.8 && < 5
+                 , ghc-prim
+                 , primitive
+                 , containers
+                 , random
+                 , atomic-primops >= 0.6.0.5
                    -- For Testing:
-                   time, HUnit, test-framework, test-framework-hunit
+                 , time
+                 , HUnit
+                 , test-framework
+                 , test-framework-hunit
     -- Optional: Debugging generated code:
     --    ghc-options: -keep-tmp-files -dsuppress-module-prefixes -ddump-to-file -ddump-core-stats -ddump-simpl-stats -dcore-lint -dcmm-lint
     --    ghc-options: -ddump-ds -ddump-simpl -ddump-stg -ddump-asm -ddump-bcos -ddump-cmm -ddump-opt-cmm -ddump-inlinings
+    default-language: Haskell2010
 
 -- Cabal can get confused if there is no executable or library... so here's a dummy executable.
 -- Also it provides a good test of compile/link issues, apart from everything else.
 Executable hello-world-atomic-primops
   main-is: hello.hs
-  build-depends: base >= 4.5, atomic-primops
+  build-depends: base >= 4.8 && < 5
+               , atomic-primops
+  default-language: Haskell2010
 
 -- This is separated out, because a bug in GHC 7.6 make this fail on Linux.
 Test-suite template-haskell-atomic-primops
   type:       exitcode-stdio-1.0
   main-is: ghci-test.hs
-  if impl(ghc >= 7.8) {
-    Buildable: True
-    build-depends: base >= 4.5, atomic-primops >= 0.6.0.5, template-haskell,
-                   -- For Testing:
-                   test-framework, test-framework-hunit
-  } else {
-    Buildable: False
-  }
-
+  other-modules: TemplateHaskellSplices
+  Buildable: True
+  build-depends: base >= 4.8 && < 5
+               , atomic-primops >= 0.6.0.5
+               , template-haskell
+               , test-framework
+               , test-framework-hunit
+  default-language: Haskell2010
 -- A very simple test of one primop included in GHC 7.8:
 Test-suite raw_CAS
   type:    exitcode-stdio-1.0
-  build-depends: base, ghc-prim, atomic-primops >= 0.6.0.5
+  build-depends: base >= 4.8 && < 5
+               , ghc-prim
+               , atomic-primops >= 0.6.0.5
 -- ghc-prim, primitive, containers, random, atomic-primops >= 0.5.0.2,
   main-is: Raw781_test.hs
-  if impl(ghc < 7.7) {
-    Buildable: False  
-  }
-
+  default-language: Haskell2010
 
 Test-suite Issue28
   type:    exitcode-stdio-1.0
-  build-depends: base, ghc-prim, atomic-primops >= 0.6.0.5
+  build-depends: base >= 4.8 && < 5
+               , ghc-prim
+               , atomic-primops >= 0.6.0.5
   main-is: Issue28.hs
   ghc-options: -main-is Issue28.main
-  -- if impl(ghc < 7.7) {
-  --   Buildable: False  
-  -- }
+  default-language: Haskell2010
 
 Benchmark atomic-primops-MicroBench
   type:    exitcode-stdio-1.0
-  build-depends: base, ghc-prim, primitive, containers, random,
-                 atomic-primops >= 0.6.0.5, 
-                 deepseq >= 1.3, 
-                 time, HUnit, test-framework, test-framework-hunit
-  build-depends: criterion >= 1.0
+  build-depends: base >= 4.8 && < 5
+               , ghc-prim
+               , primitive
+               , containers
+               , random
+               , atomic-primops >= 0.6.0.5
+               , deepseq >= 1.3
+               , time
+               , HUnit
+               , test-framework
+               , test-framework-hunit
+               , criterion >= 1.2.1
   main-is: MicroBench.hs
+  default-language: Haskell2010
