LLVM IR 类型系统

LLVM类型系统是中间表示的最重要的特征之一。类型化使得可以直接对中间表示执行许多优化,而不必在转换之前在侧面进行额外的分析。强大的类型系统使读取生成的代码变得更加容易,并且支持新的分析和转换,这些分析和转换在普通的三个地址代码表示上不可行。

void类型

概述:void类型不代表任何值并且没有大小。

Syntax: void

函数类型

概述
函数类型可以被认为是函数签名。它由一个返回类型和一个形式参数类型列表组成。函数类型的返回类型是void类型或第一类类型
标签元数据类型除外。

Syntax:<returntype> (<parameter list>)

…其中<parameter list>是逗号分隔的类型说明符列表。可选地,参数列表可以包括类型…,该类型指示该函数采用可变数目的参数。变量参数函数可以通过处理内部函数的变量参数来访问它们的参数。<returntype>是除标签元数据之外的任何类型。

例如:

实例 说明
i32 (i32) 函数接受i32,返回一个i32
float (i16, i32 *) * 指针,以接受一个函数i16和一个i32指针来返回float。
i32 (i8*, …) 可变参数函数,它有至少一个指针到i8(在C中是char),它返回一个整数。LLVM中这是printf的签名。
{i32, i32} (i32) 一个函数i32,返回一个包含两个i32值的结构

第一类类型(First Class Types)

第一类的类型可能是最重要的。这些类型的值是唯一可以通过指令产生的类型。

单值类型

这些是从CodeGen的角度来看在寄存器中有效的类型。

整数类型

概述:
整数类型是一个非常简单的类型,它简单地为所需的整数类型指定一个任意的位宽。可以指定从1位到223-1(约8百万)的任何位宽。

Syntax:

iN

整数将占据的位数由该N值指定。

例子:
i1 一个单位整数。
i32 一个32位整数。
i1942652 一个超过100万位的大整数。

浮点类型

类型 描述
half 16位浮点值
float 32位浮点值
double 64位浮点值
fp128 128位浮点值(112位尾数)
x86_fp80 80位浮点值(X87)
ppc_fp128 128位浮点值(两个64位)

half,float,double和fp128的二进制格式分别对应于binary16,binary32,binary64和binary128的IEEE-754-2008规范。

X86_mmx类型

概述:
x86_mmx类型表示在x86机器上的MMX寄存器中保存的值。允许的操作相当有限:参数和返回值,load和store以及bitcast。用户指定的MMX指令表示为具有参数and/or此类型结果的内部调用或asm调用。没有这种类型的数组、向量或常量。

Syntax:

x86_mmx

指针类型

概述: 指针类型用于指定内存位置。指针通常用于引用内存中的对象。

指针类型可能有一个可选的地址空间属性,用于定义指向对象所在的编号地址空间。默认地址空间是数字零。非零地址空间的语义是特定于目标的。

请注意,LLVM不允许指向void(void*)的指针,也不允许指向标签(label*)的指针。改为使用i8*

Syntax:

<type> *

例子:

[4 x i32]* 4个的i32值数组的指针。
i32 (i32*) * 函数指针,它接受一个i32*,并返回i32。
i32 addrspace(5)* i32值的指针,驻留在地址空间#5中的值。

矢量类型

概述:
矢量类型是表示元素矢量的简单派生类型。当使用单个指令(SIMD)并行操作多个原始数据时,使用矢量类型。矢量类型需要大小(元素数量)和基础原始数据类型。矢量类型被认为是第一类

Syntax:

< <# elements> x <elementtype> >

元素的数量是一个大于0的常数整数值; elementtype可以是任何整数、浮点或指针类型。大小为零的矢量是不允许的。

例子:

<4 x i32> 4个32位整数值的向量。
<8 x float> 8个32位浮点值的向量。
<2 x i64> 2个64位整数值的向量。
<4 x i64*> 4个64位整数值指针的向量。

标签类型

概述: 标签类型代表代码标签。

Syntax:

label

令牌(token)类型

概述:
当值与指令相关联时使用token类型,但该值的所有用法不得试图反思或模糊它。因此,具有phiselect类型令牌是不合适的。

Syntax:

token

元数据类型

概述:
元数据类型表示嵌入的元数据。除函数参数外,不得从元数据创建派生类型。

句法:

metadata

聚合类型

聚合类型是派生类型的一个子集,可以包含多个成员类型。数组结构是聚合类型。向量不被视为聚合类型。

数组类型

概述:
数组类型是一种非常简单的派生类型,它将元素按顺序排列在内存中。数组类型需要大小(元素数量)和基础数据类型。

Syntax: [<# elements> x
<elementtype>]

元素的数量是一个常数整数值; elementtype可以是任何尺寸的类型。

例子:
[40 x i32] 包含40个32位整数值的数组。
[41 x i32] 41个32位整数值的数组。
[4 x i8] 包含4个8位整数值的数组。

以下是多维数组的一些示例:
[3 x [4 x i32]] 3×4 32位整数值数组。
[12 x [10 x float]] 单精度浮点值的12×10数组。
[2 x [3 x [4 x i16]]] 2x3x4的16位整数值数组。

除了静态类型隐含的数组末尾之外,没有对索引的限制(尽管在某些情况下索引超出了分配对象的范围)。这意味着可以在零长度数组类型的LLVM中实现单维“可变大小数组”。例如,在LLVM中实现“pascal样式数组”可以使用类型“{ i32, [0 x float]}”。

结构类型

概述:
结构类型用于在内存中一起表示数据成员的集合。结构的元素可以是任何具有大小的类型。

使用’load‘和’store‘通过使用’getelementptr‘指令获取指向字段的指针来访问内存中的结构。使用’extractvalue‘和’insertvalue‘指令访问寄存器中的结构。

结构可以选择是“压缩”结构,其指示结构的对齐是一个字节,并且元素之间没有填充。在非压缩结构中,字段类型之间的填充按照DataLayout字符串在模块中定义的方式插入,该模块需要与基础代码生成器的预期匹配。

结构可以是“文字”或“识别符”。字面结构与其他类型(例如{i32, i32}*)内联定义,而标识类型始终在顶层使用名称定义。文字类型被其内容所独占,因为没有办法编写它们,所以永远不会递归或不透明。识别符的类型可以是递归的,可以是不透明的,并且永远不会被分离。

Syntax:

%T1 = type { <type list> }     ; Identified normal struct type
%T2 = type <{ <type list> }>   ; Identified packed struct type

例子:
{ i32, i32, i32 } 三个i32值的结构
{ float, i32 (i32) * } 一对,其中第一个元素是a
float,第二个元素是一个指向函数的指针,该函数接受一个i32返回值i32。
<{ i8, i32 }> 一个已知为5字节大小的打包结构。

不透明结构类型

概述:
不透明结构类型用于表示没有指定主体的命名结构类型。这符合(例如)正向声明结构的C概念。

Syntax:

%X = type opaque
%52 = type opaque

例子:
opaque 一种不透明的类型。