From 19dfd6fc5b4c1f09b4aee82874bcb179ed6cd0cc Mon Sep 17 00:00:00 2001 From: "simonpj@microsoft" Date: Mon, 8 May 2006 14:25:07 +0000 Subject: [PATCH] Make unsafePerformIO lazy The stricteness analyser used to have a HACK which ensured that NOINLNE things were not strictness-analysed. The reason was unsafePerformIO. Left to itself, the strictness analyser would discover this strictness for unsafePerformIO: unsafePerformIO: C(U(AV)) But then consider this sub-expression unsafePerformIO (\s -> let r = f x in case writeIORef v r s of (# s1, _ #) -> (# s1, r #) The strictness analyser will now find that r is sure to be eval'd, and may then hoist it out. This makes tests/lib/should_run/memo002 deadlock. Solving this by making all NOINLINE things have no strictness info is overkill. In particular, it's overkill for runST, which is perfectly respectable. Consider f x = runST (return x) This should be strict in x. So the new plan is to define unsafePerformIO using the 'lazy' combinator: unsafePerformIO (IO m) = lazy (case m realWorld# of (# _, r #) -> r) Remember, 'lazy' is a wired-in identity-function Id, of type a->a, which is magically NON-STRICT, and is inlined after strictness analysis. So unsafePerformIO will look non-strict, and that's what we want. --- GHC/IOBase.lhs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/GHC/IOBase.lhs b/GHC/IOBase.lhs index 27f7dab..499899a 100644 --- a/GHC/IOBase.lhs +++ b/GHC/IOBase.lhs @@ -215,7 +215,7 @@ help of 'unsafePerformIO'. So be careful! -} {-# NOINLINE unsafePerformIO #-} unsafePerformIO :: IO a -> a -unsafePerformIO (IO m) = case m realWorld# of (# _, r #) -> r +unsafePerformIO (IO m) = lazy (case m realWorld# of (# _, r #) -> r) -- Why do we NOINLINE unsafePerformIO? See the comment with -- GHC.ST.runST. Essentially the issue is that the IO computation @@ -223,6 +223,22 @@ unsafePerformIO (IO m) = case m realWorld# of (# _, r #) -> r -- not at all. If we let the compiler see the application of the IO -- to realWorld#, it might float out part of the IO. +-- Why is there a call to 'lazy' in unsafePerformIO? +-- If we don't have it, the demand analyser discovers the following strictness +-- for unsafePerformIO: C(U(AV)) +-- But then consider +-- unsafePerformIO (\s -> let r = f x in +-- case writeIORef v r s of (# s1, _ #) -> +-- (# s1, r #) +-- The strictness analyser will find that the binding for r is strict, +-- (becuase of uPIO's strictness sig), and so it'll evaluate it before +-- doing the writeIORef. This actually makes tests/lib/should_run/memo002 +-- get a deadlock! +-- +-- Solution: don't expose the strictness of unsafePerformIO, +-- by hiding it with 'lazy' + + {-| 'unsafeInterleaveIO' allows 'IO' computation to be deferred lazily. When passed a value of type @IO a@, the 'IO' will only be performed -- 1.7.10.4