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.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还有不少有用的接口,使用方法都是类似的,读者可以通过上述的简单学习验证方法快速掌握,
这里就不再赘述了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

!力扣 108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按升序排列&#xff0c;请你将其转换为一棵 平衡二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确答案…

Java——异常

1.什么是异常 将程序执行过程中发生的不正常行为称为异常。 常见的异常有&#xff1a;算数异常&#xff0c;空指针异常&#xff0c;数组越界异常 每一种异常都有对应的类对齐描述 为了对每一种异常进行管理&#xff0c;Java内部实现了一个对异常的体系结构 1. Throwable&#x…

HNCTF2022 REVERSE

[HNCTF 2022 WEEK2]esy_flower 简单花指令 Nop掉 然后整段u c p然后就反汇编 可能反编译的不太对&#xff0c;&#xff0c;看了别人的wp就是ida反编译的有问题 #include<stdio.h> #include<string.h> int main() {int i,j;char ch[]"c~scvdzKCEoDEZ[^roDICU…

Unity协程详解

什么是协程 协程&#xff0c;即Coroutine&#xff08;协同程序&#xff09;&#xff0c;就是开启一段和主程序异步执行的逻辑处理&#xff0c;什么是异步执行&#xff0c;异步执行是指程序的执行并不是按照从上往下执行。如果我们学过c语言&#xff0c;我们应该知道&#xff0…

node-sass和sass-loader安装Error经验

一、问题 当前笔记本环境版本&#xff1a;node-v16.15.1&#xff1b;npm-8.11.0&#xff0c;在面对五年前vue项目的依赖sass-loader8.0.2&#xff0c;node-sass4.14.1的情况下&#xff0c;怎么参考大神们的安装教程&#xff0c;始终存在Error&#xff0c;经过坚持不懈的努力&a…

list的简单模拟实现

文章目录 目录 文章目录 前言 一、使用list时的注意事项 1.list不支持std库中的sort排序 2.去重操作 3.splice拼接 二、list的接口实现 1.源码中的节点 2.源码中的构造函数 3.哨兵位头节点 4.尾插和头插 5.迭代器* 5.1 迭代器中的operator和-- 5.2其他迭代器中的接口 5.3迭代器…

Nginx源码编译安装

Nginx NginxNginx的特点Nginx的使用场景Nginx 有哪些进程 使用源码编译安装Nginx准备工作安装依赖包编译安装Nginx检查、启动、重启、停止 nginx服务配置 Nginx 系统服务方法一&#xff1a;方法二&#xff1a; 访问Nginx页面 升级Nginx准备工作编译安装新版本Nginx验证 Nginx N…

顶底背离的终极猜想和运用

这几天圈内都在传底蓓离什么的。作为严肃的量化自媒体&#xff0c;我们就不跟着吃这波瓜了。不过&#xff0c;我一直很关注技术指标的顶背离和底背离&#xff0c;一直在追问它的成因如何&#xff0c;以及如何预测。 底蓓离把我目光再次吸引到这个领域来&#xff0c;于是突然有…

Kubernetes-使用集群CA证书给用户颁发客户端证书访问Api-Server

一、官网地址 证书和证书签名请求 | Kubernetes 二、Demo 一、创建测试文件夹 cd ~ mkdir add_k8s_user_demo cd add_k8s_user_demo 二、创建符合X509标准的证书 openssl genrsa -out myuser.key 2048 openssl req -new -key myuser.key -out myuser.csr -subj "/CNmy…

【30天精通Prometheus:一站式监控实战指南】第14天:jmx_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…

mybatis增删改查模板设置及设置调用

mybatis增删改查模板设置 系统配置文件完成以及连接好数据之后&#xff0c;就可以用这个mybatis了&#xff0c;首先写这个数据库的增删改查模板StashMapper.xml&#xff0c;这个东西是要放在DAO层中的奥&#xff0c;切记。 1.编写mybatis对应数据库的增删改查模板 在我的Sta…

[Qt学习笔记]Qtxlsx在Qt下的配置和调用

背景分析 Qt操作Excel文件一般有QAxObject和QtXlsx两种方法&#xff0c;前者需要调用wps或office组件进行读写操作&#xff0c;具有一定的局限性&#xff0c;下面列出两种方法的优缺点对比 QAxObject&#xff1a; 优点&#xff1a;支持xls和xlsx等版本。office组件读写速度快&…

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

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

LLaMA-Factory推理实践

运行成功的记录 平台&#xff1a;带有GPU的服务器 运行的命令 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory/ conda create -n py310 python3.10 conda activate py310由于服务器不能直接从huggingface上下载Qwen1.5-0.5B&#xff0c;但本地可…

轻松拿捏C语言——【文件操作】

&#x1f970;欢迎关注 轻松拿捏C语言系列&#xff0c;来和 小哇 一起进步&#xff01;✊ &#x1f389;创作不易&#xff0c;请多多支持&#x1f389; &#x1f308;感谢大家的阅读、点赞、收藏和关注&#x1f495; &#x1f339;如有问题&#xff0c;欢迎指正 目录 &#x1f…

Python高阶学习记录

文章导读 阅读本文需要一定的python基础&#xff0c;部分知识点是对python入门篇学习记录和python并发编程学习记录的深入探究&#xff0c;本文记录的Python知识点包括函数式编程&#xff0c;装饰器&#xff0c;生成器&#xff0c;迭代器&#xff0c;正则表达式&#xff0c;内存…

HTML蓝色爱心

目录 写在前面 HTML入门 完整代码 代码分析 运行结果 系列推荐 写在后面 写在前面 最近好冷吖&#xff0c;小编给大家准备了一个超级炫酷的爱心&#xff0c;一起来看看吧&#xff01; HTML入门 HTML全称为HyperText Markup Language&#xff0c;是一种标记语言&#…

最小时间差

首先可以想到&#xff0c;可以计算出任意两个时间之间的差值&#xff0c;然后比较出最小的&#xff0c;不过这种蛮力方法时间复杂度是O(n^2)。而先将时间列表排序&#xff0c;再计算相邻两个时间的差值&#xff0c;就只需要计算n个差值&#xff0c;而排序阶段时间复杂度通常为O…

Docker成功启动Rabbitmq却访问不了管理页面问题解决

目录 启动步骤&#xff1a; 无法访问问题总结&#xff1a; 启动步骤&#xff1a; 拉取镜像&#xff1a; docker pull rabbitmq 运行&#xff1a; docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq进入容器&#xff1a; docker exec -it 容器id /bin/…

C++ C (1152) : 循环赛日程表

文章目录 一、题目描述二、参考代码 一、题目描述 二、参考代码 #include<iostream> #include<vector> #include<cstdlib> using namespace std;void generateSchedule(vector< vector<int> >& table, int numPlayers, int rounds) {// 生…