GHC在将它们传递给函数时是否会解压缩类型?例如,假设我们有以下类型:
data Foo
= Foo1 {-# UNPACK #-} !Int {-# UNPACK #-} !Word
| Foo2 {-# UNPACK #-} !Int
| Foo3 {-# UNPACK #-} !Word
Run Code Online (Sandbox Code Playgroud)
然后我在其Foo参数中定义一个严格的函数:
consumeFoo :: Foo -> Int
consumeFoo x = case x of ...
Run Code Online (Sandbox Code Playgroud)
在运行时,当我打电话时,我consumeFoo可以期待发生什么?该GHC调用约定是通过参数寄存器(或在栈上一次有太多).我可以看到论证传递的两种方式:
Foo堆上的指针作为一个参数传入.Foo,一个参数表示使用的数据构造函数,另外两个表示数据构造函数中的可能Int和Word值.我更喜欢第二种表示,但我不知道它是否真的发生了什么.我知道UnpackedSumTypes登陆GHC 8.2,但目前还不清楚它是否符合我的要求.如果我把函数写成:
consumeFooAlt :: (# (# Int#, Word# #) | Int# | Word# #) -> Int
Run Code Online (Sandbox Code Playgroud)
那么我希望评估(2)会发生什么.而且开箱部分解压后的款项页面的指示,我能做到这一点,以及:
data Wrap = Wrap {-# UNPACK #-} !Foo
consumeFooAlt2 :: Wrap -> Int
Run Code Online (Sandbox Code Playgroud)
我认为那应该也有我想要的代表性.
所以我的问题是,在不使用包装器类型或原始解压缩总和的情况下,当我将它作为参数传递给函数时,如何保证将总和解压缩到寄存器(或堆栈中)?如果有可能,它是GHC 8.0已经可以做的事情,还是仅在GHC 8.2中可用的东西?
第一:保证优化和GHC不能很好地混合.由于高水平,很难预测GHC在每种情况下都会产生的代码.唯一可以确定的方法是看核心.如果您正在使用GHC开发一个性能极其依赖的应用程序,那么您需要熟悉Core I.
我不知道GHC中的任何优化完全符合您的描述.这是一个示例程序:
module Test where
data Sum = A {-# UNPACK #-} !Int | B {-# UNPACK #-} !Int
consumeSum :: Sum -> Int
consumeSum x = case x of
A y -> y + 1
B y -> y + 2
{-# NOINLINE consumeSumNoinline #-}
consumeSumNoinline = consumeSum
{-# INLINE produceSumInline #-}
produceSumInline :: Int -> Sum
produceSumInline x = if x == 0 then A x else B x
{-# NOINLINE produceSumNoinline #-}
produceSumNoinline :: Int -> Sum
produceSumNoinline x = if x == 0 then A x else B x
test :: Int -> Int
--test x = consumeSum (produceSumInline x)
test x = consumeSumNoinline (produceSumNoinline x)
Run Code Online (Sandbox Code Playgroud)
让我们先来看看如果我们不内联consumeSum也不会发生什么produceSum.这是核心:
test :: Int -> Int
test = \ (x :: Int) -> consumeSumNoinline (produceSumNoinline x)
Run Code Online (Sandbox Code Playgroud)
(制作ghc-core test.hs -- -dsuppress-unfoldings -dsuppress-idinfo -dsuppress-module-prefixes -dsuppress-uniques)
在这里,我们可以看到GHC(在这种情况下为8.0)不会取消作为函数参数传递的和类型.如果我们无论是内联没有任何变化consumeSum或produceSum.
但是,如果我们内联两者,则会生成以下代码:
test :: Int -> Int
test =
\ (x :: Int) ->
case x of _ { I# x1 ->
case x1 of wild1 {
__DEFAULT -> I# (+# wild1 2#);
0# -> lvl1
}
}
Run Code Online (Sandbox Code Playgroud)
这里发生的事情是,通过内联,GHC最终得到:
\x -> case (if x == 0 then A x else B x) of
A y -> y + 1
B y -> y + 2
Run Code Online (Sandbox Code Playgroud)
通过案例(if只是一个特殊的case)变成了:
\x -> if x == 0 then case (A x) of ... else case (B x) of ...
Run Code Online (Sandbox Code Playgroud)
现在这是一个已知构造函数的情况,因此GHC可以在编译时减少大小写的结果:
\x -> if x == 0 then x + 1 else x + 2
Run Code Online (Sandbox Code Playgroud)
所以它完全消除了构造函数.
总之,我认为GHC在版本8.2之前没有任何"未装箱的"类型的概念,它也适用于函数参数.获得"未装箱"总和的唯一方法是通过内联完全消除构造函数.