附录

LLVM 参考

标识符

LLIR中命名标识符有两种:全局标识符、局部标识符。命名的标识符命名规则是:[@%][a-zA-Z$._][a-zA-Z$._0-9]*,以@开头的是全局标识符,以%开头的是局部标识符。

全局标识符

全局标识符主要用于表示全局的变量或常量,还有全局函数的命名。下面是全局标识符的一些例子:

@count = global i32 21
@hello = internal constant [6 x i8] c"hello\00"

declare i32 @puts(i8* nocapture) nounwind

define void @main() {
	ret void
}

其中@count是一个全局的变量,类型是i32,初始值为21;@hello是一个内部的字符串常量;@puts是外部导入的函数名;@main是新定义的函数名。

因为$.都是合法的标识符名称,可以通过$.来为全局的标识符构造不同层级的名字空间。比如下面的例子:

@main.count = global i32 21
@std.math.pi = global double 3.14

@main.count.$type = ....

其中@main.count可以表示为main包下面的count全局变量,而@std.math.pi可以表示为std.math包下面的pi变量。最后的@main.count.$type带有$字符,可以用户表示和count全局变量相关的其它信息。

关于全局变量的其它细节可以暂时忽略,在后文会详细解释。

局部标识符

除了全局标识符之外还有局部标识符。局部标识符主要用于表示函数的参数和函数内局部变量(局部变量可以对应未寄存器)。下面是局部标识符的一些例子:

define i32 @main(i32 %argc, i8** %argv) nounwind {
	ret i32 0
}

其中%argc%argv都是以%开头表示局部变量,对应@main函数的两个输出参数。

也可以通过$.来为布局的标识符构造不同层级的名字空间。比如下面的例子:

define i32 @main() {
	%str.addr = getelementptr [6 x i8],[6 x i8]* @"hello\0a", i64 0, i64 0
	ret i32 0
}

%str.addr可以用于表示字符串中的地址属性。

未命名标识符

除了命名的标识符之外,还有未命名的标识符,它们以无符号的数字表示:

@0 = global i32 100
@1 = global i32 200
@2 = global i32 300

define void @main() {
	%1 = load i32, i32* @0
	ret void
}

其中@0/@1/@2分别是三个未命名的全局标识符,对应三个i32类型的全局变量。而@main函数内部的%1表示一个局部的未命名标识符,可以理解为一个寄存器。

在LLVM8中,全局的未命名标识符好像必须从@0开始编号,局部的未命名标识符好像必须从%1开始编号,具体原因还有待分析。

特殊字符

在标识符还可以通过转义字符的方式表示任意的特殊字符:

@"hello\0a" = internal constant [6 x i8] c"hello\00"

如果标识符有转义字符存在则必须用双引号包围起来,比如@"hello\0a"。转义字符串的格式为\xx,其中xx是十六进制表达的ASCII值,比如\0a表示换行符\n。几乎可以使用任意的Unicode字符来命名标识符,只要用UTF8编码后再用转义字符就可以表示了。

© 2021-2022 | 柴树杉 保留所有权利