Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • GHC GHC
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,868
    • Issues 4,868
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 456
    • Merge requests 456
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Releases
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Glasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #20591
Closed
Open
Created Nov 01, 2021 by David Feuer@treeowlReporter

INLINABLE exports bottom crud

INLINABLE bindings have "unoptimized" unfoldings, the better to optimize in context and with RULES. One aspect of this is a bit annoying: error calls are included in the unfolding. Example:

module Gunk where

import GHC.Err

{-# INLINABLE potato #-}
potato :: (Num a, Eq a) => a -> a
potato 3 = errorWithoutStackTrace "3 is a fake number"
potato n = n - 17

Compiling with -O2 -ddump-simpl gives the following:

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl_r1pI :: Integer
[GblId, Unf=OtherCon []]
lvl_r1pI = 17

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl1_r1qY :: GHC.Prim.Addr#
[GblId, Unf=OtherCon []]
lvl1_r1qY = "3 is a fake number"#

-- RHS size: {terms: 4, types: 4, coercions: 0, joins: 0/0}
lvl2_r1qZ :: forall {a}. a
[GblId, Str=b, Cpr=b]
lvl2_r1qZ
  = \ (@a_a1oE) ->
      errorWithoutStackTrace
        @'GHC.Types.LiftedRep
        @a_a1oE
        (GHC.CString.unpackCString# lvl1_r1qY)

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl3_r1r0 :: Integer
[GblId, Unf=OtherCon []]
lvl3_r1r0 = 3

-- RHS size: {terms: 20, types: 13, coercions: 0, joins: 0/0}
potato [InlPrag=INLINABLE] :: forall a. (Num a, Eq a) => a -> a
[GblId,
 Arity=3,
 Str=<S(LC(C(S))LLLLL),U(A,1*C1(C1(U)),A,A,A,A,C(U))><S(C(C(S))L),1*U(1*C1(C1(U)),A)><L,U>,
 Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True, Guidance=IF_ARGS [90 30 0] 460 0
         Tmpl= \ (@a_a1oE)
                 ($dNum_a1oF :: Num a_a1oE)
                 ($dEq_a1oG [Occ=Once1] :: Eq a_a1oE)
                 (ds_d1p7 :: a_a1oE) ->
                 case ==
                        @a_a1oE $dEq_a1oG ds_d1p7 (fromInteger @a_a1oE $dNum_a1oF 3)
                 of {
                   False ->
                     - @a_a1oE $dNum_a1oF ds_d1p7 (fromInteger @a_a1oE $dNum_a1oF 17);
                   True ->
                     errorWithoutStackTrace
                       @'GHC.Types.LiftedRep
                       @a_a1oE
                       (GHC.Base.build
                          @Char
                          (\ (@b_a1pT) ->
                             GHC.CString.unpackFoldrCString# @b_a1pT "3 is a fake number"#))
                 }}]
potato
  = \ (@a_a1oE)
      ($dNum_a1oF :: Num a_a1oE)
      ($dEq_a1oG :: Eq a_a1oE)
      (ds_d1p7 :: a_a1oE) ->
      case ==
             @a_a1oE
             $dEq_a1oG
             ds_d1p7
             (fromInteger @a_a1oE $dNum_a1oF lvl3_r1r0)
      of {
        False ->
          - @a_a1oE
            $dNum_a1oF
            ds_d1p7
            (fromInteger @a_a1oE $dNum_a1oF lvl_r1pI);
        True -> lvl2_r1qZ @a_a1oE
      }

This unfolding means that each specialization of potato will have its very own

                     errorWithoutStackTrace
                       @'GHC.Types.LiftedRep
                       @a_a1oE
                       (GHC.Base.build
                          @Char
                          (\ (@b_a1pT) ->
                             GHC.CString.unpackFoldrCString# @b_a1pT "3 is a fake number"#)

To the best of my knowledge, there is no possible way this can reveal optimization opportunities. It's also rather difficult to imagine someone writing RULES that try to match on a particular error call. Thus it seems to me that terms known to be bottom should be allowed to float to the top level from even INLINABLE bindings, if they can do so. A great many cases (if not most) are of the trivial sort exhibited here, where a term can be seen to be bottom because it's a fully saturated call to a top-level function known to throw an exception or diverge.

The current workaround is for user's to manually pull error calls out of functions marked INLINABLE and class method definitions. It's tolerable, but it's awfully annoying!

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking