[GHC] #14248: GHC misses optimization opportunity

GHC ghc-devs at haskell.org
Mon Sep 18 22:24:19 UTC 2017


#14248: GHC misses optimization opportunity
-------------------------------------+-------------------------------------
           Reporter:  vagarenko      |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:
          Component:  Compiler       |           Version:  8.2.1
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 Consider this code:

 {{{#!hs
 {-# LANGUAGE TypeFamilies            #-}
 {-# LANGUAGE MultiParamTypeClasses   #-}
 {-# LANGUAGE TypeOperators           #-}
 {-# LANGUAGE FlexibleInstances       #-}
 {-# LANGUAGE FlexibleContexts        #-}
 {-# LANGUAGE ScopedTypeVariables     #-}
 {-# LANGUAGE UndecidableInstances    #-}
 {-# LANGUAGE TypeApplications        #-}
 {-# LANGUAGE TypeInType              #-}
 {-# LANGUAGE AllowAmbiguousTypes     #-}

 module Unzip where

 import Prelude hiding (unzip)
 import GHC.TypeLits
 import Data.Kind

 -- | Data family of unboxed vectors.
 class IsVector (n :: Nat) e where
     data Vector n e :: Type

     fromList :: [e] -> Vector n e

 -- | Unrolled unzip. Type param @n@ is the length of the input list.
 class Unzip (n :: Nat) where
     unzip :: [(a, b)] -> ([a], [b])

 instance {-# OVERLAPPING #-} Unzip 0 where
     unzip _ = ([], [])
     {-# INLINE unzip #-}

 instance {-# OVERLAPPABLE #-} (Unzip (n - 1)) => Unzip n where
     unzip []       = error "Not enough elements."
     unzip (x : xs) = (\(a, b) (as, bs) -> (a : as, b : bs)) x (unzip @(n -
 1) xs)
     {-# INLINE unzip #-}

 -- | Make pair of vectors from list of pairs of vector's elements.
 unzipVec :: forall (n :: Nat) e. (IsVector n e, Unzip n) => [(e, e)] ->
 (Vector n e, Vector n e)
 unzipVec ps =
     let (es1, es2) = unzip @n ps
     in (fromList es1, fromList es2)
 {-# INLINE unzipVec #-}

 --------------------------------
 instance IsVector 2 Float where
     data Vector 2 Float = Vector2f {-# UNPACK #-} !Float {-# UNPACK #-}
 !Float

     fromList [a, b] = Vector2f a b
     fromList []     = error "Not enough elements."

 unzipVecSpecialized :: [(Float, Float)] -> (Vector 2 Float, Vector 2
 Float)
 unzipVecSpecialized = unzipVec
 }}}

 GHC-8.2.1 generates the following Core for `unzipVecSpecialized` function:
 {{{#!hs
 -- RHS size: {terms: 84, types: 113, coercions: 4, joins: 0/1}
 unzipVecSpecialized
   :: [(Float, Float)] -> (Vector 2 Float, Vector 2 Float)
 unzipVecSpecialized
   = \ (eta :: [(Float, Float)]) ->
       let {
         ds :: ([Float], [Float])
         ds
           = case eta of {
               [] -> lvl20;
               : x xs ->
                 case x of { (a, b) ->
                 case xs of {
                   [] -> lvl20;
                   : x1 xs1 ->
                     case x1 of { (a1, b1) ->
                     (: @ Float a (: @ Float a1 ([] @ Float)),
                      : @ Float b (: @ Float b1 ([] @ Float)))
                     }
                 }
                 }
             } } in
       (case ds of { (es1, es2) ->
        case es1 of {
          [] -> $fIsVector2Float1;
          : a ds1 ->
            case ds1 of {
              [] -> $fIsVector2Float1;
              : b ds2 ->
                case ds2 of {
                  [] ->
                    case a of { F# dt1 ->
                    case b of { F# dt3 -> (Vector2f dt1 dt3) `cast` <Co:2>
 }
                    };
                  : ipv ipv1 -> $fIsVector2Float1
                }
            }
        }
        },
        case ds of { (es1, es2) ->
        case es2 of {
          [] -> $fIsVector2Float1;
          : a ds1 ->
            case ds1 of {
              [] -> $fIsVector2Float1;
              : b ds2 ->
                case ds2 of {
                  [] ->
                    case a of { F# dt1 ->
                    case b of { F# dt3 -> (Vector2f dt1 dt3) `cast` <Co:2>
 }
                    };
                  : ipv ipv1 -> $fIsVector2Float1
                }
            }
        }
        })
 }}}

 Notice how it constructs tuple of lists `ds :: ([Float], [Float])` and
 then deconstructs it twice. I would expect the compiler to get rid of
 intermediate tuple and lists, so the Core would look like this:

 {{{#!hs
 unzipVecSpecialized
   :: [(Float, Float)] -> (Vector 2 Float, Vector 2 Float)
 unzipVecSpecialized
   = \ (eta :: [(Float, Float)]) ->
     case eta of {
         [] -> lvl20;
         : x xs ->
             case x of { (a, b) ->
             case xs of {
                 [] -> lvl20;
                 : x1 xs1 ->
                     case x1 of { (a1, b1) ->
                         (case a of { F# dt1 ->
                          case a1 of { F# dt2 -> (Vector2f dt1 dt2) }},
                          case b of { F# dt3 ->
                          case b1 of { F# dt4 -> (Vector2f dt3 dt4) }}
                         )
                     }
             }
             }
     }
 }}}

 I've tried putting different phase control options on the INLINE pragmas
 to no success.

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14248>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list