LLVM学习笔记③

调用约定:
LLVM functions, calls 和 invokes 都可以为调用指定一个可选的调用约定。每一对caller/callee(调用者/被调用者)的调用约定必须匹配,不然这个程序的行为是未定义的。
LLVM支持以下调用约定,以后可能会添加更多:
“ccc” - The C calling convention
这个调用约定(如果没有指定其他调用约定,则默认为此调用约定)与目标C的调用约定相匹配。这种调用约定支持可变长参数函数调用,并允许在函数声明的原型和实现的声明中存在一些不匹配(与普通C语言一样)。
“fastcc” - The fast calling convention
这个调用约定试图使调用尽可能的快速(如,通过寄存器传递数据)。这个调用约定允许目标使用任何它想要并且能够使用的技巧来为其产生快速的代码,而不要求符合外部指定的ABI (Application Binary Interface). 尾部调用只能在 tailcc、 GHC 或者 HiPE 约定被使用的时候优化。这种调用约定不支持变长参数切要求所有被调用者的原型与函数定义的原型相匹配。
”coldcc” - The cold calling convention
这种调用约定试图在调用不经常执行的假设下,使调用者中的代码尽可能高效。因此,这些调用通常会保留所有寄存器,这样调用就不会破坏调用方的任何活动范围。这种调用约定不支持可变参数,并要求所有调用的原型与函数定义的原型完全匹配。此外,内联器不考虑这样的函数调用。
“cc 10” - GHC convention
这个调用约定已经被格拉斯哥Haskell编译器(GHC)专门实现了。它传递寄存器中的所有信息,通过禁用calllee save寄存器来达到这个目的。这种调用约定不应被轻易使用,而应仅用于特定的情况,例如实现函数式编程语言时经常使用的寄存器固定性能技术的替代方法。目前只有X86支持这个约定,它有以下限制:
①在X86-32下只支持长度大于4bit的类型的形参,不支持浮点型;
②在X86-64下只支持长度大于10bit的类型形参且只支持 (6 floating point parameters)
这个调用约定支持尾部调用优化,但要求调用方和被调用方都在使用它。
”cc 11” - The HiPE calling convention
这个调用约定是专门为高性能Erlang (HiPE)编译器而实现的,高性能Erlang (HiPE)编译器是爱立信开源Erlang/OTP系统的本机代码编译器。它比普通的C调用约定使用更多的寄存器来传递参数,并且没有定义被调用者保存的寄存器。调用约定正确地支持尾部调用优化,但要求调用方和被调用方都使用它。它使用一种寄存器固定机制,类似于GHC的约定,用于将频繁访问的运行时组件固定到特定的硬件寄存器。目前只有X86支持此约定(32位和64位)。
“webkit_jscc” - WebKit’s JavaScript calling convention
这个调用约定已经在WebKit FTL JIT中实现。它在堆栈上从右到左传递参数(就像cdecl做的那样),并在平台的习惯返回寄存器中返回一个值。
“anyregcc” - Dynamic calling convention for code patching
这是一种特殊的约定,它支持用任意代码序列来代替调用站点。这种约定强制调用参数进入寄存器,但允许动态分配它们。目前只能在调用llvm.experimental.patchpoint时使用,因为只有这个内部变量记录了它的参数在边表中的位置。参见LLVM中的堆栈映射和补丁点。
“preserve_mostcc” - The PreserveMost calling convention
这种调用约定试图使调用方中的代码尽可能不受干扰。这个约定在参数和返回值如何传递方面的行为与C调用约定相同,但它使用了一组不同的调用者/被调用者保存的寄存器。这减轻了调用方在调用前后保存和恢复大量寄存器集的负担。如果参数是在被调用方保存的寄存器中传递的,那么它们将在整个调用过程中由被调用方保存。这不适用于调用保存的寄存器中返回的值。
在X86-64上,被调用者保留所有通用寄存器,R11除外。
R11可以用作scratch寄存器。浮点寄存器(XMMs/YMMs)不需要保存,需要由调用者保存。
这种约定背后的思想是支持对具有热路径和冷路径的运行时函数的调用。热门路径通常是一小段不使用很多寄存器的代码。冷路径可能需要调用另一个函数,因此只需要保存调用方保存的寄存器,调用方还没有保存这些寄存器。在调用者/被调用者保存的寄存器方面,preservmost调用约定与非预约调用约定非常相似,但它们用于不同类型的函数调用。Coldcc用于很少执行的函数调用,而preserve_mostcc函数调用则是在热路径上执行的,肯定执行了很多次。此外,preserve_mostcc不会阻止内联程序内联函数调用。
这个调用约定将会被ObjectiveC运行时的未来版本所使用,因此目前仍然被认为是实验性的。尽管这个约定是为了优化对objective - c运行时的某些运行时调用而创建的,但它并不局限于这个运行时,将来也可能被其他运行时使用。目前的实现只支持X86-64,但其目的是在未来支持更多的体系结构。
“preserve_allcc” - The PreserveAll calling convention
这个调用约定试图使调用方中的代码比preservmost调用约定的侵入性更小。这个调用约定在参数和返回值如何传递方面的行为与C调用约定相同,但它使用了一组不同的调用者/被调用者保存的寄存器。这样就消除了在调用方中调用前后保存和恢复大量寄存器集的负担。如果参数是在被调用方保存的寄存器中传递的,那么它们将在整个调用过程中由被调用方保存。
这不适用于调用保存的寄存器中返回的值。
这种约定背后的思想是支持对运行时函数的调用,而不需要调用任何其他函数。这个调用约定,就像preservmost调用约定一样,将被ObjectiveC运行时的未来版本所使用,现在应该被认为是实验性的。
“cxx_fast_tlscc” - The CXX_FAST_TLS calling convention for access functions
Clang生成一个访问函数来访问c++风格的TLS。访问函数通常具有入口块,出口块和第一次运行的初始化块。入口和出口块可以访问一些TLS IR变量,每次访问将降低到特定于平台的序列。
这个调用约定的目的是通过保留尽可能多的寄存器(由入口和出口块组成的快速路径上保留的所有寄存器)来最小化调用方的开销。
这个调用约定在参数和返回值如何传递方面的行为与C调用约定相同,但它使用了一组不同的调用者/被调用者保存的寄存器。
由于每个平台都有自己的降低序列,因此有自己的保留寄存器集,因此我们不能使用现有的preservmost。
在X86-64上,被调用者保留所有通用寄存器,RDI和RAX除外。
“tailcc” - Tail callable calling convention
这个调用约定确保在尾部位置的调用将始终是优化过的尾部调用。这个调用约定等价于fastcc,除了一个额外的保证,即只要可能就会产生tail调用。只有当使用fastcc、GHC或HiPE约定时,尾部调用才能被优化。这种调用约定不支持可变参数,并要求所有调用的原型与函数定义的原型完全匹配。
“swiftcc” - This calling convention is used for Swift language.
“swifttailcc”
这个调用约定在大多数方面与swiftcc类似,但被调用方也会弹出堆栈的参数区域,这样就可以像在tailcc中那样进行强制尾部调用。
“cfguard_checkcc” - Windows Control Flow Guard (Check mechanism)
此调用约定用于Control Flow Guard检查函数,可以在间接调用之前插入对该函数的调用,以检查调用目标是否是有效的函数地址。check函数没有返回值,但如果地址不是有效目标,它将触发一个os级错误。由检查函数保存的寄存器集和包含目标地址的寄存器都是特定于体系结构的。
“cc ” - Numbered convention
任何调用约定都可以由数字指定,允许使用特定于目标的调用约定。目标特定的调用约定从64开始。
可见性风格
所有全局变量和函数都有一种可见性类型:
“default” - Default style
在使用ELF对象文件格式的目标上,默认可见性意味着声明对其他模块是可见的,而在共享库中,意味着声明的实体可能被覆盖。在Darwin上,默认可见性意味着该声明对其他模块是可见的。默认可见性对应于语言中连接类型中的“外部链接”。
“hidden” - Hidden style
一个对象的具有hidden可见性的两个对象声明,在一个share object中,则他们指向同一个对象。通常,hidden的可见性表示该符号不会被放入动态符号表中,因此其他模块(可执行或共享库)不能直接引用它。
“protected” - Protected style
在ELF上,受保护的可见性指示符号将被放置在动态符号表中,但是定义模块中的引用将绑定到本地符号。也就是说,该符号不能被另一个模块覆盖。
带internal或private链接的符号必须具有default可见性。
DLL Storage Classes
DLL(Dynamic Link Library)动态链接库
每个全局变量和函数都可以有一个DLL storage class“
dllimport
" dllimport "使编译器通过一个全局指针引用一个函数或变量,该指针是由导出符号的DLL建立的。在Microsoft Windows目标上,指针名由__imp_和函数名或变量名组合而成(比如__imp_函数名/变量名)。
dllexport
" dllexport "使编译器提供一个指向DLL中的指针的全局指针,这样它就可以通过dllimport属性被引用。在Microsoft Windows目标上,指针名由__imp_和函数名或变量名组合而成。由于这个存储类的存在是为了定义dll接口,编译器、汇编器和连接器知道它是外部引用的,因此必须避免删除该符号。

你可能感兴趣的:(LLVM,llvm)