Ticket #3198 (closed bug: fixed)

Opened 4 years ago

Last modified 4 years ago

inliner fails to kick in for Double (*)

Reported by: JulesBean Owned by:
Priority: normal Milestone:
Component: Compiler Version: 6.11
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

In GHC HEAD as of approx March 2009, the following very simple code fails to inline the (*), which means it fails to generate FPU code:

module Print where

printTimes :: Double -> Double -> IO ()
printTimes f g = print (f*g)

My evidence for this is that the generated asm (from -O2 -ddump-asm) includes

	movl %esi,-4(%ebp)
	movl 12(%esi),%eax
	movl %eax,-16(%ebp)
	movl 8(%esi),%eax
	movl %eax,-20(%ebp)
	movl $_sC3_info,-12(%ebp)
	addl $-20,%ebp
	jmp _base_GHCziFloat_timesDouble_info

...which calls 'GHC.Float.timesDouble' as a regular function with regular stack calling.

Someone confirmed for me that this also happens for them with the released GHC 6.10 versions.

On my copy of 6.8.3 the inlining is fine, and I get proper x87 opcodes:

#	gmull %fake0,%fake1,%fake0
	#GMUL-xxxcase1
	ffree %st(7) ; fld %st(1) ; fmulp %st(0),%st(1)

Now, if I try a pure version of the code, that is very simply:

module Times where

f :: Double -> Double -> Double
f x y = x * y

I don't find it inlines in either HEAD or 6.8. It compiles again to a jump to _base_GHCziFloat_timesDouble_info.

In all cases (that is, HEAD, 6.8, pure code and print code) simply replacing x*y with 1+x*y solves the problem.

It seems this is some kind of inliner fragility but I'm reporting it as a bug since it is also a regression from 6.8, in the first form I discovered.

Change History

Changed 4 years ago by JulesBean

Adding an explicit {-# INLINE timesDouble #-} to Float.lhs doesn't change the result.

Changed 4 years ago by JulesBean

Neither do -fdicts-strict or -fdicts-cheap

Changed 4 years ago by simonpj

  • difficulty set to Unknown

Suppose you have

f :: Double -> Double -> Double
f x y = x * y

What is gained if we inline (*) here? Virtually nothing: simply a jump from f to *. After all, f is just (*), and we know nothing about x or y. (It'd be quite differnet if it was x*1!.) Moreover, if we don't inline * here, we'll replace f by (*) at every call site.

(Remember too that this is Haskell, so it (*) was inlined you'd get

f x y = case x of D# a -> case y of D# b -> D# (plusDouble# a b)

In general, if GHC sees a call (foo x y), where

  • nothing whatsoever is known about x or y (for example, they are lambda-bound), and
  • there is nothing interesting about the context of the cal

then GHC does not inline foo, even if foo has an INLINE pragma. Why? Because it simply give code bloat with zero or small benefit.

This is an adjustable decision, but I'm not yet convinced it needs adjusting.

Simon

Changed 4 years ago by JulesBean

Well perhaps I was emphasising the wrong part of the issue. The part that initially suprised me was the the code for print (x*y) generates a call to timesDouble instead of FPU code - and this is a change from 6.8.3 in my tests. Maybe that doesn't matter as much as I thought, though.

Changed 4 years ago by simonpj

  • status changed from new to closed
  • resolution set to fixed

Actually in the HEAD the inlining is kicking in:

Print.a =
  \ (f_af9 :: GHC.Types.Double)
    (g_afb :: GHC.Types.Double)
    (eta_B1 :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case GHC.IO.a23
           GHC.Handle.stdout
           (case f_af9 of _ { GHC.Types.D# x_aAO ->
            case g_afb of _ { GHC.Types.D# y_aAS ->
            GHC.Float.$w$sshowSignedFloat1
              GHC.Float.$sshowFloat
              GHC.Base.zeroInt
              (GHC.Prim.*## x_aAO y_aAS)
              (GHC.Types.[] @ GHC.Types.Char)
            }
            })
           eta_B1
    of _ { (# new_s_azB, _ #) ->
    GHC.IO.$wa13 GHC.Handle.stdout '\n' new_s_azB
    }

Because the context of the call to timesDouble suggests that doing so is a good idea.

I'm not quite sure why 6.10 does not do so, but it's not a big deal either way, so I think I'll close this ticket.

Keep an eye out when we move to 6.12... there are quite a few changes to inlining in the pipeline, so we need vigilant users to tell us if there are regressions.

thanks

Simon

Note: See TracTickets for help on using tickets.