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,867
    • Issues 4,867
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 454
    • Merge requests 454
  • 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
  • #13014
Closed
Open
Created Dec 20, 2016 by nfrisby@trac-nfrisby

Seemingly unnecessary marking of a SpecConstr specialization as a loopbreaker

!SpecConstr creates the following rules, with the right cajoling. (I've used unboxed integers merely to avoid w/w, which only adds noise in this example.)

(See ticket:13014#comment:129293 for a Minimal Working Example.)

data VL :: [k] -> * where
  VLZ :: VL '[]
  VLS :: VL as -> VL (a ': as)

lengthVL :: GHC.Types.SPEC -> VL as -> Int#
{-# INLINABLE lengthVL #-}
lengthVL !sPEC VLZ = 0#
lengthVL !sPEC (VLS vl) = 1# +# lengthVL sPEC vl

==================== Tidy Core rules ====================
"SC:lengthVL0" [ALWAYS]
    forall (@ a) (@ (as :: [*])) (sc :: VL as).
      lengthVL @ (a : as)
               SPEC
               (VLS
                  @ * @ (a : as) @ as @ a @~ (<a : as>_N :: (a : as) ~ (a : as)) sc)
      = lengthVL_$slengthVL1 @ a @ as sc
"SC:lengthVL1" [ALWAYS]
    forall (sc :: VL '[]).
      lengthVL @ '[] SPEC sc
      = lengthVL_$slengthVL sc

But the cons-case specialization, lengthVL_$slengthVL1, is marked as a loopbreaker. Consider the following idiomatic usage to see why that is problematic.

class KnownSpine (as :: [k]) where sing :: VL as
instance KnownSpine '[] where   -- '
  {-# INLINE sing #-}
  sing = VLZ
instance KnownSpine as => KnownSpine (a ': as) where   -- '
  {-# INLINE sing #-}
  sing = VLS sing

example :: Int
example = I# $ lengthVL SPEC (sing :: VL '[Int,Char,Bool])

The right-hand side of example would ideally be simplified to 3. It's not, ultimately because the specialization is marked as a loopbreaker.

I switched on -dverbose-core2core to track the simplification of the right-hand side of example. 1) The sing dictionary is unfolded to constructor applications. 2) Those are floated out but then pre-inlined-unconditionally right back in before CSE gets a chance to spoil it. 3) Thus the VLS rule fires. But it only fires once, because of the loopbreaker designation!

I have not yet investigated why the specialization in the cons-case is marked a loopbreaker.

(Even if the specialization wasn't being considered a loopbreaker --- which immediately makes this approach to optimization a dead-end --- I don't know with any certainty how to force the specialization to be inlined in those cases where its right-hand side was relatively large.)

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