论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation
MLIR:针对特定领域计算扩展编译器基础设施
文章目录
1. 论文下载
链接: https://pan.baidu.com/s/1IJ_ZuD4NX5n2TWWEhQefmQ?pwd=rkqw 提取码: rkqw 复制这段内容后打开百度网盘手机App,操作更方便哦
在正式开始阅读MLIR论文前,我们先一起看下TVM社区的大佬们,对MLIR有哪些见解,感受下圈内大佬的视角.
2. TVM关于MLIR的讨论
我在学习MLIR的时候,里面的一些概念让我很是恼火呀, 每一个字我都认识,看完却不知道ta究竟在讲什么.所以我就搜集了网上各路人马对MLIR的博客,评价等.
收集了一些好玩的观点,在这里简短记录一下.
- 观点一: MLIR就是XML
- tianqi大佬说MLIR有点像
XML
, 这个角度很轻奇. 当初我看到这个观点的时候有点不太理解, 后来通读了MLIR的文档后,发现果然不愧是圈内大佬,一言概括了MLIR的核心工作.
- tianqi大佬说MLIR有点像
- 然后有人对大佬提问: 担心MLIR把TVM饭碗打翻了不?
- MLI R的方言在各个领域定义,并且有对应的pass,与LLVM紧密联系,会让MLIR的IR更接近底层.
- TVM的I R相对于MLIR的IR来说就更加上层,我们的pass和IR看起来就像是对MLIR的包装.
- 大佬对其进行了回应
- 大佬还提了一嘴TVM的原则
- 这个好像是知乎的蓝色大佬?
- 有人觉得MLIR这个是对ONNX的回应?
- TVM如何快速支持tensorflow?
- MLIR做前端, TVM做后端: 讨论?
- 以上讨论是2019年的, 截止2022年,有人发帖询问为什么社区还没实现?
我们开始正式了解MLIR吧~
3. 论文正文
0. 摘要
研究背景、研究问题、研究目的和研究的意义
一种构建可重用和可扩展编译器基础设施的新方法。 MLIR 解决了软件碎片问题、异构硬件编译问题,显着降低了构建特定领域编译器以及将现有编译器连接在一起的成本。
MLIR 在不同的抽象层次,不同应用领域,不同硬件目标和不同执行环境下加快代码生成器,翻译器和优化器的设计和实现。
主要贡献:
- 从研究的角度探讨MLIR 的扩展和演进,给出该方法在设计、语义、优化、系统及工程方面的机遇与挑战
- 评估 MLIR 作为一种通用基础设施,在降低构建编译器的成本方面的功效如何,通过多个实际案例来展示未来在编程语言,编译器,执行环境和计算机体系结构方面的科研教育的机会.
- 本文还介绍了 MLIR 的基本原理、其原始设计原则、结构和语义。
1. 导论
研究背景
译器设计是一个有着广泛的知名算法的成熟领域,在代码生成,静态分析,程序变换方面有广泛应用。这个领域也有许多成熟的技术和平台,这些平台使得不同的编译器社区能够大量复用已有技术,包括 LLVM 编译器基础架构,JAVA虚拟机诸如此类。这些成熟系统的一个共同点是“一体多用”(one size fits all)——系统接口只有一套抽象:LLVM 中间表示(IR)基本上可以看成是带向量的C(C with vectors),JVM提供了一个“带垃圾回收的面向对象的类型系统”抽象。这种“一体多用“的方案相当有价值——在实践中,从不同的源语言(C/C++或者JAVA)到这些领域的映射很直接。
与此同时,许多问题在一个high-level 或者 low-level 的抽象层次上更容易建模——如,在LLVM IR 上进行C++的源码级分析就十分困难。我们发现许多语言(包括swift,rust,juia,fortan)设计自己的 IR 以便解决语言/库相关的优化,控制流敏感的类型检查(eg 线性类型),优化 lowering 过程实现等领域特定的问题。类似的,机器学习也采用类似的方法,使用“ML图”(ML graph)作为领域特定抽象。所以MLIR就想要提供一个通用的IR,用于各个领域.
研究问题
虽然特定领域 IR 的开发是一门经过充分研究的艺术,但其工程和实施成本仍然很高。对于这些系统的实施者来说,基础设施的质量并不总是首要任务(或容易证明其合理性)。因此,这可能会导致编译器系统质量较低,包括用户可见的问题,例如编译时间慢、执行错误、诊断质量欠佳、优化代码的调试体验差等。
研究目的
MLIR 项目旨在直接解决这些编程语言设计和实现挑战 -MLIR 项目通过低成本地设计和引入新的抽象层,,并提供“内置”“开箱即用”(in the box)基础设施来解决常见的编译器工程问题。 MLIR 通过
(1) 标准化基于静态单分配 (SSA) 的 IR 数据结构 ( 理解SSA )
(2) 提供用于定义 IR 方言的声明性系统 (ODS,DDR)
(3) 提供广泛的通用基础设施,包括文档,解析和打印的逻辑,位置跟踪,多线程编译支持,PASS 管理等。
本文进一步介绍了 MLIR 设计和实现的总体原则。我们将探讨系统的基本设计点以及它们与总体原则的关系,并分享我们将 MLIR 应用到许多编译问题的经验。
1.1 贡献
大多数 MLIR 系统都是根据众所周知的概念和算法构建的。然而目标和设计已经足够研究它们提供了巨大的研究机会,
设计原则
-
简约
-
将奥卡姆剃刀应用于内置语义、概念和编程接口。通过抽象操作和类型的属性来利用内在和附带的复杂性。指定一次不变量,但始终验证正确性。在给定编译过程的上下文中查询属性。只需很少的内置功能,这就为可扩展性和定制性打开了大门。
-
奥卡姆剃刀的核心思想是:
如无必要,勿增实体。
也就是说,在面对多个解释或假设时,应该选择最简单的那个,不要引入不必要的复杂性。
-
-
-
可追溯性
- 保留而不是恢复信息。声明规则和属性以启用转换,而不是逐步命令式规范。可扩展性伴随着跟踪信息的通用方法,并通过广泛的验证来强制执行。可组合抽象源于“玻璃盒”其属性并分离其角色——类型、控制、数据流等。
-
渐进性
- 过早降低是万恶之源。除了表示层之外,还允许多种转换路径,以降低各个区域的需求。与抽象无关的原则和接口一起,这使得跨多个域的重用成为可能。
虽然这些原则已得到充分确立,但其中一项原则的实施往往以牺牲另一项原则为代价;例如,网络和操作系统堆栈中的分层符合渐进原则,但打破了简约性。具有多层 IR 的编译器也是如此。此外,遵循这些原则可能会损害表达力和有效性;例如,安全关键型和安全系统中的可追溯性涉及限制优化及其攻击性。
简而言之,我们确定了编译器构建的设计和工程原则,以便在支持开放语义生态系统的狭窄中间蓬勃发展。我们发现可以在不限制表达能力的情况下控制复杂性,从而允许跨领域进行快速 IR 设计探索和整合,而这两者在生产系统中都严重缺乏。
本文的贡献是
- 根据经过验证的设计和工程原理,构建可扩展和模块化编译器系统;
- 遵循这些原则的新型编译器基础设施的描述,具有重要的工业和研究应用;
- 探索了几个 MLIR 在多个领域的应用.分享基于MLIR 基础设施的开发系统的经验。
1.2 MLIR 为了解决什么问题?
MLIR 的工作始于认识到现代机器学习框架由许多不同的编译器、图形技术和运行时系统组成.而这些框架并不共享通用的基础设施或设计原则。这以多种用户可见的方式表现出来,包括糟糕的错误消息、边缘情况下的故障、不可预测的性能以及难以推广堆栈以支持新硬件。
我们很快意识到整个编译器行业也存在类似的问题:像 LLVM 这样的现有系统非常糟糕。
成功地统一和集成了一系列不同语言的工作,但高级语言通常最终会构建自己的高级 IR 并重新发明相同类型的技术以实现更高级别的抽象,与此同时,LLVM 社区在并行结构的表示以及如何共享前端降低基础设施(例如 C 调用约定或 OpenMP 等跨语言功能)方面遇到了困难,但没有令人满意的解决方案。
面对这一挑战,考虑到我们无力实现N个改进的编译器实例,我们决定寻求更通用的解决方案:投资高质量的基础设施,使多个领域受益,逐步升级现有系统,更容易解决紧迫的问题诸如专用加速器的异构编译等问题,并提供新的研究机会。现在,我们在构建和部署基于 MLIR 的系统方面积累了大量经验,我们能够回顾其基本原理和设计,并讨论为什么要追求这个方向。
2. 设计准则
-
尽可能少的内置,尽可能多的定制(Little Builtin, Everything Customizabl)
- 该系统基于最少数量的基本概念,使大部分中间表示完全可定制。 IR 中最常见的一些抽象(类型、操作和属性)应该用于表达其他所有内容,从而允许更少且更一致的抽象,从而易于理解、扩展和采用。从广义上讲,可定制性确保系统能够适应不断变化的需求,并且更有可能适用于未来的问题。从这个意义上说,我们应该将 IR 构建为一个丰富的基础设施,其中包含可重用的组件和支持其中间语言的语法和语义的编程抽象。
- 定制的成功标准是表达多种抽象的可能性,包括机器学习图、AST、多面体等数学抽象、控制流图 (CFG) 和指令级 IR(例如 LLVM IR)都无需将这些抽象概念硬编码到系统中。
- 当然,由于抽象兼容性差,可定制性会产生内部碎片的风险。虽然不太可能有纯粹的技术解决方案,但系统应该鼓励人们设计可重用的抽象,并假设它们将在其初始范围之外使用。
-
SSA 和区域(region)
- SSA形式 是编译器 IR 中广泛使用的表示形式。它提供了许多优点,包括使数据流分析变得简单和稀疏,因其与连续传递风格的关系而被编译器社区广泛理解,并在主要框架中建立。因此,IR 强制执行 SSA 的基于值的语义、其引用透明度和算法效率,所有这些都被认为对于现代编译器基础设施至关重要。然而,虽然许多现有的 IR 使用平坦的线性化 CFG,但表示更高级别的抽象推动将嵌套区域引入为 IR 中的一流概念。这超越了传统的区域形成,提升了更高级别的抽象(例如循环树),加速了编译过程或提取指令,或 SIMD 并行性 [4]、[5]、[6]。为了支持异构编译,系统必须支持结构化控制流、并发构造、源语言中的闭包的表达以及许多其他目的。一项具体的挑战是在嵌套区域上进行基于 CFG 的分析和转换。
- 在此过程中,我们是内置牺牲了LLVM 的正规化( normalization )甚至规范化( canonicalization )特性。可以将复杂的数据结构和控制结构翻译(lowering)成一个小的正规化的集合是控制编译器复杂的关键。。具有pre-header, header,latch,body的规范循环结构是前端语言中各种循环结构的线性化控制流表示的原型案例。我们的目标是为用户提供一种选择:根据感兴趣的编译算法、编译流程中的传递,嵌套循环可以捕获为嵌套区域或线性化控制流。通过提供这样的选择,我们摆脱了 LLVM 的仅规范化方向,同时保留了在重要时处理更高级别抽象的能力。反过来,利用这些选择会引发有关如何控制抽象规范化的问题,这就是下一段的目的。
-
渐进式的翻译(lowering)渐进性]:
-
系统需要保留分析或优化性能所需的信息和结构。一旦降低抽象语义,尝试恢复抽象语义是脆弱的,并且在低级别硬塞这些信息通常是侵入性的(例如,在使用调试信息来记录结构的情况下,需要重新访问所有通道)。相反,系统应该保持计算结构并逐渐降低到硬件抽象。然后,结构的丢失是有意识的,并且仅在不再需要结构来匹配底层执行模型的情况下发生。例如,系统应在相关转换过程中保留结构化控制流,例如循环结构;删除这个结构,即降低到 CFG (
Control Flow Graphs (CFGs)
)本质上意味着不会执行利用该结构的进一步转换。在生产编译器中对并行计算结构进行建模的最新技术突显了该任务通常是多么困难 [7]、[8]。 -
作为推论,在同一 IR 中混合不同的抽象级别和不同的概念是允许一部分表示保留在较高级别的抽象中而另一部分则降低的关键。例如,这将使自定义加速器的编译器能够重用系统定义的一些高级结构和抽象以及特定于加速器的原始标量/向量指令。
-
另一个推论是系统应该支持逐步降低,从较高级别的表示到最低级别,沿着多个抽象以小步骤执行。对多个抽象级别的需求源于编译器基础设施必须支持的各种平台和编程模型。
以前的编译器已经在其管道中引入了多个固定的抽象级别,例如Open64 WHIRL 表示形式 [9] 有五个级别,Clang 编译器也是如此,它从 AST 降低到 LLVM IR、SelectionDAG、MachineInstr 和 MCInst。需要更灵活的设计来支持可扩展性。这对变换的相序具有深远的影响。随着编译器专家开始实现越来越多的转换过程,这些过程之间的复杂交互开始出现。早期的研究表明,组合优化过程可以让编译器发现有关程序的更多事实。组合传递的好处的第一个例子是混合常量传播、值编号和无法访问的代码消除[10]。
-
-
声明和验证[简洁性和可追溯性]
- 定义表示修饰符应该像引入新的抽象一样简单;编译器基础设施的好坏取决于它支持的转换。常见的转换应该可以实现为以机器可分析的格式以声明方式表达的重写规则,以推理重写的属性,例如复杂性和完成度。重写系统因其健全性和效率而被广泛研究,并应用于从类型系统到指令选择的众多编译问题。由于我们的目标是前所未有的可扩展性和增量降低功能,因此这为将程序转换建模为重写系统开辟了多种途径。它还提出了一些有趣的问题,例如如何表示重写规则和策略,以及如何构建能够通过多个抽象级别引导重写策略的机器描述。系统需要解决这些问题,同时保持可扩展性并强制执行单调和可再现的行为。
- 生态系统的开放性还需要广泛的验证机制。虽然验证和测试对于检测编译器错误和捕获 IR 不变量很有用,但需要强大的验证方法和工具在可扩展的系统中得到了增强。该机制的目标应该是使其易于定义,并尽可能具有声明性,并提供单一的事实来源。长期目标是重现翻译验证 [11]、[12]、[13]、[14] 和现代编译器测试方法 [15] 的成功。两者目前都是可扩展编译器上下文中未解决的问题。
-
源位置跟踪[可追溯性]:
-
操作的详细信息(包括其原始位置和应用的转换)应该可以在系统内轻松追踪。这样做的目的是解决复杂编译系统中常见的缺乏透明度的问题,在这种情况下,几乎不可能理解最终表示是如何从原始表示构建的。
在编译安全关键和敏感应用程序时,这尤其成问题,其中跟踪降低和优化步骤是软件认证程序的重要组成部分[16]。当对安全代码(例如对隐私敏感数据进行操作的加密协议或算法)进行操作时,编译器经常面临看似冗余或繁琐的计算,这些计算嵌入了源程序的功能语义未完全捕获的安全或隐私属性:此代码可能会阻止暴露侧通道或强化代码以抵御网络或故障攻击。优化可能会改变或完全无效此类保护[17];这种缺乏透明度在安全编译中被称为 WYSINWYX [18]。将高级信息准确传播到较低级别的一个间接目标是帮助支持安全且可追踪的编译。
-
3. IR 设计
MLIR 具有通用文本表示,支持 MLIR 的可扩展性并充分反映内存中表示,这对于可追溯性、手动 IR 验证和测试至关重要。可扩展性带来了冗长的负担,这可以通过 MLIR 支持的自定义语法来补偿;例如,图 7 说明了图 3 的用户定义语法。
Operations
: MLIR中的语义单位是“操作”,简称Op。在这个系统中,从“指令”到“函数”再到“模块”的所有内容都被建模为 Ops。 MLIR 没有固定的操作集,但根据简约和“一切可定制”原则,允许(并鼓励)用户定义的扩展。该基础设施提供了用于定义基于 TableGen [19] 的 Ops 的声明性语法,如图 5 所示。
Ops(参见图 4)有一个唯一的操作码,一个标识操作及其方言的字符串。操作获取并产生零个或多个值,分别称为操作数和结果,并且这些值以 SSA 形式维护。值代表运行时的数据,
总体的论文看下来,但从字面意思,从工程师的角度,还是很难理解MLIR设计的玄妙之处的.
我还是需要从官方的dmeo里面,来感受这些设计的奇妙之处.
这一部分另写一篇文章来介绍.