MachineSink - 优化阅读笔记

news2024/11/26 20:31:50

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

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

该优化过程并非旨在替代或完全替代 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Huggingface中Transformer模型使用

一、Huggingface介绍 1、Huggingface定位 NLP自从Transformer模型出现后&#xff0c;处理方式有大统一的趋势&#xff0c;首先回答几个基础问题&#xff1a; 1、自然语言处理究竟要做一件什么事呢&#xff1f;自然语言处理最终解决的是分类问题&#xff0c;但是它不仅仅输出…

基于单片机的智能小车泊车系统设计

摘 要:随着信息技术的进步,汽车逐渐朝着安全、智能方向发展,智能泊车系统的出现不仅能帮助人们更加快速、安全地完成泊车操作,而且适用于狭小空间的泊车操作,降低驾驶员泊车负担,减轻泊车交通事故发生率。文章基于单片机设计自动泊车系统,以单片机为核心来实现信息收集及…

洛谷P6022快乐水

他来到了一家商店门前。 这家商店为了吸引顾客来买快乐水&#xff0c;搞了这么一个活动&#xff1a;「55 个瓶盖换一瓶快乐水」。于是&#xff0c;人们纷纷来他的店里买快乐水。 买完快乐水&#xff0c;他想到了一个问题&#xff1a; 如果一瓶快乐水有m 个附属品&#xff0c…

Java线程的6种状态

线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。 NEW&#xff1a;初始状态&#xff0c;线程被创建出来但没有被调用start()RUNNABLE&#xff1a;运行状态&#xff0c;线程被调用了start()等待运行的状态BLOCKED&#xff1a;阻塞状态&#xf…

uview upicker时间选择器(附Demo)

目录 前言正文 前言 uniapp时间选择器&#xff0c;是upicker&#xff0c;与微信小程序还是有些区别 补充官网的基本知识&#xff1a;uview官网 官网的展示例子如下&#xff1a;&#xff08;但是没Demo&#xff09; 正文 通过上面的展示图&#xff0c;复刻一个类似Demo图&am…

15双体系Java学习之数组的声明和创建

数组的声明 ★小贴士 可以使用int[] a;或者int a[];建议使用第一种风格&#xff0c;因为它将元素类型int[]&#xff08;整型数组&#xff09;与变量名清晰分开了。 在Java中声明数组时不能指定其长度。这种定义是非法的&#xff1a;int a[5]; 注意&#xff1a;上图显示的内存…

学习数据节构和算法的第15天

单链表的实现 链表的基本结构 #pragma once #include<stdio.h> typedf int SLTDataType; typedy struct SListNode {SLTDataType data;struct SListNode*next; }SLTNode;void Slisprint(SLTNode*phead);打印链表 #include<stdio.h> void SListPrint(SLTNode*phe…

【LeetCode】升级打怪之路 Day 18:二叉树题型 —— 树的深度、高度、路经

今日题目&#xff1a; 104. 二叉树的最大深度111. 二叉树的最小深度110. 平衡二叉树257. 二叉树的所有路径112. 路径总和 目录 Problem 1&#xff1a;树的深度LC 104. 二叉树的最大深度 【easy】LC 111. 二叉树的最小深度 【易错】 Problem 2&#xff1a;树的高度LC 110. 平衡二…

嵌入式系统软件及操作系统

0、前言 本专栏为个人备考软考嵌入式系统设计师的复习笔记&#xff0c;未经本人许可&#xff0c;请勿转载&#xff0c;如发现本笔记内容的错误还望各位不吝赐教&#xff08;笔记内容可能有误怕产生错误引导&#xff09;。 考查选择题为多&#xff1a;嵌入式系统软件特点是什么…

解决Klipper下位机ID获取失败问题

使用硬件&#xff1a; 上位机&#xff1a;必趣派&#xff0c;版本CB1_Debian11_Klipper_kernel5.16_20230303 下位机&#xff1a;八爪鱼STM32F407 问题&#xff1a;上位机获取下位机ID失败。 解决&#xff1a;调试过程中&#xff0c;发现上位机和下位机之间没有物理连接&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Stepper)

步骤导航器组件&#xff0c;适用于引导用户按照步骤完成任务的导航场景。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 仅能包含子组件StepperItem。 接口 Stepper(value?: { index?…

2021年江苏省职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书

2021年江苏省职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书 一、赛项时间&#xff1a;二、赛项信息三、竞赛内容&#xff1a;第一阶段任务书&#xff08;300分&#xff09;任务1&#xff1a;网络平台搭建&#xff08;60分&#xff09;任务2&#xff1a;网络安全设备…

AI 技术:改变世界的力量

人工智能&#xff08;AI&#xff09;是当今科技领域最热门的话题之一&#xff0c;它已经成为推动社会进步和经济发展的重要力量。AI 技术的应用范围非常广泛&#xff0c;从智能手机、自动驾驶汽车到医疗保健、金融服务等领域&#xff0c;都可以看到 AI 的身影。 那么&#xff0…

GIS学习笔记(四):GIS数据可视化综合(矢量数据)

矢量数据 arcgis的主要可视化工具&#xff1a;属性 符号系统 符号系统 按类别 这里不会涉及到数字的大小因素&#xff0c;只是按照字符的分类去做可视化 “唯一值”的含义 “建筑年代”字段共有10个年份&#xff0c;一个年份也许有多个数据( eg.1990年的建筑有20个)&…

JavaWeb——013SpringBootWeb综合案例(事务管理、AOP)

事务&AOP 目录 事务&AOP1. 事务管理1.1 事务回顾1.2 Spring事务管理1.2.1 案例1.2.2 原因分析1.2.3 Transactional注解 1.3 事务进阶1.3.1 rollbackFor1.3.3 propagation1.3.3.1 介绍1.3.3.2 案例 2. AOP基础2.1 AOP概述2.2 AOP快速入门2.3 AOP核心概念 3. AOP进阶3.1 …

传统SessionID,Cookie方式与SringSecurity+JWT验证方式

在Spring Boot框架中&#xff0c;可以使用Spring Session来处理会话管理。Spring Session允许开发者在不同的存储后端&#xff08;如Redis、数据库等&#xff09;之间共享和管理会话状态。通过Spring Session&#xff0c;开发者可以轻松地实现会话管理、会话失效以及跨多个节点…

使用函数返回值的循环、使用带返回值的函数

本文参考C Primer Plus进行C语言学习 文章目录 使用函数返回值的循环使用带返回值的函数 一.使用函数返回值的循环 #include<stdio.h> double power(double n,int p); int main() {double x,xpow;int exp;printf("Enter a number and the posotive integer power&…

J1周-ResNet-50算法

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 我的环境&#xff1a; 1.语言&#xff1a;python3.7 2.编译器&#xff1a;pycharm 3.深度学习框架Tensorflow/Pytorch 1.8.0cu111 一、问题引出 CNN能够提取低、中、…

Early if-conversion - 优化阅读笔记

Early if-conversion 用于对于没有很多可预测指令的乱序CPU。目标是消除可能误预测的条件分支。 来自分支两侧的指令都会被推测性地执行&#xff0c;并使用 cmov 指令选择结果。 // SSAIfConv 类在确定可能的情况下&#xff0c;对SSA形式的机器码执行if-conversion。该类不包…