--- /dev/null
+module OptimizationFuel
+ ( OptimizationFuel, canRewriteWithFuel, maybeRewriteWithFuel, oneLessFuel
+ , tankFilledTo, diffFuel
+ , FuelConsumer
+ , FuelUsingMonad, FuelState
+ , lastFuelPass, fuelExhausted, fuelRemaining, fuelDecrement
+ , lastFuelPassInState, fuelExhaustedInState, fuelRemainingInState
+ , fuelDecrementState
+ , runFuel, runFuelIO, runFuelWithLastPass, fuelConsumingPass
+ , FuelMonad(..)
+ )
+where
+
+import GHC.Prim
+import Panic
+
+import Data.IORef
+
+#include "HsVersions.h"
+
+type FuelConsumer a = OptimizationFuel -> (a, OptimizationFuel)
+
+canRewriteWithFuel :: OptimizationFuel -> Bool
+oneLessFuel :: OptimizationFuel -> OptimizationFuel
+maybeRewriteWithFuel :: OptimizationFuel -> Maybe a -> Maybe a
+diffFuel :: OptimizationFuel -> OptimizationFuel -> Int
+ -- to measure consumption during compilation
+tankFilledTo :: Int -> OptimizationFuel
+
+#ifdef DEBUG
+newtype OptimizationFuel = OptimizationFuel Int
+ deriving Show
+
+tankFilledTo = OptimizationFuel
+canRewriteWithFuel (OptimizationFuel f) = f > 0
+maybeRewriteWithFuel fuel ma = if canRewriteWithFuel fuel then ma else Nothing
+oneLessFuel (OptimizationFuel f) = ASSERT (f > 0) (OptimizationFuel (f - 1))
+diffFuel (OptimizationFuel f) (OptimizationFuel f') = f - f'
+#else
+-- type OptimizationFuel = State# () -- would like this, but it won't work
+data OptimizationFuel = OptimizationFuel
+ deriving Show
+tankFilledTo _ = undefined -- should be impossible to evaluate
+ -- realWorld# might come in handy, too...
+canRewriteWithFuel OptimizationFuel = True
+maybeRewriteWithFuel _ ma = ma
+oneLessFuel f = f
+diffFuel _ _ = 0
+#endif
+
+-- stop warnings about things that aren't used
+_unused :: State# () -> FS.FastString
+_unused = undefined panic
+
+
+data FuelState = FuelState { fs_fuellimit :: OptimizationFuel, fs_lastpass :: String }
+newtype FuelMonad a = FuelMonad (FuelState -> (a, FuelState))
+
+fuelConsumingPass :: String -> FuelConsumer a -> FuelMonad a
+fuelConsumingPass name f = do fuel <- fuelRemaining
+ let (a, fuel') = f fuel
+ fuelDecrement name fuel fuel'
+ return a
+
+runFuel :: FuelMonad a -> FuelConsumer a
+runFuelWithLastPass :: FuelMonad a -> FuelConsumer (a, String)
+
+runFuelIO :: IORef String -> IORef OptimizationFuel -> FuelMonad a -> IO a
+runFuelIO pass_ref fuel_ref (FuelMonad f) =
+ do { pass <- readIORef pass_ref
+ ; fuel <- readIORef fuel_ref
+ ; let (a, FuelState fuel' pass') = f (FuelState fuel pass)
+ ; writeIORef pass_ref pass'
+ ; writeIORef fuel_ref fuel'
+ ; return a
+ }
+
+initialFuelState :: OptimizationFuel -> FuelState
+initialFuelState fuel = FuelState fuel "unoptimized program"
+
+runFuel (FuelMonad f) fuel = let (a, s) = f $ initialFuelState fuel
+ in (a, fs_fuellimit s)
+runFuelWithLastPass (FuelMonad f) fuel = let (a, s) = f $ initialFuelState fuel
+ in ((a, fs_lastpass s), fs_fuellimit s)
+
+lastFuelPassInState :: FuelState -> String
+lastFuelPassInState = fs_lastpass
+
+fuelExhaustedInState :: FuelState -> Bool
+fuelExhaustedInState = canRewriteWithFuel . fs_fuellimit
+
+fuelRemainingInState :: FuelState -> OptimizationFuel
+fuelRemainingInState = fs_fuellimit
+
+fuelDecrementState
+ :: String -> OptimizationFuel -> OptimizationFuel -> FuelState -> FuelState
+fuelDecrementState new_optimizer old new s =
+ FuelState { fs_fuellimit = lim, fs_lastpass = optimizer }
+ where lim = if diffFuel old (fs_fuellimit s) == 0 then new
+ else panic $
+ concat ["lost track of ", new_optimizer, "'s transactions"]
+ optimizer = if diffFuel old new > 0 then new_optimizer else fs_lastpass s
+
+class Monad m => FuelUsingMonad m where
+ fuelRemaining :: m OptimizationFuel
+ fuelDecrement :: String -> OptimizationFuel -> OptimizationFuel -> m ()
+ fuelExhausted :: m Bool
+ lastFuelPass :: m String
+
+
+instance Monad FuelMonad where
+ FuelMonad f >>= k = FuelMonad (\s -> let (a, s') = f s
+ FuelMonad f' = k a
+ in f' s')
+ return a = FuelMonad (\s -> (a, s))
+
+instance FuelUsingMonad FuelMonad where
+ fuelRemaining = extract fuelRemainingInState
+ lastFuelPass = extract lastFuelPassInState
+ fuelExhausted = extract fuelExhaustedInState
+ fuelDecrement p f f' = FuelMonad (\s -> ((), fuelDecrementState p f f' s))
+
+extract :: (FuelState -> a) -> FuelMonad a
+extract f = FuelMonad (\s -> (f s, s))