mlir-reduce
🦕

mlir-reduce

AI keywords
Created
Sep 22, 2024 05:53 AM
MLIR
mlir-reduce 是 MLIR 生态系统中的一个工具,主要用于最小化 MLIR IR(中间表示)测试案例,帮助开发者调试和分析编译器问题或优化问题。类似于编译器中的 bugpoint,它通过自动简化问题来生成更小的测试用例,便于问题的定位和修复


工作原理

mlir-reduce 的核心原理是使用一种"delta debugging"(增量调试)的技术。它会不断尝试去除不必要的操作、块、或模块,并测试剩余的代码是否仍然能触发目标行为(如崩溃、性能问题、错误等)。具体步骤如下:
  1. 输入测试用例:你提供一个可能导致问题的 MLIR 输入文件。
  1. 指定测试条件:提供一个测试脚本或命令,它会返回成功或失败,来判断简化后的 IR 是否依然能重现问题。
  1. 递归简化mlir-reduce 逐步简化这个输入文件,不断删除不必要的操作和模块。
  1. 输出最小化测试用例:最终生成一个最小的测试用例文件,该文件仍然会触发错误或表现出原有的问题。

使用方法

1. 创建Test脚本

  • 创建脚本文件,给予执行权限
touch run.sh chmod +x run.sh

2. 写入执行命令

执行命令就是能复现错误的方式,如果返回值是期望的(错误代码), 脚本返回1, 否则返回0,表示不感兴趣
#!/bin/bash mlir-opt -convert-vector-to-spirv $1 | grep "failed to materialize" if [[ $? -eq 1 ]]; then exit 1 else exit 0 fi

3. 使用mlir-reduce

  • 替换InputTEST_SCRIPT路径为实际路径
mlir-reduce $INPUT -reduction-tree='traversal-mode=0 test=$TEST_SCRIPT'

mlir-reducer工具二次开发

一般项目中会定义自己的Dialect,需要对该工具进行二次开发,引入针对于自己Dialect的相关pattern进行IR缩减,这一节主要介绍如何基于官方的reducer开发一个自己的reducer

1. 工具实现

在工程的Tools目录下新建一个xxx-reducer的文件夹,并构造相应的文件
. ├── CMakeLists.txt └── reducer.cpp
  • CMakeLists.txt可以直接照抄官方的,做一点简单的修改即可
  • cpp文件里面对自己的Dialect进行注册

2. Pattern实现

reducer会调用一些相关的缩减pattern进行IR缩减, 这一部分需要自己实现或者使用td文件自动定义
使用DialectInterface对相关pattern进行注册:
#include "mlir/Reducer/ReductionPatternInterface.h" // populateWithGenerated是td文件自己生成的函数,对td生成的pattern进行注册 void populateMyReductionPatterns(RewritePatternSet &patterns) { populateWithGenerated(patterns); } // 该接口进行Pattern的注册 struct MyReductionPatternInterface : public DialectReductionPatternInterface { virtual void populateReductionPatterns(RewritePatternSet &patterns) const final { populateMyReductionPatterns(patterns); } } // 调用Dialect的registerInterfaces对接口进行注册, 该函数需要在td的Dialect定义里面进行定义 void YourDialect::registerInterfaces() { addInterface<MyReductionPatternInterface>(); }
实现完成之后,reducer会自动的调用这里注册的pattern对IR进行缩减

mlir-reduce代码走读

1. lib/Reducer

实现缩减树算法
. ├── CMakeLists.txt ├── OptReductionPass.cpp ├── ReductionNode.cpp ├── ReductionTreePass.cpp └── Tester.cpp
  • OptReductionPass.cpp
    • Reduction lib的入口函数,实现了一个名为OptReductionPass的Pass,然后使用缩减树对原始IR进行缩减,得到最小的能够复现错误的子图
    • 初始化一个passManager, 添加缩减相关的Pass
    • 检查初始状态是否满足条件, 初始必须是interested的
    • runPipeLine,得到缩减后的module
  • ReductionNode.cpp
    • 缩减树的节点实现
    • ReductionNode::ReductionNode()
      • 构造函数,初始化 ReductionNode。如果是根节点,parentNode 指向自身,并调用 initialize() 来克隆父模块并映射区域。
    • ReductionNode::initialize(ModuleOp parentModule, Region &targetRegion)
      • 初始化 ReductionNode,通过 IRMapping 克隆模块,并确保目标区域与克隆后的模块相对应。
    • ReductionNode::generateNewVariants()
      • 生成新的简化变体(variants)。
      • 如果尚未生成任何变体,删除一个范围来创建新变体。
      • 如果已经生成过变体,寻找最大范围,将其分成两部分,并基于此生成两个新变体。
    • ReductionNode::update(std::pair<Tester::Interestingness, size_t> result)
      • 更新当前节点的状态。
      • 根据给定的测试结果,更新有趣性(interestingness)和大小(size)。
      • 如果节点被认为是有趣的,重置范围;如果不有趣,释放内存以节省资源。
    • ReductionNode::iterator<SinglePath>::getNeighbors(ReductionNode *node)
      • 获取节点的邻居节点。
      • 使用 "Single Path" 策略,遍历最小且有趣的变体,直到无法生成新的成功变体。
      • 如果当前节点的所有变体都未测试,返回空;否则,继续遍历最小变体或生成新变体。
  • ReductionTreePass.cpp 该文件实现了 ReductionTreePass,它为不同的减少(简化)操作提供了一个框架,并允许用户自定义生成变体的行为。这个 pass 构建了一个简化树结构,该结构用于在简化过程中跟踪生成的不同简化变体。主要功能包括遍历不同的简化树路径,以找到最优(最小化)的变体,并通过用户定义的测试器(Tester)来验证变体的有效性。
    • ReductionTreePass::runOnOperation()
      • 流程:
        • 获取顶层 Operation
        • 构建 workList(工作列表),初始化为当前 Operation
        • 循环处理工作列表中的每个操作:
            1. 对每个操作中的所有 Region 调用 reduceOp 进行简化。
            1. 对有嵌套区域的操作,将其放入 workList 继续处理。
      • 作用: 运行这个 pass,将对操作中的 Region 进行简化遍历,处理所有包含子区域的操作。
    • ReductionTreePass::reduceOp(ModuleOp module, Region &region)
      • 流程:
        • 创建 Tester 对象来测试模块是否有趣(interesting)。
        • 根据不同的遍历模式(如 SinglePath),调用相应的简化方法 findOptimal
      • 作用: 对给定的 Region 应用简化策略并测试简化后的有趣性,生成最小化的变体。
    • findOptimal (两个重载版本)
      • 流程:
        • 第一步: 在 eraseOpNotInRange 设置为 true 的情况下,仅保留有趣的操作并删除其他操作。
        • 第二步: 在 eraseOpNotInRange 设置为 false 时,应用简化模式将操作转化为更简单的形式。
      • 作用: 通过两步过程实现模块的简化,先选择有趣的操作范围,再进行操作简化。
    • applyPatterns
      • 流程:
        • 遍历区域内的操作,按索引范围筛选需要保留的操作。
        • 对符合条件的操作应用简化模式。
        • 如果设置了 eraseOpNotInRange,则删除未在范围内的操作。
      • 作用: 根据 ReductionNode 的范围,对操作进行简化或删除非必要的操作。

2. lib/Tools/mlir-reducer

mlir-reduce.cpp 实现了 MLIR reducer 工具的主要框架,该工具用于简化 MLIR 测试用例。它从命令行读取输入 MLIR 文件,通过一系列的简化 pass 操作,输出最简化后的变体。其核心功能是通过提供简化框架,帮助用户减少复杂的 MLIR 测试用例,使其更容易调试和分析
  • 命令行参数解析:
    • 使用 llvm::cl::opt 定义输入文件、输出文件以及是否插入隐式 module 操作等命令行选项。
    • 注册和隐藏不相关选项,确保用户只看到与 mlir-reduce 工具相关的参数。
  • 文件加载与解析:
    • 通过 loadModule() 函数解析输入的 MLIR 文件。如果文件解析失败,工具会直接返回错误。
  • Pass 管线设置:
    • 创建 PassManager 来管理和执行不同的简化 pass。
    • 使用 PassPipelineCLParser 解析从命令行指定的 pass 管线,并将这些 pass 添加到 PassManager 中。
  • 运行 Pass 管线:
    • 对克隆的 MLIR 操作(Op)执行简化 pass 管线。
    • 简化后的 MLIR 操作输出到用户指定的文件。

3. Tools/mlir-reducer

主要用于Dialect register,然后生成基于给定Dialect的bin文件,核心是调用lib里面的mlir-reducer