Switch to using a patched GHC

This commit is contained in:
Ryan Trinkle 2023-07-09 12:34:47 -04:00
parent ebdd46e76e
commit 6b27e947ee
3 changed files with 46 additions and 25 deletions

View File

@ -1,15 +1,21 @@
{-# LANGUAGE RecursiveDo #-} {-# LANGUAGE RecursiveDo #-}
{-# OPTIONS_GHC -fplugin UIO #-} {-# OPTIONS_GHC -fplugin UIO #-}
module Test.UIO where module Test.UIO (test) where
import GHC.IO
import GHC.Magic
import Data.IORef import Data.IORef
import Control.Monad import Control.Monad
import UIO import UIO
import Test.UIO.MultiModule import Test.UIO.MultiModule
import Test.UIO.Builder
test :: IO () test :: IO ()
test = do test = do
putStrLn "testUIOBuilder"
testUIOBuilder
{-
putStrLn "testUIOFix" putStrLn "testUIOFix"
testUIOFix testUIOFix
putStrLn "testUIOUnique" putStrLn "testUIOUnique"
@ -26,8 +32,11 @@ test = do
testIO testIO
putStrLn "testMultiModule" putStrLn "testMultiModule"
testMultiModule testMultiModule
-- putStrLn "testUIOPrintLots" putStrLn "testFloatIn"
-- testUIOPrintLots testFloatIn
putStrLn "testUIOPrintLots"
testUIOPrintLots
-}
{-# NOINLINE testUIOUnique #-} {-# NOINLINE testUIOUnique #-}
testUIOUnique :: IO () testUIOUnique :: IO ()
@ -106,6 +115,16 @@ testUIOCycle = runUIO $ mdo
r <- timeless $ newIORef $ CycleRef r r <- timeless $ newIORef $ CycleRef r
pure () pure ()
{-# NOINLINE testFloatIn #-}
testFloatIn :: IO ()
testFloatIn = runUIO $ do
r <- timeless $ newIORef 0
x <- unordered $ atomicModifyIORef r $ \x -> (succ x, succ x)
let {-# NOINLINE blah #-}
blah :: IO ()
blah = IO $ oneShot (\s -> unIO (print x) s)
unordered $ replicateM_ 10 blah
{-# NOINLINE testIO #-} {-# NOINLINE testIO #-}
testIO :: IO () testIO :: IO ()
testIO = do testIO = do

View File

@ -11,6 +11,7 @@ import Data.Foldable
import Data.Semigroup import Data.Semigroup
import GHC.IO import GHC.IO
import GHC.Prim import GHC.Prim
import GHC.Magic
import UIO.Plugin as X import UIO.Plugin as X
@ -42,12 +43,12 @@ instance Applicative UIO where
{-# INLINE (<*>) #-} {-# INLINE (<*>) #-}
pure x = UIO (\_ -> (RemainingWork, x)) pure x = UIO (\_ -> (RemainingWork, x))
UIO m *> UIO k = UIO (\s -> UIO m *> UIO k = UIO (\s ->
let (ms, _) = m (uniqueState 1# s) let (ms, _) = m (uniqueState# 1# s)
(ks, b) = k (uniqueState 2# s) (ks, b) = k (uniqueState# 2# s)
in (ms <> ks, b)) in (ms <> ks, b))
UIO m <*> UIO k = UIO (\s -> UIO m <*> UIO k = UIO (\s ->
let (ms, f) = m (uniqueState 1# s) let (ms, f) = m (uniqueState# 1# s)
(ks, x) = k (uniqueState 2# s) (ks, x) = k (uniqueState# 2# s)
in (ms <> ks, f x)) in (ms <> ks, f x))
instance Monad UIO where instance Monad UIO where
@ -55,8 +56,8 @@ instance Monad UIO where
{-# INLINE (>>=) #-} {-# INLINE (>>=) #-}
(>>) = (*>) (>>) = (*>)
UIO m >>= k = UIO (\s -> UIO m >>= k = UIO (\s ->
let (ms, a) = m (uniqueState 1# s) let (ms, a) = m (uniqueState# 1# s)
(ks, b) = unUIO (k a) (uniqueState 2# s) (ks, b) = unUIO (k a) (uniqueState# 2# s)
in (ms <> ks, b)) in (ms <> ks, b))
instance MonadFix UIO where instance MonadFix UIO where
@ -68,7 +69,7 @@ instance MonadFix UIO where
runUIO :: UIO a -> IO a runUIO :: UIO a -> IO a
runUIO (UIO m) = do runUIO (UIO m) = do
-- We use a bang pattern here instead of "evaluate", because "evaluate" leaves a "seq#" clutting up our core, but the bang pattern does not -- We use a bang pattern here instead of "evaluate", because "evaluate" leaves a "seq#" clutting up our core, but the bang pattern does not
(!RemainingWork, result) <- IO (\s -> (# s, m s #)) --TODO: This returns the same state we were given; should we call uniqueState 1 or something on it? (!RemainingWork, result) <- IO (\s -> (# s, m s #)) --TODO: This returns the same state we were given; should we call uniqueState# 1# or something on it?
pure result pure result
-- The following is marked NOINLINE because unsafeDupablePerformIO is marked NOINLINE. I don't really understand it. -- The following is marked NOINLINE because unsafeDupablePerformIO is marked NOINLINE. I don't really understand it.
@ -81,11 +82,10 @@ unordered (IO m) = UIO (\s -> let x = case m s of (# _, x #) -> x in (x `seq` Re
timeless :: IO a -> UIO a timeless :: IO a -> UIO a
timeless (IO m) = UIO (\s -> (RemainingWork, case m s of (# _, x #) -> x)) timeless (IO m) = UIO (\s -> (RemainingWork, case m s of (# _, x #) -> x))
-- Force GHC to treat each of these state tokens as unique. This way, multiple identical calls, e.g. to newIORef are not treated as identical, because they have different state tokens. Ideally, we would inline this after common sub-expression elimination finishes, so that it is costless. {-# INLINE listen #-}
{-# NOINLINE uniqueState #-} listen :: UIO a -> UIO (RemainingWork, a)
uniqueState :: Int# -> State# RealWorld -> State# RealWorld listen (UIO m) = UIO (\s -> let (done, a) = m s in (done, (done, a)))
uniqueState = uniqueState'
{-# NOINLINE uniqueState' #-} {-# INLINE after #-}
uniqueState' :: Int# -> State# RealWorld -> State# RealWorld after :: RemainingWork -> UIO a -> UIO a
uniqueState' _ s = s after w (UIO m) = UIO (\s -> m (w `seq` s))

View File

@ -4,20 +4,21 @@
module UIO.Plugin (plugin) where module UIO.Plugin (plugin) where
import GHC.Plugins import GHC.Plugins
import Data.Generics.Schemes (everywhereM) --import Data.Generics.Schemes (everywhereM)
import Data.Generics.Aliases (mkM) --import Data.Generics.Aliases (mkM)
plugin :: Plugin
plugin = defaultPlugin
{ {- installCoreToDos = install
, -} pluginRecompile = purePlugin
}
{-
isSpecificFunction :: Id -> Bool isSpecificFunction :: Id -> Bool
isSpecificFunction f = case nameModule_maybe (idName f) of isSpecificFunction f = case nameModule_maybe (idName f) of
Just m -> moduleNameString (moduleName m) == "UIO" && getOccString f == "uniqueState" Just m -> moduleNameString (moduleName m) == "UIO" && getOccString f == "uniqueState"
Nothing -> False Nothing -> False
plugin :: Plugin
plugin = defaultPlugin
{ installCoreToDos = install
, pluginRecompile = purePlugin
}
install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo] install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
install _ todos = do install _ todos = do
let myPass = CoreDoPluginPass ("Inline UIO.uniqueState") inlineSpecificFunction let myPass = CoreDoPluginPass ("Inline UIO.uniqueState") inlineSpecificFunction
@ -53,3 +54,4 @@ inlineSpecificFunctionBindM transform (NonRec b e) = do
inlineSpecificFunctionBindM transform (Rec pairs) = do inlineSpecificFunctionBindM transform (Rec pairs) = do
pairs' <- mapM (\(b, e) -> everywhereM (mkM transform) e >>= \e' -> return (b, e')) pairs pairs' <- mapM (\(b, e) -> everywhereM (mkM transform) e >>= \e' -> return (b, e')) pairs
return (Rec pairs') return (Rec pairs')
-}