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,869
    • Issues 4,869
    • 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
  • #21128
Closed
Open
Created Feb 24, 2022 by Sebastian Graf@sgraf812Developer

Inadequate FloatIn pass may leave redundant allocations in the hot path

Consider

module T21128a where

indexError :: Show a => a -> a -> a ->  b
indexError a b c = error (show a ++ show b ++ show c)
{-# NOINLINE indexError #-}

index :: Int -> Int -> Int -> Int
index l u i
  | l <= i && i < u = i-l
  | otherwise       = indexError l u i
{-# INLINE index #-}
module T21128 where

import T21128a

theresCrud :: Int -> Int -> Int
theresCrud x y = go x
  where
    go 0 = index 0 y 0
    go 1 = index x y 1
    go n = go (n-1)
{-# NOINLINE theresCrud #-}

The GHC versions I've looked at (8.10 and master) will unbox x and y. But that will stop with the following simplified Core (output here from master):

lvl = \ y -> $windexError $fShowInt_$cshow l y l

lvl1 = \ ww y -> $windexError $fShowInt_$cshow (I# ww) y i

$wtheresCrud
  = \ ww ww1 ->
      let { y = I# ww1 } in
      join {
        lvl2
          = case <=# ww 1# of {
              __DEFAULT -> case lvl1 ww y of wild { };
              1# ->
                case <# 1# ww1 of {
                  __DEFAULT -> case lvl1 ww y of wild { };
                  1# -> -# 1# ww
                }
            } } in
      join {
        lvl3
          = case <# 0# ww1 of {
              __DEFAULT -> case lvl y of wild { };
              1# -> 0#
            } } in
      joinrec {
        $wgo ww2
          = case ww2 of wild {
              __DEFAULT -> jump $wgo (-# wild 1#);
              0# -> jump lvl3;
              1# -> jump lvl2
            }; } in
      jump $wgo ww

NB: Those exit join points correspond to the two index calls.

Note the reboxing of y. While the reboxing of x happens in the bottoming join point exit_X18, y is shared among both index calls and can't be floated into the exit join points without duplicating the binding. Similarly, y won't be inlined, because ... I don't know why. Code size concerns maybe.

The result is that y is allocated in the hot path where it is never needed boxed.

This issue is merely to document this inconvenience. Not sure if we want to take concrete action on such a narrow use case; probably depends on how involved the fix is. Something for @simonpj to ponder.

Edited Mar 01, 2022 by Sebastian Graf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking