- 凹语言(Go实现, 面向WASM设计): https://github.com/wa-lang/wa
- WaBook(Go语言实现的MD电子书构建工具): https://github.com/wa-lang/wabook
附录
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编码后再用转义字符就可以表示了。