如何读取简化器输出?

时间:2019-10-26 11:12:14

标签: haskell compilation

考虑recent question中的一个简单功能:

myButLast :: [a] -> a
myButLast [x, y] = x
myButLast (x : xs) = myButLast xs
myButLast _ = error "List too short"

我们可以要求GHC使用ghc -ddump-simpl给我们简化输出。 (可能与some additional flags类似-dsuppress-module-prefixes -dsuppress-uniques。)据我了解,它 是编译的最后阶段,结果仍然与原始高点有相似之处 级别代码。所以这就是它的意思:

-- RHS size: {terms: 21, types: 22, coercions: 0, joins: 0/0}
myButLast :: forall a. [a] -> a
[GblId,
 Arity=1,
 Str=<S,1*U>,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True, Guidance=IF_ARGS [30] 100 0}]
myButLast
  = \ (@ a) (ds :: [a]) ->
      case ds of {
        [] -> myButLast1 @ a;
        : x ds1 ->
          case ds1 of {
            [] -> myButLast1 @ a;
            : y ds2 ->
              case ds2 of {
                [] -> x;
                : ipv ipv1 -> myButLast_$smyButLast1 @ a y ipv ipv1
              }
          }
      }

这是怎么回事?让我们看看。

  1. 现在使用显式量词为类型签名附加一些注释。 我可能会猜到他们说“全局标识符,一元,顶级” ,这完全是对的 功能。其他注解,例如WorkFree=TrueStr=<S,1*U>,对我来说是个秘密。

  2. “值” 定义现在是一个lambda,除了列表之外,它还接受类型变量 论证,然后通过案例分析来研究列表。 [] -> myButLast1 @ a是荣耀的 错误调用,因此让我们暂时忽略它。有趣的部分是 myButLast_$smyButLast1 (那是什么名字?我以为$符号不能成为 标识符。),它实际上是遍历列表的尾部递归函数。

  3. 在这里,这是我们公认的相互递归块的单个成员:

    Rec {
    -- RHS size: {terms: 13, types: 12, coercions: 0, joins: 0/0}
    myButLast_$smyButLast1 [Occ=LoopBreaker]
      :: forall a. a -> a -> [a] -> a
    [GblId,
     Arity=3,
     Caf=NoCafRefs,
     Str=<L,1*U><L,1*U><S,1*U>,
     Unf=OtherCon []]
    myButLast_$smyButLast1
      = \ (@ a) (sc :: a) (sc1 :: a) (sc2 :: [a]) ->
          case sc2 of {
            [] -> sc;
            : ipv ipv1 -> myButLast_$smyButLast1 @ a sc1 ipv ipv1
          }
    end Rec }
    

    这很清楚,但是它确实有一些新功能,例如递归块 定界符Rec ... end Rec和隐含的注释[Occ=LoopBreaker]。注释也 不同:Unf数组为空,而出现Caf字段。我只能推断 Unf字段有趣的是程序员定义的名称质量,而 myButLast_$smyButLast1由编译器创建。

因此,我可以通过行数了解到简化器给我的一半,但是在某些部分,我什至无法开始猜测其含义。

  • 是前提,简化器输出通常是最有用的中间 表示,对吗?

  • 我的阅读到目前为止是否正确?

  • 是否有关于所有这些神秘言论的手册?他们是什么意思?

1 个答案:

答案 0 :(得分:2)

我知道Core上没有任何手册或独立文档,它涉及到您正在寻找的详细信息。 Wiki中当然有这个page on Core,但它只是从较高的层次上解释了Core语言,并且主要是根据用于表示Core抽象语法树的编译器数据结构来解释的,而不是具体的。 “漂亮打印”语法。

获取所需详细信息的唯一方法是下载GHC源的副本并开始浏览ghc/compiler/coreSyn/中的代码。

GHC既庞大又复杂,但是大多数代码是用Haskell编写的,因此很多代码都非常高级且可读性强,并且源代码受到大量注释,并附有出色的解释和注释。

例如,如果您想知道WorkFree=True是什么意思,

  1. PprCore.hs中查找生成注释的代码,以确定它是uf_is_work_free的{​​{1}}字段。

  2. 检查CoreUnfoldingCoreUnfolding的定义及其相关注释,可以看到CoreSyn.hs是标识符的表示,可以在内联时替换该标识符,并且Unfolding标志是uf_is_work_free的缓存副本,它某种程度上表明内联展开不会“浪费工作”。

  3. exprIsWorkFreeCoreSyn.hsCoreUnfold.hs中的注释中查找有关CoreUtils.hs及其含义的其他解释:

      

    exprIsWorkFree用于确定是否内联某些内容;我们       如果这样做可能会重复工作,请不要内联,方法是将       表达式的完整副本。

    举一个例子:

    exprIsWorkFree

    指出let x = a #+ b in x +# x -- I think `#+` is a typo and should be `+#` 不是“免费的”,因为如果在RHS上对其进行内联,则会导致x操作被两次评估

在您的情况下,Core输出中的a +# b版本是不工作的,因为它不会独立于每次应用myButLast时都可以重用的参数来求值任何表达式。 / p>