Skip to content
  • Sebastian Graf's avatar
    DmdAnal: Annotate top-level function bindings with demands (#18894) · 5bd71bfd
    Sebastian Graf authored and Marge Bot's avatar Marge Bot committed
    It's useful to annotate a non-exported top-level function like `g` in
    
    ```hs
    module Lib (h) where
    
    g :: Int -> Int -> (Int,Int)
    g m 1 = (m, 0)
    g m n = (2 * m, 2 `div` n)
    {-# NOINLINE g #-}
    
    h :: Int -> Int
    h 1 = 0
    h m
      | odd m     = snd (g m 2)
      | otherwise = uncurry (+) (g 2 m)
    ```
    
    with its demand `UCU(CS(P(1P(U),SP(U))`, which tells us that whenever `g` was
    called, the second component of the returned pair was evaluated strictly.
    
    Since #18903 we do so for local functions, where we can see all calls.
    For top-level functions, we can assume that all *exported* functions are
    demanded according to `topDmd` and thus get sound demands for
    non-exported top-level functions.
    
    The demand on `g` is crucial information for Nested CPR, which may the
    go on and unbox `g` for the second pair component. That is true even if
    that pair component may diverge, as is the case for the call site `g 13
    0`, which throws a div-by-zero exception.
    
    In `T18894b`, you can even see the new demand annotation enabling us to
    eta-expand a function that we wouldn't be able to eta-expand without
    Call Arity.
    
    We only track bindings of function type in order not to risk huge compile-time
    regressions, see `isInterestingTopLevelFn`.
    
    There was a CoreLint check that rejected strict demand annotations on
    recursive or top-level bindings, which seems completely unjustified.
    All the cases I investigated were fine, so I removed it.
    
    Fixes #18894.
    5bd71bfd