依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 55|回复: 0

现代化地编写LLVM Pass -- part I

[复制链接] 主动推送

1万

主题

1万

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
12008
发表于 2024-10-24 09:46:22 | 显示全部楼层 |阅读模式
现代化地编写LLVM Pass -- part I
Writing LLVM Pass in 2018-part I译者注:
最近在写pass的东西,找了很多文章来看,本来也没注意到新版的PassManager,看到了mudium上的一篇文章,发现和现有的pass编写变化还是蛮大的,鉴于适配新版本llvm不太方便(api变化蛮大),故翻译此文以作记录。之后会把剩下的几篇翻译下。

LLVM文档中有详细的关于编写PASS的教程,不过那是基于老版本的PassManager的,具体可参见[官方教程]。建议按照教程了解写一个简单pass后阅读。(http://llvm.org/docs/WritingAnLLVMPass.html)
  • 新版本的PassManager还没有纳入llvm9,但从dev list看,很可能融入三月发布的llvm10。
  • 新版本的PassManager一定层面上简化了pass的注册机制,对未来高版本llvm作适配,适配new PM必不可缺。目前,除了看PM源码,很少有针对new PM作阐述的文章,官网对此还处于更新阶段。
    本文有四个系列,即四篇文章。在第一篇里面,我会把序章一并融入其中介绍翻译。
  • 注:文中翻译的时候会使用一些缩写,必要的地方会保留英文原文。例如PM是Pass Manager的缩写
序章
遵循官方教程,你已经写过一个HelloWorld Pass……

现在你希望学习更多、看得更多……

因此你深入神奇的LLVM源代码,为了理解如何用pass实现那些著名而强大的优化……

但奇怪的pass构造语法让人困惑,它和你刚学的教程一点都不像,也没有记录在官方网站的任何地方——这正是我几年前遇到的,当时我仍然是一个LLVM新手,我之前仅仅听说过LLVM pass。

作为LLVM里最重要的核心组件之一,Pass与PassManager系统的改进始于2014,起因于许多优化机会缺失的情形以及编译速度退化。旧的pass实现与新的pass实现并存,整个代码也开始反映这些改变。通过提供特殊的命令行选项,使用者转向新的pass manager,同时旧的pass manager仍然是默认使用的。

不过,正如前言部分指出的,目前没有官方文档谈及这个革新。虽然新的PM以及其文档越来越接近它们的发布日期,我仍然希望为那些热切想知道代码树里现在发生了什么的热心者写一篇简单的教程。我将这个系列分为四部分:
  • part I:用新的PM风格编写HelloWorld Pass。
  • part II:如何使用新的AnalysisManager来替换旧的getAnalysis<…>()语法。
  • part III:pass如何整合进LLVM源代码树。剧透:LLVM源代码树里的(旧的)pass要求一些额外的语法,这将告诉你如何做。我知道在一篇文章里谈论旧的PM有点奇怪。但考虑到LLVM项目巨大的体量以及在业界广泛的应用,迁移绝对是需要时间的。因此只需把这部分视作对那些对源代码树的演进(in-tree development)感兴趣的人迟来的教程(事实上接近10年)。
  • part IV:如何添加一个clang选项来使用我构建在LLVM里酷酷的特性?这部分将告诉你如何做。尽管这看起来与前面关于新PM无关(be orthogonal to),它总是我的“应该被官方归档的教程/提示”之一。就是这样。
我将不会讨论设计细节。我将从一个PM使用者的角度来写这篇文章,关注在中端(middle-end)的优化以及分析开发。因此,不会涉及后端。
用新的PM风格编写HelloWorld Pass
通过一个简单的HelloNewPass的示例,本文将对新版 PassManager 系统进行概述。

大多有关写一个传统LLVM Pass的教程都会告诉你从写一个继承llvm:ass类开始,比如 llvm:FunctionPass .然后实现一些必要的函数,比如 bool FunctionPass::runOnFunction(Function &F) <br />现在你要做的第一步也类似:
1
2
3
4
5
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
    return PreservedAnalyses::all();
  }
};

我们同样需要继承父类,但是这次,PassInfoMixin没有包含任何我们需要覆写的虚函数。他出现在这里的唯一原因就是提供一个默认的 HelloNewPMPass::name() 的实现。我们只使用 run 方法的参数(即 Function 和 FunctionAnalysisManager )来表示我们具体要实现的IR单元。

run 方法,正如其名字的意思,和FunctionPass::runOnFunction, ModulePass::runOnModule等过去的一些方法相同。但它不在是一个虚函数,所以这里不需要 override 关键字。而且,返回值的类型也不同,多了一个 FunctionAnalysisManager 的函数参数。两者都和分析框架有关。前者被用于分析数据合法性,后者用于接收分析的结果,和以往pass中的 getAnalysis<…>() 类似。我们将把分析的内容留到partII。<br />因为我们现在不打算修改IR,我们仅返回 PreservedAnalyses::all() 来告诉框架,运行这个pass吼,所有的分析结果是一致的。<br />接着我们添加一些缺少的片段来丰富run方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "llvm/IR/PassManager.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
    if(F.hasName())
      errs() << "Hello " << F.getName() << "\n";
    return PreservedAnalyses::all();
  }
};
} // end anonymous namespace

这就是构建一个新遍时我们所需要做的!然后我们只需注册它。下面是注册代码
1
2
3
4
5
6
7
extern "C" ::llvm:assPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &B) {...}
  };
}

和传统pass注册不同的是,我们不需要使用 RegisterPass<…> 实例或是别的Pass registry静态函数。我们仅仅从函数中返回了pass的入口点,也就是这里的 llvmGetPassPluginInfo 。return后的大括号内将会构建一个llvm:assPluginInfo 对象,包含一些pass的信息(HelloNewPMPass是pass的名字,v0.1是pass的版本)。最后一个地方,lambda函数中的PassBuilder用于构建PassManager pipeline。我们打算用它将我们的pass插入到pipeline中合适的位置。

让我们先看下如何用opt工具运行我们基于新版PassManager的pass。新版的PassManager使用字符串来描述我们需要使用的Pass的pipeline,而不需要命令行选项来描述pass
1
opt -passes="sroa,instcombine" foo.ll

它会首先运行SROA,然后运行instruction combiner. 当然,还有很多复杂的语法,但我们只需要知道可以从这种文本描述构造Pass的pipeline。

如果我们可以拦截上述的parsing过程,就可以在某个特定的Pass名字出现的地方插入我们的Pass(即可以在命令行解析的时候自定义我们的pass加载)。看下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extern "C" ::llvm:assPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &B) {
      PB.registerPipelineParsingCallback(
        [](StringRef PassName, FunctionPassManager &FPM, ...) {
          if(PassName == "hello-new-pm-pass"){
            FPM.addPass(HelloNewPMPass());
            return true;
          }
          return false;
        }
      );
    }
  };
}

即 registerPipelineParsingCallback .你可以注册一个回调函数,当一个pass的名字被文本解析的时候,将会执行这个回调函数。在这里,当我们遇到pipeline中的 hello-new-pm-pass 字符串的时候,将会添加我们的HelloNewPMPass。于是,我们用opt来运行我们的pass:
1
opt -passes="hello-new-pm-pass"

最后,附上完整的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
  PreservedAnalyses run(Function &F,
                        FunctionAnalysisManager &FAM) {
    if(F.hasName())
      errs() << "Hello " << F.getName() << "\n";
    return PreservedAnalyses::all();
  }
};
} // end anonymous namespace

extern "C" ::llvm:assPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
    [](PassBuilder &B) {
      PB.registerPipelineParsingCallback(
        [](StringRef Name, FunctionPassManager &FPM,
           ArrayRef<assBuilder:ipelineElement>) {
          if(Name == "hello-new-pm-pass"){
            FPM.addPass(HelloNewPMPass());
            return true;
          }
          return false;
        }
      );
    }
  };
}

然后和构建传统pass一样构建这个pass即可。(传统pass构建可参考http://llvm.org/docs/WritingAnLLVMPass.html#setting-up-the-build-environment)<br />最后通过下面的命令运行我们的pass
1
2
3
opt -disable-output \
    -load-pass-plugin=/path/to/libHelloNewPMPass.so
    -passes="hello-new-pm-pass" foo.ll

为了简化构建新Pass的过程,我已经创建了一个样例生成工具https://github.com/mshockwave/generator-llvm.

PassBuilder 在新的 PassManager 中非常重要。还有很多主题没有谈论,例如analysis pipeline, pipeline extension point 和一些有趣的textual pipeline representation语法.

相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员5折;永久VIP免费
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2024-11-23 22:54

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

快速回复 返回顶部 返回列表