Andreas Klebinger pushed to branch wip/apk/unfold-dicts at Glasgow Haskell Compiler / GHC
Commits:
-
9201a2cf
by Andreas Klebinger at 2026-09-13T10:53:23+02:00
5 changed files:
- + changelog.d/27802-classop-arg-size
- compiler/GHC/Core/Unfold.hs
- + testsuite/tests/simplCore/should_compile/T27802.hs
- + testsuite/tests/simplCore/should_compile/T27802.stderr
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| 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 |
| ... | ... | @@ -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
|
| 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 |
| 1 | +Inlining done: T27802.applyUnaryFew
|
|
| 2 | +Inlining done: T27802.applyRegularFew |
| ... | ... | @@ -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']) |