Andreas Klebinger pushed to branch wip/apk/unfold-dicts at Glasgow Haskell Compiler / GHC

Commits:

5 changed files:

Changes:

  • changelog.d/27802-classop-arg-size
    1
    +section: compiler
    
    2
    +synopsis: For code like `m x y z` where m is a class method of a unary type class
    
    3
    +  we now account (again) for the size of applying the x/y/z arguments.
    
    4
    +mrs: !16675
    
    5
    +issues: #27802

  • compiler/GHC/Core/Unfold.hs
    ... ... @@ -42,7 +42,7 @@ import GHC.Core.Utils
    42 42
     import GHC.Core.DataCon
    
    43 43
     import GHC.Core.Type
    
    44 44
     import GHC.Core.Class( Class )
    
    45
    -import GHC.Core.Predicate( isUnaryClass )
    
    45
    +import GHC.Core.Predicate( isUnaryClass, isDictId )
    
    46 46
     
    
    47 47
     import GHC.Types.Id
    
    48 48
     import GHC.Types.Literal
    
    ... ... @@ -782,16 +782,26 @@ classOpSize :: UnfoldingOpts -> Class -> [Id] -> [CoreExpr] -> ExprSize
    782 782
     classOpSize _opts _cls _top_args []
    
    783 783
       = sizeZero   -- A non-applied classop
    
    784 784
     classOpSize opts cls top_args (dict_arg:other_val_args)
    
    785
    -  = SizeIs size (arg_discount dict_arg) 0
    
    785
    +  = SizeIs size dict_arg_discount 0
    
    786 786
       where
    
    787
    -    size | isUnaryClass cls = 0    -- See (UCM4) in Note [Unary class magic] in GHC.Core.TyCon
    
    788
    -         | otherwise        = 20 + (10 * length other_val_args)
    
    787
    +    -- See (UCM4) in Note [Unary class magic] in GHC.Core.TyCon
    
    788
    +    op_app_size = if isUnaryClass cls then 0 else 20
    
    789
    +
    
    790
    +    -- Size penalty for applying the extracted class method to it's
    
    791
    +    -- arguments.
    
    792
    +    method_app_size
    
    793
    +      | null other_val_args = 0
    
    794
    +      | otherwise           = callSize (length other_val_args) 0
    
    795
    +
    
    796
    +    size = op_app_size + method_app_size
    
    789 797
     
    
    790 798
         -- If the class op is scrutinising a lambda bound dictionary then
    
    791 799
         -- give it a discount, to encourage the inlining of this function
    
    792
    -    arg_discount (Cast arg _co)                    = arg_discount arg
    
    793
    -    arg_discount (Var dict) | dict `elem` top_args = unitBag (dict, dict_discount)
    
    794
    -    arg_discount _                                 = emptyBag
    
    800
    +    dict_arg_discount = case getIdFromTrivialExpr_maybe dict_arg of
    
    801
    +      Nothing -> emptyBag
    
    802
    +      Just dict
    
    803
    +        | dict `elem` top_args -> unitBag (dict, dict_discount)
    
    804
    +        | otherwise -> emptyBag
    
    795 805
     
    
    796 806
         -- If we have (class-op d arg1 .. argn) then it's super-good to inline
    
    797 807
         -- to expose `d`; not only can we do the dictionary selection
    
    ... ... @@ -802,6 +812,12 @@ classOpSize opts cls top_args (dict_arg:other_val_args)
    802 812
           | null other_val_args = unfoldingDictDiscount opts
    
    803 813
           | otherwise           = unfoldingDictDiscount opts + unfoldingFunAppDiscount opts
    
    804 814
     
    
    815
    +    isTopArgDict expr
    
    816
    +      | Just v <- getIdFromTrivialExpr_maybe expr
    
    817
    +      = v `elem` top_args
    
    818
    +      | otherwise
    
    819
    +      = False
    
    820
    +
    
    805 821
     -- | The size of a function call
    
    806 822
     callSize
    
    807 823
      :: Int  -- ^ number of value args
    

  • testsuite/tests/simplCore/should_compile/T27802.hs
    1
    +-- See #27802.
    
    2
    +-- The whole idea is that if we have `(op d) x1 x2 ... xn` even if the dictionary
    
    3
    +-- is known we have to account for the size resulting from applying `x1 .. xn`.
    
    4
    +
    
    5
    +-- For this we construct functions containing the pattern `(op d) x .. xn`.
    
    6
    +-- with: `d` being either a unary or regular class dictionary, and n either being
    
    7
    +-- small enough for the function containing the pattern to inline, or large enough
    
    8
    +-- that we surely shouldn't inline the containing function.
    
    9
    +
    
    10
    +-- We test this by grepping -ddump-inlinings
    
    11
    +
    
    12
    +module T27802
    
    13
    +  -- We export the functions to avoid unconditional ininlining to fire.
    
    14
    +  ( applyUnaryFew, applyRegularFew, applyUnaryMany, applyRegularMany
    
    15
    +  , useUnaryFew, useRegularFew, useUnaryMany, useRegularMany
    
    16
    +  ) where
    
    17
    +
    
    18
    +
    
    19
    +class Unary a where
    
    20
    +  m_unary :: (a -> b) -> b
    
    21
    +
    
    22
    +-- The super dict makes it a non-unary type class.
    
    23
    +class Eq a => Regular a where
    
    24
    +  m_regular :: (a -> b) -> b
    
    25
    +
    
    26
    +instance Unary Int where
    
    27
    +  m_unary f = f 42
    
    28
    +
    
    29
    +instance Regular Int where
    
    30
    +  m_regular f = f 43
    
    31
    +
    
    32
    +-- We instantiate `b` with these function types.
    
    33
    +type Args4 = Int -> Int -> Int -> Int -> Int
    
    34
    +
    
    35
    +type Args50 = Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int ->
    
    36
    +              Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int ->
    
    37
    +              Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int ->
    
    38
    +              Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int ->
    
    39
    +              Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int ->
    
    40
    +              Int
    
    41
    +
    
    42
    +-- apply* functions are the ones containing the `op d x1 .. xn` pattern.
    
    43
    +applyUnaryFew :: Unary a => (a -> Args4) -> Int
    
    44
    +applyUnaryFew g = m_unary g 1 2 3 4
    
    45
    +
    
    46
    +applyRegularFew :: Regular a => (a -> Args4) -> Int
    
    47
    +applyRegularFew g = m_regular g 1 2 3 4
    
    48
    +
    
    49
    +applyUnaryMany :: Unary a => (a -> Args50) -> Int
    
    50
    +applyUnaryMany g = m_unary g
    
    51
    +      1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    
    52
    +      1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    
    53
    +      1 1 1 1 1 1 1 1 1 1
    
    54
    +
    
    55
    +applyRegularMany :: Regular a => (a -> Args50) -> Int
    
    56
    +applyRegularMany g = m_regular g
    
    57
    +      1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    
    58
    +      1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    
    59
    +      1 1 1 1 1 1 1 1 1 1
    
    60
    +
    
    61
    +-- The call sites, where the dictionary is known. And we check if we should inline the
    
    62
    +-- apply* functions.
    
    63
    +useUnaryFew, useRegularFew :: (Int -> Args4) -> Int
    
    64
    +useUnaryFew   g = applyUnaryFew g
    
    65
    +useRegularFew g = applyRegularFew g
    
    66
    +
    
    67
    +useUnaryMany, useRegularMany :: (Int -> Args50) -> Int
    
    68
    +useUnaryMany   g = applyUnaryMany g
    
    69
    +useRegularMany g = applyRegularMany g

  • testsuite/tests/simplCore/should_compile/T27802.stderr
    1
    +Inlining done: T27802.applyUnaryFew
    
    2
    +Inlining done: T27802.applyRegularFew

  • testsuite/tests/simplCore/should_compile/all.T
    ... ... @@ -618,3 +618,8 @@ test('T27556', [only_ways('ghci'), extra_hc_opts('-O -fno-unoptimized-core-for-i
    618 618
     test('T27749',
    
    619 619
          multiline_grep_errmsg(r'ordering\n.*\n(\s+src<[^>]*>\n)+'),
    
    620 620
          compile, ['-O -g3 -ddump-simpl -dsuppress-uniques'])
    
    621
    +
    
    622
    +# Check that we account for the size of arguments x1..xn when calculating size
    
    623
    +# of expressions like `op d x1 ... xn`  See #27802.
    
    624
    +test('T27802', [grep_errmsg(r'Inlining done: T27802\.apply')], compile,
    
    625
    +     ['-O -fno-specialise -ddump-inlinings -dsuppress-uniques'])