affine.for 专用于编译期可静态分析的仿射循环优化,适用于矩阵乘、卷积等规则访存场景,需边界与索引均为仿射表达式;非仿射行为(如min/max/div/abs/mod/间接寻址)将导致验证失败,此时应退至scf.for。

affine.for 适合做静态可分析的嵌套循环优化
当循环边界、步长、数组访问模式在编译期能表达为仿射表达式(即形如 a*i + b*j + c 的线性组合)时,affine.for 才真正发挥价值。它不是通用循环替代品,而是为编译器做自动并行、tiling、fusion、vectorization 等变换提供语义支撑。
常见适用场景包括:
- 矩阵乘、卷积、Stencil 计算等规则数据访存的数值计算内核
- 需要显式建模迭代空间以做依赖分析或调度(比如用多面体模型推导合法并行维度)
- 作为降级流水线中承上启下的中间表示:上接
linalg.generic,下连scf.for或omp.parallel - 与
memref配合使用,通过affine_map精确描述索引到内存偏移的映射关系
不适合用 affine.for 的典型情况
一旦出现非仿射行为,MLIR 的 affine dialect 就会拒绝验证或无法做可靠变换。这类写法在 IR 构建阶段就会报错,比如:
- 循环上界含
min、max、div(非线性运算)、条件分支导致的动态截断 - 数组索引含
abs、mod、查表、间接寻址(如A[B[i]]) - 循环变量参与函数调用或被外部副作用修改(破坏 SSA 和作用域假设)
- 使用运行时决定的
memref维度(即 dynamic shape 但未绑定为symbol)
此时应退回到 scf.for 或手写 llvm.br 控制流——affine 不是银弹,只是对一类问题建模更精确的工具。
affine.parallel 要求严格的数据独立性
affine.parallel 不是“加个关键字就能并行”,它隐含要求所有迭代实例之间无写-写、写-读、读-写依赖。MLIR 会在 lowering 前做依赖检查,失败则报错 affine.parallel op contains dependent memory operations。
实际使用前需确认:
- 所有
memref.store写入地址必须由不同 iteration 独占(例如写入A[i, j],且i,j是该 parallel 的 induction vars) - 避免跨 iteration 读共享 buffer 且该 buffer 在其他地方被写(即使没直接 store,也可能因 alias 分析失败而拒掉)
- 若存在 reduction,必须显式用
affine.reduce或转成scf.parallel+reductiondialect
和 OpenMP / GPU lowering 的衔接有隐含约束
想把 affine.parallel 自动转成 omp.parallel 或 gpu.launch,不能只靠 -convert-affine-to-openmp 这类 pass。关键前提包括:
- 所有
symbol必须在函数入口或顶层常量中定义好(如%N = arith.constant 1024 : index),不能来自 runtime 参数未绑定 - 嵌套层级需匹配目标后端能力(例如 GPU lowering 通常只接受 2–3 层
affine.parallel,外层分 block,内层分 warp) - 内存访问需满足 coalescing 模式,否则生成的
gpu.shared_mem或affine.load映射会失效
最容易被忽略的是 symbol 生命周期管理:一个 memref.alloc 若用 [s0] 声明 shape,但 s0 在某次调用中未被传入或未被 affine.apply 正确传播,后续 lowering 就会卡在 shape 推导阶段,报错信息往往只显示 failed to infer result type,实际根源在 symbol 绑定缺失。

















