
#13227: Loss of full laziness in mapFB -------------------------------------+------------------------------------- Reporter: simonpj | Owner: nomeata Type: bug | Status: patch Priority: normal | Milestone: Component: Compiler | Version: 8.0.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D3067 Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Joachim, I have resurrected from my hind-brain what was, perhaps, the original issue. Consider something like {{{ recover (f (\x. blah)) bloo }}} where `recover` :: (Exception -> IO a) -> IO a -> IO a` is some kind of exception handling. And `f` is something like {{{ f :: (Exception -> a) -> Exception -> IO a f g exn = return (g exn) }}} Now, is the `\x` one-shot or not? Well, if `f` is applied to three args (the exception and the state token, then it'll call `g` exactly once`, so yes the `\x` will be one shot. And hence there is no point floating a thunk out of `blah`. Indeed doing so is positively harmful because it adds allocation before calling `recover`, and that allocation is only used on the cold (exception-handling) path. And yet with your change, we won't recognise that, because the call to `f` is not syntactically saturated. Moreover, the occurrence analyser is the '''only''' place where one-shot info is added to lambdas. See your patch in #11770. (I'd like this fact to be more clearly called out... I had to re-discover it today. It is stated in `Note [Use one-shot information]` but not very loudly.) So we might ask 1. Does this matter? Does it actually occur in practice? Your measurements suggest not. 2. Would be easy to fix? I think it might be. Concerning (2), look at the call to `argsOneShots` in `occAnalApp`. We pass `n_val_args` to `argsOneShots`. Consider something like {{{ f (g (\y.e)) }}} and suppose * `f` has a strictness sigature like `C1(L)`, saying that it calls its argument at most once * `g` has a strictness signature like `C1(L)L`, saying that when applied to two args it calls its first arg at most once. Then when we are doing `occAnalApp` for `g (\y.e)` we will have `[NoOneShot]` in the `occ_one_shots` field of `OccEnv`... that comes from `f` which guarantees to call its arg once. So I think we can just add the length of `occ_one_shots` to `n_val_args` before passing to `argsOneShots`. And that is so easy it'd be worth doing. Here's a concrete test case {{{ w :: (Int -> a) -> a w g = g 1 {-# NOINLINE w #-} f :: (Int -> Bool) -> a -> [Bool] f g _ = [g 1, True, False] {-# NOINLINE f #-} h xs = w (f (\y -> null (reverse xs))) }}} You'll see that `null (reverse xs)` is floated out -- and it should not be. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13227#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler