[Git][ghc/ghc][wip/andreask/expose-overloaded-unfoldings] Tidy: Add flag to expose unfoldings if they take dictionary arguments.

Andreas Klebinger (@AndreasK) gitlab at gitlab.haskell.org
Sun May 12 01:31:09 UTC 2024



Andreas Klebinger pushed to branch wip/andreask/expose-overloaded-unfoldings at Glasgow Haskell Compiler / GHC


Commits:
1e378a02 by Andreas Klebinger at 2024-05-12T03:15:28+02:00
Tidy: Add flag to expose unfoldings if they take dictionary arguments.

Add the flag `-fexpose-overloaded-unfoldings` to be able to control this
behaviour.

For ghc's boot libraries file size grew by less than 1% when it was
enabled. However I refrained from enabling it by default for now.

I've also added a section on specialization more broadly to the users
guide.

-------------------------
Metric Decrease:
    MultiLayerModulesTH_OneShot
Metric Increase:
    T12425
    T13386
    hard_hole_fits
-------------------------

- - - - -


11 changed files:

- compiler/GHC/Driver/Config/Tidy.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Iface/Tidy.hs
- docs/users_guide/9.12.1-notes.rst
- docs/users_guide/hints.rst
- docs/users_guide/using-optimisation.rst
- + testsuite/tests/simplCore/should_compile/ExposeOverloaded.hs
- + testsuite/tests/simplCore/should_compile/ExposeOverloaded.stderr
- testsuite/tests/simplCore/should_compile/T16038/Makefile
- testsuite/tests/simplCore/should_compile/all.T


Changes:

=====================================
compiler/GHC/Driver/Config/Tidy.hs
=====================================
@@ -36,6 +36,7 @@ initTidyOpts hsc_env = do
     , opt_unfolding_opts    = unfoldingOpts dflags
     , opt_expose_unfoldings = if | gopt Opt_OmitInterfacePragmas dflags -> ExposeNone
                                  | gopt Opt_ExposeAllUnfoldings dflags  -> ExposeAll
+                                 | gopt Opt_ExposeOverloadedUnfoldings dflags  -> ExposeOverloaded
                                  | otherwise                            -> ExposeSome
     , opt_expose_rules      = not (gopt Opt_OmitInterfacePragmas dflags)
     , opt_trim_ids          = gopt Opt_OmitInterfacePragmas dflags


=====================================
compiler/GHC/Driver/Flags.hs
=====================================
@@ -328,6 +328,7 @@ data GeneralFlag
    | Opt_IgnoreInterfacePragmas
    | Opt_OmitInterfacePragmas
    | Opt_ExposeAllUnfoldings
+   | Opt_ExposeOverloadedUnfoldings
    | Opt_KeepAutoRules -- ^Keep auto-generated rules even if they seem to have become useless
    | Opt_WriteInterface -- forces .hi files to be written even with -fno-code
    | Opt_WriteHie -- generate .hie files
@@ -581,6 +582,7 @@ codeGenFlags = EnumSet.fromList
 
      -- Flags that affect generated code
    , Opt_ExposeAllUnfoldings
+   , Opt_ExposeOverloadedUnfoldings
    , Opt_NoTypeableBinds
    , Opt_Haddock
 


=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -2442,6 +2442,7 @@ fFlagsDeps = [
   flagSpec "error-spans"                      Opt_ErrorSpans,
   flagSpec "excess-precision"                 Opt_ExcessPrecision,
   flagSpec "expose-all-unfoldings"            Opt_ExposeAllUnfoldings,
+  flagSpec "expose-overloaded-unfoldings"     Opt_ExposeOverloadedUnfoldings,
   flagSpec "keep-auto-rules"                  Opt_KeepAutoRules,
   flagSpec "expose-internal-symbols"          Opt_ExposeInternalSymbols,
   flagSpec "external-dynamic-refs"            Opt_ExternalDynamicRefs,


=====================================
compiler/GHC/Iface/Tidy.hs
=====================================
@@ -57,7 +57,7 @@ import GHC.Core.Tidy
 import GHC.Core.Seq         ( seqBinds )
 import GHC.Core.Opt.Arity   ( exprArity, typeArity, exprBotStrictness_maybe )
 import GHC.Core.InstEnv
-import GHC.Core.Type     ( Type, tidyTopType )
+import GHC.Core.Type
 import GHC.Core.DataCon
 import GHC.Core.TyCon
 import GHC.Core.Class
@@ -87,6 +87,7 @@ import GHC.Types.Name.Cache
 import GHC.Types.Avail
 import GHC.Types.Tickish
 import GHC.Types.TypeEnv
+import GHC.Tc.Utils.TcType (tcSplitNestedSigmaTys)
 
 import GHC.Unit.Module
 import GHC.Unit.Module.ModGuts
@@ -367,7 +368,9 @@ three places this is actioned:
 
 data UnfoldingExposure
   = ExposeNone -- ^ Don't expose unfoldings
-  | ExposeSome -- ^ Only expose required unfoldings
+  | ExposeSome -- ^ Expose mandatory unfoldings and those meeting inlining thresholds.
+  | ExposeOverloaded -- ^ Expose unfoldings useful for inlinings and those which
+                     -- which might be specialised. See Note [Exposing overloaded functions]
   | ExposeAll  -- ^ Expose all unfoldings
   deriving (Show,Eq,Ord)
 
@@ -793,6 +796,10 @@ addExternal opts id
     show_unfold    = show_unfolding unfolding
     never_active   = isNeverActive (inlinePragmaActivation (inlinePragInfo idinfo))
     loop_breaker   = isStrongLoopBreaker (occInfo idinfo)
+    -- bottoming_fn: don't inline bottoming functions, unless the
+    -- RHS is very small or trivial (UnfWhen), in which case we
+    -- may as well do so. For example, a cast might cancel with
+    -- the call site.
     bottoming_fn   = isDeadEndSig (dmdSigInfo idinfo)
 
         -- Stuff to do with the Id's unfolding
@@ -800,30 +807,87 @@ addExternal opts id
         -- In GHCi the unfolding is used by importers
 
     show_unfolding (CoreUnfolding { uf_src = src, uf_guidance = guidance })
-       = opt_expose_unfoldings opts == ExposeAll
+       = stable || profitable || explicitly_requested
+       where
+        -- Always expose things whose
+        -- source is an inline rule
+        stable = isStableSource src
+        -- Good for perf as it might inline
+        profitable
+          | never_active = False
+          | loop_breaker = False
+          | otherwise =
+              case guidance of
+                UnfWhen {}  -> True
+                UnfIfGoodArgs {} -> not bottoming_fn
+                UnfNever -> False
+        -- Requested by the user through a flag.
+        explicitly_requested =
+          case opt_expose_unfoldings opts of
             -- 'ExposeAll' says to expose all
             -- unfoldings willy-nilly
-
-       || isStableSource src     -- Always expose things whose
-                                 -- source is an inline rule
-
-       || not dont_inline
-       where
-         dont_inline
-            | never_active = True   -- Will never inline
-            | loop_breaker = True   -- Ditto
-            | otherwise    = case guidance of
-                                UnfWhen {}       -> False
-                                UnfIfGoodArgs {} -> bottoming_fn
-                                UnfNever {}      -> True
-         -- bottoming_fn: don't inline bottoming functions, unless the
-         -- RHS is very small or trivial (UnfWhen), in which case we
-         -- may as well do so For example, a cast might cancel with
-         -- the call site.
+            ExposeAll -> True
+            -- Overloaded functions like @foo :: Bar a => ...@
+            -- See Note [Exposing overloaded functions]
+            ExposeOverloaded ->
+              not bottoming_fn && isOverloaded id
+            ExposeSome -> False
+            ExposeNone -> False
 
     show_unfolding (DFunUnfolding {}) = True
     show_unfolding _                  = False
 
+isOverloaded :: Id -> Bool
+isOverloaded fn =
+  let fun_type = idType fn
+      -- TODO: The specialiser currently doesn't handle newtypes of the
+      -- form `newtype T x = T (C x => x)` well. So we don't bother
+      -- looking through newtypes for constraints.
+      -- (Newtypes are opaque to tcSplitNestedSigmaTys)
+      -- If the specialiser ever starts looking through newtypes properly
+      -- we might want to use a version of tcSplitNestedSigmaTys that looks
+      -- through newtypes.
+      (_ty_vars, constraints, _ty) = tcSplitNestedSigmaTys fun_type
+      -- NB: This will consider functions with only equality constraints overloaded.
+      -- While these sorts of constraints aren't currently useful for specialization
+      -- it's simpler to just include them.
+  in not . null $ constraints
+
+{- Note [Exposing overloaded functions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+See also #13090 and #22942.
+
+The basic idea is that exposing only overloaded function is reasonably cheap
+but allows the specializer to fire more often as unfoldings for overloaded
+functions will generally be available. So we make the unfoldings of overloaded
+functions available when `-fexpose-overloaded-unfoldings is enabled.
+
+We use `tcSplitNestedSigmaTys` to see the constraints deep within in types like:
+  f :: Int -> forall a. Eq a => blah
+
+We could simply use isClassPred to check if any of the constraints responds to
+a class dictionary, but that would miss (perhaps obscure) opportunities
+like the one in the program below:
+
+    type family C a where
+      C Int = Eq Int
+      C Bool = Ord Bool
+
+
+    bar :: C a => a -> a -> Bool
+    bar = undefined
+    {-# SPECIALIZE bar :: Int -> Int -> Bool #-}
+
+GHC will specialize `bar` properly. However `C a =>` isn't recognized as class
+predicate since it's a type family in the definition. To ensure it's exposed
+anyway we allow for some false positives and simply expose all functions which
+have a constraint. This means we might expose more unhelpful unfoldings. But
+it seems like the better choice.
+
+Currently this option is off by default and has to be enabled manually. But
+we might change this in the future.
+-}
+
 {-
 ************************************************************************
 *                                                                      *


=====================================
docs/users_guide/9.12.1-notes.rst
=====================================
@@ -44,6 +44,7 @@ and the migration guide.
 
   The tradeoff is that calling `whoCreated` on top level value definitions like `foo` will be less informative.
 
+- A new flag ``-fexpose-overloaded-unfoldings`` has been added providing a lightweight alternative to ``-fexpose-all-unfoldings``.
 
 GHCi
 ~~~~


=====================================
docs/users_guide/hints.rst
=====================================
@@ -366,6 +366,8 @@ extreme cases making it impossible to compile certain code.
 For this reason GHC offers various ways to tune inlining
 behaviour.
 
+.. _inlining-unfolding-creation:
+
 Unfolding creation
 ~~~~~~~~~~~~~~~~~~
 
@@ -374,8 +376,9 @@ GHC requires the functions unfolding. The following flags can
 be used to control unfolding creation. Making their creation more
 or less likely:
 
-* :ghc-flag:`-fexpose-all-unfoldings`
 * :ghc-flag:`-funfolding-creation-threshold=⟨n⟩`
+* :ghc-flag:`-fexpose-overloaded-unfoldings`
+* :ghc-flag:`-fexpose-all-unfoldings`
 
 Inlining decisions
 ~~~~~~~~~~~~~~~~~~
@@ -414,6 +417,96 @@ There are also flags specific to the inlining of generics:
 * :ghc-flag:`-finline-generics`
 * :ghc-flag:`-finline-generics-aggressively`
 
+.. _control-specialization:
+
+Controlling specialization
+--------------------------------------------
+
+.. index::
+    single: specialize-pragma, controlling, specialization
+    single: unfolding, controlling
+
+GHC has the ability to optimize polymorphic code for specific type class instances
+at the use site. We call this specialisation and it's enabled through :ghc-flag:`-fspecialise`
+which is enabled by default at `-O1` or higher.
+
+GHC does this by creating a copy of the overloaded function, optimizing this copy
+for a given type class instance. Calls to the overloaded function using a statically
+known typeclass we created a specialization for will then be replaced by a call
+to the specialized version of the function.
+
+This can often be crucial to avoid overhead at runtime. However since this
+involves potentially making many copies of overloaded functions GHC doesn't
+always apply this optimization by default even in cases where it could do so.
+
+For GHC to be able to specialise, at a miminum the instance it specializes for
+must be known and the overloaded functions unfolding must be available.
+
+Commonly used flag/pragma combinations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For applications which aren't very compute heavy the defaults are often good enough
+as they try to strike a reasonable balance between compile time and runtime.
+
+For libraries, if exported functions would benefit significantly from specialization,
+it's recommended to enable :ghc-flag:`-fexpose-overloaded-unfoldings` or manually
+attach INLINEABLE pragmas to performance relevant functions.
+This will ensure downstream users can specialize any overloaded functions exposed
+by the library if it's beneficial.
+
+If there are key parts of an application which rely on specialization for performance
+using `SPECIALIZE` pragmas in combination with either :ghc-flag:`-fexpose-overloaded-unfoldings`
+or `INLINEABLE` on key overloaded functions should allow for these functions to
+specialize without affecting overall compile times too much.
+
+For compute heavy code reliant on elimination of as much overhead as possible it's
+recommended to use a combination of :ghc-flag:`-fspecialise-aggressively` and
+:ghc-flag:`-fexpose-overloaded-unfoldings` or :ghc-flag:`-fexpose-all-unfoldings`.
+However this comes at a big cost to compile time.
+
+Unfolding availabiliy
+~~~~~~~~~~~~~~~~~~~~~
+
+Unfolding availabiliy is primarily determined by :ref:`these flags <inlining-unfolding-creation>`.
+
+Of particular interest for specialization are:
+
+* :ghc-flag:`-fexpose-all-unfoldings`
+* :ghc-flag:`-fexpose-overloaded-unfoldings`
+
+The former making *all* unfoldings available, potentially at high compile time cost.
+The later only makes available the functions that are overloaded. It's generally
+better to use :ghc-flag:`-fexpose-overloaded-unfoldings` over :ghc-flag:`-fexpose-all-unfoldings`
+when the goal is to ensure specializations.
+
+When does GHC generate specializations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Functions get considered for specialization either implicitly when GHC sees a use
+of an overloaded function used with concrete typeclass instances or explicitly
+when a user requests it through pragmas, see :ref:`specialize-pragma` and :ref:`specialize-instance-pragma`.
+
+The specializer then checks a number of conditions *in order* to decide weither or not
+specialization should happen. Below is a best effort of the list of conditions GHC checks
+currently.
+
+* If any of the type class instances have type arguments and :ghc-flag:`-fpolymorphic-specialisation`
+  is not enabled (off by default) the function **won't** be specialised, otherwise
+* if the specialization was requested through a pragma GHC **will** try to create a specialization, otherwise
+* if the function is imported and:
+  + if the unfolding is not available the function **can't** be specialized, otherwise
+  + if :ghc-flag:`-fcross-module-specialise` is not enabled (enabled by `-O`) the function **won't** be specialised, otherwise
+  + if the flag is enabled, and the function has no INLINABLE/INLINE pragma it **won't** be specialised, otherwise
+* if :ghc-flag:`-fspecialise-aggressively` is enabled GHC **will** try to create a specialization, otherwise
+* if the overloaded function is defined in the current module, and all type class instances
+  are statically known it **will** be specialized, otherwise
+* the function **won't** be specialized.
+
+Note that there are some cases in which GHC will try to specialize a function and fail.
+For example if a functions has an OPAQUE pragma or the unfolding is not available.
+
+Once a function is specialized GHC will create a rule, similar to these created by `RULE` pragmas
+which will fire at call sites involving known instances, replacing calls to the overloaded
+function with calls to the specialized function when possible.
 
 .. _hints-os-memory:
 


=====================================
docs/users_guide/using-optimisation.rst
=====================================
@@ -519,9 +519,45 @@ as such you shouldn't need to set any of them explicitly. A flag
 
     :default: off
 
-    An experimental flag to expose all unfoldings, even for very large
-    or recursive functions. This allows for all functions to be inlined
-    while usually GHC would avoid inlining larger functions.
+    A flag to expose all unfoldings, even for very large or recursive functions.
+
+    However GHC will still use the usual heuristics to make inlining
+    and specialization choices. This means further measures are needed to
+    get benefits at use sites. Usually this involves one of:
+
+    * :ghc-flag:`-fspecialise-aggressively` to force as much specialization
+      as possible.
+    * `{-# SPECIALIZE #-}` pragmas to ensure specialization to specific types.
+    * Use of the magic `inline` function to force inlining.
+
+.. ghc-flag:: -fexpose-overloaded-unfoldings
+    :shortdesc: Expose unfoldings carrying constraints, even for very large or recursive functions.
+    :type: dynamic
+    :reverse: -fno-expose-overloaded-unfoldings
+    :category:
+
+    :default: off
+
+    This experimental flag is a slightly less heavy weight alternative
+    to :ghc-flag:`-fexpose-all-unfoldings`.
+
+    Instead of exposing all functions it only aims at exposing constrained functions.
+    This is intended to be used for cases where specialization is considered
+    crucial but :ghc-flag:`-fexpose-all-unfoldings` imposes too much compile
+    time cost.
+
+    Currently this won't expose unfoldings where a type class is hidden under a
+    newtype. That is for cases like: ::
+
+        newtype NT a = NT (Integral a => a)
+
+        foo :: NT a -> T1 -> TR
+
+    GHC won't recognise `foo` as specialisable and won't expose the unfolding
+    even with :ghc-flag:`-fexpose-overloaded-unfoldings` enabled.
+
+    All the other caveats about :ghc-flag:`-fexpose-overloaded-unfoldings`
+    still apply, so please see there for more details.
 
 .. ghc-flag:: -ffloat-in
     :shortdesc: Turn on the float-in transformation. Implied by :ghc-flag:`-O`.
@@ -1120,13 +1156,14 @@ as such you shouldn't need to set any of them explicitly. A flag
 
     :default: off
 
-    By default only type class methods and methods marked ``INLINABLE`` or
-    ``INLINE`` are specialised. This flag will specialise any overloaded function
-    regardless of size if its unfolding is available. This flag is not
-    included in any optimisation level as it can massively increase code
-    size. It can be used in conjunction with :ghc-flag:`-fexpose-all-unfoldings`
-    if you want to ensure all calls are specialised.
+    This flag controls the specialisation of *imported* functions only.  By default, an imported function
+    is only specialised if it is marked ``INLINEABLE`` or ``INLINE``.
+    But with :ghc-flag:`-fspecialise-aggressively`, an imported function is specialised
+    if its unfolding is available in the interface file.  (Use :ghc-flag:`-fexpose-all-unfoldings`
+    or :ghc-flag:`-fexpose-overloaded-unfoldings` to ensure that the unfolding is put into the interface file.)
 
+    :ghc-flag:`-fspecialise-aggressively` is not included in any optimisation level
+    as it can massively increase code size.
 
 .. ghc-flag:: -fcross-module-specialise
     :shortdesc: Turn on specialisation of overloaded functions imported from


=====================================
testsuite/tests/simplCore/should_compile/ExposeOverloaded.hs
=====================================
@@ -0,0 +1,43 @@
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -O -fno-worker-wrapper -funfolding-creation-threshold=50 #-}
+{-# OPTIONS_GHC -dno-typeable-binds -fexpose-overloaded-unfoldings #-}
+
+module ExposeOverloaded where
+
+-- Will get an unfolding because of the Functor
+foo :: Functor f => Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (f a))))))
+    -> (a -> b)
+    -> Maybe (Maybe (Maybe (Maybe (f b))))
+foo (Just (Just (Just (Just (Just (Just x)))))) f = Just $ Just $ Just $ Just $ fmap f x
+foo _ _ = Nothing
+
+type family C a where
+  C Int = Eq Int
+  C Bool = Ord Bool
+
+-- The Enum constraint should cause bars unfolding to be exposed.
+bar :: (C a, Enum a) => a -> a -> Bool
+bar a b = fromEnum (succ a) > fromEnum (pred . pred . pred . pred . pred $ b)
+
+-- But even if there is just *a* constraint that's not obviously a class the
+-- unfolding should be expose.
+fam :: (C a) => a -> Char -> Int -> (Bool, a)
+fam c a b = (fromEnum (succ a) > fromEnum (pred . pred . pred . pred . pred $ b), c)
+
+-- While the constraint itself is useless to the specialiser, this still gets exposed
+-- by -fexpose-overloaded-unfoldings since we check for the presence of a constraint
+-- and not it's usefulness.
+{-# NOINLINE eq_constraint #-}
+eq_constraint :: (a~b) => a -> b -> (a,b)
+eq_constraint a b = (a,b)
+
+newtype F t a = F {unF :: (Functor t => t a) }
+
+-- Will get NO unfolding currently since the class dictionary is hidden under the newtype.
+-- We might fix this eventually. But since the specializer doesn't handle this well
+-- this isn't important yet.
+baz :: Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (F t a))))))
+    -> (a -> b)
+    -> Maybe (Maybe (Maybe (Maybe (F t b))))
+baz (Just (Just (Just (Just (Just (Just (x))))))) f = Just $ Just $ Just $ Just $ F $ fmap f (unF x)
+baz _ _ = Nothing
\ No newline at end of file


=====================================
testsuite/tests/simplCore/should_compile/ExposeOverloaded.stderr
=====================================
@@ -0,0 +1,254 @@
+
+==================== Tidy Core ====================
+Result size of Tidy Core
+  = {terms: 186, types: 410, coercions: 10, joins: 0/0}
+
+-- RHS size: {terms: 6, types: 8, coercions: 3, joins: 0/0}
+unF :: forall (t :: * -> *) a. F t a -> Functor t => t a
+[GblId[[RecSel]],
+ Arity=2,
+ Str=<1C(1,L)><L>,
+ Unf=Unf{Src=<vanilla>, TopLvl=True,
+         Value=True, ConLike=True, WorkFree=True, Expandable=True,
+         Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=True)}]
+unF
+  = \ (@(t_aPM :: * -> *))
+      (@a_aPN)
+      (ds_d14Y :: F t_aPM a_aPN)
+      ($dFunctor_aPP :: Functor t_aPM) ->
+      (ds_d14Y
+       `cast` (ExposeOverloaded.N:F[0] <t_aPM>_N <a_aPN>_N
+               :: F t_aPM a_aPN ~R# (Functor t_aPM => t_aPM a_aPN)))
+        $dFunctor_aPP
+
+-- RHS size: {terms: 44, types: 123, coercions: 0, joins: 0/0}
+foo
+  :: forall (f :: * -> *) a b.
+     Functor f =>
+     Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (f a))))))
+     -> (a -> b) -> Maybe (Maybe (Maybe (Maybe (f b))))
+[GblId,
+ Arity=3,
+ Str=<MP(1C(1,C(1,L)),A)><1L><L>,
+ Unf=Unf{Src=<vanilla>, TopLvl=True,
+         Value=True, ConLike=True, WorkFree=True, Expandable=True,
+         Guidance=NEVER}]
+foo
+  = \ (@(f_a11y :: * -> *))
+      (@a_a11z)
+      (@b_a11A)
+      ($dFunctor_a11B :: Functor f_a11y)
+      (ds_d14D
+         :: Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (f_a11y a_a11z)))))))
+      (f1_aON :: a_a11z -> b_a11A) ->
+      case ds_d14D of {
+        Nothing ->
+          GHC.Internal.Maybe.Nothing
+            @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+        Just ds1_d14P ->
+          case ds1_d14P of {
+            Nothing ->
+              GHC.Internal.Maybe.Nothing
+                @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+            Just ds2_d14Q ->
+              case ds2_d14Q of {
+                Nothing ->
+                  GHC.Internal.Maybe.Nothing
+                    @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+                Just ds3_d14R ->
+                  case ds3_d14R of {
+                    Nothing ->
+                      GHC.Internal.Maybe.Nothing
+                        @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+                    Just ds4_d14S ->
+                      case ds4_d14S of {
+                        Nothing ->
+                          GHC.Internal.Maybe.Nothing
+                            @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+                        Just ds5_d14T ->
+                          case ds5_d14T of {
+                            Nothing ->
+                              GHC.Internal.Maybe.Nothing
+                                @(Maybe (Maybe (Maybe (f_a11y b_a11A))));
+                            Just x_aOM ->
+                              GHC.Internal.Maybe.Just
+                                @(Maybe (Maybe (Maybe (f_a11y b_a11A))))
+                                (GHC.Internal.Maybe.Just
+                                   @(Maybe (Maybe (f_a11y b_a11A)))
+                                   (GHC.Internal.Maybe.Just
+                                      @(Maybe (f_a11y b_a11A))
+                                      (GHC.Internal.Maybe.Just
+                                         @(f_a11y b_a11A)
+                                         (fmap
+                                            @f_a11y $dFunctor_a11B @a_a11z @b_a11A f1_aON x_aOM))))
+                          }
+                      }
+                  }
+              }
+          }
+      }
+
+-- RHS size: {terms: 31, types: 20, coercions: 0, joins: 0/0}
+bar :: forall a. (C a, Enum a) => a -> a -> Bool
+[GblId,
+ Arity=4,
+ Str=<A><SP(MC(1,L),LC(S,L),A,SC(S,L),A,A,A,A)><L><L>,
+ Unf=Unf{Src=<vanilla>, TopLvl=True,
+         Value=True, ConLike=True, WorkFree=True, Expandable=True,
+         Guidance=NEVER}]
+bar
+  = \ (@a_a10F)
+      _ [Occ=Dead]
+      ($dEnum_a10H :: Enum a_a10F)
+      (a1_aOV :: a_a10F)
+      (b_aOW :: a_a10F) ->
+      case fromEnum @a_a10F $dEnum_a10H (succ @a_a10F $dEnum_a10H a1_aOV)
+      of
+      { GHC.Types.I# x_a15W ->
+      case fromEnum
+             @a_a10F
+             $dEnum_a10H
+             (pred
+                @a_a10F
+                $dEnum_a10H
+                (pred
+                   @a_a10F
+                   $dEnum_a10H
+                   (pred
+                      @a_a10F
+                      $dEnum_a10H
+                      (pred @a_a10F $dEnum_a10H (pred @a_a10F $dEnum_a10H b_aOW)))))
+      of
+      { GHC.Types.I# y_a15Z ->
+      GHC.Prim.tagToEnum# @Bool (GHC.Prim.># x_a15W y_a15Z)
+      }
+      }
+
+-- RHS size: {terms: 46, types: 21, coercions: 0, joins: 0/0}
+fam :: forall a. C a => a -> Char -> Int -> (Bool, a)
+[GblId,
+ Arity=4,
+ Str=<A><L><ML><MP(1L)>,
+ Unf=Unf{Src=<vanilla>, TopLvl=True,
+         Value=True, ConLike=True, WorkFree=True, Expandable=True,
+         Guidance=NEVER}]
+fam
+  = \ (@a_aZM)
+      _ [Occ=Dead]
+      (c_aP4 :: a_aZM)
+      (a1_aP5 :: Char)
+      (b_aP6 :: Int) ->
+      (case a1_aP5 of { GHC.Types.C# c#_a15M ->
+       case GHC.Prim.ord# c#_a15M of wild1_a15O {
+         __DEFAULT ->
+           case b_aP6 of { GHC.Types.I# x1_a15B ->
+           case x1_a15B of wild3_a15D {
+             __DEFAULT ->
+               GHC.Prim.tagToEnum#
+                 @Bool
+                 (GHC.Prim.>#
+                    (GHC.Prim.+# wild1_a15O 1#) (GHC.Prim.-# wild3_a15D 5#));
+             -9223372036854775808# -> case GHC.Internal.Enum.$fEnumInt1 of {};
+             -9223372036854775807# -> case GHC.Internal.Enum.$fEnumInt1 of {};
+             -9223372036854775806# -> case GHC.Internal.Enum.$fEnumInt1 of {};
+             -9223372036854775805# -> case GHC.Internal.Enum.$fEnumInt1 of {};
+             -9223372036854775804# -> case GHC.Internal.Enum.$fEnumInt1 of {}
+           }
+           };
+         1114111# -> case GHC.Internal.Enum.$fEnumChar2 of {}
+       }
+       },
+       c_aP4)
+
+-- RHS size: {terms: 8, types: 10, coercions: 0, joins: 0/0}
+eq_constraint [InlPrag=NOINLINE]
+  :: forall a b. (a ~ b) => a -> b -> (a, b)
+[GblId,
+ Arity=3,
+ Str=<A><L><L>,
+ Unf=Unf{Src=<vanilla>, TopLvl=True,
+         Value=True, ConLike=True, WorkFree=True, Expandable=True,
+         Guidance=ALWAYS_IF(arity=3,unsat_ok=True,boring_ok=True)}]
+eq_constraint
+  = \ (@a_aZD)
+      (@b_aZE)
+      _ [Occ=Dead]
+      (a1_aP7 :: a_aZD)
+      (b1_aP8 :: b_aZE) ->
+      (a1_aP7, b1_aP8)
+
+-- RHS size: {terms: 45, types: 146, coercions: 7, joins: 0/0}
+baz
+  :: forall (t :: * -> *) a b.
+     Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (F t a))))))
+     -> (a -> b) -> Maybe (Maybe (Maybe (Maybe (F t b))))
+[GblId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
+baz
+  = \ (@(t_aYx :: * -> *))
+      (@a_aYy)
+      (@b_aYz)
+      (ds_d148
+         :: Maybe (Maybe (Maybe (Maybe (Maybe (Maybe (F t_aYx a_aYy)))))))
+      (f_aPa :: a_aYy -> b_aYz) ->
+      case ds_d148 of {
+        Nothing ->
+          GHC.Internal.Maybe.Nothing
+            @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+        Just ds1_d14m ->
+          case ds1_d14m of {
+            Nothing ->
+              GHC.Internal.Maybe.Nothing
+                @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+            Just ds2_d14n ->
+              case ds2_d14n of {
+                Nothing ->
+                  GHC.Internal.Maybe.Nothing
+                    @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+                Just ds3_d14o ->
+                  case ds3_d14o of {
+                    Nothing ->
+                      GHC.Internal.Maybe.Nothing
+                        @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+                    Just ds4_d14p ->
+                      case ds4_d14p of {
+                        Nothing ->
+                          GHC.Internal.Maybe.Nothing
+                            @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+                        Just ds5_d14q ->
+                          case ds5_d14q of {
+                            Nothing ->
+                              GHC.Internal.Maybe.Nothing
+                                @(Maybe (Maybe (Maybe (F t_aYx b_aYz))));
+                            Just x_aP9 ->
+                              GHC.Internal.Maybe.Just
+                                @(Maybe (Maybe (Maybe (F t_aYx b_aYz))))
+                                (GHC.Internal.Maybe.Just
+                                   @(Maybe (Maybe (F t_aYx b_aYz)))
+                                   (GHC.Internal.Maybe.Just
+                                      @(Maybe (F t_aYx b_aYz))
+                                      (GHC.Internal.Maybe.Just
+                                         @(F t_aYx b_aYz)
+                                         ((\ ($dFunctor_aZi :: Functor t_aYx) ->
+                                             fmap
+                                               @t_aYx
+                                               $dFunctor_aZi
+                                               @a_aYy
+                                               @b_aYz
+                                               f_aPa
+                                               ((x_aP9
+                                                 `cast` (ExposeOverloaded.N:F[0] <t_aYx>_N <a_aYy>_N
+                                                         :: F t_aYx a_aYy
+                                                            ~R# (Functor t_aYx => t_aYx a_aYy)))
+                                                  $dFunctor_aZi))
+                                          `cast` (Sym (ExposeOverloaded.N:F[0] <t_aYx>_N <b_aYz>_N)
+                                                  :: (Functor t_aYx => t_aYx b_aYz)
+                                                     ~R# F t_aYx b_aYz)))))
+                          }
+                      }
+                  }
+              }
+          }
+      }
+
+
+


=====================================
testsuite/tests/simplCore/should_compile/T16038/Makefile
=====================================
@@ -6,5 +6,5 @@ T16038:
 	'$(TEST_HC)' $(TEST_HC_OPTS) -O -c A.hs-boot
 	'$(TEST_HC)' $(TEST_HC_OPTS) -O -c B.hs
 	# All `fEqHsExpr` bindings should be in one recursive group:
-	'$(TEST_HC)' $(TEST_HC_OPTS) -O -c A.hs -ddump-simpl -dsuppress-all | \
+	'$(TEST_HC)' $(TEST_HC_OPTS) -O -c A.hs -ddump-simpl -dsuppress-all -fno-expose-overloaded-unfoldings | \
 		grep -e "^\$$fEqHsExpr" -e "Rec"


=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -34,6 +34,10 @@ test('T7162', normal, compile, [''])
 
 test('dfun-loop', normal, compile, [''])
 test('strict-float', normal, compile, [''])
+
+# Check if overloaded functions get unfoldings
+test('ExposeOverloaded', [only_ways('[optasm]'), grep_errmsg('Unf=Unf')], compile, ['-ddump-simpl -dno-suppress-unfoldings'])
+
 test('T3118', normal, compile, ['-Wno-overlapping-patterns'])
 test('T4203', normal, compile, [''])
 
@@ -332,7 +336,7 @@ test('T18231', [ only_ways(['optasm']), grep_errmsg(r'^[\w\.]+ ::.*->.*') ], com
 # Cast WW
 test('T17673', [ only_ways(['optasm']), grep_errmsg(r'^\w+\.\$wf') ], compile, ['-ddump-simpl -dsuppress-uniques -dppr-cols=9999'])
 test('T18078', [ only_ways(['optasm']), grep_errmsg(r'^\w+\.\$wf') ], compile, ['-ddump-simpl -dsuppress-uniques -dppr-cols=9999'])
-test('T18328', [ only_ways(['optasm']), grep_errmsg(r'Arity=') ], compile, ['-ddump-simpl -dsuppress-uniques'])
+test('T18328', [ only_ways(['optasm']), grep_errmsg(r'(Arity=2)',1) ], compile, ['-ddump-simpl -dsuppress-uniques'])
 test('T18347', normal, compile, ['-dcore-lint -O'])
 test('T18355', [ grep_errmsg(r'OneShot') ], compile, ['-O -ddump-simpl -dsuppress-uniques'])
 test('T18399', normal, compile, ['-dcore-lint -O'])
@@ -363,7 +367,7 @@ test('T19599a', normal, compile, ['-O -ddump-rules'])
 # Look for a specialisation rule for wimwam
 test('T19672', normal, compile, ['-O2 -ddump-rules'])
 
-test('T20103',  [ grep_errmsg(r'Arity') ], compile, ['-O -ddump-simpl -dsuppress-uniques'])
+test('T20103',  [ grep_errmsg(r'(Arity=[0-9]*)',[1]) ], compile, ['-O -ddump-simpl -dsuppress-uniques'])
 test('T19780', normal, compile, ['-O2'])
 test('T19794', normal, compile, ['-O'])
 test('T19890',  [ grep_errmsg(r'= T19890.foo1') ], compile, ['-O -ddump-simpl'])



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1e378a028509859fdcb6086962e400eba42e1d62

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1e378a028509859fdcb6086962e400eba42e1d62
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240511/ad7eea90/attachment-0001.html>


More information about the ghc-commits mailing list