MachineSink - 优化阅读笔记

注:该优化与全局子表达式消除刚好是相反的过程,具体该不该做这个优化得看代价模型算出来的结果(有采样文件指导算得会更准确)

该优化过程将指令移动到后继基本块中,以便它们不会在不需要其结果的路径上执行。

该优化过程并非旨在替代或完全替代 LLVM IR 级别的下沉优化。它仅设计用于下沉简单的结构,这些结构在 lowering 和指令选择之前不会显现.

测试用例:

grep "machine-sink" llvm/test/* -Rnw|grep X86|grep -v debug
./build4/bin/llvm-lit llvm/test/DebugInfo/MIR/X86/machinesink.mir -a
./build4/bin/llc -run-pass=dot-machine-cfg llvm/test/DebugInfo/MIR/X86/machinesink.mir
dot .Process.dot -T svg -o Process.dot.svg
dot .test2.dot -T svg -o test2.dot.svg
dot .test3.dot -T svg -o test3.dot.svg

./build4/bin/llc -mtriple=x86_64-unknown-unknown -run-pass=machine-sink -o - ./llvm/test/DebugInfo/MIR/X86/machinesink.mir &> dd
./build4/bin/llc -mtriple=x86_64-unknown-unknown -run-pass=removeredundantdebugvalues -o - ./llvm/test/DebugInfo/MIR/X86/machinesink.mir &> ddd

效果:
在这里插入图片描述在这里插入图片描述

几个重要的接口:

bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
  // 更新寄存器信息
  RegClassInfo.runOnMachineFunction(MF);
  // 遍历每个 BB, 寻找优化的场景
  for (auto &MBB: MF)
      MadeChange |= ProcessBlock(MBB);
  // 处理需要打破的临界边
  auto NewSucc = Pair.first->SplitCriticalEdge(Pair.second, *this);
void RegisterClassInfo::runOnMachineFunction(const MachineFunction &mf) {
  // 1. 检查 CSR 数组的每个元素是否与之前分析的相同
  const MCPhysReg *CSR = MRI.getCalleeSavedRegs();
  // 2. 如果有改变,记录每一个 CSR 及它的别名
        for (MCRegAliasIterator AI(*I, TRI, true); AI.isValid(); ++AI)
        CalleeSavedAliases[*AI] = *I;
  // 3. 更新每个 CSRs 的分配优先级
    if (IgnoreCSRForAllocOrder.size() != CSRHintsForAllocOrder.size() ||
      IgnoreCSRForAllocOrder != CSRHintsForAllocOrder) {
  // 4. 获取寄存器的成本
  RegCosts = TRI->getRegisterCosts(*MF);
  // 5. 更新保留寄存器
  const BitVector &RR = MF->getRegInfo().getReservedRegs();
  // 6. 更新寄存器压力
  unsigned NumPSets = TRI->getNumRegPressureSets();
      // build4/lib/Target/X86/X86GenRegisterInfo.inc:11902
      // build4/lib/Target/AArch64/AArch64GenRegisterInfo.inc:103840
}
bool MachineSinking::ProcessBlock(MachineBasicBlock &MBB) {
  // 待优化BB需要有多个后继,且不是死的基本块
  // 逆序遍历每条指令
  do {
    // 记录指令的 debug 信息
    // 执行琐碎的前向聚合, SRC=DRC的拷贝指令的替换
    bool Joined = PerformTrivialForwardCoalescing(MI, &MBB);
    // 尝试下沉指令
    if (SinkInstruction(MI, SawStore, AllSuccessors))
  } while (!ProcessedBegin);
}
bool MachineSinking::SinkInstruction(MachineInstr &MI, bool &SawStore,
                                     AllSuccsCache &AllSuccessors) {
  TII->shouldSink(MI); // X86 always true
  MI.isSafeToMove(AA, SawStore); // MachineInstr.cpp:1259 !store !call !load !phi !inlineasm !positino ...
  MachineBasicBlock *SuccToSinkTo =
      FindSuccToSinkTo(MI, ParentBlock, BreakPHIEdge, AllSuccessors);
  // 不要破坏隐式空指针检查。这是一种性能启发,而非正确性所必需
  // 如果 MI 可能被隐式空指针检查优化用作内存操作,则返回 true
  SinkingPreventsImplicitNullCheck(...);
  // llvm/test/CodeGen/X86/implicit-null-check.ll
  // br i1 %c, label %is_null, label %not_null, !make.implicit !0
  
  // 这应该包括支持在当前块内下沉指令,以缩短其寄存器活跃范围。我们经常将指令下沉到大块的顶部,但最好在它们在块中第一次使用之前也将其下沉。但是,此变换必须小心,不要增加寄存器压力,例如,如果下沉 "x = y + z",但它杀死了 y 和 z,那么会增加 y 和 z 的寄存器活跃范围,而只会减小 x 的寄存器活跃范围
  MachineBasicBlock *SuccToSinkTo = FindSuccToSinkTo(MI, ParentBlock, BreakPHIEdge, AllSuccessors);
  // 检查是否会引入僵尸寄存器
  if (SuccToSinkTo->isLiveIn(Reg))

  // 如果需要打破危险边界, 
  // 或 会破坏 PHI 边界
  if (SuccToSinkTo->pred_size() > 1) 
  if (blockPrologueInterferes(SuccToSinkTo, InsertPos, MI, TRI, TII, MRI)) || 
  if (BreakPHIEdge)
    // 延后打破危险边界,就记录一下
    bool Status = PostponeSplitCriticalEdge(MI, ParentBlock, SuccToSinkTo, BreakPHIEdge);
      // ToSplit.insert(std::make_pair(FromBB, ToBB));
  // 查找插入点, 跳过 phi和序幕指令
  SuccToSinkTo->SkipPHIsAndLabels(SuccToSinkTo->begin());

  // 收集需要一并下沉的debug指令
  DbgUsersToSink.push_back({DbgMI, SmallVector<unsigned, 2>(1, MO.getReg())});

  // 下沉指令及其相关调试指令
  performSink(MI, *SuccToSinkTo, InsertPos, DbgUsersToSink);
    // 移动指令:SuccToSinkTo.splice(InsertPos, ParentBlock, MI, ++MachineBasicBlock::iterator(MI));
    // 处理debug信息:attemptDebugCopyProp(MI, *DbgMI, Reg)

  // 清除 kill 标记
  RegsToClearKillFlags.insert(MO.getReg());
}
MachineBasicBlock *
MachineSinking::FindSuccToSinkTo(MachineInstr &MI, MachineBasicBlock *MBB,
                                 bool &BreakPHIEdge,
                                 AllSuccsCache &AllSuccessors) {
  // 检查要下沉的指令的每个操作数
  for (const MachineOperand &MO : MI.operands()) {
    
    // 对于虚拟寄存器
    // 需要能安全移动
    if (!TII->isSafeToMoveRegClassDefs(MRI->getRegClass(Reg)))
    // llvm/lib/Target/X86/X86InstrInfo.cpp:7534

    // 否则,我们应该查看所有的后继块并决定应该下沉到哪一个。如果我们有可靠的块频率信息(frequency != 0),则将具有较小频率的后继块优先级更高,否则优先考虑较小的循环深度
    for (MachineBasicBlock *SuccBlock :
            GetAllSortedSuccessors(MI, MBB, AllSuccessors)) {
              // 除了MBB的直接后继, 被 MBB 作为直接支配者的 DomTree 子节点也会被遍历
      /// AllUsesDominatedByBlock - 如果指定寄存器的所有使用都在指定块支配的块中发生,则返回 true, 如果任何使用在定义块中,则返回 false,因为在使用之后移动定义是不合法的。
      if (AllUsesDominatedByBlock(Reg, SuccBlock, MBB,
                                      BreakPHIEdge, LocalUse)) {}
      // 检查收益
      if (!isProfitableToSinkTo(Reg, MI, MBB, SuccToSinkTo, AllSuccessors))
  }
}

bool MachineSinking::isProfitableToSinkTo(Register Reg, MachineInstr &MI,
                                          MachineBasicBlock *MBB,
                                          MachineBasicBlock *SuccToSinkTo,
                                          AllSuccsCache &AllSuccessors) {
  // 1. 不 反向支配 Def BB
  !PDT->dominates(SuccToSinkTo, MBB)
  // 2. 循环嵌套更浅
  if (CI->getCycleDepth(MBB) > CI->getCycleDepth(SuccToSinkTo))
  // 3. 寄存器压力不超限
  if (isRegisterPressureSetExceedLimit(MRI->getRegClass(Reg)))
}

遗留问题:

  1. 与 llvm ir 中 sink 的区别
  2. split critical edge 具体是怎么做的

LLVM IR 中搜索到其他三个 Sink 相关的pass:

# find llvm/lib/ -name "*.cpp"|grep "sink" -i
llvm/lib/Transforms/Scalar/Sink.cpp
llvm/lib/Transforms/Scalar/LoopSink.cpp
llvm/lib/Transforms/Scalar/GVNSink.cpp
llvm/lib/CodeGen/MachineSink.cpp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/451578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

按键+串口发送实验

摸鱼记录 Day_15 &#xff5e;(&#xffe3;▽&#xffe3;&#xff5e;)(&#xff5e;&#xffe3;▽&#xffe3;)&#xff5e; review 前边已经学习了&#xff1a; 串口发送Vivado 串口通信(UART)------串口发送-CSDN博客 按键基于状态机的按键消抖实现-CSDN博客 1. …

CANopen转Profinet网关连接西门子PLC与变流器通讯

CANopen转Profinet网关&#xff08;XD-COPNm20&#xff09;在智能领域&#xff0c;变流器的应用非常广泛&#xff0c;变流器一般会采用CANopen协议。现场采用台达的变流器&#xff08;支持CANopen协议&#xff09;作为CANopen从站&#xff0c;S7-1500系列PLC做主站&#xff0c;…

ENVI 如何批量拆分多波段栅格

在处理遥感图像时&#xff0c;需要将多波段栅格进行拆分是很常见的需求。下面介绍一种方法&#xff0c;可以实现图像批量拆分并重命名。 打开ENVI的App Store 搜索并下载应用 在ENVI的App Store中搜索"将多波段图像拆分成多个单波段文件"&#xff0c;并下载安装。 打…

索引失效的介绍和避免方法

索引是什么 在关系数据库 中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种 存储结构 &#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。 索引的作用相当于图书的目录&#xff0c;可以根据…

20240309web前端_第一周作业_完成用户注册界面

作业一&#xff1a;完成用户注册界面 成果展示&#xff1a; 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-…

MyBatis拦截器四种类型和自定义拦截器的使用流程

文章目录 MyBatis拦截器四种类型和自定义拦截器的使用流程一、MyBatis拦截器四种类型的详细解释&#xff1a;1. **ParameterHandler 拦截器**&#xff1a;2. **ResultSetHandler 拦截器**&#xff1a;3. **StatementHandler 拦截器**&#xff1a;4. **Interceptor Chain 拦截器…

软考高级:统计过程阶段和工作流概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的常见车型识别系统(Python+PySide6界面+训练代码)

摘要&#xff1a;本文深入探讨了如何应用深度学习技术开发一个先进的常见车型识别系统。该系统核心采用最新的YOLOv8算法&#xff0c;并与早期的YOLOv7、YOLOv6、YOLOv5等版本进行性能比较&#xff0c;主要评估指标包括mAP和F1 Score等。详细解析了YOLOv8的工作机制&#xff0c…

逆向案例七——中国天气质量参数搜不到加密,以及应对禁止打开开发者工具和反debuger技巧

进入相关城市数据页面&#xff0c;发现不能调试 应对方法&#xff0c;再另一个页面&#xff0c;打开开发者工具&#xff0c;选择取消停靠到单独页面 接着&#xff0c;复制链接在该页面打开。接着会遇到debugger 再debugger处打上断点&#xff0c;一律不在此处暂停。 然后点击继…

InnoDB索引优化

索引 覆盖索引 最左前缀原则 索引下推优化 如果我执行 select * from T where k between 3 and 5 这条语句&#xff08;k 是索引&#xff09;&#xff0c;需要执行几次树的搜索操作&#xff0c;会扫描多少行&#xff1f; 这条 SQL 查询语句的执行流程…

Promise其实也不难

难点图解&#xff1a;then&#xff08;&#xff09;方法 ES6学习网站&#xff1a;ES6 入门教程 解决&#xff1a;回调地狱&#xff08;回调函数中嵌套回调&#xff09; 两个特点&#xff1a; &#xff08;1&#xff09;对象的状态不受外界影响。Promise对象代表一个异步操作&…

前端精准测试简介

一&#xff0c; 前端精准测试架构介绍 今年从零开始完成了前端精准测试整体功能的开发&#xff0c;在基于istanbul插件的基础之间&#xff0c;实现覆盖率数据的采集&#xff0c;并根据服务端和移动端精准测试的整体架构&#xff0c;实现前端精准测试体系。整体架构及核心功能…

【李沐论文精读】GPT、GPT-2和GPT-3论文精读

论文&#xff1a; GPT&#xff1a;Improving Language Understanding by Generative Pre-Training GTP-2&#xff1a;Language Models are Unsupervised Multitask Learners GPT-3&#xff1a;Language Models are Few-Shot Learners 参考&#xff1a;GPT、GPT-2、GPT-3论文精读…

七个项目掌握freertos

1、闪烁LED&#xff1a; 最基本的示例项目&#xff0c;涉及到创建一个简单的任务&#xff0c;用于控制LED的闪烁。这个项目会教你如何初始化FreeRTOS并创建任务。 #include "FreeRTOS.h" #include "task.h" #define LED_PIN (某个GPIO引脚)void vBlinkTas…

555经典电路

1、555介绍&#xff1a; 555 定时器是一种模拟和数字功能相结合的中规模集成器件。一般用双极性工艺制作的称为 555&#xff0c;用 CMOS 工艺制作的称为 7555&#xff0c;除单定时器外&#xff0c;还有对应的双定时器 556/7556。555 定时器的电源电压范围宽&#xff0c;可在 4…

20240312-2-贪心算法

贪心算法 是每次只考虑当前最优&#xff0c;目标证明每次是考虑当前最优能够达到局部最优&#xff0c;这就是贪心的思想&#xff0c;一般情况下贪心和排序一起出现&#xff0c;都是先根据条件进行排序&#xff0c;之后基于贪心策略得到最优结果。 面试的时候面试官一般不会出贪…

I2C驱动AT24C02

文章目录 一、硬件电路设备地址 二、使用步骤字节写:页写入:任意写:任意读: 一、硬件电路 设备地址 设备需要一个8位的设备地址字&#xff0c;后面跟着一个启动条件&#xff0c;以使芯片能够进行读或写操作 设备地址字由一个强制的1,0序列的前四个最有效的位&#xff0c;如所示…

嵌入式面试收到了两个offer,一个单片机开发,一个Linux开发,不知道如何选择?

今天看到一个问题&#xff1a; 如果你想真正解决选择困难症的问题&#xff0c;请花几分钟&#xff0c;看完这篇内容。 我做了单片机开发多年&#xff0c;还是有感而发的。 我职业生涯里&#xff0c;做过最错误的决定&#xff0c;就是哪个公司工资开的高&#xff0c;就去哪里。 …

数据结构·复杂度

目录 1 时间复杂度 2 大O渐进表示法 举例子&#xff08;计算时间复杂度为多少&#xff09; 3 空间复杂度 前言&#xff1a;复杂度分为时间复杂度和空间复杂度&#xff0c;两者是不同维度的&#xff0c;所以比较不会放在一起比较&#xff0c;但是时间复杂度和空间复杂度是用…

LeetCode 每日一题 Day 95-101

2917. 找出数组中的 K-or 值 给你一个整数数组 nums 和一个整数 k 。让我们通过扩展标准的按位或来介绍 K-or 操作。在 K-or 操作中&#xff0c;如果在 nums 中&#xff0c;至少存在 k 个元素的第 i 位值为 1 &#xff0c;那么 K-or 中的第 i 位的值是 1 。 返回 nums 的 K-o…