diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,8 @@
+0.1.4.0
+
+* Fix a bug that occurred with GHC 9.2.4 or later that would cause incorrect
+  measurements.
+
 0.1.3.0
 
 * Change `criterion_rdtsc` to return `mach_absolute_time` on macOS. This is a
diff --git a/criterion-measurement.cabal b/criterion-measurement.cabal
--- a/criterion-measurement.cabal
+++ b/criterion-measurement.cabal
@@ -1,5 +1,5 @@
 name:                criterion-measurement
-version:             0.1.3.0
+version:             0.1.4.0
 synopsis:            Criterion measurement functionality and associated types
 description:         Measurement-related functionality extracted from Criterion, with minimal dependencies. The rationale for this is to enable alternative analysis front-ends.
 homepage:            https://github.com/haskell/criterion
@@ -22,8 +22,9 @@
   GHC==8.4.4,
   GHC==8.6.5,
   GHC==8.8.4,
-  GHC==8.10.4,
-  GHC==9.0.1
+  GHC==8.10.7,
+  GHC==9.0.2,
+  GHC==9.2.2
 
 flag fast
   description: compile without optimizations
diff --git a/src/Criterion/Measurement/Types.hs b/src/Criterion/Measurement/Types.hs
--- a/src/Criterion/Measurement/Types.hs
+++ b/src/Criterion/Measurement/Types.hs
@@ -563,7 +563,7 @@
     -- ^ Action that creates the environment for a single run.
     -> (env -> IO b)
     -- ^ Function returning the IO action that should be benchmarked with the
-    -- newly genereted environment.
+    -- newly generated environment.
     -> Benchmarkable
 perRunEnv alloc = perRunEnvWithCleanup alloc noop
 
@@ -578,7 +578,7 @@
     -- ^ Clean up the created environment.
     -> (env -> IO b)
     -- ^ Function returning the IO action that should be benchmarked with the
-    -- newly genereted environment.
+    -- newly generated environment.
     -> Benchmarkable
 perRunEnvWithCleanup alloc clean work = bm { perRun = True }
   where
diff --git a/src/Criterion/Measurement/Types/Internal.hs b/src/Criterion/Measurement/Types/Internal.hs
--- a/src/Criterion/Measurement/Types/Internal.hs
+++ b/src/Criterion/Measurement/Types/Internal.hs
@@ -20,6 +20,7 @@
 module Criterion.Measurement.Types.Internal (fakeEnvironment, nf', whnf') where
 
 import Data.Int (Int64)
+import Control.Exception
 
 -- | A dummy environment that is passed to functions that create benchmarks
 -- from environments when no concrete environment is available.
@@ -45,7 +46,17 @@
 -- benchmark code itself could be changed by the user's optimization level. By
 -- marking them @NOINLINE@, the core benchmark code is always the same.
 --
--- See #183 and #184 for discussion.
+-- Finally, it's important that both branches of the loop depend on the state
+-- token from the IO action. This is achieved by using `evaluate` rather than `let !y = f x`
+-- in order to force the value to whnf. `evaluate` is in the IO monad and therefore the state
+-- token needs to be passed through the loop.
+--
+-- See ghc#21948 where a change in eta-expansion behaviour
+-- caused the work to be performed in the wrong place because the otherwise branch
+-- did not depend on the state token at all, and the whole loop could be evaluated to
+-- a single return function before being run in the IO monad.
+--
+-- See #183, #184 and #264 for discussion.
 
 -- | Generate a function which applies an argument to a function a
 -- given number of times, reducing the result to normal form.
@@ -53,8 +64,9 @@
 nf' reduce f x = go
   where
     go n | n <= 0    = return ()
-         | otherwise = let !y = f x
-                       in reduce y `seq` go (n-1)
+         | otherwise = do
+            y <- evaluate (f x)
+            reduce y `seq` go (n-1)
 {-# NOINLINE nf' #-}
 
 -- | Generate a function which applies an argument to a function a
@@ -63,5 +75,7 @@
 whnf' f x = go
   where
     go n | n <= 0    = return ()
-         | otherwise = f x `seq` go (n-1)
+         | otherwise = do
+            _ <- evaluate (f x)
+            go (n-1)
 {-# NOINLINE whnf' #-}
