LLVM后端__llc中值定义信息的查询方法示例

关于LiveIntervals pass中相关数据结构的含义,在寄存器分配前置分析(5.1) - LiveInterval这篇博客中已经做了清晰的讲解,此处不再赘述,本文主要讲解值定义信息VNInfo的使用方法和注意事项。

1. VNInfo含义

在LLVM的源码中,VNInfo定义在include/llvm/CodeGen/LiveInterval.h中,该结构主要表示machine级别的值定义信息,例如下面的
MachineBasicBlock bb1中,%4是一个vreg,对于96处的use点,其定义在SlotIndex 80B的位置,后面通过%4的LiveInterval查询96B处使用的%4的定义信息时,返回的就是VNInfo对象。

值得注意的是,VNInfo还包含了isPHIDef的接口,可以查询一个值是否是Phi定义的(即使在PHI指令被消除后也可以查),实现原理主要是
看该值的def是不是Basic block boundary类型的SlotIndex,这是因为一般的值定义点SlotIndex类型都是Normal register def,只有PHI值定义点才是Basic block boundary类型。

64B	bb.1:; predecessors: %bb.0successors: %bb.3(0x80000000); %bb.3(100.00%)80B	  %4:gpr32all = COPY $wzr
96B	  %3:gpr32all = COPY %4:gpr32all
112B	  %12:gpr32all = COPY %3:gpr32all
128B	  B %bb.3
  /// VNInfo - Value Number Information./// This class holds information about a machine level values, including/// definition and use points.///class VNInfo {public:using Allocator = BumpPtrAllocator;/// The ID number of this value.unsigned id;/// The index of the defining instruction.SlotIndex def;/// VNInfo constructor.VNInfo(unsigned i, SlotIndex d) : id(i), def(d) {}/// VNInfo constructor, copies values from orig, except for the value number.VNInfo(unsigned i, const VNInfo &orig) : id(i), def(orig.def) {}/// Copy from the parameter into this VNInfo.void copyFrom(VNInfo &src) {def = src.def;}/// Returns true if this value is defined by a PHI instruction (or was,/// PHI instructions may have been eliminated)./// PHI-defs begin at a block boundary, all other defs begin at register or/// EC slots.bool isPHIDef() const { return def.isBlock(); }/// Returns true if this value is unused.bool isUnused() const { return !def.isValid(); }/// Mark this value as unused.void markUnused() { def = SlotIndex(); }};

2. 如何在llc后端pass中查询一个值的在Use点对应的定义点

2.1 编写一个简单的示例

// learn_vni_info.cc
int VNIInfoLearn(int n) {int res = 0;for (int i = 0; i < n; i++) {res += i;}return res;
}

使用命令clang -O1 -S -emit-llvm learn_vni_info.cc -o learn_vni_info.ll将learn_vni_info.cc编译为ll文件(这里我是用的NDK中自带的clang,
因为我编出来的代码一般在安卓设备上运行,ndk中已经配置好了交叉编译环境,对应LLVM-17)

gwz@DESKTOP-VNM3O2M:~/work/learn_llvm/vni_info$ cat learn_vni_info.ll
; ModuleID = 'learn_vni_info.cc'
source_filename = "learn_vni_info.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable
define dso_local noundef i32 @_Z12VNIInfoLearni(i32 noundef %0) local_unnamed_addr #0 {%2 = icmp sgt i32 %0, 0br i1 %2, label %3, label %133:                                                ; preds = %1%4 = add i32 %0, -1%5 = zext i32 %4 to i33%6 = add i32 %0, -2%7 = zext i32 %6 to i33%8 = mul i33 %5, %7%9 = lshr i33 %8, 1%10 = trunc i33 %9 to i32%11 = add i32 %10, %0%12 = add i32 %11, -1br label %1313:                                               ; preds = %3, %1%14 = phi i32 [ 0, %1 ], [ %12, %3 ]ret i32 %14
}attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{!"Android (11349228, +pgo, +bolt, +lto, -mlgo, based on r487747e) clang version 17.0.2 (https://android.googlesource.com/toolchain/llvm-project d9f89f4d16663d5012e5c09495f3b30ece3d2362)"}

然后使用命令~/work/llvm-project/build/bin/opt --passes='view-cfg' learn_vni_info.ll生成CFG图,然后使用
dotty /tmp/cfg._Z12VNIInfoLearni-9cd348.dot观察结构。(ndk中没有prebuilt的opt和llc,这里的opt我是自己编译的LLVM-19版本,
SSH界面使用的mobaxterm)
在这里插入图片描述
可以看到这里有个PHI值%14,在Phi消除后,在该Phi值的2个source block中,都会被替换为同一个vreg,
这里的过程可以参考寄存器分配前置分析(1) — PHIElimination.

为了简单起见,直接对lib/CodeGen/RegisterCoalescer.cpp代码进行一点小改造。为啥改这个pass,是因为
LiveIntervals pass知之后就是这个pass,对值定义点进行分析需要LiveIntervals 分析的结果,从代码中
也可以看到register-coalescer的依赖pass。

char &llvm::RegisterCoalescerID = RegisterCoalescer::ID;INITIALIZE_PASS_BEGIN(RegisterCoalescer, "register-coalescer","Register Coalescer", false, false)
INITIALIZE_PASS_DEPENDENCY(LiveIntervals)
INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_END(RegisterCoalescer, "register-coalescer","Register Coalescer", false, false)

对该pass进行小改动,在执行该pass之前,check一下COPY指令SrcReg的def信息,修改代码后重新编译llc

--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -4209,6 +4209,42 @@ bool RegisterCoalescer::runOnMachineFunction(MachineFunction &fn) {LIS = &getAnalysis<LiveIntervals>();AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();Loops = &getAnalysis<MachineLoopInfo>();
+  LLVM_DEBUG(
+    dbgs() << "learn VNInfo ----------------------------\n";
+    const SlotIndexes &Indexes = *LIS->getSlotIndexes();
+    MF->print(dbgs(), &Indexes);
+    for (MachineBasicBlock& MBB : *MF) {
+      for (MachineInstr& MI : MBB) {
+        // 以COPY指令为例,分析COPY的SrcReg定义点
+        if (MI.isCopy()) {
+          MachineOperand SrcOp = MI.getOperand(1);
+          Register SrcReg = SrcOp.getReg();
+          if (SrcReg.isVirtual()) {
+            // 获取SrcReg的LiveInterval信息
+            LiveInterval& LI = LIS->getInterval(SrcReg);
+            // 获取当前MI对应的SlotIndex,也就是在MIR中的编号
+            SlotIndex SIdx = Indexes.getInstructionIndex(MI);
+            // 通过Query接口,查询SrcReg在当前使用点SIdx的定义信息
+            LiveQueryResult LRQ = LI.Query(SIdx);
+            dbgs() << "Cur SlotIndex = " << SIdx << ", MI = " << MI;
+            // valueIn返回当前MI处Use值(live-in)的def信息,如果没有则返回nullptr
+            if (VNInfo* VNI = LRQ.valueIn()) {
+              MachineInstr* DefMI = Indexes.getInstructionFromIndex(VNI->def);
+              dbgs() << SrcOp << " def SlotIndex = " << VNI->def << "\n";
+              // 需要注意,Phi值是Block boundry的SlotIndex定义,无法与MI直接对应
+              if (!VNI->isPHIDef()) {
+                dbgs() << SrcOp << " def MI = " << *DefMI;
+              }
+            } else {
+              dbgs() << "Dont find live in value!";
+            }
+            dbgs() << "\n";
+          }
+        }
+      }
+    }
+  );
+if (EnableGlobalCopies == cl::BOU_UNSET)JoinGlobalCopies = STI.enableJoinGlobalCopies();else

2. 2 分析结果

执行~/work/llvm-project/build/bin/llc -march=aarch64 -filetype=obj -debug-only=regalloc -stop-after=register-coalescer learn_vni_info.ll -o learn_vni_info.o > vni.log 2>&1命令将debug信息输出到vni.log中,下面我将关键的部分截取出来(为了减少干扰,在执行完register-coalescer pass后就停止)。

为了便于阅读,我直接将分析写在log中。

********** REGISTER COALESCER **********
********** Function: _Z12VNIInfoLearni
learn VNInfo ----------------------------
# Machine code for function _Z12VNIInfoLearni: NoPHIs, TracksLiveness, TiedOpsRewritten
Function Live Ins: $w0 in %20B	bb.0 (%ir-block.1):successors: %bb.2(0x50000000), %bb.1(0x30000000); %bb.2(62.50%), %bb.1(37.50%)liveins: $w0
16B	  %2:gpr32common = COPY $w0
32B	  %5:gpr32 = SUBSWri %2:gpr32common, 1, 0, implicit-def $nzcv
48B	  Bcc 10, %bb.2, implicit killed $nzcv64B	bb.1:; predecessors: %bb.0successors: %bb.3(0x80000000); %bb.3(100.00%)80B	  %4:gpr32all = COPY $wzr
96B	  %3:gpr32all = COPY %4:gpr32all
112B	  %12:gpr32all = COPY %3:gpr32all
128B	  B %bb.3144B	bb.2 (%ir-block.3):; predecessors: %bb.0successors: %bb.3(0x80000000); %bb.3(100.00%)160B	  %7:gpr32common = SUBWri %2:gpr32common, 2, 0
176B	  %8:gpr64 = UMADDLrrr %5:gpr32, %7:gpr32common, $xzr
192B	  %9:gpr64 = UBFMXri %8:gpr64, 1, 63
208B	  %10:gpr32 = COPY %9.sub_32:gpr64
224B	  %11:gpr32 = ADDWrr %5:gpr32, %10:gpr32
240B	  %0:gpr32all = COPY %11:gpr32
256B	  %12:gpr32all = COPY %0:gpr32all272B	bb.3 (%ir-block.13):; predecessors: %bb.2, %bb.1288B	  %1:gpr32all = COPY %12:gpr32all
304B	  $w0 = COPY %1:gpr32all
320B	  RET_ReallyLR implicit $w0# End machine code for function _Z12VNIInfoLearni.Cur SlotIndex = 96B, MI = %3:gpr32all = COPY %4:gpr32all
%4:gpr32all def SlotIndex = 80r
%4:gpr32all def MI = %4:gpr32all = COPY $wzr// %3在112B处被使用,这里定义的%12是bb.3中原Phi值(Phi消除后对应vreg %12)的第一个Source值的定义点,是常量0值
Cur SlotIndex = 112B, MI = %12:gpr32all = COPY %3:gpr32all
%3:gpr32all def SlotIndex = 96r
%3:gpr32all def MI = %3:gpr32all = COPY %4:gpr32allCur SlotIndex = 208B, MI = %10:gpr32 = COPY %9.sub_32:gpr64
%9.sub_32:gpr64 def SlotIndex = 192r
%9.sub_32:gpr64 def MI = %9:gpr64 = UBFMXri %8:gpr64, 1, 63Cur SlotIndex = 240B, MI = %0:gpr32all = COPY %11:gpr32
%11:gpr32 def SlotIndex = 224r
%11:gpr32 def MI = %11:gpr32 = ADDWrr %5:gpr32, %10:gpr32// %0在256B处被使用,这里定义的%12是bb.3中原Phi值的第二个Source值的定义点
Cur SlotIndex = 256B, MI = %12:gpr32all = COPY %0:gpr32all
%0:gpr32all def SlotIndex = 240r
%0:gpr32all def MI = %0:gpr32all = COPY %11:gpr32// %12在288B处被使用,这里定义的%1是bb.3中的Phi值,可以看到这里%12的定义点
// 对应的SlotIndex是272B,而不是272r。
Cur SlotIndex = 288B, MI = %1:gpr32all = COPY %12:gpr32all
%12:gpr32all def SlotIndex = 272BCur SlotIndex = 304B, MI = $w0 = COPY %1:gpr32all
%1:gpr32all def SlotIndex = 288r
%1:gpr32all def MI = %1:gpr32all = COPY %12:gpr32all

对于查询结果LiveQueryResult还有不少有用的接口,使用方法都是类似的,读者可以通过上述的简单学习验证方法快速掌握,
这里就不再赘述了。

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

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

相关文章

视频汇聚EasyCVR综合安防平台对接GA/T1400公安视图库及应用方案

随着科技的不断进步&#xff0c;视频监控系统在公共安全领域发挥着越来越重要的作用。GA/T1400公安视图库作为公安视频图像信息应用系统的标准&#xff0c;为视频监控系统的对接提供了统一的规范和技术要求。 GA/T1400标准的应用范围广泛&#xff0c;涵盖了公安系统的视频图像信…

图解大模型分布式并行各种通信原语

背景 在分布式集群上执行大模型任务时候&#xff0c;往往使用到数据并行&#xff0c;流水线并行&#xff0c;张量并行等技术&#xff0c;这些技术本质上也就是对数据进行各种方案的切分&#xff0c;然后放到不同的节点上运算。不同节点在计算的过程中需要对数据分发或者同步等…

【精读文献】J. Environ. Manage.|青藏高原生态恢复项目下植被覆盖动态及其对生态系统服务的约束效应

目录 文章简介 01 文章摘要 02 研究背景、目标及创新点 2.1 研究背景 2.2 研究现状 03 研究区域与数据集 3.1 研究区域 3.2 研究数据 04 研究方法 4.1 趋势分析 4.2 残差趋势分析 4.3 偏相关 4.4 生态系统服务评价 4.5 约束线的定义和提取 05 研究结果 5.1 植被…

秒杀基本功能开发(不考虑高并发情况)

文章目录 1.显示秒杀状态1.controller修改GoodsController.java的toDetail方法&#xff0c;响应秒杀状态和秒杀剩余时间 2.前端1.goodsDetail.html 图片下面添加一行秒杀开始时间2.goodsDetail.html 添加计时器js代码 3.测试1.秒杀进行中2.修改db的秒杀开始时间为明天3.出现秒杀…

<Rust><iced>基于rust使用iced库构建GUI实例:动态改变主题色

前言 本专栏是Rust实例应用。 环境配置 平台&#xff1a;windows 软件&#xff1a;vscode 语言&#xff1a;rust 库&#xff1a;iced、iced_aw 概述 本篇构建了这样的一个实例&#xff0c;可以动态修改UI的主题&#xff0c;通过菜单栏来选择预设的自定义主题和官方主题&#…

python列表的扩展操作

列表的扩展操作 zip() 函数 我们先学习 zip() 函数&#xff0c;将排名与分数挂钩。 还记得期中考试的顺序排名和分数吗&#xff1f;我们把排名放在了列表 midterm_rank 中&#xff0c;把分数放在了 scores 中。不过当时 scores 并没有排序&#xff0c;我们要对数据进行预处理…

深入理解文件系统和日志分析

文件是存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区&#xff0c;每个扇区的大小是512字节。 inode&#xff1a;存储元信息&#xff08;包括文件的属性&#xff0c;权限&#xff0c;创建者&#xff0c;创建日期等等&#xff09; block&#xff1a;块&#xff0c;连续…

小白跟做江科大32单片机之LED闪烁

原理介绍 原理介绍详见&#xff1a; 【STM32】江科大STM32学习笔记汇总(已完结)_stm32江科大笔记-CSDN博客https://blog.csdn.net/u010249597/article/details/134762513 项目准备 1.在项目文件夹中新建3-1 LED文件夹 2.keil新建项目&#xff0c;打开新建的3-1 LED&#xf…

【CC2530-操作外部flash】

zigbee cc2530操作flash&#xff0c;以cc2530读flash_id为例子&#xff1b; void InitIO() {CLKCONCMD & ~0x40; //设置系统时钟源为32MHZ晶振 while(CLKCONSTA & 0x40); //等待晶振稳定为32M CLKCONCMD & ~0x47; //设置系统主时钟频率为32MHZ…

面试(五)

目录 1. 知道大顶堆小顶端吗&#xff0c;代码怎么区分大顶端小顶端 2. 计算机中栈地址与内存地址增长方向相反吗&#xff1f; 3. %p和%d输出指针地址 4. 为什么定义第二个变量时候&#xff0c;地址反而减了 5. 12&#xff0c;32&#xff0c;64位中数据的占字节&#xff1f;…

物质的量质量,它们可不是一个概念

物质的量&质量&#xff0c;它们可不是一个概念。 物质的量&质量 乍一听物质的量&#xff0c;还以为是和质量有什么关系&#xff0c;是不是&#xff1f;其实物质的量和质量没什么直接的联系。 物质的量是国际单位制中7个基本物理量之一&#xff0c;其符号为n&#xf…

Aras Innovator-Team(群组)的使用方法

当Aras Innovator在处理权限时&#xff0c;在不使用Team的情况下&#xff0c;系统的权限配置可以满足大部分业务场景&#xff0c;如&#xff1a;常见的按照组织架构&#xff0c;成员和角色分配权限&#xff0c;按照生命周期分配权限等。 如果遇到比较复杂的权限需求&#xff0c…

AltiumDesigner/AD添加数据库连接

1.首先确保本机电脑有无对应的数据库驱动&#xff0c;例如我这边要添加MySQL的数据&#xff0c;则需要首先下载MySQL数据驱动&#xff1a;MySQL :: Download MySQL Connector/ODBC (Archived Versions) 2.运行“odbcad32.exe”&#xff0c;如下图添加对应的数据库配置&#xf…

【C/C++】C/C++车辆交通违章管理系统(源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

面试题:useEffect的Clean Up 什么时候触发?

​ useEffect作为做常用的Hook&#xff0c;以下三个知识点你有必要了解下~ 防止写出奇怪的代码祸害队友&#xff0c;而我不幸就是这个受害者&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; useEffect的依赖项为空 useEffect的dependencyList作为一个可选参数…

Pytest框架中用例用例执行常用参数介绍

pytest 支持通过命令行参数来定制测试运行的方式。以下是一些常用的 pytest 执行参数介绍。 学习目录 -q 或 --quiet: 安静模式&#xff0c;只显示进度和摘要 -s : 选项允许在测试的输出中捕获 stdout 和 stderr。 -v : 选项会使 pytest 的输出更加详细。 -k &#xff1a;…

Git 恢复已删除的branch

六一节晚上改了点code, 做完之后commit, 然后误删了这个branch, 并且新建了branch. 那么怎样恢复已删除的branch呢&#xff1f; 网上查询一番&#xff0c;找到了答案&#xff1a; 1. git reflog 找到被删的branch中最后一笔commit, 记录它的SHA1。 怎么看SHA1是被删除的bra…

鸿蒙应用Stage模型【应用/组件级配置】

应用/组件级配置 在开发应用时&#xff0c;需要配置应用的一些标签&#xff0c;例如应用的包名、图标等标识特征的属性。本文描述了在开发应用需要配置的一些关键标签。 应用包名配置 应用需要在工程的AppScope目录下的[app.json5配置文件]中配置bundleName标签&#xff0c;…

Python PyInstaller打包方法介绍

为了将开发好的Python工具交付给其他人使用&#xff0c;除了在目标电脑部署Python编译环境以外&#xff0c;我们还可以将它打包成可执行文件&#xff0c;这样目标电脑不需要安装Python环境就可以运行。将Python程序打包成可执行文件的方法有多种&#xff0c;比如Nuitka、PyInst…

微博增强-tampermonkey脚本实现网页管理悄悄关注

不是很明白微博为什么不出个x的列表功能&#xff0c;毕竟现在信息洪流&#xff0c;有些东西只是要看要了解&#xff0c;但不希望天天在首页轰炸眼睛&#xff0c;扰乱心智。 这个tampermonkey脚本适配了pc web和手机pwa版本&#xff08;weibo.com/m.weibo.cn&#xff09;,解决了…