
#8598: IO hack in demand analyzer gets in the way of CPR -------------------------------------+------------------------------------ Reporter: nomeata | Owner: Type: task | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Unknown/Multiple Type of failure: None/Unknown | Difficulty: Unknown Test Case: | Blocked By: Blocking: | Related Tickets: -------------------------------------+------------------------------------ Comment (by nomeata): Also note that the current IO hack is very erratic with regard to IO inside `unsafePerformIO` (including non-IO FFI calls): It only zaps strictness demands if it actually sees the `IO` type. If, in the example above, `isDoubleFinite` were not inlined, but a separate function of type `Double -> IO Bool`, it would behave different. Very unsatisfying. This proposal should eliminate the need for special-casing in `StrAnal` completely, and make the analysis more precise. We could elaborate the `DmdResult` lattice some more (and I include the `Converges` from the nested-cpr-branch here, to show how that goes together). Basically we want the `DmdResult` to keep track of: * whether function may or will diverge. * whether the function may or will exit cleanly (this is new) * if it returns, what is the result. So a first approximation of that would be {{{ #!haskell data CPR = NoCRP | RetCon Int [DmdResult] data TriState = Yes | No | Maybe -- Maybe is top data DmdResult = DmdResult { diverges :: TriState, exits :: TriState, cpr :: CPR } -- Product lattice }}} which is easy to understand and handle, but it has some invalid states, i.e. if it definitely exits or diverges, we do not really want a `CPR` field, and also not both `diverges` and `exists` should be `True` at the same time. A format that captures it more precisely would be {{{ #!haskell data DmdResult = Diverges | Exits | DmdResult { mayExit :: True, mayDiverge :: True, cpr :: CPR} }}} which neatly expresses definite convergence with `DmdResult False False cpr`. I like that. So the idea is now that primitive operations have their `DmdResult` annotation manually (and I guess most of them do not exit). FFI calls either get a conservative default of `DmdResult True True NoCPR` for things in the `IO` monad and `DmdResult False True NoCPR` for the others. Then in the demand analyser no special handling of IO is required any more. Instead, in `bothDmdType`, one has to handle the `mayExit` flag of the right argument and possibly zap strictness, just like we do already for diverging things. And even `IO`-infested code will, as long as it does not call anything with `mayExit = True` (e.g. numerical calculation, working with arrays and `IORef`s), will get good strictness demands inferred. How does that sound? Or is it overkill adding this to the demand types, given that (hopefully) almost everything will have `mayExit = False`? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/8598#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler