0

我正在开发一个需要与 QNX-Momentics(基于 Eclipse,g++ 4.6.1 工具链)和 Visual Studio 2010 编译的项目。对于某些例程,我决定进行手动组装实现,因为即使内在函数也不好优化。第一个编译器具有 ATt&T 语法,可以使用 -masm=intel 标志“intelized”,第二个编译器仅是 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];

以及上面给出的定义块之后的简单计算:

_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&T asm 编译器成功。其次,我担心额外的 O2-Flag 可能会优化其他变量,因此不能保证堆栈在不同编译上的构建相同。

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

4

2 回答 2

0

您可以在 GCC 的内联程序集中按名称访问局部变量,只是您必须以不同于在 VS2010 中的方式进行操作。在内联汇编结束时,您应该/必须提供输入列表、输出列表和“破坏”列表;其中输入列表和输出列表都可以包含局部变量。另请注意,“clobbered”列表非常重要(编译器假定任何未列为 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
于 2012-11-12T11:44:29.910 回答
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 ");

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

于 2012-11-12T15:19:56.097 回答