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

news2024/12/23 4:29:05

关于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.0
	  successors: %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, 0
  br i1 %2, label %3, label %13

3:                                                ; 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, -1
  br label %13

13:                                               ; 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 %2

0B	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 $nzcv

64B	bb.1:
	; predecessors: %bb.0
	  successors: %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

144B	bb.2 (%ir-block.3):
	; predecessors: %bb.0
	  successors: %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:gpr32all

272B	bb.3 (%ir-block.13):
	; predecessors: %bb.2, %bb.1

288B	  %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:gpr32all

Cur 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, 63

Cur 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 = 272B

Cur SlotIndex = 304B, MI = $w0 = COPY %1:gpr32all
%1:gpr32all def SlotIndex = 288r
%1:gpr32all def MI = %1:gpr32all = COPY %12:gpr32all

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

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

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

相关文章

Rustdesk 自建服务器教程

一、环境 阿里云轻量服务器、debian11 系统 二、服务端搭建 2.1、开放防火墙指定端口 TCP(21115, 21116, 21117, 21118, 21119)UDP(21116) 2.2、安装 rustdesk 服务器文件 在 github 下载页https://github.com/rustdesk/rustdesk-server/releases/&#xff0c;下载 rustde…

(VS2019+VTK9.3)第一个VTK程序

经过配置后&#xff0c;VS2019VTK 9.3&#xff0c;第一个VTK程序&#xff0c;可显示。 #include <vtkAutoInit.h> #include <vtkActor.h> #include <vtkCamera.h> #include <vtkCylinderSource.h> #include <vtkNamedColors.h> #include <vt…

【十大排序算法】选择排序

选择就像是在谱曲&#xff0c;每个决定就是一个音符&#xff0c;只有将它们有序地安排在一起&#xff0c;才能奏响美妙的乐章。 文章目录 一、选择排序的思想二、选择排序的发展历程三、选择排序具象化四、选择排序算法实现五、选择排序的特性推荐阅读 一、选择排序的思想 选…

快速排序(Quick Sort)(C语言) 超详细解析!!!

生活的本质是什么呢? 无非就是你要什么就不给你什么. 而生活的智慧是什么呢? 是给你什么就用好什么. ---马斯克 索引 一. 前言二. 快速排序的概念三. 快速排序的实现1. hoare2. 挖坑法3. 前后指针法 总结 正文开始 一. 前言 接上文, 前面我们了解了插入排序, 与优化版本希尔…

ssh远程管理yum源进阶

文章目录 sshNFS 共享存储服务实验yum的进阶使用Apanche做一个网页形式的源用vsftpd做一个源混合源 ssh ssh是一种安全通道协议&#xff0c;用来实现字符界面的远程登录&#xff0c;远程复制&#xff0c;远程文本传输 ssh对通信双方的数据进行了加密 用户名和密码登录 秘钥…

Mysql学习(四)——SQL通用语法之DQL

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 DQLDQL-语法基本查询条件查询聚合函数分组查询排序查询分页查询 DQL DQL数据查询语言&#xff0c;用来查询数据库中表的记录。 DQL-语法 select 字段列表 from 表…

C++ | Leetcode C++题解之第136题只出现一次的数字

题目&#xff1a; 题解&#xff1a; class Solution { public:int singleNumber(vector<int>& nums) {int ret 0;for (auto e: nums) ret ^ e;return ret;} };

图相似度j计算——SimGNN

图相似性——SimGNN 论文链接&#xff1a;个人理解&#xff1a;数据处理: feature_1 [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # "A"[0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], # "B"[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0] # "C" 第二个循环&#xff…

Keil MDK Armcc6 总是全编译项目的问题

我碰到的问题是因为使用lib库待代替原本的源码引起的&#xff0c;把lib库去除&#xff0c;使用源码编译就不会出现全编译的问题了。但是至于一定要使用LIB库但是又不想全编译暂时不知道怎么弄&#xff0c;具体为什么会这样暂不清楚。但是可以确定的是编译器参数可能选的不对&am…

代理结算不再繁琐,Xinstall让App推广更轻松

在移动互联网时代&#xff0c;App的推广与获客已成为企业发展的重要一环。然而&#xff0c;随着推广模式的多样化&#xff0c;如何高效地管理App推广的代理结算&#xff0c;成为了许多企业面临的难题。Xinstall凭借其强大的超级渠道功能&#xff0c;为企业提供了一个完美的解决…

php实现抖音小程序支付

开发者发起下单_小程序_抖音开放平台 第一步、抖音小程序发起支付 tt.pay_小程序_抖音开放平台 前端提交订单数据到后端接口&#xff0c;然后使用 tt.pay发起支付 请求参数 属性 类型 必填 说明 order_id string 是 担保交易服务端订单号 order_token string 是 …

C语言 | Leetcode C语言题解之第136题只出现一次的数字

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> singleNumbers(vector<int>& nums) {int eor 0;for (int num:nums)eor ^ num;int rightOne eor & (~eor 1); // 提取出最右的1int onlyOne 0;for (int cur : nums) {if ((cur…

图像算法---自动对焦AF

一&#xff0c;CDAF反差对焦原理 CDAF&#xff0c;全称Contrast Detection Auto Focus&#xff0c;即反差式对焦或对比度检测自动对焦&#xff0c;是一种广泛应用于入门级数码相机和相机模块化智能手机上的自动对焦技术。以下是关于CDAF反差对焦的详细介绍&#xff1a; 工作原…

nginx动静分离和反向代理

一、动静分离 动静分离指的是将动态内容和静态内容分开处理。动态内容通常由后端应用程序生成&#xff0c;例如PHP、Python或Node.js&#xff0c;静态内容则包括图片、CSS、JavaScript等文件。 例子&#xff1a; #代理服务器一 server{listen 80;server_name www.dj.com;r…

C语言 | Leetcode C语言题解之第135题分发糖果

题目&#xff1a; 题解&#xff1a; int candy(int* ratings, int ratingsSize) {int ret 1;int inc 1, dec 0, pre 1;for (int i 1; i < ratingsSize; i) {if (ratings[i] > ratings[i - 1]) {dec 0;pre ratings[i] ratings[i - 1] ? 1 : pre 1;ret pre;inc…

ceph对象储存的使用

radosgw-admin user create --uid“user1” --display-name“user1” #创建用户 sudo apt install s3cmd cephadminceph-mgr01:~/ceph-cluster/s3$ s3cmd --configure Enter new values or accept defaults in brackets with Enter. Refer to user manual for detailed desc…

YUM安装httpd实验配置apache

实验目的及实验要求&#xff1a; 实验目的&#xff1a; 2.实验要求&#xff1a; &#xff08;1&#xff09;完成命令的编写&#xff0c;并能正确运行&#xff1b; &#xff08;2&#xff09;从中熟练掌握命令的功能及作用。 实验设备及软件&#xff1a; pc机 配置好Lin…

我们设计制造MW级水冷负载电阻器-数据中心船舶岸电发电机组测试大功率负载RLC阻感容集装箱负载

UEPR系列电阻采用先进材料制造&#xff0c;采用专利设计&#xff0c;将电阻与冷却液完全隔离&#xff0c;为水冷应用提供重量轻、体积小、超大功率的解决方案。其革命性的模块化设计意味着它们可以串联在一起&#xff0c;以满足您的电力需求。应用于发电、电力传输、电气传动等…

2024年汉字小达人活动还有4个多月开赛:来做18道历年选择题备考

根据近年的安排&#xff0c;2024年第11届汉字小达人比赛还有4个多月就启动&#xff0c;那么孩子们如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。②把历年真题刷…

JavaSE中的if语句、switch语句:如何控制程序流程?

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…