【译】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 中端优化器和后端代码生成器交互? 对用于构建编译器的更高级别的源语言结构有什么 阅读详情

相关推荐

Cadence学习之路(六)元器件封装绘制

使用Allegro PCB Designer绘制元器件封装

qq_46757048的博客 1万+

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

SPSS&Mplus—共同方法偏差(Common Method Bias)控制与检验

共同方法偏差(Common Method Bias)的控制方法:程序控制(Procedural)和统计控制(Statistical)。

m0_54401011的博客 1万+

学习 LLVM(8) ArrayRef 类

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

weixin_33984032的博客 1770

ESP32-CAM图像糊成马赛克?OV2640参数调优保姆级教程(亮度/对比度/饱和度)

本文提供了一份详细的ESP32-CAM图像调优教程,针对OV2640摄像头常见的图像模糊问题,从分辨率设置、亮度/对比度/饱和度调整到高级寄存器配置,帮助用户优化网页显示画质。通过代码示例和实用技巧,解决马赛克、色彩暗淡等问题,提升摄像头性能。

weixin_30410119的博客 594

BSL编程语言 —— 系统化程序设计

BSL编程语言简介 文章目录0. 选择编程语言1. 写简单的表达式(Expression) 0. 选择编程语言 如果你第一次使用DrRacket,打开后需要选择Beginning Student Language (BSL) Language -> Choose Language (或直接按Ctrl + L),看到如下洁面后选择Beginning Student,点击OK 定义区【De...

CSDoneRight的博客 6450

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

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

西风世界 6666

虚拟化 之三 详解 ARM 平台 jailhouse 的移植适配、构建及使用(飞腾派)

本文将探索学习通过在飞腾官方提供的 Buildroot 构建系统移植适配 openEuler 内核以及 jailhouse 源码 Package 进而直接构建一个适用于飞腾派开发板的带有 jailhouse 的 openEuler 发行版。

技术干货 1004

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

TC397 EB MCAL开发从0开始系列 之 [13.1] Dma配置实战 -实现M2M数据传输(读存储写存储)

TC3XX TC397 MCAL DMA 从0搭建测试用例-实现M2M 存储到存储数据传输

yy九歌的博客 1913

解锁Vscode:C/C++环境配置超详细指南

本文详细介绍如何在 VSCode 中配置 C/C++ 开发环境,帮助开发者快速搭建高效的编码调试环境。内容涵盖前期准备、软件下载安装、环境变量配置、工作区文件设置及测试运行全流程:首先需准备 VSCode 编辑器、MinGW-w64 编译器及 C/C++ 插件;接着完成工具下载与安装,配置系统环境变量以确保编译器可全局调用;然后通过创建.vscode工作区文件夹,配置核心文件,实现代码智能提示与调试功能;最后通过编写 Hello World 示例验证环境可用性,并附上常见问题排查方案。

邓邓子的博客 3047

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

【VS Code插件开发】通用功能(二)

本篇主要从command(命令)、menus(菜单)、快捷键三个方面讲解,都归于通用功能。

编码世界 1万+

LLVM学习之基础知识

1

u014738665的博客 4574

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

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

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

LLVM每日谈之二 LLVM IR

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

SHINING的博客 2万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

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、付费专栏及课程。

余额充值