
Arith Dialect不可或缺的基础算术与逻辑运算抽象在前面探讨的底层方言中无论是控制流的循环SCF、多维内存的读写MemRef、寄存器级的向量化Vector还是直达硬件的底层表示LLVM / NVVM我们都会频繁看到诸如arith.constant或arith.addf的身影。这就是Arith Dialect算术方言。在 MLIR 的底层方言生态中它扮演着“粘合剂”和“基石”的角色。它不负责复杂的控制逻辑也不管数据存在哪里它唯一专注的事情就是定义最纯粹、硬件无关的基础数学运算和位逻辑操作。为什么在 MLIR 中算术运算要独立成方言在传统的编译器如经典的 LLVM中基础的加减乘除是直接硬编码在核心 IR 里的指令。而在 MLIR 的开放解耦设计中为了让核心框架尽可能轻量即便是像add这样基础的操作也被剥离了出来。Arith Dialect 的精妙之处在于它与目标平台CPU/GPU完全无关同时它不绑定任何特定的数据容器。它可以无缝地应用在各种各样的底层数据类型上作用于标量如i32、f64用于计算循环边界或单点数据。作用于多维虚拟向量如vector4x8xf32为后端生成 SIMD 向量化代码提供数学语义。作用于高维张量如tensor1x256xf32在高级别图优化中表达元素级Element-wise的纯数学变换。核心操作Operations的一对一精细拆解Arith Dialect 的操作划分非常严密特别是为了后续能平滑降级到硬件指令它将整数Integer与浮点数Floating-point的算术运算在 Op 层面上做了显式的隔离。1. 常量定义arith.constant在编译期创建一个纯粹的常量值。这是最常用的底层 Op。%c42 arith.constant 42 : i32 // 32位整型常量 %f_pi arith.constant 3.14159265 : f32 // 单精度浮点常量 %vec arith.constant dense1.0 : vector4xf32 // 稠密向量常量2. 整数基础算术与位运算由于计算机底层的整数运算区分符号位且有溢出截断处理Arith 进行了精确抽象**arith.addi/arith.subi/arith.muli**基础的整数加、减、乘。**arith.divsi/arith.divui**带符号整数除法Signed与无符号整数除法Unsigned。**arith.remsi/arith.remui**带符号与无符号的求余数运算。**arith.andi/arith.ori/arith.xori**标准的按位与、按位或、按位异或。**arith.shli/arith.shrsi/arith.shrui**逻辑左移、算术右移保留符号位、逻辑右移。3. 浮点数算术运算浮点数没有位移和位逻辑的概念其核心是符合 IEEE 754 标准的物理运算**arith.addf/arith.subf/arith.mulf/arith.divf**浮点数的加、减、乘、除。arith.remf浮点数求余。arith.negf浮点数取负类似于x−xx -xx−x。4. 关系比较与选择arith.cmpi整数比较。它通过属性Predicate来指定具体的比较条件如eq等于、ne不等于、slt带符号小于、uge无符号大于等于。// 判断 %a 是否小于 %b (带符号) %is_less arith.cmpi slt, %a, %b : i32arith.cmpf浮点数比较。同样通过属性支持如oeq有序且等于、olt有序且小于等条件。arith.select条件选择器。根据一个布尔值i1从两个操作数中选择一个返回。它对应硬件级别的条件移动指令如 x86 的cmov从而避免产生破坏流水线的控制流分支。// 如果 %cond 为真返回 %true_val否则返回 %false_val %res arith.select %cond, %true_val, %false_val : f325. 类型转换Casting底层计算涉及大量的位宽拓展、截断以及整浮转换**arith.extsi/arith.extui**符号扩展与零扩展低位宽整数向高位宽整数转换。arith.trunci整数截断高位宽向低位宽转换。**arith.sitofp/arith.uitofp**带符号/无符号整数转换为浮点数。**arith.fptosi/arith.fptoui**浮点数截断转换为带符号/无符号整数。**arith.extf/arith.truncf**浮点数精度的提升如f16转f32与压缩如f32转bf16。生产环境中的高频协同机制在一个完整的全线降级流程中Arith Dialect 的运算通常夹杂在内存管理和循环变换的缝隙中。以下是一个将基础数学计算融合在向量载入与内存交互中的完整工业片段// 业务目标读取二维内存中的一行数据将其每个元素乘以 2.0 并加上偏置最后写回。 llvm.func vector_math_pipeline(%buffer: memref100x8xf32, %row_idx: index) { // 1. 在编译期准备好数学运算所需的标量与向量常量 %c0 arith.constant 0 : index %f_zero arith.constant 0.000000e00 : f32 %f_scale arith.constant dense2.000000e00 : vector8xf32 %f_bias arith.constant 1.500000e00 : f32 // 2. 使用 Vector 方言从多维内存块中拉取一行数据包含 8 个单精度浮点数 %raw_vec vector.transfer_read %buffer[%row_idx, %c0], %f_zero : memref100x8xf32, vector8xf32 // 3. 混合执行 Arith 向量算术运算先做向量与向量的按元素乘法Element-wise Mul %scaled_vec arith.mulf %raw_vec, %scale : vector8xf32 // 4. 将标量偏置 %f_bias 广播Splat成全 1.5 的虚拟向量 %bias_vec vector.splat %f_bias : vector8xf32 // 5. 执行向量与向量的按元素加法Element-wise Add %final_vec arith.addf %scaled_vec, %bias_vec : vector8xf32 // 6. 将最终计算完的高性能向量结果一次性刷回多维物理内存 vector.transfer_write %final_vec, %buffer[%row_idx, %c0] : vector8xf32, memref100x8xf32 llvm.return }Arith 方言承上启下的关键价值极大简化了公共子表达式消除CSE与常数折叠Constant Folding无论是高层的tensor运算还是底层的寄存器级运算只要它们使用了arith的操作MLIR 的标准优化 Pass 就能用同一套代码逻辑完成跨抽象层级的常数合并和无效计算剔除。提供了完美的降级缓冲如果直接从高级张量转换到硬件对应的llvm.fadd由于 LLVM 指令绑定的机器属性过于密集会使中间的转换 Pass 极其臃肿。Arith作为一个纯净的、没有平台偏见的数据运算中间层允许高级方言快速卸下高层语义让底层的通用 Pass 专注于做数学逻辑上的等价化简。