2012-11-12 44 views
0

我正在研究一个需要与QNX-Momentics(基于eclipse,g ++ 4.6.1工具链)和Visual Studio 2010编译的项目。对于一些例程,我决定去实现手动装配,因为即使内在函数也没有很好的优化。第一个编译器具有ATt & T语法,可以使用-masm = intel标志进行“智能化”,第二种是intel方言。不是很好,但工作 - -双重asm方言项目的问题intel/AT&T

使用Intel标志,我可以用超越记法方面定义绝招:现在

#ifdef _WIN32 
    #define _cmd(...) __VA_ARGS__ 
    __asm { 
#else 
    #define _cmd(...) #__VA_ARGS__ 
    asm volatile (
#endif 
    // constants 
    // set loop counter 
    _cmd(xor  eax, eax;) 
     : 
     : 
#ifdef _WIN32 
    } 
#else 
    ); 
#endif 

,一个问题是,我不能访问本地变量或参数按名称功能使用内联AT & T.一个提示我在另一个线程获得,使用像

register __m128i x asm("xmm6"); 

东西没有工作,工作的局部变量,它被分配到XMM0。没有内部函数定义的局部变量或参数,导致AT &牛逼未定义的引用,所以我决定用裸栈处理,如

_cmd(movupd xmmword ptr [eax], xmm3;) 

,并遇到了新的问题:

两个函数的参数和局部两种方言的变量处理完全不同。请看下面的例子:

template<typename T> 
void linearRegression2DAsm(unsigned int p_oNumPoints, T *p_pXcoords, T *p_pYcoords, 
double *oX, double *oY, double *oXY, 
double p_oAvgX, double p_oAvgY) 
{ 
unsigned int p_rLoopsize = p_oNumPoints - (p_oNumPoints % 2); 
double oAvgX[2]; 

,这简单的计算上面的定义块发出后:

_cmd(xor  eax, eax;) 
// p_pXccoords 
_cmd(mov  ecx, dword ptr [ebp+12];) 
// p_pYcoords 
_cmd(mov  edx, dword ptr [ebp+16];) 
// p_oAvgX 
_cmd(movhpd xmm6, qword ptr [ebp+20];) 
// p_oAvgY 
_cmd(movhpd xmm7, qword ptr [ebp+28];) 
_cmd(movlpd xmm6, qword ptr [ebp+20];) 
_cmd(movlpd xmm7, qword ptr [ebp+20];) 
_cmd(addpd  xmm7, xmm6;) 
// result into oAvgX 
_cmd(mov eax, [ebp-32];) 
_cmd(movupd xmmword ptr [ebp-32], xmm7;) 

结果应该是在oAvgX,其正常工作与英特尔,但使用不会导致成功intel标记AT & T asm编译器。其次,我担心额外的O2-Flag可能会优化其他变量,因此不能保证在不同的编译中构建相同的堆栈。

我需要内联,但是看不到解决双重方言问题的任何方法。

回答

0

您可以在GCC的内联程序集中通过名称访问本地变量,这只是您必须以不同于在VS2010中执行此操作的方式来执行此操作。在内联程序集的最后,您应该/必须提供一个输入列表,一个输出列表和一个“clobbered”列表;输入列表和输出列表都可以包含局部变量。还要注意,“clobbered”列表非常重要(编译器假定没有列出为破坏或列为输出的内容不会改变,包括所有内存内容等)。不管你使用的是哪个编译器,不管你使用的是哪种编译器,依靠偶然的事情(比如发生在哪个寄存器中,或者发生在哪个内存位置或堆栈位置)是一个严重的错误。在一些有限的测试案例中工作。唯一正确的方法是依赖为此提供的设施(例如,GCC内联汇编中的输入/输出列表)。

对于任何非平凡的内联程序集;因为内联汇编不是任何(C/C++)标准的一部分;我假设让它在多个编译器中可靠工作的唯一方法是复制内联程序集。

还要注意不同的操作系统有不同的约定(例如不同的ABIs,不同的内核系统调用等)。从本质上讲,(作为最坏的情况下),你可能需要做这样的事情:

#ifdef WIN32_VS2010 
    /* Inline assembly to suit Visual Studio 2010 for Win32 here */ 
#elifdef WIN32_ICC 
    /* Inline assembly to suit Intel's "ICC" compiler for Win32 here */ 
#elifdef LINUX_ICC 
    /* Inline assembly to suit Intel's "ICC" compiler for Linux here */ 
#elifdef WIN32_GCC 
    /* Inline assembly to suit GCC compiler for Win32 here */ 
#elifdef LINUX_GCC 
    /* Inline assembly to suit GCC compiler for Linux here */ 
#else 
    /* Generate error about unsupported target here */ 
#endif 
+0

问候并感谢您的回复。我知道at&t/gcc程序集中的列表和clobber列表,但是我可以如何访问代码块中间的变量?使用列表中的内容,将其分配给某些内存,然后再访问它?而且,最重要的是,如何将变量分配给sse寄存器?我到目前为止失败了...... – gilgamash

+0

这是一个巨大的秘密;但变量只是一种幻觉 - 它们不存在。存在的是地址和这些地址的内存内容;并注册。您可能想要告诉GCC,组件的输入是某个地址(例如“变量”),以便内联程序集可以将该地址处的数据加载到SSE寄存器中。你可能想告诉GCC SSE寄存器也是一个输入(虽然我不确定GCC是否以这种方式正确支持SSE)。 – Brendan

0

一种方法可能是变量包装成一个结构,有足够的虚拟变量来强制清洁对准4,8或16.然后可以使用offsetof(struct x,member)计算成员的相对位置,并希望这些数字可以作为兼容字符串在编译时注入。

#define LOCAL(a) ((offsetof(struct mystruct,a)==0?"0":offsetof(a)==4?"4":"error")) 

asm(" push ebp \n\t" 
    " mov ebp, %0 \n\t" 
    " mov %0, " LOCAL(a) "\n\t" // this would convert to [ebp + 4] 
    " pop ebp \n\t" 
    :"=0" (&my_struct) :::); 

VC版可以开始:

asm(" push ebp "); 
asm(" lea ebp, struct.a "); 

之后,相同的(丑陋的)语法和相同数量的局部变量。