summaryrefslogtreecommitdiff
path: root/compiler/cmm/OptimizationFuel.hs
blob: bc32626c9831351ef1bc8daeae07afbd5eeaa414 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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.Exts (State#)
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

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))