双asm方言项目intel / AT& T的问题

时间:2012-11-12 10:26:28

标签: visual-studio gcc assembly intel inline-assembly

我正在开发一个需要与QNX-Momentics(基于eclipse,g ++ 4.6.1工具链)和Visual Studio 2010兼容的项目。对于某些例程,我决定采用手动汇编实现,甚至内在函数没有很好地优化。第一个编译器具有ATt& T语法,可以使用-masm = intel标志“智能化”,第二个是英特尔方言。

使用intel-flag,我可以超越符号方面 - 不是很好但是工作定义的技巧:

#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& T中的未定义引用,因此我决定使用裸栈处理,例如

_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];

和上面给出的define块之后的这个简单计算:

_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,它适用于英特尔,但不会导致使用英特尔标记的AT&amp; T asm编译器成功。 其次,我担心额外的O2-Flag可能会优化其他变量,因此不能保证堆栈在不同的编译中具有相同的构建。

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

2 个答案:

答案 0 :(得分:0)

您可以在GCC的内联汇编中按名称访问本地变量,这只是您必须以与在VS2010中执行此操作不同的方式执行此操作。在内联汇编结束时,您应该/必须提供输入列表,输出列表和“破坏”列表;输入列表和输出列表都可以包含局部变量。另请注意,“clobbered”列表非常重要(编译器假定未列为已破坏或列为输出的任何内容都不会更改,包括所有内存内容等)。

依赖于偶然的事情(比如在哪些寄存器中发生的事情,或者在哪些内存位置或堆栈位置发生的事情)是一个严重的错误,无论您使用哪个编译器,无论它是否真的有效在一些有限的测试用例中。唯一正确的方法是依靠为此目的提供的设施(例如GCC内联汇编中的输入/输出列表)。

对于任何非平凡的内联装配;鉴于内联汇编不是任何(C / C ++)标准的一部分;我认为让它在多个编译器中可靠地工作的唯一合理方法是复制内联汇编。

另请注意,不同的操作系统具有不同的约定(例如,不同的ABI,不同的内核系统调用等)。基本上,(作为最坏的情况)你可能需要做类似的事情:

#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

答案 1 :(得分: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 ");

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