GHC高阶函数参数内联优化对应的实现机制问询
GHC递归函数的参数内联优化逻辑探究
我试图探究GHC中一类特定内联行为的实现逻辑,该行为指作为参数传入另一个函数的函数会被内联。举例来说,我编写了如下定义(使用自定义List类型以避免触发内置重写规则):
data MyList a = Nil | Cons a (MyList a) deriving (Show) mapMyList :: (a -> b) -> MyList a -> MyList b mapMyList f Nil = Nil mapMyList f (Cons a rest) = Cons (f a) $ mapMyList f rest
随后的调用代码如下:
fromList :: [a] -> MyList a fromList = ... main = do print $ mapMyList (*2) $ fromList [1..5]
mapMyList是递归函数,正常来说无法直接被内联。但在生成的Core中间代码中,我看到了如下定义:
Rec { -- RHS size: {terms: 16, types: 11, coercions: 0, joins: 0/0} Main.main_$smapMyList [Occ=LoopBreaker] :: MyList Int -> MyList Int [GblId, Arity=1, Str=<S,1*U>, Unf=OtherCon []] Main.main_$smapMyList = \ (sc_s2Rb :: MyList Int) -> case sc_s2Rb of { Nil -> Main.Nil @Int; Cons a_aBe rest_aBf -> Main.Cons @Int (case a_aBe of { GHC.Types.I# x_a24u -> GHC.Types.I# (GHC.Prim.*# x_a24u 2#) }) (Main.main_$smapMyList rest_aBf) } end Rec }
可以注意到,生成的smapMyList不再接受函数作为参数,且传入的(* 2)已经被完全内联到代码逻辑中。我想知道是哪一个优化Pass生成了这段代码,具体运行逻辑是怎样的?
我找到了相关的GHC issue,其中提到希望通过SPECIALIZE编译指示来保证该行为,这让我最初认为是特化Pass实现了该优化。但我查阅GHC特化相关文档时发现,特化通常针对类型类字典而非普通函数参数(我的示例中完全没有用到类型类)。
我也查阅了静态参数变换(Static Argument Transformation)相关内容,它看起来与该优化高度相关,GHC源码中对该Pass的描述如下:
可被视为移除循环中的不变量:递归函数的参数若在递归调用中不会发生变化,则会从递归环节中移除,仅在本地传递实际发生变化的参数。
然而我尝试使用-fno-static-argument-transformation -fno-specialise编译参数禁用这两个Pass后,发现该转换仍然会发生。
我提出该问题的动机是我正在另一个函数式语言Koka中实现同类转换,因此希望了解GHC的具体实现方案。
内容的提问来源于stack exchange,提问作者Steven Fontanella
相关产品推荐
相关产品推荐

