LLVM的调用协议与内存对齐

标签: llvm 协议 内存 | 发表时间:2011-08-17 13:58 | 作者:空明流转 chacoo
出处:http://www.cppblog.com/

在设计一门语言与其他语言交互的API与ABI(Application Binary Interface,二进制接口)时,调用协议和内存对齐是两个无从回避的问题。

本文将讨论如何在LLVM上生成正确的内存对齐和调用协议的代码。

在这里为了方便和标准起见,假定应用LLVM的语言的Extending和Embedding的对象都是C。

调用协议

先来讨论调用协议。调用协议用于保证调用方和被调用方在二进制/汇编一级上是相容的。合适的调用协议可以帮助构造出以下代码:

// Callee Signature of LLVM code
void __cdecl foo( int a, float b, float4 c);

// C caller
typedef void (__cdecl* fn_ptr)(int, float, float4)
fn_ptr p = static_cast<fn_ptr>( get_jit_function("foo") );
p(1, 1.0, vec);

一般来说调用协议包括参数传递和返回值传递和堆栈平衡三个部分。在x86平台上的C/C++编译器中常见的调用协议有cdecl, fastcall和stdcall。具体的协议内容请参见MSDN。

在C++中还有一类特殊的调用协议thiscall,用于调用对象的成员函数。但是这一类调用协议不同的平台,不同的编译器实现皆有不同,既无书面标准,也无事实标准,再加上virtual call等复杂的情况存在,并不适合用于做跨语言的调用。

对于x64平台而言,在windows下和linux下分别有两种调用协议。

先来看x86。由于x86在cdecl和fastcall上是有着跨平台的标准的,因此LLVM对它的支持是比较完整的。程序只要在创建Function的时候指定Call Convention即可。

但是对于x64,LLVM的支持便不是那么完善。以windows为例,windows的x64调用协议要求以rcx,rdx,r8,r9寄存器传递前四个不大于64bit的参数,其余参数放在栈上。如果参数大于64bit,则要求传递它的指针。浮点使用xmm0-3来传递。但是对于LLVM而言,一旦参数大于64bit,它便会将整个对象而不是指针压到栈上传递。因此在遇到x64时,需要小心处理API部分的调用协议。

在这里,我们需要将所有超过64bit的结构体处理成指针(或者拷贝后处理成指针)传递。

同时,LLVM提供了readonly和byval两个参数属性(Attribute)来确保参数的值语义。前者意味着传入的指针所指向的值是不被修改的,(类似于T const*),而后者会对传入的指针做一份内存拷贝,确保写值不被传递出函数(类似于值拷贝)。这样,LLVM生成的函数便可以MSVC生成的x64代码正确调用了。

内存对齐

与移动平台的体系结构相比,x86对内存对齐的条件算是相当宽松的了。大部分的指令对内存对齐基本上是没有特殊要求的。只有一些SIMD的指令会对内存对齐有所限定,例如movaps。

为了方便后端生成SIMD代码,LLVM提供了vector类型,例如vector<float, 1>。在代码生成的时候,vector会编译成最有可能的SIMD类型。因此在x86平台上,vector<float, 1-4>都被处理成类似于__m128的类型,更长的vector则被拆分成多个__m128类型。

这实际上意味着,所有的vector都应该遵循16Bytes对齐的原则。

考虑到我们的需求,类似于struct{ float[3]; }这样的结构,如果能表示为vector<float, 3>显然适合一些数学运算,例如shuffle,逐元素的add,sub,mul,同时LLVM指令的选择也更加灵活。但是显然,这个结构体有两个条件是不满足的:16字节对齐和16字节的大小(movups和movaps都是一次取16字节)。这会造成边界下读写的内存越界。因此非常可惜,这些数据必须表示为struct{ float ,float, float }。在读取的时候,也会生成正确的指令:movss。

那么,对于一般的非对齐的vec4应用vector<float,4>行不行呢?

答案是,很困难。对于LLVM而言,他们在设计的时候就没有过多的考虑vector在非对齐时候的应用。尽管load和store都能够指定alignment以生成非对齐的内存操作(例如movups)并且确实会起效,但是由于代码优化、临时存取等特性的存在,导致一些非load和store的内存操作仍然是要求对齐的(例如生成了addaps xmm, [addr])。此时仍然有可能为非对齐的数据生成了内存对齐的指令。

因此综合权衡,SASL在API界面上使用了struct{float x,y,z,w;} 这样的ABI来表示数据,在代码生成时,会首先将struct的数据转换成vector,然后再执行其它的操作,兼顾ABI与SIMD;同时对于Intrinsic,由于并不暴露给Host,所以它们仍然尽可能使用Vector,便于LLVM进行优化。



空明流转 2011-08-17 13:58 发表评论

相关 [llvm 协议 内存] 推荐:

LLVM的调用协议与内存对齐

- chacoo - C++博客-首页原创精华区
在设计一门语言与其他语言交互的API与ABI(Application Binary Interface,二进制接口)时,调用协议和内存对齐是两个无从回避的问题. 本文将讨论如何在LLVM上生成正确的内存对齐和调用协议的代码. 在这里为了方便和标准起见,假定应用LLVM的语言的Extending和Embedding的对象都是C.

RMS谈GCC、LLVM和Copyleft

- - Solidot
ESR(Eric S.Raymond)在GCC邮件列表上发贴预言,LLVM/Clang编译器将在3到5年内威胁到GCC的统治地位,认为GCC编译器的反插件政策正成为一大障碍. 虽然Clang尚未达到GCC的成熟度,但在某些方面它拥有比GCC更出色的特性,例如错误信息. ESR建议GCC应允许非自由的插件.

LLVM的独特设计和实现

- - ITeye资讯频道
过去五年,LLVM从一个学术研究项目进化成C、C++和Objective C编译器的通用后端. 成功的关键是性能和适应能力,两者都得益于LLVM独特的设计和实现. LLVM项目主要作者Chris Lattner在Dr.Dobb's上刊文讲述了LLVM的设计. Clang编译器相比GCC编译器具有不少优势,因为 LLVM提供了某些独一无二的能力.

GCC、LLVM-GCC、DragonEgg和Clang编译性能对比

- allengaller - Solidot
Phoronix利用Phoronix Test Suite测试了GCC 4.2.1、4.3.0、4.4.0、4.5.0、GCC 4.6.0 2010-10-30开发预览版,以及LLVM-GCC 2.8、LLVM DragonEgg 2.8和Clang 2.8编译器的编译性能. 测试结果显示,与新贵LLVM-GCC和Clang相比,有二十多年历史的GCC编译器确实比较慢 虽然LLVM和Clang的新版本提供了完整的C++支持,并且能编译Linux kernel,但它们目前还达不到取代成熟GCC的程度,Clang或DragonEgg还无法完成某些常见任务的编译,性能也未能如意.

編譯器技術的虛擬化發展!?淺談LLVM

- ĐƋɳƞѵїχ - iOS Bible
撇開特殊的情況不說,一般而言從程式碼的撰寫到應用程式的完成,最後總不免地需要進行一道程式碼『編譯』的動作. 而這項編譯的工作,其實是透過『編譯器』的演算處理來完成. 其編譯的主要目的,是要把本來撰寫者原本較容易閱讀的程式語法轉換成硬體機械可以直接執行的指令. 以C語言為例子來說,編譯的動作其實指的就是從C語言轉換至組合語言的這個過程.

内存事务如何选择加锁协议,乐观好还是悲观好?

- yuanxinz - Changming的blog
最近我用 Multiverse STM做了一点东西. 在开始动手之前,我专门测了multiverse的性能,发现它可以达到每秒2000多万次事务(每个CPU),于是我觉得它所带来的overhead应该是极小的,于是就放心大胆的用了. 可是后来用profile工具看,我的程序始终是在openForRead这样的函数上占据了绝大部分时间.

memcached协议

- - 开源软件 - ITeye博客
旧版: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt. 新版: https://github.com/memcached/memcached/blob/master/doc/protocol.txt.

https协议

- - 互联网 - ITeye博客
SSL 协议的握手过程   .       为了便于更好的认识和理解 SSL 协议,这里着重介绍 SSL 协议的握手协议. SSL 协议既用到了公钥加密技术(非对称加密)又用到了对称加密技术,SSL对传输内容的加密是采用的对称加密,然后对对称加密的密钥使用公钥进行非对称加密. 这样做的好处是,对称加密技术比公钥加密技术的速度快,可用来加密较大的传输内容,公钥加密技术相对较慢,提供了更好的身份认证技术,可用来加密对称加密过程使用的密钥.

http2协议

- - 企业架构 - ITeye博客
http2协议的草案已经出来了,阅读了一下网上的中文版,http2尽可能的兼容http1.1. 改进了http1.1协议的不足. http1.0和http1.1的缺点:. 1.http1.0只允许在一个连接上建立当前未完成的请求. 2.http1.1管道只部分处理了请求并发和包头堵塞问题,客户端多建立TCP连接,减少延迟.

PPP协议

- - CSDN博客推荐文章
PPP(Point-to-Point Protocol点到点协议)是为在同等单元之间传输数据包这样的简单链路设计的链路层协议. 这种链路提供全双工操作,并按照顺序传递数据包.   PPP是目前使用最广泛的数据链路层协议,不管是低速的拨号猫连接还是高速的光纤链路,都适用PPP协议. 因特网用户通常都要连接到某个ISP 才能接入到因特网.