Ticket #2625 (new bug)

Opened 5 years ago

Last modified 5 years ago

Unexpected -ddump-simpl output for derived Ord instance and UNPACKed fields

Reported by: aslatter Owned by:
Priority: low Milestone: _|_
Component: Compiler Version: 6.8.3
Keywords: Cc: aslatter@…
Operating System: Unknown/Multiple Architecture: x86_64 (amd64)
Type of failure: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

In the following example, with either -O or -O2

In the derived Eq instance for A, in '==' nothing ever gets re-packed into a B constructor.

However in the derived Ord instance, in the 'compile' function the code from -ddump-simpl shows that the RHS of 'compare' is unpacked from the 'A' constructor only to be repacked in 'B' constructor and then passed on to a different function.

Is there any way we can do for 'compare' what was done for '==' ?

Thanks

module Bug where

data A = A {-# UNPACK #-} !B
 deriving (Eq, Ord)

data B = B {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
 deriving (Eq, Ord)

Change History

Changed 5 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.10 branch

Thanks for the report; we'll take a look.

Changed 5 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple

Changed 5 years ago by simonpj

  • priority changed from normal to low
  • milestone changed from 6.10 branch to _|_

Here's what is happening. The compare functions for A and B look like this (initially):

compareA :: A -> A -> Ordering
compareA (A x1 .. x8) (A y1 .. y8)
  = compare (B x1 .. x8) (B y1 .. y8)

compareB :: B -> B -> Ordering
compareB (B x1 .. x8) (B y1 .. y8) 
  = if x1>y1 then GT else ... blah blah blah...

Note that the signature of compareB must be B -> B -> Ordering, so compareA is obliged to re-box the B argument. That always potentially happens when you use {-# UNPACK #-}: if you match on the constructor you may need to construct a value of the unpacked type to pass on to some other function.

OK after strictness analysis you might hope that compareB gets 16 arguments. But GHC is wary about giving workers an arbitrarily large number of arguments, so it has an arbitrary threshold of 10. As a result, the worker for compareB looks like this:

$wcompareB :: Int# -> ... -> Int# -> B -> Ordering
$wcompareB x1 .. x8 (B y1 .. y8) = <blah>

The first argument is unpacked but not the second. And that is what is happening to you.

I don't know a good solution in general. {-# UNPACK #-} can (and must) lead to the possibility of reboxing. (In this case compareB is strict, so its wrapper may do unboxing ,up to the threshold, but in general the called function may be lazy.)

There is a workaround here: the flag -fmax-worker-args=N will change the threshold

to N, and that fixes the problem. But it's not a satisfying fix.

I'll leave this open at low priority. By all means contribute suggestions!

Simon

Note: See TracTickets for help on using tickets.