1. LLVM在x86和x64下都和Microsft C++ ABI的吻合程度不够。目前已知在以下情况下会出错:
  • 参数为结构体的
  • 返回值为结构体
以下情况我没有完整测试过:
  • 返回值为单个浮点
  • 返回值为向量(_m128 / <4 x float>)
  • 参数为向量(_m128 / <4xfloat>)
所以建议大家统一将是结构体的返回值和参数以引用/指针的形式传递。
对于大小为4个或者8个字节的结构体如果希望按值传递,那么需要在LLVM函数的签名上使用i32/i64作为参数类型,并使用bit cast在函数体内强制转换成结构体。

2. LLVM提供了很多的Intrinsics,例如SSE指令集。它在Module上提供了一个getOrCreateTargetIntrinsic,但实际上这个函数是坑爹的。有两个方法可以正确的创建并获取指令集:
  • 使用Module::getOrInsertFunction( intrinsic_name, intrinsic_function_type )。它会自动识别intrinsic的名称并创建function或者是intrinsic。指令需要使用全名。例如 llvm.x86.sse.sqrt.ps.
  • 或者使用Ilvm::Intrinsic::getDeclaration( id ) 来创建。这个id可以在intrinsics.gen中找到。
因为LLVM生成的Intrinsic是全平台的,所以可以在x86上指定ARM汇编的生成,反之亦然。

3. 默认情况下,LLVM的JIT是不会启用InliningPass的,Optimization Level指定为Aggressive也不会。这意味着inlinehint和alwaysinline都是失效的。如果需要inlining得自己修改JIT的源代码。

4. UndefValue是个好东西。这个常量可以使生成的汇编少一条初始化指令。比方说用0初始化,可能对应的汇编就是 xor reg, reg。如果用了Undef,那这条指令就没了。

5.
TypeBuilder很好用,只是不能生成struct等复杂的类型。不过你可以对它做一些修改以让它支持struct和vector。这个时候Boost.MPL就能派上用场了。不过要当心MPL带来漫长的编译时间。
posted @ 2011-11-23 18:28 空明流转 阅读(612) 评论(0) 编辑

在设计一门语言与其他语言交互的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进行优化。

 

Direct3D 10+ Like 软件渲染器SALVIA项目URL:http://code.google.com/p/softart

posted @ 2011-08-18 08:51 空明流转 阅读(988) 评论(0) 编辑

Autodesk Maya组现希望招聘一名图形方面的开发人员以及一名UI开发人员。具体要求如下:

 

Rendering

 

  • 有计算机及相关专业本科或以上学历

 

  • 会一点C++,有良好的代码风格
  • 会一点DX或者OGL
  • 懂一点实时渲染的理论
  • 英语的听说读写都需要一些
  • 喜爱图形学
  • 有团队合作精神,能面对挑战

 

  • 最好离线渲染的理论也能懂一点

 

Qt

 

  • 有计算机相关专业本科或以上学历
  • 会一点C++
  • 会使用Qt进行图形界面开发并有相关工作经验
  • 英语的听说读写都需要一些
  • 有团队合作精神,工作细致认真
  • 最好能懂一些图形学

 

工作地点:上海市浦东新区浦电路399

 

如果有有意者,请将简历投送至  ye.wu@autodesk.com

posted @ 2010-07-30 11:01 空明流转 阅读(104) 评论(0) 编辑
边定制边学习一点CSS……很郁闷啊。。。第一次给博客定制界面,估计要丑很长很长的一段时间。。。呵呵。
posted @ 2006-11-29 22:51 空明流转 阅读(66) 评论(2) 编辑