LLVM Cpu0 新后端7 第二部分 窥孔优化

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

LLVM 后端实践笔记

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

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

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

目录

一、第三节

1.1 新增的文件

1.1.1 Cpu0DelaySlotFiller.cpp

1.2 修改的文件

1.2.1 Cpu0.h

1.2.2 Cpu0TargetMachine.cpp

1.2.3 Cpu0AsmPrinter.cpp

二、第四节

2.1 修改的文件

2.1.1 Cpu0InstrInfo.td

2.1.2 Cpu0ISelLowering.cpp/.h

2.2 窥孔优化


一、第三节

这是个功能性的 pass。很多 RISC 机器采用多级流水线设计,有些 phase 会产生延迟,为了保证软件运行正确,可能会需要软件(编译器)在需要延迟的指令做处理。Cpu0 就符合这种情况,对于所有的跳转指令,需要有一个 cycle 的延迟,编译器需要负责对这些跳转指令做延迟插入指令。为了让实现简单,我们目前的实现只是将一条 nop 指令填充到跳转指令之后。有关于将其他有用的指令插入到跳转之后,可以参考 Mips 的实现(更加有意义,不单单是一条无用的等待),比如 MipsDelaySlotFiller.cpp 文件。

    jne    $sw, $BB_0nop    // 这里是插入的指令
$BB_1:... other instructions

对于 jne 指令,因为需要为其填充延迟指令,所以实际我们代码运行之后,会在汇编中,jne 的下一条指令,输出一条 nop 指令,这样就可以保证在 jne 执行完毕之后,再进行后续的运行。

与上一节的设计类似,我们依然是设计一个 pass,专门去识别这样一个模式,并创建一个 nop 指令并与跳转指令打到一个 bundle 中。bundle 是 LLVM 在 MI 层支持的一种指令扩展,它会在 bundle emit 之前,将 bundle 看做一条指令,而 bundle 内部却可以包含多条指令。

1.1 新增的文件

1.1.1 Cpu0DelaySlotFiller.cpp

新 pass 的实现代码。和上一小节类似的实现就不赘述了。

定义了一个 hasUnoccupiedSlot() 函数,用来判断某条指令是否满足我们上文指定的模式,首先判断这条指令是否具有延迟槽,调用 hasDelaySlot() 函数,然后判断这条指令是否已经属于一个 bundle 或者是最后一条指令,调用 isBundledWithSucc() 函数。这两个函数都是 LLVM 内置函数,在 MachineInstr.h 中实现。

当满足条件时,先使用 BuildMI 创建 nop 指令,并插入到跳转指令的后边;然后调用 MIBundleBuilder 函数,将跳转指令和 nop 指令打到一个 bundle。

1.2 修改的文件

1.2.1 Cpu0.h

添加创建新 pass 的工厂函数。

1.2.2 Cpu0TargetMachine.cpp

在 addPreEmitPass() 函数中增加我们的 pass,和上一小节同理。

1.2.3 Cpu0AsmPrinter.cpp

void Cpu0AsmPrinter::emitInstruction(const MachineInstr *MI) {......do {if (I->isPseudo())llvm_unreachable("Pseudo opcode found in emitInstruction()");MCInst TmpInst0;MCInstLowering.Lower(&*I, TmpInst0);OutStreamer->emitInstruction(TmpInst0, getSubtargetInfo());} while ((++I != E) && I->isInsideBundle()); // Delay slot check
}

这里是汇编代码发射的地方,需要检查要发射的指令是否是 bundle,如果是,则将 bundle 展开,依次发射其中的每一条指令。这一个 while 代码在之前的章节已经添加。如果不做这个检查,则只有 bundle 中的第一条指令会被发射,这将会导致代码错误。

二、第四节

在这一节我们会增加条件MOV指令。条件 MOV 指令也叫做 Select 指令,和 C 语言中的 select 操作语义一致,由一个条件值、两个指定值和一个定义值(输出)组成。在满足一个条件时,将指定值赋给定义值,否则把另一个指定值赋给定义值。我们在 Cpu0 中将实现两条 MOV 指令,分别是 movz 和 movn,表示当条件成立时(或条件不成立时),赋值第一个值,否则,赋值另一个值。

由于编码位有限,通常的条件 MOV 指令和 Select 指令均设计为其中一个指定值与定义值是同一个操作数(或者也有设计为条件值与定义值是同一个操作数):

movz $1, $2, $3;    @ $3 为条件值,当 $3 满足(为 true)时,将 $2 赋值给 $1,@ 否则,保持 $1 值不变
movn $1, $2, $3;    @ $3 为条件值,当 $3 不满足(为 false)时,将 $2 赋值给 $1,@ 否则,保持 $1 值不变

可以发现,movz 和 movn 是可以相互替代的,即:

movz $1, $2, $3;    @ 等价于
movn $2, $1, $3;    @ 当然,还需要保证上下文数据正确

在 LLVM IR 中,只有一个指令来处理这个情况,叫做 select 指令,我们需要做的就是在后端代码中,将这个 IR 转换为正确的指令表示:

%ret = select i1 %cond, i32 %a, i32 %b

2.1 修改的文件

2.1.1 Cpu0InstrInfo.td

新增和条件 MOV 相关的指令实例和用于窥孔优化的 Pattern 描述。

前者即定义 movz 和 movn 指令。注意到在 class 中使用 let Constraints = "$F = $ra" 的属性来指定两个操作符是同一个值,这种写法通常用于当其中一个 def 操作数同时也需要作为 use 操作数的情况下,比如当前的 select 示例中。

各种Pat模式中是将 IR 过来的 select + cmp 节点组合优化为一条 movz 或 movn 指令。select 指令的 condition 需要一条比较(或其他起相同作用的)指令来得出条件结果,在 Cpu032I 机器中是 cmp 指令,在 Cpu032II 机器中是 slt 指令。因为通常比较两个值是否相等,还可以采用 xor 指令,所以对于低效的 Cpu032I 比较 cmp 指令,可以使用 xor 做替换,但对于大于、小于等条件代码则只能继续使用 cmp 指令,体现在 .td 文件中就是不特别去优化 select 指令组合下的条件指令。

这个优化的路径是:

IR:   icmp + (eq, ne, sgt, sge, slt, sle) + br
DAG:  ((seteq, setne, setgt, setge, setlt, setle) + setcc) + select
Cpu0: movz, movn

2.1.2 Cpu0ISelLowering.cpp/.h

需要做一点配置。首先,LLVM 的后端会默认把 SetCC 和 Select 两个 Node 合并成一条 Select_cc 指令,这是为能够支持 Select_cc 指令的后端而准备的,这种指令是通过 condition code 来作为 select 指令的条件,比如在 X86 机器中。我们的 Cpu0 不支持这种指令,所以需要在 Cpu0ISelLowering.cpp 中,将 Select_cc 设置为 Expand 类型,表示我们希望 LLVM 帮我们替代这个类型的节点。

另一件事是将 ISD::SELECT 这个 Node 的默认下降关掉,也就是设置其为 Custom 类型,在我们自定义的下降中,直接将这个 Node 返回。因为我们不希望 select Node 在 lowering 阶段被选择为 select,这样它会无法选到指令。我们的条件 MOV 指令和这里的 select 指令有一些差异,所以只能通过在指令选择时的优化合并来实现从 select Node 到后端指令的 lowering。

2.2 窥孔优化

上边提到了一个名词窥孔优化,这里简单介绍一下。

窥孔优化(Peephole Optimization)是一种编译器优化技术,通常在生成目标代码的最后阶段应用。窥孔优化通过在目标代码中寻找和替换特定的指令序列,以改进代码的效率和性能。这种优化技术通常针对短小的指令序列,称为“窥孔”(peephole),并尝试通过替换这些指令序列来提高代码的质量。

窥孔优化的目标是通过识别和替换无效、低效或冗余的指令序列,以减少指令的执行次数、减少内存访问或提高代码的局部性,从而提高程序的性能。这种优化技术通常是基于一组预定义的优化规则或模式,编译器会在目标代码中寻找这些模式,并根据规则进行相应的优化操作。

通过窥孔优化,编译器可以在不改变程序逻辑的情况下,对生成的目标代码进行微调和改进,以使程序在运行时更加高效。这种优化技术在编译器中扮演着重要的角色,有助于提高代码的执行速度、减少资源消耗,并改善程序的整体性能。

窥孔优化其实不单单指一种优化,泛指后端多种指令层面的优化,我们后端流水线里默认就带有这个pass:

void TargetPassConfig::addMachineSSAOptimization() {
......addPass(&PeepholeOptimizerID);// Clean-up the dead code that may have been generated by peephole// rewriting.addPass(&DeadMachineInstructionElimID);
}

LLVM的窥孔优化是在PeepholeOptimizer.cpp这个文件里实现的。文件头的注释中例举出来了几个优化的典型场景。

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

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

相关文章

170.二叉树:平衡二叉树(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr, right(nullptr) {}* Tree…

第103天: 权限提升-Linux 系统辅助项目脏牛Dirty内核漏洞SUIDGUID

项目下载地址 综合类探针: https://github.com/liamg/traitor 自动化提权: https://github.com/AlessandroZ/BeRoot 信息收集: https://github.com/rebootuser/LinEnum https://github.com/sleventyeleven/linuxprivchecker 漏洞探针&#xf…

Webpack前端打包工具详解

目录 Webpack前端打包工具详解一、Webpack 的作用二、Webpack 的安装和基本使用1. 安装 Webpack2. 创建 Webpack 配置文件3. 运行 Webpack 三、Webpack 核心概念1. 入口(Entry)2. 输出(Output)3. 加载器(Loaders&#…

【wiki知识库】06.文档管理页面的添加--前端Vue部分

📝个人主页:哈__ 期待您的关注 目录 一、🔥今日目标 二、🐻前端Vue模块的改造 BUG修改 1.wangeditor无法展示问题 2.弹窗无法正常关闭问题 2.1 添加admin-doc.vue 2.1.1 点击admin-ebook中的路由跳转到admin-doc 2.2.2 进入…

Rust-06-所有权

所有权(系统)是 Rust 最为与众不同的特性,它让 Rust 无需垃圾回收即可保障内存安全,下面是所有权以及相关功能:借用(borrowing)、slice 以及 Rust 如何在内存中布局数据。 通过所有权系统管理内…

CSDN个人主页动态地图(前端/后端)

前段时间有人问我,关于CSDN个人主页动态地图的实现,我来解答一下。 这里要注意一下:使用CSDN的API需要遵循其开发者协议和使用规范,确保你的使用方式符合相关规定 前端部分: 创建一个HTML页面作为个人主页。在页面上放…

TCP/IP协议分析实验:通过一次下载任务抓包分析

TCP/IP协议分析 一、实验简介 本实验主要讲解TCP/IP协议的应用,通过一次下载任务,抓取TCP/IP数据报文,对TCP连接和断开的过程进行分析,查看TCP“三次握手”和“四次挥手”的数据报文,并对其进行简单的分析。 二、实…

Prompt实现简单英语单词教学

model: gpt-3.5-turbo Bot: 用于执行翻译任务 OutPutDefend: 用于判断任务输出结果是否完整 具体实现及Prompt Bot 模型配置 使用 gpt-3.5-turbo 便可完成任务 考虑到该任务是生成文本的任务,因此将temperature设置为了0.7 Prompt 将任务描述,输出…

数据结构:旋转数组

方法1 &#xff08;三次逆置法&#xff09;&#xff1a; void reverse(int* nums, int start, int end) {while (start < end) {int temp nums[start];nums[start] nums[end];nums[end] temp;start;end--;} }void rotate(int* nums, int numsSize, int k) {k k % numsS…

大模型常用推理参数工作原理

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 do_sample do_sample 参数控制是否使用采样…

Spring Security 应用详解

一、 集成SpringBoot 1.1 Spring Boot 介绍 Spring Boot 是一套 Spring 的快速开发框架&#xff0c;基于 Spring 4.0 设计&#xff0c;使用 Spring Boot 开发可以避免一些繁琐的工程 搭建和配置&#xff0c;同时它集成了大量的常用框架&#xff0c;快速导入依赖包&#xff0…

【C++11】多线程常用知识

知识体系 thread C++ thread中最常用的两个函数是join和detach,怎么选择呢,简单来说,如果希望等待线程结束,用join,如果希望异步执行,且不等待执行结果,那么就用detach;thread_local可以简单理解为一个线程级别的全局变量;线程id在调试多线程程序时是非常有用的东西;…

【学习笔记】Linux文件编译调试相关(问题未解决)

//-I意为include 指定头文件搜索路径 -l&#xff1a;告诉编译器链接时需要的库 gcc *.c -I /usr/include/fastdfs/ -I /usr/include/fastcommon/ -l fdfsclient//调试gcc -g -rdynamic main.c如何解决 “ 段错误(吐核) ” &#xff1f;&#xff1f;&#xff1f; 【线上排错】记…

c语言基础篇A

A1.程序和程序设计语言 程序 程序算法数据结构程序设计方法语言工具和环境数据结构:数据的类型和数据的组织形式算法&#xff1a;对数据操作的方法和步骤 程序设计语言的种类 第一代语言&#xff1a;机器语言第二代语言&#xff1a;汇编语言第三代语言&#xff1a;高级语言…

coap:使用californium建立coap server和client的简单示例

【pom.xml】 <dependency><groupId>org.eclipse.californium</groupId><artifactId>californium-core</artifactId><version>2.0.0-M7</version> </dependency> <dependency><groupId>org.eclipse.californium&l…

zabbix-agent,zabbix_agentd和zabbix-agent2,zabbix_agent2的区别

所以带横杠- 的是应用程序名&#xff0c;比如zabbix-server和zabbix-agent 带下划线_ 的是应用程序所对应的进程名&#xff0c;zabbix_server和zabbix_agentd 从zabbix5版本开始&#xff0c;应用程序zabbix-agent分为zabbix-agent和zabbix-agent2&#xff0c;zabbix-agent2是…

【第13章】SpringBoot实战篇之项目部署

文章目录 前言一、准备1. 引入插件2. 打包3. 启动4. 后台启动 二、跳过测试模块三、外置配置文件1.引入插件2.忽略配置文件3. 外置配置文件 总结 前言 项目部署需要把项目部署到Linux服务器上&#xff0c;SpringBoot项目通过Maven打包即可快速生成可运行Jar包程序。 一、准备 …

Comfyui容器化部署与简介

目前使用 Stable Diffusion 进行创作的工具主要有两个&#xff1a;Stable Diffusion WebUI 和 ComfyUI。本文重点介绍ComfyUI的部署使用。 ComfyUI 可定制性很强&#xff0c;可以让创作者搞出各种新奇的玩意&#xff0c;通过工作流的方式&#xff0c;也可以实现更高的自动化水平…

Kimichat使用案例010:快速识别出图片中的表格保存到Excel

文章目录 一、介绍二、图片信息三、输入内容四、输出内容五、markdown提示词六、markdown输出一、介绍 如果有一张图片格式的表格,想要快速复制到Excel表格中,那么一般要借助于OCR工具。之前试过不少在线OCR工具,识别效果差强人意。其实,kimichat就可以非常好的完成这个任务…

文件怎么去重?5个技巧,教你删除重复文件!

一般来说&#xff0c;在处理大量文件时&#xff0c;你可能会遇到重复的类似文件。这些文件占据了电脑上不必要的磁盘空间&#xff0c;导致系统性能下降。而这些文件可以是不同类型的&#xff0c;如照片、视频、音频、存档、文档等。正因如此&#xff0c;您需要通过文件去重来删…