LLVM Cpu0 新后端7 第一部分 DAG调试 dot文件 Machine Pass

 想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章:

LLVM 后端实践笔记

代码在这里(还没来得及准备,先用网盘暂存一下):

链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?pwd=ggu5 提取码: ggu5 

这一章会介绍与控制流有关的功能实现,比如 if、else、while 和 for 等,还会介绍如何将控制流的 IR 表示转换为机器指令;之后会引入几个后端优化,处理一些跳转需求引入的问题,同时来说明如何编写后端优化的 pass。在条件指令小节中,会介绍 LLVM IR 中的特殊指令 select 和 select_cc,以及如何处理这种指令,从而来支持更细节的控制流支持实现。

目录

一、第一节

1.1 修改的文件

1.1.1 Cpu0ISelLowering.cpp/.h

1.1.2 Cpu0InstrInfo.td

1.1.3 Cpu0MCInstLower.cpp

1.1.4 Cpu0AsmBackend.cpp

1.1.5 Cpu0ELFObjectWriter.cpp

1.1.6 Cpu0FixupKinds.h

1.1.7 Cpu0MCCodeEmitter.cpp

1.2 结果

二、第二节

2.1 新增的文件

2.1.1 Cpu0DelUselessJMP.cpp

2.2 修改的文件

2.2.1 Cpu0.h

2.2.2 Cpu0TargetMachine.cpp


一、第一节

从机器层面上来看,所有的跳转只分为无条件跳转和有条件跳转,从跳转方式上来分,又分为直接跳转(绝对地址)和间接跳转(相对偏移),所以我们只需要将 LLVM IR 的跳转 node 成功下降到机器跳转指令,并维护好跳转的范围、跳转的重定位信息即可。

Cpu032I 型机器支持 J 类型的跳转指令,比如无条件跳转 JMP,有条件跳转 JEQ、JNE、JLT、JGT、JLE、JGE,这部分指令是需要通过检查 condition code (SW 寄存器)来决定跳转条件的;Cpu032II 型机器除了支持 J 类型跳转指令之外,还支持 B 类型的跳转指令,比如 BEQ 和 BNE,这两个是通过直接比较操作数值关系来决定跳转条件的。相比较,后者的跳转依赖的资源少,指令效率更高。

SelectionDAG 中的 node,无条件跳转是 ISD::br,有条件跳转是 ISD::brcond,我们需要在 tablegen 中通过指定指令选择 pattern 来对这些 node 做映射。

另外,J 类型指令依赖的 condition code 是通过比较指令(比如 CMP)的结果来设置的,我们在之前的章节已经完成了比较指令,LLVM IR 的 setcc node 通常会被翻译为 addiu reg1, zero, const + cmp reg1, reg2 指令。

1.1 修改的文件

1.1.1 Cpu0ISelLowering.cpp/.h

Cpu0ISelLowering.cpp文件设置本章需要的几个 node 为 custom 的 lowering 类型,即我们会通过自定义的 lowering 操作来处理它们,这包括 BlockAddress,JumpTable 和 BRCOND。这分别对应 lowerBlockAddress(),lowerJumpTable() 和 lowerBRCOND() 函数(在Cpu0TargetLowering::LowerOperation函数内),具体实现可参见代码,其中 getAddrLocal() 和 getAddrNonPIC() 是我们前边章节已经实现的自定义 node 生成函数。BRCOND 是条件跳转节点(包括 condition 的 op 和 condition 为 true 时 跳转的 block 的地址),BlockAddress 字面可知是 BasicBlock 的起始地址类型的节点,JumpTable 是跳转表类型的节点。后两者是叶子节点类型。

另外,设置 SETCC 在 i1 类型时做 Promote。增加了几行代码来说明额外的一些 ISD 的 node 需要做 Expand,有关于 Expand 我们在之前的章节介绍过,就是采用 LLVM 内部提供的一些展开方式来展开这些我们不支持的操作。这些操作包括:BR_JT,BR_CC,CTPOP,CTTZ,CTTZ_ZERO_UNDEF,CTLZ_ZERO_UNDEF。其中 BR_JT 操作的其中一个 op 是 JumpTable 类型的节点(保存 JumpTable 中的一个 index)。BR_CC 操作和 SELECT_CC 操作类似,区别是它保存有两个 op,通过比较相对大小来选择不同的分支。

; ModuleID = 'test.bc'
source_filename = "test.cpp"
target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
target triple = "mips-unknown-linux-gnu"define i8 @parity_8(i8 %x) {%1 = tail call i8 @llvm.ctpop.i8(i8 %x)%2 = and i8 %1, 1ret i8 %2
}
declare i8 @llvm.ctpop.i8(i8 %x)

对于这样一个ctpop指令我们就能让llvm将其进行扩展(当前还没实现call调用,这个暂时还编不过)。如果我们不加那条的话在指令选择的阶段会报错:

加上之后就会在legalize阶段转化成其他的表示形式,然后也就能顺利输出对应的汇编。

附上DAG调试时候的常用命令(需要debug版本的llc):

上述指令的输出数dot文件,我们还需要使用dot将其转化成可视化的图:

dot -Tsvg test.dot -o test.svg
dot -Tpng test.dot -o test.png

1.1.2 Cpu0InstrInfo.td

增加两个和跳转有关的操作数类型:brtarget16 和 brtarget24,前者是 16 位偏移的编码,将用于 BEQ、BNE 一类的指令,这一类指令是属于 Cpu032II 型号中特有;后者是 24 位偏移的编码,将用于 JEQ、JNE 一类的指令。两个操作数均指定了编码函数和解码函数的名称。还定义了 jmptarget 操作数类型,用来作为无条件跳转 JMP 的操作数。之后便是定义这几条跳转指令,包括它们的匹配 pattern 和编码。无条件跳转 JMP 的匹配 pattern 直接指明到了 [(br bb::$addr)],很好理解。类似的语法在之前的章节中我们距离介绍过。

然后我们做一些优化来定义 比较+跳转指令选择 Pattern,也就是将 brcond + seteq/setueq/setne/setune/setlt/setult/setgt/setugt/setle/setule/setge/setuge 系列模式转换为机器指令的比较+跳转指令组合。对于 J 系列的跳转指令,实际上会转换为 Jxx + CMP 模式,而对于 B 系列的跳转指令,则直接转换成指令本身。比如:

def : Pat<(brcond (i32 (setne RC:$lhs, RC:$rhs)), bb:$dst), (JNEOp (CMPOp RC:$lhs, RC:$rhs), bb:$dst)>;
def : Pat<(brcond (i32 (setne RC:$lhs, RC:$rhs)), bb:$dst), (BNEOp RC:$lhs, RC:$rhs, bb:$dst)>;

1.1.3 Cpu0MCInstLower.cpp

因为跳转的地址既可以是跳转表偏移,也可以是一个 label,所以需要在 MachineOperand 这里对相关的类型做 lowering。在 LowerSymbolOperand() 函数中增加对 MO_MachineBasicBlock、MO_BlockAddress 和 MO_JumpTableIndex 类型的 lowering。

1.1.4 Cpu0AsmBackend.cpp

Cpu0 的架构和其他 RISC 机器一样,采用五级流水线结构,跳转指令会在 decode 阶段实现跳转动作(也就是将 PC 修改为跳转后的位置),但跳转指令在 fetch 阶段时,PC 会自动先移动到下一条指令位置,fetch 阶段在 decode 阶段之前,所以实际上,在 decode 阶段执行前,PC 已经自动 +4 (一个指令长度),所以实际上跳转指令中的偏移,并不是从跳转指令到目标位置的差,而应该是跳转指令的下一条指令到目标位置的差。比如说:

jne $BB0_2
jmp $BB0_1         # jne 指令 decode 之前,PC 指向这里
$BB0_1:
ld $4, 36($fp)
addiu $4, $4, 1
st $4, 36($fp)
jmp $BB0_2
$BB0_2:
ld $4, 32($fp)     # jne 指令 decode 之后,假设 PC 指向这里

jne 指令中的偏移,应该是 jmp 指令到 最后一条 ld 指令之间的距离,也就是 20 (而不是 24)。为了实现这样的修正,我们在 adjustFixupValue() 函数中,针对重定位类型 fixup_Cpu0_PC16 和 fixup_Cpu0_PC24,指定其 Value 应该在自身的基础上减 4。

1.1.5 Cpu0ELFObjectWriter.cpp

添加重定位类型的一些设置,在 getRelocType() 函数中增加内容。

1.1.6 Cpu0FixupKinds.h

添加重定位类型 fixup_Cpu0_PC16 和 fixup_Cpu0_PC24。

1.1.7 Cpu0MCCodeEmitter.cpp

实现地址操作数的编码实现函数,包括 getBranch16TargetOpValue(),getBranch24TargetOpValue() 和 getJumpTargetOpValue() 函数,对 JMP 指令同时还是表达式类型的跳转位置的情况,选择正确的 fixups,fixups 类型在 Cpu0FixupKinds.h 文件中定义。

1.2 结果

	st	$2, 4($sp)ld	$2, 12($sp)addiu	$3, $zero, 9sltu	$2, $3, $2bne	$2, $zero, $BB0_18nop
# %bb.17:                               #   in Loop: Header=BB0_15 Depth=1jmp	$BB0_15
$BB0_18:jmp	$BB0_20
$BB0_19:                                # %.loopexit
$BB0_20:ld	$2, 12($sp)addiu	$3, $zero, 10bne	$2, $3, $BB0_22nop

二、第二节

LLVM 的大多数优化操作都是在中端完成,也就是在 LLVM IR 下完成。除了中端优化以外,其实还有一些依赖于后端特性的优化在后端完成。比如说,Mips 机器中的填充延迟槽优化,就是针对 RISC 下的 pipeline 优化。如果你的后端是一个带有延迟槽的 pipeline RISC 机器,那么也可以使用 Mips 的这一套优化。

这一小节,我们实现一个简单的后端优化,叫做消除无用的 JMP 指令。这个算法简单且高效,可以作为一个优化的教程来学习,通过学习,也可以了解如何新增一个优化 pass,以及如何在真实的工程中编写复杂的优化算法。

    jmp    %BB_0
%BB_0:... other instructions

汇编指令中,若无跳转指令的话指令都是顺序执行的。当jmp 指令的下一条指令就是 jmp 指令需要跳转的 BasicBlock 块时,这里的 jmp 指令是多余的,即使删掉这条 jmp 指令,程序流也一样可以顺序执行正确。所以,我们的目的就是识别这种模式,并删除对应的 jmp 指令。

2.1 新增的文件

2.1.1 Cpu0DelUselessJMP.cpp

这是我们实现该优化 pass 的具体代码。

#define DEBUG_TYPE "del-jmp"...
LLVM_DEBUG(dbgs() << "debug info");

这里是为我们的优化 pass 添加一个调试宏,这样我们可以通过在执行编译命令时,指定该调试宏来打印出我们想要的调试信息。注意需要以 debug 模式来编译编译器,并且在执行编译命令时,指定参数, 或直接打开所有调试信息:

llc -debug-only=del-jmp
llc -debug

在写代码的时候调试信息是非常非常重要的!!!如果我们要实现的是个较复杂的功能的话,没事挑事信息的话,在遇到bug的时候我们定位起来很不方便,经常可能需要自己添加一些打印信息,每次都添加的话很影响效率,这样的话,为什么不一开始就在关键的地方加上调试信息打印呢?另一方面有调试信息的话,也方便他人能够更好地理解我们的代码,明白我们各种数据结构中都是什么样的内容。

STATISTIC(NumDelJmp, "Number of useless jmp deleted");

这个表示我们定义了一个全局变量 NumDelJmp,可以允许我们在执行编译命令时,当执行完毕时,打印出这个变量的值。这个变量的作用是统计这个优化 pass 一共消除了多少个无用的jmp 指令,变量的累加是在实现该 pass 的逻辑中手动设计进去的。在执行编译命令时,指定参数就可以打印出所有的统计变量的值。:

llc -stats
static cl::opt<bool> EnableDelJmp(......
);

这部分代码是向 LLVM 注册了一个编译参数,参数名称是这里第一个元素,还指定了参数的默认值,描述信息等。我们使用参数名为:enable-cpu0-del-useless-jmp,默认是打开的。这就是说,如果我们指定了这个参数,并且令其值为 false,则会关闭这个优化 pass。

具体的实现代码中,继承了 MachineFunctionPass 类,并在 runOnMachineFunction 中重写了逻辑,这个函数会在每次进入一个新的 Function 时被执行,runOnMachineFunction是后端Machine Function的pass的入口,因此我们要改写这个函数。

我们的基本思路是,在每个函数中遍历每一个基本块,直接取其最后一条指令,判断是否为 jmp 指令,如果是,再判断这条指令指向的基本块是否是下一个基本块。如果都满足,则调用 MBB.erase(I) 删除 I 指向的指令(jmp 指令)并且累加 NumDelJmp 变量。

LLVM这种结构安排很清晰的,处理起来也比较容易。中端就是Instruction->Basic Block->Function->Module,后端就是Machine Instruction->Machine Basic Block->Machine Function。我们要做的功能是修改的什么量级的,就遍历到其中,然后筛选是否是我们要处理的场景,是的话就做相应的增删改查。

2.2 修改的文件

2.2.1 Cpu0.h

声明这个 pass 的工厂函数。

2.2.2 Cpu0TargetMachine.cpp

覆盖 addPreEmitPass() 函数,在其中添加我们的 pass。调用这个函数表示我们的 pass 会在代码发射之前被执行。

后端的PASS流水线的管理主要是在TargetPassConfig.cpp这个文件里边,包括后端对于LLVM IR的pass、对于DAG的pass、对于Machine Function的pass等等。 TargetPassConfig::addMachinePasses接口就是Machine Function的pass流水线,当中包括Pre RA、RA、Post RA等等,如果有需要的话,我们也能够在其中修改。

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

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

相关文章

contos7使用docker安装vulhub

contos7下使用docker安装vulhub 1. 安装docker 1. 更新yum &#xff08;1&#xff09;切换root用户 su root &#xff08;2&#xff09;更新yum yum update 2. 卸载旧版本的docker sudo yum remove docker sudo yum remove docker-client sudo yum remove docker-clien…

高并发ping多台主机IP

简介 社区或者是大型公司往往有成千上万或者几百台设备&#xff0c;保持设备始终在线对网络运维人员来说至关重要&#xff0c;然而一个一个登录检查&#xff0c;或者一个一个ping并不明智&#xff0c;累人且效率极低&#xff0c;并出错率高。花钱买检测服务当我没说。 shell编…

K210视觉识别模块学习笔记5:(嘉楠)训练使用模型_识别人脸

今日开始学习K210视觉识别模块:(嘉楠)训练与使用模型_识别人脸 亚博智能的K210视觉识别模块...... 固件库版本: canmv_yahboom_v2.1.1.bin 之前的训练网址部署模型时需要我们自己更换固件&#xff0c;而且还不能用亚博的图像操作库函数了&#xff0c;这十分不友好&#xff0…

vue 使用 Vxe UI vxe-print 实现复杂的 Web 打印,支持页眉、页尾、分页的自定义模板

Vxe UI vue 使用 Vxe UI vxe-print 实现复杂的 Web 打印&#xff0c;支持页眉、页尾、分页的自定义模板 官方文档 https://vxeui.com 查看 github、gitee 页眉-自定义标题 说明&#xff1a;vxe-print-page-break标签用于定义分页&#xff0c;一个标签一页内容&#xff0c;超…

YOLOv5改进 | 主干网络 | 用SimRepCSP作为主干网络提取特征【全网独家 + 降本增效】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; SimRepCSP 类似于 YOLOv7的主干网络&#xff0c;由卷积模块和重参数化卷积&#xff08;RepConv&#xff09;模块组合而成&#xff0c;以 Cro…

学习使用 Frida 过程中出现的问题

一、adb shell命令报错&#xff1a;error: no devices found 目前该问题解决方法仅供参考&#xff0c;可先看看再选择试试&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 查看此电脑也会发现没有出现手机型号文件夹。 第一步&#xff1a; 检查一下手机开了u…

qmt量化交易策略小白学习笔记第16期【qmt编程之获取北向南向资金(沪港通,深港通和港股通)】

qmt编程之获取北向南向资金 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 北向南向资金&#xff08;沪港通&#xff0c;深港通和港股通&#xff09; #北向南向资金交易日历 获取交易日列表…

【CentOS 7】CentOS 7极致指南:高级部署PyCharm 2022.3.3专业版,实现定制化配置与无缝桌面集成

【CentOS 7】CentOS 7极致指南&#xff1a;高级部署PyCharm 2022.3.3专业版&#xff0c;实现定制化配置与无缝桌面集成 大家好 我是寸铁&#x1f44a; 总结了一篇CentOS 7极致指南&#xff1a;高级部署PyCharm 2022.3.3专业版&#xff0c;实现定制化配置与无缝桌面集成✨ 喜欢的…

线性代数|机器学习-P10最小二乘法的四种方案

文章目录 1. 概述2. SVD奇异值分解3. 最小二乘法方程解4. 最小二乘法图像解释5. Gram-Schmidt 1. 概述 当我们需要根据一堆数据点去拟合出一条近似的直线的时候&#xff0c;就会用到 最小二乘法 .根据矩阵A的情况&#xff0c;有如下四种方法 在r n m 时&#xff0c;SVD奇异…

计算机网络-数制转换与子网划分

目录 一、了解数制 1、计算机的数制 2、二进制 3、八进制 4、十进制 5、十六进制 二、数制转换 1、二进制转十进制 2、八进制转十进制 3、十六进制转十进制 4、十进制转二进制 5、十进制转八进制 6、十进制转十六进制 三、子网划分 1、IP地址定义 2、IP的两种协…

【NetTopologySuite类库】C#生成带约束(线、面)的Delaunay三角网

介绍 API地址&#xff1a;https://nettopologysuite.github.io/NetTopologySuite/api/NetTopologySuite.Triangulate.ConformingDelaunayTriangulationBuilder.html#NetTopologySuite_Triangulate_ConformingDelaunayTriangulationBuilder_Constraints 约束为线 效果图 红色…

经典文献阅读之--P2O-Calib(利用点对空间遮挡关系的相机-激光雷达标定)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

整除及求余运算符、数字的提取、顺序结构程序

1.运算符 在有余数的除法运算中&#xff0c;如果要知道商和余数分别是多少&#xff0c;可以用/和%这两个运算符号来得到。 (1)/(整除)&#xff0c;当被除数和除数均为整数时&#xff0c;结果也为整型&#xff0c;只取商的整数部分。 如:10/25 10/33 5/10 0 (2)%(求余)&…

倩女幽魂搬砖攻略:云手机自动托管搬砖刷本选哪家云手机?

欢迎来到《倩女幽魂手游》的世界&#xff0c;一个充满江湖恩怨的世界。在这个游戏中&#xff0c;你将扮演各个门派中的不同职业&#xff0c;踏上一段属于你自己的江湖之路。本攻略将为你详细介绍如何利用多开挂机搬砖&#xff0c;快速提升自己的实力&#xff0c;成为江湖中的一…

python - pandas常用计算函数

文中所用数据集有需要的可以私聊我获取 学习目标 知道排序函数nlargest、nsmallest和sort_values的用法 知道Pandas中求和、计数、相关性值、最小、最大、平均数、标准偏差、分位数的函数使用 1 排序函数 导包并加载数据集 import pandas as pd ​ # 加载csv数据, 返回df对…

VBA excel 表格将多行拆分成多个表格或 文件 或者合并 多个表格

excel 表格 拆分 合并 拆分工作表按行拆分为工作表工作表按行拆分为工作薄 合并操作步骤 拆分 为了将Excel中的数万行数据拆分成多个个每个固定行数的独立工作表&#xff0c;并且保留每个工作表的表头&#xff0c;你可以使用以下VBA脚本。这个脚本会复制表头到每个新的工作表&…

Java案例:找素数

文章目录 题目问题反思代码改进 题目 找素数 判断101-200之间有多少个素数&#xff0c;并输出所有素数 只需要除到 n/2 即可。 算数平方根。&#xff08;j*j<i&#xff09;实际上可以更高效地只除到Math.sqrt(n)&#xff08;或者说Math.sqrt(n) 1为了处理整数除法&#xf…

Web学习_sqli-labs_1~10关

less1-GET-Error based - Single quotes - String &#xff08;基于错误的GET单引号字符型注入&#xff09; 我每次操作都会在Hackbar中&#xff0c;代码都在Hackbar框中&#xff0c;可放大看 有题目知道了是字符型注入&#xff0c;我们先判断表格有几列&#xff0c;可以发现…

Istio_1.17.8安装

项目背景 按照istio官网的命令一路安装下来&#xff0c;安装好的istio版本为目前的最新版本&#xff0c;1.22.0。而我的k8s集群的版本并不支持istio_1.22的版本&#xff0c;导致ingress-gate网关安装不上&#xff0c;再仔细查看istio的发布文档&#xff0c;如果用istio_1.22版本…

屏幕空间反射技术在AI绘画中的作用

在数字艺术和游戏开发的世界中&#xff0c;真实感渲染一直是追求的圣杯。屏幕空间反射&#xff08;Screen Space Reflection&#xff0c;SSR&#xff09;技术作为一种先进的图形处理手段&#xff0c;它通过在屏幕空间内模拟光线的反射来增强场景的真实感和视觉冲击力。随着人工…