首页
论坛
课程
招聘
[翻译]现代化地编写LLVM Pass -- part II
2020-2-14 20:06 5725

[翻译]现代化地编写LLVM Pass -- part II

2020-2-14 20:06
5725

Writing LLVM Pass in 2018-part II

第二部分原文
Analysis —Thing that deserves its own article
在LLVM PassManager中,收集程序的分析任务也被构建为了Passes,它们不会也不应该修改IR内容。而且,和旧版PM相比,在新PM中,分析数据的管理和开发有很大的改变,所以我单独用一篇文章来阐述。这篇文章将会谈论如何用新的 AnalysisManager 来接受分析数据。打开你上手的编辑器,开始吧~

analysis pass

在旧版Pass中,你会发现一个重要的特性就是analysis manager和PassManager高度融合。你可以通过 getAnalysis<...> 方法来获取某个分析数据,这也是 Pass 类的成员之一。然而在新版的PassManager中,analysis manager是一个单独的实例,可以在任何地方独立使用。为了让你理解这个特点,让我们在 旧版Pass 中使用 新版 AnalysisManager .下面是主干代码

bool MyFuncPass::runOnFunction(Function& F) override {
  PassBuilder PB;
  FunctionAnalysisManager FAM;
  PB.registerFunctionAnalyses(FAM);
  // ...
  return false;
}

PassBuilder我们很熟悉,需要他来向PM注册所有可用的Pass。在这之后,这里的FunctionAnalysisManager可以独立使用。 AnalysisManager 负责管理所有已注册的analysis Pass和它们的分析结果。比如,缓存一个analysis pass的结果,直到它对应的IR单元被修改。

 

所以我们应该如何从manager中获取分析结果呢?和旧版Pass中的 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作为我们想获得的分析数据。如果用旧的语法, 你需要先获取Pass的一个实例,然后用其中的一个成员函数来获取分析结果。在新语法中,你只需要用analysis Pass的类型(此处为 AAManager ),连同你的目标IR单元实例(此处为 Function )来调用 getResult<...>

 

这里指出 getAnalysisgetResult 返回类型是不同的。一方面, getAnalysis<T> 的返回类型是 T ,它的模板类型T是你希望的analysis Pass的类型,而不是analysis的结果。另一方面, getResult<T> 中的模板类型T仍代表你希望的analysis pass的类型,但 getResult 的返回类型是 T::Result (T中的Result成员)。这个区别揭示了analyses management设计中的一个重要变化: analysis结果与analysis Pass解耦。这将使得某些管理、数据验证更简单且高效。

AnalysisManager

再回头来看新的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来构建。

analysis data invalidation

最后让我们谈论下分析数据的invalidation. 我们打算仅讨论normal Pass中最常用的部分。
PreservedAanlyses 是run方法所需的返回类型,记录着在Pass后仍然有效的一组分析数据。如果你只是想查看IR而不是修改它们,那么所有的分析在这之后都有效,只需要返回 PreservedAnalyses::all() 即可。

 

但如果你使用一些数据修改了分支的可能性并因此改变了block的频率信息,你需要从被保留的set中移除它们

#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 中移除分析结果,而是声明一些被保留的分析。例如,你知道你的pass在函数内不会修改控制流(control flow graph)和循环信息。如下面的代码

#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<...> 方法声明了单个analysis的保留集(通过向其模板类型中传入analysis 类型)。 而preserveSet<...> 有些区别,它会保留一组analyse,你需要传入一个analysis set的类型(不同于Pass的概念)。有很多可用的analysis set的类型,比如此处的 CFGAnalyses 表示所有的控制流相关的analyse.

 

PS:
还有一些重要话题没有讨论,例如:

  • 如何写一个analysis pass
  • 如何查询一个analysis是否被保留

或许你可以从源码中找到答案 :-)


第五届安全开发者峰会(SDC 2021)议题征集正式开启!

最后于 2020-2-14 20:08 被微笑明天编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (5)
雪    币: 4505
活跃值: 活跃值 (407)
能力值: ( LV6,RANK:96 )
在线值:
发帖
回帖
粉丝
微笑明天 活跃值 2020-2-14 20:08
2
0
板块放错了,望版主移动到翻译板块
雪    币: 2498
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_xghoecki 活跃值 2020-2-15 10:25
3
0
感谢分享
雪    币: 944
活跃值: 活跃值 (5554)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2020-2-26 16:59
4
0
微笑明天 板块放错了,望版主移动到翻译板块
移过来了,建议同一篇文章,文章不长时,用跟帖的形式发上来。
雪    币: 4505
活跃值: 活跃值 (407)
能力值: ( LV6,RANK:96 )
在线值:
发帖
回帖
粉丝
微笑明天 活跃值 2020-3-8 13:53
5
0
Writing LLVM Pass in 2018-part III
原文https://medium.com/@mshockwave/writing-llvm-pass-in-2018-part-iii-d44cd0c2c354
你已经能够写一个pass并用opt来动态加载。但你希望能够在 opt 或者 clang 中自动运行你的pass。本文将介绍一些将你的pass整合进legacy PassManager pipeline的方法。下篇文章将介绍如何整合进clang的命令选项。
为了在Pass pipeline中默认运行Pass,我们需要了解pipeline是如何开始构建的. PassManagerBuilder 这个builder类用于构建legacy PassManager和默认的pass pipeline.但这个builder class文件的路径有些奇怪,在 include/llvm/Transforms/IPO/PassManagerBuilder.h 和 lib/Transform/IPO/PassManagerBuilder.cpp 中,而不是在 IR 或者 PasManager 文件夹中。
在 PassManagerBuilder 类中,有很多顾名思义的函数,比如 addInstructionCombiningPass 和 addFunctionSimplificationPasses 这些函数可以向pipeline中添加某个pass的目录。 除了显式调用它们来更新