在2018年编写LLVM遍——部分II

分析——它值得有自己的一篇文章

原文地址:https://medium.com/@mshockwave/writing-llvm-pass-in-2018-part-i-531c700e85eb

作者:Bekket McClane

收集程序分析的任务也被构造为LLVM PassManager里的遍,除了它们从不(也不应该)修改IR内容。同样,与旧式PassManager相比,在新式PassManager里分析数据以这样一个不同的方式管理与开发,我决定使用单独的文章来讨论它。本文将讨论如何使用新的AnalysisManager获取分析数据。因此拿起你喜欢的编辑器,让我们开始吧!

在旧式遍里你能找到的最重要的特性之一是分析管理器与PassManager高度整合。你将通过getAnalysis<…>方法获取分析数据,这个方法是Pass类的成员。不过,在新的PassManager系统中,分析管理者是一个独立的实例,可以在任何地方单独使用。为了给你留下深刻印象,让我们在一个旧式遍里使用新式AnalysisManager!下面是引导(主干)代码:

bool MyFuncPass::runOnFunction(Function& F) override {

  PassBuilder PB;

  FunctionAnalysisManager FAM;

  PB.registerFunctionAnalyses(FAM);

  // ...

  return false;

}

看起来有我们熟悉的东西:PassBuilder。你是对的,我们仍然需要它的辅助来向管理者注册所有可用的分析遍,但在完成后,分析管理者,这里是FunctionAnalysisManager,可以单独使用。AnalysisManager负责安排注册分析遍以及管理它们的分析结果。例如,缓存一个结果直到由于对应IR单元的某些修改而失效。

因此,我们如何从管理者获取分析的结果呢?答案是类似于旧式遍里的getAnalysis<…>接口,但以稍稍不同的名字以及不同的方式:

#include "llvm/Analysis/AliasAnalysis.h"

 

bool MyFuncPass::runOnFunction(Function& F) override {

  PassBuilder PB;

  FunctionAnalysisManager FAM;

  PB.registerFunctionAnalyses(FAM);

 

  // How we do in legacy Passes:

  AAResultWrapperPass& WrapperPass = getAnalysis<AAResultsWrapperPass>();

  AAresults& AAR1 = WrapperPass.getAAResults();

 

  // How we do with new AnalysisManager

  AAResults& AAR2 = FAM.getResult<AAManager>(F);

 

  return false;

}

上面的代码使用AliasAnalysis作为我们希望拿到的分析数据。正如你看到的,使用旧语法,你首先需要获得遍实例的一个引用,然后使用其中一个成员函数来获取分析结果。相反,为了得到分析结果,你仅需要以期望的分析遍类型(在这个情形里AAManager),连同你的目标IR单元实例(在这个情形里Function)来调用getResult<…>。

这里,我们希望指出getAnalysis与getResult之间返回类型的差异。一方面,getAnalysis<T>的返回类型是模板类型参数T,这是你期望的分析遍的类型,不是分析结果。另一方面,getResult<T>的模板类型参数T仍然代表你期望的分析遍的类型,但getResult的返回类型将是T::Result,Result类型嵌套在类T里。这个差异透露了在分析管理设计方面一个重要的突破性改变:分析结果与分析遍解耦。这将使得某些管理、数据失效更简单且高效。

现在让我们回到新的PassManager。你可能已经注意到在新的PassManager遍里run方法的第二个实参是对应IR单元的一个AnalysisManager实例:

struct MyNewPass : public PassInfoMixin<MyNewPass> {

  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

    AAResults& AAR = FAM.getResult<AAManager>(F);

    // ...

    return PreservedAnalyses::all();

  }

};

因此你只需要使用它,无需通过PassBuilder填充(populating)。

如果你不熟悉新PassManager遍的语法,你可以去到我前面讨论为新PassManager编写一个简单“Hello World”遍的文章

最后,我们准备讨论分析数据失效。我们仅准备谈及在一个普通遍里用得最多的部分,在你开发一个分析遍时,会涉及更多的失效技术(invalidation technique)。

PreservedAnalysis,要求从run方法返回的类型,记录了在这个遍之后仍然有效的分析数据。如果你只是看一下IR而不修改任何东西,那么当然所有的分析在这之后仍然是有效的,这是PreservedAnalyses::all(),我们在前面例子里使用的通用保留集。

但比如,如果你使用某些剖面数据来改变分支的可能性,因而改变了块频率信息,你需要从通用保留集删除它:

#include "llvm/Analysis/BlockFrequencyInfo.h"

 

struct MyNewPass : public PassInfoMixin<MyNewPass> {

  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

    // ...Use some profile data to change BasicBlock frequencies...

    PreservedAnalyses PA = PreservedAnalyses::all();

    PA.abandon<BlockFrequencyAnalysis>();

    return PA;

  }

};

与其从PreservedSet删除分析,实际上声明要保留某些分析更常见。例如,你只知道你的遍不会触及控制流图以及函数里的循环信息:

#include "llvm/Analysis/LoopInfo.h"

 

struct MyNewPass : public PassInfoMixin<MyNewPass> {

  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

    // ...

    PreservedAnalyses PA = PreservedAnalyses::none();

    PA.preserve<LoopAnalysis>();

    PA.preserveSet<CFGAnalyses>();

    return PA;

  }

};

preserve<…>方法声明保留一个分析,其中你的遍要求的分析类型作为模板参数。另一方面preserveSet<…>有点特殊。它将保留一组分析,你需要传递一个分析组类型作为模板参数,它是不同于分析遍的概念。有几个可用的分析集,例如这里的CFGAnalyses,代表了所有控制流相关的分析。

本文仍然有某些重要的话题没有谈及。例如:

  1. 如何编写一个分析遍?
  2. 如何查询一个分析是否被保留?

希望你能从源代码树里找到答案,并享受这个过程?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值