Changes between Initial Version and Version 5 of Ticket #1592


Ignore:
Timestamp:
Aug 9, 2007 9:38:02 AM (7 years ago)
Author:
simonpj
Comment:

(Written before I'd seen Tim's correct remarks.) OK this is an interesting one. Here's the smallest program that demonstrates the problem.

foreign import ccall unsafe "stdio.h getchar" getchar :: IO CInt

f56 :: State# RealWorld -> Int -> Int
f56 s v2 = case (unIO getchar s) of
           (# s' , v6  #) ->
              case v2 of I# _ -> f56 s' v2

GHC says this is lazy in v2, which it obviously isn't. Why? Because there's a special hack (introduced after an earlier bug report) in the strictness analyser to account for the fact that a ccall might exit the program. Suppose instead of calling 'getchar' we called 'exit'! Then f56 is not strict in v2 any more.

Here was a larger program that demonstrated the problem:

        do { let len = <expensive> ;
           ; when (...) (exitWith ExitSuccess)
           ; print len }

Suppose exitWith doesn't exit; it loops or returns. Then 'len' is sure to be evaluated, and GHC will evaluate it before the 'when'.

The hack is in the demand analyser, to make it believe that any I/O operation (including getchar!) might exit instead of returning.

OK, so that's the reason you aren't getting proper strictness in your inner loop. What to do about it? It would be easy to revert to the non-hack situation, in which case 'len' might well be evaluated in the program above, even in the program above. To make the program sufficiently lazy you could write

        do { let len = <expensive> ;
           ; when (...) (exitWith ExitSuccess)
           ; lazy (print len) }

Here 'lazy' is a (documented) function that makes its argument appear to be evaluated lazily, so far as the demand analyser is concerned. But this is horribly non-compositional. ANYWHERE you say

        do { a; b; c }

and b might exit, then you should really say 'lazy c'.

One could imagine an analysis for "definitely does not exit". But it only really makes sense for IO-ish things.

In short, it's hard to see a beautiful solution. Does anyone else have ideas?

Simon

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #1592

    • Property Cc chevalier@… added
  • Ticket #1592 – Description

    initial v5  
    1 argument being passed around, but that GHC 6.6.1 isn't unboxing. In 
     1I've got an inner loop that I think I can see is strict in the Int argument being passed around, but that GHC 6.6.1 isn't unboxing. In 
    22the following example both functions take a GHC.Base.Int, which I 
    33think should be an Int#.