【译】LLVM 类型相等判断

【LLVM编译器系列-4】llvm IR中Value类 LLVM中万般皆Value,类的继承图已经表现的非常充分,具体Value类型怎么使用,本节只是简单介绍了类型方法,后面章节会重点介绍value类,use和user的使用方法。 阅读详情

在 LLVM 的类型系统中,进行两个类型是否等价的判断,对于做代码分析而言是很重要的,本文将介绍遇到的难题以及解决的方法。

内容来源: https://lowlevelbits.org/type-equality-in-llvm/
原文标题:TYPE EQUALITY IN LLVM
作者:AlexDenisov
译者:流左沙
注:翻译较原文会有一定的精简、重排和添删,主要是为了提取重要内容以达到更好的理解,请以原文为准。

1、LLVM 类型系统的简述

关于 LLVM 类型系统的背景,可以看我之前写的一篇译文或者原文,类型系统在本文不再过多展开。

简单再概述一下:

  • LLVMContext 管理 LLVM‘s core infrastructure 的 core “global” data,包括类型和常量的表
  • 类型的实例会被分配到堆内存上(如 llvm::Type *type = new llvm::Type;)
  • 类型的比较是通过指针的比较来完成的
  • LLVM 类型分成三种:原始类型(primitive types)、派生类型(derived types)、提前声明类型(forward-declared types)

2、问题的产生

如果两个模块(modules)使用了有相同名字的相同结构体,那么他们被加载到同一个上下文(context,内容)时,LLVM 会重命名其中一个,来避免命名冲突。

; Module1
%struct.Point = type { i32, i32 }

; Module 2
%struct.Point = type { i32, i32 }

; same context
%struct.Point = type { i32, i32 }
%struct.Point.1 = type { i32, i32 }

如果想消除重复的数据类型,明显的解决方法是:判断有相似名字的结构体,它们的实现是否相同。

注:直接去掉 LLVM 给名字加上的数字后缀,然后判断名字是否相等是不可行的,因为名字冲突的两个结构体,有可能有不同的实现体。

而 llvm::StructType::isLayoutIdentical 判断类型等价的操作,是基于指针的。导致会出现下面代码中的判断情况。

; these two have identical layout 判断为相同
%Point = type { i32, i32 }
%Pair = type { i32, i32 }

; these two DO NOT have identical layout 判断为不同
%PointWrap = type { %Point }
%PairWrap = type { %Pair }

说明这种方式也是不可行的。

2.1、Primitive Types Equality 原始类型等价判断

在 LLVM 当中,类型都归 LLVMContext 管理。原始类型,如 int32, float, double 是提前分配好,然后再重复使用。在 LLVMContext 的同一个上下文当中,你只能创建一种原始类型的一个实例。有了这个前提条件,很容易就能判断类型是否等价,直接比较它们的指针就够了。

但是如果是来自于不同的上下文,就不能这么比较了。比如,来自一个 LLVMContext 的 int32 类型,与来自另一个 LLVMContext 的 int32 类型。

2.2、Struct Types Equality 结构体类型等价判断

对于结构体类型,判断是否等价就更加复杂了。

; these two have identical layout 判断为相同
%Point = type { i32, i32 }
%Pair = type { i32, i32 }

; these two DO NOT have identical layout 判断为不同
%PointWrap = type { %Point }
%PairWrap = type { %Pair }

llvm::StructType::isLayoutIdentical通过指针来进行类型比较,只有结构体里的类型元素都相等,才返回 true。

2.3、IRLinker/llvm-link

LLVM 通过 IRLinker类来合并两个模块。LLVM 还提供了它的命令行工具 llvm-link。 IRLinker 运作正常,但它会丢掉一些重要信息。

下面是运行 IRLinker 的 IR 的例子:

%struct.Point = type { i32, i32 }
%struct.Tuple = type { i32, i32 }

然后变成

%struct.Point = type { i32, i32 }

另一个结构体被丢掉了(优化掉)了,因为它们有相同的实现体。但我们并不想丢失这个信息。

还有, IRLinker 还可能会引入源码级别没有出现的类型。

3、Solution 解决方法

解决方法的灵感来源是 Tree Automata(树自动机) 和 Ranked Alphabets(字母序列)。

字母序列(ranked alphabet) 包含有符号 F的有限集合, 以及一个算术函数 Arity(f), 而 f 属于 F的集合。

算术函数 Arity 表示符号 f 有多少个参数。符号可以是 constant(常量), unary(一元运算符), binary(二元), ternary(三元), or n-ary.

比如: a, b, f(,), g(), h(,,,,)。

a 和 b 是常量, f(,) 是二元运算, g() 是一元运算, 还有 h(,,,,) 是 n 元。 这些符号的参数数量分别是 0, 0, 2, 1, 5。

3.1、construction 构造

给定字母表 a, b, f(,), g() 我们可以构造以下这些树:

  • f(a, b)
  • g(b)
  • g(f(b, b))
  • f(g(a), f(f(a, a), b))
  • f(g(a), g(f(a, a)))
  • 等等

如果我们知道每个符号的参数数量,那么我们可以生成圆括号和逗号,来将一棵树序列化成一个字符串。这棵树以深度优先算法来构造。比如下面这些序列串:

  • fab
  • gb
  • gfbb
  • fgaffaab
  • fgagfaa

下面是更容易理解的对应的例子:

绿色箭头表示深度优先的遍历顺序。

所以我们可以将类型问题映射成 ranked alphabet/tree automaton 的概念。

3.2、Type Equality 类型等价判断

我们把每种类型视为一个符号,而它的参数数量则是类型的属性,这正是我们要比较的。然后,我们创建类型的树,把它转换成字符串序列。如果两种类型有相同的字符串序列,那么它们就是等价的。

比如:

  • i32, i64, i156: 符号是 I, 参数数量是 1,因为我们只在乎它的位宽 (32, 64, 156)
  • float: 符号是 F, 参数数量是 0, 所有 float 类型都是一样的
  • [16 x i32]: 符号是 A, 参数数量是 2, 我们只在乎数组长度和元素类型
  • i8*: 符号是 P, 参数数量是 1, 我们只在乎指针的类型
  • { i32, [16 x i8], i8* }: 符号是 S, 参数数量是 元素数量 + 2。我们要存储结构体的 ID 和元素数量

我们可以把这些类型转为序列化串:

  • i32 -> I(32) -> I32
  • i177 -> I(177) -> I177
  • [16 x i8*] -> A(16, P(I(8))) -> A16PI8
  • { i32, i8*, float } -> S(3, S0, I(32), P(I(8)), F) -> S3S0I32PI8F

注: S 的值分别是,元素数量 (3), 结构体 ID (S0), 以及所有它所包含的类型(递归地展开)。

3.3、Structural Equality 结构体等价判断

上面有个概念 struct ID。我们需要用它来判断递归类型的结构体是否等价:

%list = type { %list*, i32 }
%node = type { %node*, i32 }
%root = type { %node*, i32 }

上面的结构体都有相同的实现:一个指针 + 一个整型。但我们不会判断它们都是等价的,根据等价的定义,得出下面的结果:

list == node
root != node
root != list

原因很简单,因为 list 和 node 有相同的实现体和相同的递归结构,而 root 是其他的递归结构。

所以需要把递归结构也纳入判断之中,但我们不使用递归结构的名字。在构建树之前,我们给它们赋值一个符号名或者 ID。

所以 list 和 node 会被定义成 S(2, S0, P(S(2, S0, x, x), I(32)) ,而 S0 是结构体 ID。当已经生成过递归结构的类型时,为了使递归能够终止,我们使用了符号 x 来代替。

而 root 被定义成 S(2, S0, P(S(2, S1, P(S(2, S1, x, x), I(32), I(32))), I(32)) ,在这里 S0 和 S1 是结构体 ID。

3.4、Opaque Struct Equality 不明确类型结构体等价判断

注:LLVM 3.0 的 new type system 已经去掉了这个 opaque struct。

对于不明确类型结构体的等价判断,同样采用符号化的名字,如果不明确类型的原始名字是一样的,那么它们就会有相同的符号名。

%struct.A = type opaque
%struct.A.0 = type opaque
%struct.B = type opaque

%foo = type { %struct.A* }
%bar = type { %struct.A.0* }
%buzz = type { %struct.B* }

这里的例子中,不明确类型的原始名字是 A (%struct.A, %struct.A.0) 和 B (%struct.B)。因此我们把 %struct.A 和 %struct.A.0 视为等价的,而 %struct.B 和 A是不等价的。即使这 3 个结构体都能指向相同的类型或者不同的类型。

参考

TYPE EQUALITY IN LLVM:https://lowlevelbits.org/type-equality-in-llvm/

llvm::LLVMContext:https://llvm.org/doxygen/classllvm_1_1LLVMContext.html

llvm常见问题 (FAQ) llvm常见问题 (FAQ) License 可以修改 LLVM 源代码并重新分发修改后的源代码吗? 可以修改 LLVM 源代码并重新分发基于二进制文件或其它工具,而无需重新分发源代码吗? 源代码 LLVM 是用什么语言编写的? LLVM 源代码的可移植性如何? 使用什么 API,将值存储到 LLVM IR 的 SSA 表示中的一个虚拟寄存器? 源语言 支持哪些源语言? 想编写一个自托管的 LLVM 编译器。应该如何与 LLVM 中端优化器和后端代码生成器交互? 对用于构建编译器的更高级别的源语言结构有什么 阅读详情

相关推荐

西门子PLC实战:基于1200/1500的工业物联网MQTT通讯全解析

本文详细解析了如何在西门子S7-1200/1500系列PLC上实现工业物联网MQTT通讯。通过从硬件准备、网络配置到TIA Portal中MQTT_Client功能块的实战编程,手把手指导工程师打通车间数据桥梁,解决信息孤岛问题,实现PLC数据高效上云或设备间互联。

whisky的博客 1089

llvm::ArrayRef和llvm::StringRef

llvm中的一个bug工作中遇到了一个关于关于llvm::ArrayRef和std::vector的内存bug,这个bug涉及到llvm::ArrayRef的实现以及相关的概念,这里做相关介绍。该bug由[Bash-autocompletion] Add support for static analyzer flags引入,引起的bug见Revert r311552: [Bash-autocomp

电影旅行敲代码 3759

T100服务端接口实战:从注册到日志排查的全链路开发指南

本文提供了一份T100服务端接口开发的全链路实战指南。从程序注册与框架签出开始,详细讲解了接口的数据结构设计、核心业务逻辑编写,到最终的部署上线与调用验证。文章重点分享了如何利用日志文件高效定位和排查接口问题,帮助开发者快速掌握T100接口开发的关键步骤与实用技巧。

weixin_28831399的博客 300

学习 LLVM(8) ArrayRef 类

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33984032的博客 1770

SQL注入绕过information_schema:实战技法与替代路径详解

SQL注入是Web安全领域的核心漏洞类型,其原理是通过构造恶意SQL语句,干扰应用程序与数据库的正常交互逻辑,从而窃取或篡改数据。在渗透测试与CTF竞赛中,绕过常见防护措施是提升技术能力的关键。当标准的信息收集方式受阻时,例如无法访问information_schema数据库,攻击者需要掌握替代的元数据获取方法。这些技术价值在于模拟真实环境中日益严密的防御体系,锻炼在受限条件下的系统性信息收集思维。典型的应用场景包括存在WAF过滤、权限限制或编码干扰的实战环境。本文将围绕SQL注入这一核心,深入探讨如何利用

weixin_33980459的博客 429

FEniCS学习笔记01:centos7下通过conda安装FEniCSx0.7

2023年版本的Fenicsx0.7,官网推荐使用conda命令安装集成好的Python包,因此我们的安装思路是先安装Anaconda,再按照官网步骤安装Fenics。

huahua_xiao的博客 5056

编译器架构的王者LLVM——(10)变量的存储与读取

变量是一款编程语言中的核心,说编译语言是一种符号处理工具,其实是有些道理的。栈式符号表可以方便的记录编译过程中的变量和语法符号,我们上节已经了解了其中的实现方法。那么,还有没有其他的办法能够简单的实现变量的存取呢?

西风世界 6666

CST入门——0. 安装CST Studio Suite 2022

安装CST Studio Suite 2022详细操作步骤

plmxu34的博客 6万+

python llvm_从LLVM-C API自动生成的LLVM的Python绑定

GoalThe main goal of llvmcpy is to provide Python bindings for the LLVM project that are fast and require the lowest possible maintainance effort. To achive this, we use CFFI to parse the (slightly ad...

weixin_39628268的博客 395

蓝牙Mesh安全机制全解析:如何防止你的智能门锁被黑客控制?

本文深入解析蓝牙Mesh安全机制,重点探讨如何保护智能门锁免受黑客攻击。详细介绍了蓝牙Mesh的多层次安全架构,包括密钥管理体系、设备入网流程及实战防护策略,帮助用户构建更安全的智能家居网络。

h3i4j的博客 729

Open3D 计算点云法向量的夹角【2026最新版】

计算法向量的夹角,博客长期更新,本文最新更新时间为:2026年3月7日。

点云侠的博客 3130

LLVM 介绍

https://blog.youkuaiyun.com/weixin_38244174/article/details/82705181 最近开始搞LLVM,下面我将从以下五个方面来介绍LLVM。分别是:(1)LLVM是什么?(2)LLVM的组成部分;(3)LLVM+Clang环境搭建;(4)LLVM的运行过程;(5)LLVM Pass的构建运行过程。 (一)LLVM是什么?? LLVM(low level virtual machine)从本质上来说,是一个开源编译器框架,能够提供程序语...

u010451780的专栏 8051

树莓派 5 AI 套件(Hailo)示例

该用户自定义类作为输入传递给回调函数,该函数在管道输出上运行。它用于主程序与回调函数之间的通信。它对 hailo_rpi_common::app_callback_class 进行了扩展,并可根据应用程序的具体情况定制附加变量和方法。

机器人控制与仿真应用 4115

LLVM学习之基础知识

1

u014738665的博客 4574

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

5个步骤快速上手llvm-tutor:LLVM 21实战教程

想要快速掌握LLVM插件开发吗?llvm-tutor是你的终极入门指南!这个基于LLVM 21的教程项目包含了完整的LLVM Pass示例,专为新手和普通用户设计。通过5个简单步骤,你就能学会如何创建、构建和运行自己的LLVM Pass。 ## 🚀 为什么选择llvm-tutor? llvm-tutor是一个自包含的LLVM Pass参考集合,基于最新的LLVM 21版本构建。它最大的优势是

gitblog_00104的博客 1051

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

LLVM每日谈之二 LLVM IR

作者:snsn1984 在介绍LLVM IR之前,我们需要先了解下LLVM的结构。传统的静态编译器分为三个阶段:前端、优化和后端。 LLVM的三阶段设计是这样的: 这样做的优点是如果需要支持一种新的编程语言,那么我们只需要实现一种新的前端。如果我们需要支持一种新的硬件设备,那我们只需要实现一个新的后端。而优化阶段因为是针对了统一的LLVM IR,所以它是一个通用的阶段,不论是支持

SHINING的博客 2万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

LLVM 3.0类型系统重写

原文地址:http://blog.llvm.org/2011/11/llvm-30-type-system-rewrite.html LLVM 3.0中最为普遍的IR(因此编译器API)改变之一是完全重新实现LLVMIR类型。这个改变拖延了很久(最初的类型系统自LLVM1.0延续),它使得编译器更快,极大地简化VMCore的一个关键子系统,并消除了IR某些通常导致混淆与不方便的设计点。本文解释为

wuhui_gdnt的专栏 2228

LLVM/Clang

LLVM/Clang

feifei 1762

type-challenges 第 19749 题 IsEqual:用类型体操实现类型相等判定

本文聚焦 [type-challenges](https://link.gitcode.com/i/26bca996979a104ff888091ebaaf9385)(Collection of TypeScript type challenges with online judge)中等难度题目 **19749-medium-isequal**,讲解如何实现一个返回布尔类型的类型相等判断工具 `

gitblog_00446的博客 17

深入理解LLVMSwift类型系统:Swift与LLVM类型映射的完整解析

LLVMSwift作为一个强大的Swift语言绑定库,为开发者提供了完整的LLVM C API封装。其中,类型系统是连接Swift高级语言特性和LLVM低级中间表示的关键桥梁。本文将深入探讨LLVMSwift如何实现Swift类型与LLVM类型之间的无缝映射,帮助开发者更好地理解和使用这个强大的编译器框架。 ## 🎯 LLVMSwift类型系统概览 LLVMSwift的类型系统设计精妙,通过

gitblog_00714的博客 324

LLVM指令选择过程理解

指令选择(instruction selection)是将中间语言转换成汇编或机器代码的过程。在LLVM后端中具体表现为模式匹配,目标指令选择阶段会把后端td文件里面的DAG模式和selection DAG的节点相匹配,如果找到一个匹配,则匹配的节点会被有具体机器指令(或者伪指令)的节点代替。1. LLVM IR基本概念LLVM IR是一门中间语言,向上承接C、JAVA等高级语言,向下可以被翻译成...

LF_AI的博客 2373

LLVM 编译器学习笔记之三十八-- STL接口使用

1、set 底层结构是红黑树,有去重特点,参考C++ STL中set是如何判断两个结构体相等的? - 知乎 SetVector 同时维护Vector及Set来保证访问顺序的唯一性,比如对象是一个指针是,如果只是使用SmallPtrSet则因为对象分配的指针地址可能存在差异,导致对象的遍历不唯一。如果用SetVector,则因为插入时会同时往Vector及Set中插入,而Vector是有序的,因此迭代器遍历Vector能保证遍历顺序的一致性,参考LLVM: LLVM: llvm::SmallSet...

~ 飞 扬 的 天 空 ~ 973

LLVM第一弹——介绍及环境搭建(LLVM3.3和LLVM5.0)

安卓开发交流群:651325026,感兴趣的可以加群。 最近开始搞LLVM,下面我将从以下五个方面来介绍LLVM。分别是:(1)LLVM是什么?(2)LLVM的组成部分;(3)LLVM+Clang环境搭建;(4)LLVM的运行过程;(5)LLVMPass的构建运行过程。 (一)LLVM是什么?? LLVM(low level virtual mac...

云瀚的自习室 5333

LLVM - LLVM 别名分析基础设施

LLVM别名分析基础设施提供了确定两个指针是否指向同一内存对象的技术框架。该框架通过AliasAnalysis类提供核心接口,支持多种别名分析算法,包括流敏感和上下文敏感等分类。关键方法包括alias()用于判断指针别名关系(Must/May/No Alias)和getModRefInfo()用于分析指令对内存的读写依赖。系统使用起始地址和大小表示内存对象,确保精确分析。此外还提供doesNotAccessMemory()等便捷方法用于优化分析。该基础设施为LLVM优化器提供强大的内存分析能力,支持各类编译

715

LLVM别名分析框架

介绍 别名分析(又名指针分析,是指针分析的一种)是一类技术,试图确定两个指针是否可以指向内存中的同一对象。别名分析有许多不同的算法和分类方法:流敏感vs.流不敏感、上下文敏感vs.上下文不敏感、字段敏感vs.字段不敏感、基于统一vs.基于子集等。传统上,别名分析用Must、May或No别名response响应查询,表示两个指针总是指向同一个对象,可能指向同一个对象,或者已知永远不会指向同一个对象。 LLVM AliasAnalysis类是LLVM系统中客户使用和别名分析实现的主要接口。这个类是别名分析信息的

m0_46220969的博客 1728

浮点数比较优化:llvm-tutor FindFCmpEq与ConvertFCmpEq全面指南

llvm-tutor是一个包含多个LLVM pass的教学项目,专为学习和教授LLVM优化技术而设计。本文将深入探讨其中两个重要的浮点数比较优化工具——FindFCmpEq和ConvertFCmpEq,帮助开发者理解如何检测和优化浮点数比较操作,提升程序性能和稳定性。 ## 为什么浮点数比较需要特殊处理? 浮点数比较在计算机科学中一直是个棘手的问题。由于浮点数的二进制表示特性,直接使用`==`

gitblog_00075的博客 1026
上一篇: 代码 bug 嗅探器:Sanitizer
下一篇: 代码分析属性图 CPG 介绍
流左沙
博客等级 码龄11年 46粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值