Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

Rethinking touch# primop
NOTE: the discussion has been moved on the wiki: https://gitlab.haskell.org/ghc/ghc/wikis/proposal/with-combinator ---------------- All GHC has to control object lifetime is: ```haskell touch# :: a -> State# RealWorld -> State# RealWorld ``` This is often useful when passing GC-managed objects (typically pinned byte arrays) to foreign functions through foreign data structures. For instance, the POSIX vectored I/O interface exposes this interface: ```c struct iovec { void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; ssize_t readv(int fd, struct iovec iovs[iov_count], int iov_count); ``` We may, for instance, wish to populate `iov_base` with a pinned `ByteArray`: ```haskell data IoVector = IoVector { iov_base :: Addr, iov_len :: CSize } instance Storable IoVector foreign import ccall "readv" c_readv :: CInt -> Ptr IoVector -> CInt -> IO CSize doRead :: Fd -> [IoVector] -> IO CSize doRead fd vecs = withArray vecs $ \vecsPtr -> readv fd vecsPtr (length vecs) read :: Fd -- ^ file descriptor from which to read -> CSize -- ^ length to read -> IO ByteArray read fd len = do arr <- newPinnedByteArray 42 result <- doRead fd [IoVector (byteArrayContents arr) len] touch# arr ``` However, this is dangerously susceptible to being dropped by the simplifier. For instance, if `doRead` were change such that the simplifier concluded that it will fail to return (e.g. because it is of the form `forever ...`), then it is tempted to drop the continuation containing `touch#`. This results in the garbage collector inappropriately freeing `arr`, resulting in catastrophe. It caused #14346 (`allocaBytes` and `allocaBytesAligned`) and #17746 (`withForeignPtr`). Mitigation ---------- A way to mitigate the issue is to ensure that functions using `touch#` can't be simplified by using ``NOINLINE`` pragmas. * #14346 has been fixed this way in 8.2 and 8.6 by adding a NOINLINE pragma to `allocaBytes[Aligned]` functions (cf 56590db07a776ce81eb89d4a4d86bd0f953fb44e). * #17746: adding a NOINLINE pragma to `withForeignPtr` fixes the issue but the price in performance is very high. An alternative is to rewrite as follows so that only the second field of the `ForeignPtr` is allocated and kept alive (instead of the whole `ForeignPtr`). Sadly it still has a huge impact on performance metrics (`withForeignPtr` is used a lot, especially in `ByteString` implementation). ```haskell withForeignPtr fo io = let !(ForeignPtr addr r) = fo IO fio = io (Ptr addr) in IO $ \s -> with# r fio s {-# NOINLINE with# #-} with# :: a -> (State# RealWorld -> (# State# RealWorld, b #)) -> State# RealWorld -> (# State# RealWorld, b #) with# a m s = case m s of (# s', r #) -> (# touch# a s', r #) ``` Fixing the issue properly with a new `with#` primop? ---------------------------------------------------- To fix the issue it has been proposed (#14375, !2566) to make the `with#` function above a primop of this form: ```haskell with# :: a -> (State# s -> (# State s, r #)) -> State# s -> (# State# s, r #) ``` which evaluates the `r`, ensuring that the `a` remains alive throughout evaluation. If we rewrite the `test` example above with `with#`, we get: ```haskell test :: IO () test = do arr <- newPinnedByteArray 42 with# arr (unIO (doSomething (byteArrayContents arr))) ``` This construction the compiler can't mangle as there is no continuation to drop. The naive implementation of !2566 is suboptimal as it allocates a closure for the continuation. This is strictly worse than the status quo provided by `touch#` which is a no-op that tricks the codeGen into keeping the value alive in a register (or spilled on the stack). #16098 proposes a special code generation for `with#`, pushing a new kind of stack frame that keeps a value alive for its scope. !2567 is an implementation of this idea.
issue