LLVM编译器的基础框架与示例代码分析

发布时间 2023-08-11 04:48:06作者: 吴建明wujianming

LLVM编译器的基础框架与示例代码分析

LLVM的命名最早来源于底层语言虚拟机(Low Level Virtual Machine)的缩写。它是一个用于建立编译器的基础框架,以C++编写。创建此工程的目的是对于任意的编程语言,利用该基础框架,构建一个包括编译时、链接时、执行时等的语言执行器。目前官方的LLVM只支持处理C/C++,Objective-C三种语言,当然也有一些非官方的扩展,使其支持ActionScript、Ada、D语言、Fortran、GLSL、Haskell、Java bytecode、Objective-C、Python、Ruby、Rust、Scala以及C#。

LLVM是一个编译器框架,如图图1.1所示。LLVM作为编译器框架,是需要各种功能模块支撑起来的,可以将clang和lld都看做是LLVM的组成部分,框架的意思是,可以基于LLVM提供的功能开发自己的模块,并集成在LLVM系统上,增加它的功能,或者就单纯自己开发软件工具,而利用LLVM来支撑底层实现。LLVM由一些库和工具组成,正因为它的这种设计思想,使它可以很容易和IDE集成(因为IDE软件可以直接调用库来实现一些如静态检查这些功能),也很容易构建生成各种功能的工具(因为新的工具只需要调用需要的库就行)。

 图1.1  LLVM编译器框架

常见的结构如下图1.2所示。

 图1.2  llvm前中后三端结构图

主要由三个部分组成。

前端:将高级语言例如C或者其他语言转换成LLVM定义的中间表达方式 LLVM IR。例如非常有名的clang, 就是一个转换C/C++的前端。

中端:中端主要是对LLVM IR本身进行一下优化,输入是LLVM, 输出还是LLVM, 主要是消除无用代码等工作,一般来讲这个部分是不需要动的,可以不管他。

后端:后端输入是LLVM IR, 输出是机器码。通常说的编译器应该主要是指这个部分。大部分优化都从这个地方实现。

至此,LLVM架构的模块化应该说的比较清楚了。很大的一个特点是隔离了前后端。

如果想支持一个新语言,就重新实现一个前端,例如华为“仓颉”就有自己的前端来替换clang。

如果想支持一个新硬件,那就重行实现一个后端,让它可以正确的把LLVM IR映射到自己的芯片。

传统编译器:

输入高级语言输出低级语言

主要问题是降低编程难度,其次是优化程序性能

AI 编译器

输入计算图/算子,输出低级语言

主要优化程序性能,其次是降低编程难度

现有 AI 编译器架构

表达上:以 PyTorch 为标杆的表达转换到计算图层 IR 进行优化。
性能上:打开计算图和算子的边界,进行重新组合优化,发挥芯片的算力。

 IR 中间表达

编译器主要分为前后端,分别针对于硬件无关和硬件相关的处理。每一个部分都有自己的 IR (Intermediate Representation,中间表达),每个部分也会对进行优化:

High-level IR:用于表示计算图,其出现主要是为了解决传统编译器中难以表达深度学习模型中的复杂运算这一问题,为了实现更高效的优化所以新设计了一套 IR。

Low-level IR:能够在更细粒度的层面上表示模型,从而能够针对于硬件进行优化,文中将其分为了三类。

Frontend 前端优化

构造计算图后,前端将应用图级优化。因为图提供了计算全局概述,所以更容易在图级发现和执行许多优化。前端优化与硬件无关,这意味着可以将计算图优化应用于各种后端目标。前端优化分为三类:
1、节点级优化,如 Zero-dim-tensor elimination、Nop Elimination
2、块级优化,如代数简化、常量折叠、算子融合
3、数据流级优化,如Common sub-expression elimination、DCE

Backend 后端优化

特定硬件的优化
目标针对特定硬件体系结构获取高性能代码。1)低级IR转换为LLVM IR,利用LLVM基础结构生成优化的CPU/GPU代码。2)使用领域知识定制优化,这可以更有效地利用目标硬件。

自动调整
由于在特定硬件优化中用于参数调整的搜索空间巨大,因此有必要利用自动调整来确定最佳参数设置。1)Halide/TVM允许调度和计算表达分开,使用自动调节来得出较佳配置。2)应用多面体模型 Polyhedral model 进行参数调整。

优化内核库
厂商特定优化内核库,广泛用于各种硬件上的加速DL训练和推理。特定优化原语可以满足计算要求时,使用优化的内核库可显著提高性能,否则可能会受到进一步优化的约束。

 IR 差异

IR 差异:AI 编译器的 IR 与传统编译器的IR所抽象出来的概念和意义并不相同。

AI编译器一般会有 high-level IR,用来抽象描述深度学习模型中的运算,如:Convolution、Matmul 等,
甚至部分会有 Transformer 带有图的结构。

传统编译器相对而言 low-level IR,用于描述基本指令运算,如 load、store 等。有了high-level IR,AI编
译器在描述深度学习模型类 DSL 更加方便。

优化策略差异

AI 编译器面向AI领域,优化时引入更多领域特定知识,从而进行更 high-level,更加
aggressive 优化手段。如:

AI编译器在 high-level IR 执行算子融合,传统编译器执行类似 loop fusion 时候,往往更加保守。缺点是可能会导致调试执行信息跟踪难;

AI编译器可以降低计算精度,比如int8、fp16、bf16等,因为深度学习对计算精度不那么敏感。但传统编译器一般不执行改变变量类型和精度等优化。

 

 

参考文献链接

https://blog.csdn.net/wsq_zqfl/article/details/130937126

https://blog.csdn.net/lovechris00/article/details/129202322