土法炼钢兴趣小组的算法知识备份

【编译器与 MLIR】环境搭建与第一个 MLIR 程序

文章导航

分类入口
compilerarchitecture
标签入口
#mlir#llvm#compiler#build#cmake#mlir-opt#pass#canonicalize#tutorial

目录

环境搭建与第一个 MLIR 程序

前两篇讲了为什么需要 MLIR 和 MLIR 的设计哲学。这一篇动手——从编译 LLVM/MLIR 工程开始,用 mlir-opt 运行第一个 Pass,读懂 IR 变换的全过程。

一、环境要求

MLIR 是 LLVM 项目的一部分,源代码在 llvm-project 仓库的 mlir/ 子目录下。构建 MLIR 等于构建 LLVM(含 MLIR 子项目)。

最低要求 [LLVM Getting Started, CMake documentation]:

项目 最低版本 推荐版本
CMake 3.20.0 3.26+
C++ 编译器 Clang 13 / GCC 11 Clang 17+ / GCC 13+
Python 3.8 3.10+
Ninja(可选) 1.10 最新稳定版

LLVM/MLIR 完整构建需要约 30–50 GB 磁盘空间(Debug 构建更大)和 16 GB 以上内存。Release 构建加上 -DLLVM_TARGETS_TO_BUILD="X86" 只编译一个目标可以大幅缩减编译时间和尺寸。

二、从源码构建

# 1. 克隆 llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-19.1.0    # 使用一个稳定版本

# 2. 创建构建目录
mkdir build && cd build

# 3. CMake 配置
cmake -G Ninja ../llvm \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX=./install \
  -DLLVM_ENABLE_PROJECTS="mlir;clang" \
  -DLLVM_BUILD_EXAMPLES=OFF \
  -DLLVM_TARGETS_TO_BUILD="X86;NVPTX" \
  -DLLVM_ENABLE_ASSERTIONS=ON \
  -DCMAKE_C_COMPILER=clang \
  -DCMAKE_CXX_COMPILER=clang++

# 4. 编译(使用所有可用核心)
ninja -j$(nproc) mlir-opt mlir-tblgen mlir-translate

关键 CMake 选项说明:

选项 说明
LLVM_ENABLE_PROJECTS="mlir;clang" 启用 MLIR 和 Clang(Clang 在后续写 C++ Pass 时需要)
LLVM_TARGETS_TO_BUILD 只编译需要的后端目标,减少编译时间
LLVM_ENABLE_ASSERTIONS=ON 开发期间强烈推荐,断言会捕获大量 IR 不变性违规
CMAKE_BUILD_TYPE=Release 日常开发用 Release 编译更快;需要调试时换成 Debug 或 RelWithDebInfo

编译完成后验证:

./bin/mlir-opt --version
# LLVM 19.1.0
# ...

三、mlir-opt:MLIR 的”瑞士军刀”

mlir-opt 是 MLIR 提供的命令行驱动工具——读取 .mlir 文件,运行指定的 Pass 序列,输出变换后的 IR。它类似于 LLVM 的 opt

基本用法:

mlir-opt input.mlir -pass1 -pass2 -pass3 -o output.mlir

mlir-opt 本身不附带任何 Pass——所有 Pass 都是从动态库中按需注册的。MLIR 内建的方言和 Pass 在构建时自动链接到 mlir-opt 中。自定义 Pass 也可以通过 --load-pass-plugin 加载。

常用 Pass 示例:

# 查看可用 Pass
mlir-opt --help

# 运行规范化 Pass
mlir-opt input.mlir -canonicalize

# 将 scf 控制流转成 cf 控制流
mlir-opt input.mlir -convert-scf-to-cf

# 组合多个 Pass
mlir-opt input.mlir -canonicalize -cse -convert-scf-to-cf

四、第一个 .mlir 文件

创建一个简单的 MLIR 程序:

// first.mlir — 第一个 MLIR 程序
func.func @add_and_multiply(%a: i32, %b: i32, %c: i32) -> i32 {
  %sum = arith.addi %a, %b : i32
  %prod = arith.muli %sum, %c : i32
  return %prod : i32
}

这个文件的组成部分:

MLIR 的类型标注语法是 : type 后缀形式,这与 LLVM IR 不同。%sum = arith.addi %a, %b : i32 读作:%sum 绑定到 arith.addi 的结果,参数是 %a%b,全部类型为 i32

SSA 值的两个来源

MLIR 中的 SSA 值(Value)来自两个地方:

  1. Op 的结果%sum = arith.addi %a, %b : i32 中的 %sum
  2. Block 参数:函数入参 %a%b%c;循环体中的归纳变量也是 Block 参数。

这与 LLVM IR 的 SSA 模型是一致的——所有变量在使用前必须定义,每个变量只赋值一次 [Lattner & Adve, 2004]。

五、运行规范化 Pass

first.mlir 运行规范化(canonicalize):

mlir-opt first.mlir -canonicalize -o canonicalized.mlir

在这个简单的例子中,规范化 Pass 可能没有明显变化——arith.addiarith.muli 没有代数恒等式可以简化。但可以看到 IR 验证已通过、Pass 成功运行。

来一个能展示规范化效果的例子:

// fold_test.mlir — 包含常量折叠机会
func.func @constant_fold() -> i32 {
  %c1 = arith.constant 10 : i32
  %c2 = arith.constant 20 : i32
  %sum = arith.addi %c1, %c2 : i32
  %c3 = arith.constant 5 : i32
  %diff = arith.subi %sum, %c3 : i32
  return %diff : i32
}

运行规范化:

mlir-opt fold_test.mlir -canonicalize

预期输出大致为(以下输出经删减,仅展示最终结果;在 MLIR 19.x 上验证):

func.func @constant_fold() -> i32 {
  %c25_i32 = arith.constant 25 : i32
  return %c25_i32 : i32
}

发生了什么?规范化 Pass 内的常量折叠(fold())先把 arith.addi %c1, %c2 折叠为 arith.constant 30 : i32,再把 arith.subi %sum, %c3 折叠为 arith.constant 25 : i32。中间态可能短暂保留 %c30_i32,但贪婪驱动会迭代到不动点,最终函数体只剩一个常量返回。

这正是 MLIR Pass 的基本工作模式:遍历 IR 中的所有 Op,对每个 Op 检查是否可以规范化,如果可以就替换它

六、从 mlir-opt 到 Pass 管线

mlir-opt 的真正威力在于串联多个 Pass。一个典型的 Pass 管线可能长这样:

mlir-opt input.mlir \
  -canonicalize \
  -cse \
  -convert-scf-to-cf \
  -convert-cf-to-llvm \
  -convert-arith-to-llvm \
  -convert-func-to-llvm \
  -reconcile-unrealized-casts \
  -o output.mlir

每一步的作用:

Pass 功能
-canonicalize 规范化——常量折叠、死代码消除、代数简化
-cse 公共子表达式消除
-convert-scf-to-cf scf.for/scf.if 降为标准 CFG(cf.brcf.cond_br
-convert-cf-to-llvm 将 CFG 控制流映射到 LLVM IR 的控制流
-convert-arith-to-llvm arith Op 映射到 LLVM 方言
-convert-func-to-llvm func.func 映射到 LLVM 函数
-reconcile-unrealized-casts 清理转换过程中产生的类型桥接

整个管线的效果是把一个混合方言(func + arith + scf)的 Module 全部降阶为纯 LLVM 方言的 Module。

七、一个完整的降阶演示

linalg.matmul 开始,演示到 LLVM IR 的降阶过程(以下管线在 MLIR 19.x 上可用;Pass 名称与顺序因版本和配置而异):

# 1. 生成输入的 .mlir 文件(使用 mlir-opt 的 --mlir-print-ir-before-all 可以观察每步前后的变化)
cat > matmul_demo.mlir << 'EOF'
func.func @matmul(%A: tensor<256x256xf32>,
                   %B: tensor<256x256xf32>) -> tensor<256x256xf32> {
  %c0 = arith.constant 0.0 : f32
  %init = tensor.empty() : tensor<256x256xf32>
  %filled = linalg.fill ins(%c0 : f32) outs(%init : tensor<256x256xf32>) -> tensor<256x256xf32>
  %result = linalg.matmul ins(%A, %B : tensor<256x256xf32>, tensor<256x256xf32>)
                          outs(%filled : tensor<256x256xf32>) -> tensor<256x256xf32>
  return %result : tensor<256x256xf32>
}
EOF

# 2. 运行部分降阶 Pass 管线
mlir-opt matmul_demo.mlir \
  -linalg-bufferize \
  -convert-linalg-to-loops \
  -lower-affine \
  -convert-scf-to-cf \
  -finalize-memref-to-llvm \
  -convert-cf-to-llvm \
  -convert-arith-to-llvm \
  -convert-func-to-llvm \
  -reconcile-unrealized-casts

这个过程展示了渐进降阶:tensor 态的 linalg.matmul → bufferized → 嵌套循环 → 仿射循环 → 结构化控制流 → CFG → LLVM IR。每一步都可以通过分别运行来观察中间状态。

八、常见问题

构建失败

Pass 未找到

如果运行 mlir-opt 时报错 unknown pass,检查是否链接了对应的方言库。mlir-opt 在 CMake 中注册了哪些 Pass 就只加载哪些。

.mlir 文件解析失败

检查: - 方言是否在 mlir-opt 中注册(内建方言自动注册,自定义方言需要指定加载路径)。 - 类型语法——MLIR 使用 : type 后缀而不是前缀。 - SSA 值命名——% 前缀,只使用字母数字和下划线。

九、动手实验

按顺序完成以下检查(环境:MLIR 19.x,mlir-optPATH 中):

  1. mlir-opt --version 输出版本号。
  2. first.mlir 运行 -canonicalize,确认无报错。
  3. fold_test.mlir 运行 -canonicalize,确认输出只剩 arith.constant 25
  4. matmul_demo.mlir 运行第七节管线,用 --mlir-print-ir-after-all 观察每步 IR 变化。

若某 Pass 报 unknown pass,对照本文第二节 CMake 选项,确认构建时启用了对应方言。

十、本篇后续

环境搭建完成之后,下一篇进入 MLIR 核心数据结构的 C++ 层面——理解 Operation、Op、Value、Block、Region 的内存布局和继承体系。这是写任何自定义 Pass 的基础。

参考资料

官方文档(A 级)

论文(A 级)

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-09 · compiler / architecture

【编译器与 MLIR】Pass 管理与分析

详解 MLIR 的 Pass 基础设施:OperationPass 与 ModulePass 的分类与适用场景、Pass 依赖管理与流水线构建、Pass 选项系统、多线程执行模型,以及 mlir-opt 的调试命令。

2026-06-09 · compiler / architecture

【编译器与 MLIR】调试与分析工作流

MLIR 编译开发的实用工具箱:mlir-opt 的流水线诊断、IR dump 与 Pass 前后对比、mlir-tblgen 的使用方法、Pass 崩溃定位与 reproducer 生成、性能剖析与 IR 可视化工具的完整操作指南。


By .