MLIR入门

文章目录

  • [Build System (Getting Started)](#Build System (Getting Started))
  • [Running and Testing a Lowering](#Running and Testing a Lowering)
    • [Two example programs](#Two example programs)
    • [Lowerings and the math-to-funcs pass](#Lowerings and the math-to-funcs pass)
  • 参考

Build System (Getting Started)

采用cmake,而不采用bazel

MLIR 自 LLVM 14 (2022) 起正式进入 LLVM 稳定发布系列,所有稳定版中 Clang 与 MLIR 完全配套、ABI 稳定.

LLVM/Clang 版本 发布时间 MLIR 状态 推荐度
LLVM 19.x 2026 最新 MLIR 功能完整、稳定、bug 少 ★★★★★
LLVM 18.x 2025 稳定 工业级稳定,Ubuntu 22.04 首选 ★★★★★
LLVM 17.x 2024 稳定 成熟稳定 ★★★★☆
LLVM 16.x 2023 稳定 MLIR 核心框架成熟、API 稳定 ★★★★☆
LLVM 15.x 2022 稳定 可用、API 基本稳定 ★★★☆☆
LLVM 14.x 2022 稳定 首个官方稳定版(基础可用) ★★★☆☆
LLVM ≤ 13 更早 MLIR 未正式进入稳定版 ❌ 不推荐

LLVM 官方 APT 源

  • 测试系统:ubuntu22.04

  • 故采用llvm-18

    安装密钥与源

    wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
    sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main"
    sudo apt update

    安装 Clang + MLIR 开发包(完整工具链)

    sudo apt install -y
    clang-18 clang-tools-18 libclang-18-dev
    llvm-18 llvm-18-dev llvm-18-tools
    mlir-18-tools libmlir-18-dev

Running and Testing a Lowering

核心机制:传统编译器仅依靠单一中间表示(IR)完成程序转换、优化与机器码生成;而 MLIR 依托多类高低级方言与逐步降级的设计,拆分编译流程,分步完成代码下沉。

流程特点:MLIR 通过专属降级步骤逐层将高级方言转为低级方言,过程中穿插优化;且降级与优化流程无严格界限,统一为 IR 重写模块。

设计初衷:MLIR 诞生的重要原因是适配多面体优化、线性代数等专项优化需求;区别于传统编译器丢失循环高层结构、重构难度大的痛点,MLIR 在高级方言中保留程序原始结构以便高效优化,再于降级过程中舍弃结构。

Two example programs

例子:用32 位整数前导零计数的两段 MLIR 代码作为实例,入门理解 MLIR 基础语法与核心概念。

复制代码
func.func @main(%arg0: i32) -> i32 {
  %0 = math.ctlz %arg0 : i32
  func.return %0 : i32
}

代码直接调用math方言的ctlz原生算子,一行完成前导零计算,写法简洁高层。

MLIR 基础语法规则

  • 变量前缀%、函数前缀@;值后跟: 类型显式标注类型。
  • 算子格式固定:方言.算子(如math.ctlz),不同方言自定义自身语法解析规则。

关键核心概念

  • func 也是独立方言,函数定义func.func属于标准 MLIR 算子。
  • Region(区域):大括号包裹的代码范围;Basic Block(基本块):单入口、单出口的指令块,对标编译器经典基础块,用于控制流、跳转逻辑。

在 MLIR 中,随着程序逐渐降低到某个最终后端目标,多个方言通常会在同一程序中共存

例子:该程序的第二个版本实现了 ctlz 函数的软件版本并调用了它

复制代码
func.func @main(%arg0: i32) -> i32 {
  %0 = func.call @my_ctlz(%arg0) : (i32) -> i32
  func.return %0 : i32
}
func.func @my_ctlz(%arg0: i32) -> i32 {
  %c32_i32 = arith.constant 32 : i32
  %c0_i32 = arith.constant 0 : i32
  %0 = arith.cmpi eq, %arg0, %c0_i32 : i32
  %1 = scf.if %0 -> (i32) {
    scf.yield %c32_i32 : i32
  } else {
    %c1 = arith.constant 1 : index
    %c1_i32 = arith.constant 1 : i32
    %c32 = arith.constant 32 : index
    %c0_i32_0 = arith.constant 0 : i32
    %2:2 = scf.for %arg1 = %c1 to %c32 step %c1 iter_args(%arg2 = %arg0, %arg3 = %c0_i32_0) -> (i32, i32) {
      %3 = arith.cmpi slt, %arg2, %c0_i32 : i32
      %4:2 = scf.if %3 -> (i32, i32) {
        scf.yield %arg2, %arg3 : i32, i32
      } else {
        %5 = arith.addi %arg3, %c1_i32 : i32
        %6 = arith.shli %arg2, %c1_i32 : i32
        scf.yield %6, %5 : i32, i32
      }
      scf.yield %4#0, %4#1 : i32, i32
    }
    scf.yield %2#1 : i32
  }
  func.return %1 : i32
}

MLIR 代码拆分出自定义 my_ctlz 函数,主函数 main 调用它,纯软件逻辑实现 32 位整数前导零计数,不再直接使用高阶 math.ctlz 算子,是 MLIR 高阶算子向底层方言降级的示例。

核心算法

  • 特殊判定:输入为 0 时直接返回 32(32 位全零),避免死循环;
  • 核心逻辑:循环逐位左移输入数,判断是否变为负数(有符号数最高位为 1),累计移位次数,即为前导零个数

用到的 MLIR 方言

  • arith:底层算术、常量定义、数值比较、加减、左移等基础运算;
  • scf:结构化控制流,实现 if 分支、for 循环,通过 scf.yield 传出代码块 / 循环的返回值。

关键语法特性

  • 多元组:%2:2 绑定双值结果,%4#1 读取元组第二个分量;
  • 类型区分:i32 固定位宽整型,index 平台相关整型,专用于循环计数、边界、数组索引。

Lowerings and the math-to-funcs pass

例子:tests/ctlz.mlir

复制代码
func.func @main(%arg0: i32) {
  %0 = math.ctlz %arg0 : i32
  func.return
}

mlir-opt 工具对任何 MLIR 代码进行无参数运行,它会解析代码,验证其格式是否正确,并进行一些轻微的规范化处理后将其输出。在本例中,它会将代码封装在一个 module 中,这是一种命名空间隔离机制。

复制代码
➜ externals git:(main) mlir-opt-18 /home/wangji/code/mlir-tutorial/tests/ctlz.mlir
module {
  func.func @main(%arg0: i32) {
    %0 = math.ctlz %arg0 : i32
    return
  }
}

--convert-math-to-funcs=convert-ctlz:作用是 将 Math dialect 中的 ctlz 操作转换为具体的函数实现。

  • 使用 arith 和 scf dialect 实现了 count leading zeros 的完整逻辑

    ➜ externals git:(main) mlir-opt-18 --convert-math-to-funcs=convert-ctlz /home/wangji/code/mlir-tutorial/tests/ctlz.mlir
    module {
    func.func @main(%arg0: i32) {
    %0 = call @__mlir_math_ctlz_i32(%arg0) : (i32) -> i32
    return
    }
    func.func private @__mlir_math_ctlz_i32(%arg0: i32) -> i32 attributes {llvm.linkage = #llvm.linkage<linkonce_odr>} {
    %c32_i32 = arith.constant 32 : i32
    %c0_i32 = arith.constant 0 : i32
    %0 = arith.cmpi eq, %arg0, %c0_i32 : i32
    %1 = scf.if %0 -> (i32) {
    scf.yield %c32_i32 : i32
    } else {
    %c1 = arith.constant 1 : index
    %c1_i32 = arith.constant 1 : i32
    %c32 = arith.constant 32 : index
    %c0_i32_0 = arith.constant 0 : i32
    %2:2 = scf.for %arg1 = %c1 to %c32 step %c1 iter_args(%arg2 = %arg0, %arg3 = %c0_i32_0) -> (i32, i32) {
    %3 = arith.cmpi slt, %arg2, %c0_i32 : i32
    %4:2 = scf.if %3 -> (i32, i32) {
    scf.yield %arg2, %arg3 : i32, i32
    } else {
    %5 = arith.addi %arg3, %c1_i32 : i32
    %6 = arith.shli %arg2, %c1_i32 : i32
    scf.yield %6, %5 : i32, i32
    }
    scf.yield %4#0, %4#1 : i32, i32
    }
    scf.yield %2#1 : i32
    }
    return %1 : i32
    }
    }

MLIR 降级逻辑
同一 ctlz(前导零计数)程序有高低两个版本:硬件支持ctlz指令时,直接将高层math.ctlz映射为机器指令;无硬件指令时,降级(Lowering) 会把该高阶运算,拆解翻译为func/arith/scf这类底层方言与基础操作,用软件逻辑实现。

mlir-opt 工具作用

MLIR 核心命令行工具,是执行各类编译 Pass(转换 / 降级)的入口:

无参数运行:解析、校验 MLIR 代码,格式化并包裹module做命名空间隔离;

携带参数(如--convert-math-to-funcs):执行内置降级 Pass,自动把高阶算子转为底层代码实现。

Pass 调度方式

每个 MLIR 转换 Pass 都有独立命令行参数,支持单独调用,也可通过--pass-pipeline串联多个 Pass、按顺序流水线执行。

参考

相关推荐
Albert Edison5 分钟前
【Redis】Centos7.9 安装 Redis 5 教程
数据库·redis·缓存
云计算磊哥@22 分钟前
运维开发宝典026-MySQL02数据库表操作
运维·数据库·运维开发
小二·37 分钟前
Redis 内存溢出(OOM)排查与恢复实战
数据库·redis·bootstrap
pqk6V6Vep38 分钟前
Redis 分布式锁进阶第一篇讲解
数据库·redis·分布式
giaz14n9X1 小时前
Redis 分布式锁进阶第六十一篇
数据库·redis·分布式
是一个Bug1 小时前
MongoDB:像搭积木一样存数据
数据库·mongodb
ULIi096kr1 小时前
MySQL解决Too many connections报错:连接数爆满排查、优化与永久解决方案
数据库·mysql·adb
SL-staff2 小时前
(一)数据源配置 —— JVS-Rules规则引擎 V2.5 操作说明介绍
数据库·jar·规则引擎·数据源·jvs-rules·api 接口·jvs低代码
摇滚侠3 小时前
Spring 零基础入门到进阶 基于 XML 管理 Bean 14-28
xml·数据库·spring
Metaphor6923 小时前
使用 Python 给 PDF 设置背景色或背景图
数据库·python·pdf