【问题标题】:Problems with dual asm dialect project intel/AT&T双 asm 方言项目 intel/AT&T 的问题
【发布时间】:2012-11-12 10:26:27
【问题描述】:

我正在开发一个需要同时使用 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 可能会优化其他变量,因此不能保证堆栈在不同编译时构建相同。

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

【问题讨论】:

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


    【解决方案1】:

    您可以在 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
    

    【讨论】:

    • 问候并感谢您的回复。我知道 at&t/gcc 程序集中的列表和 clobber 列表,但是,例如,如何访问代码块中间的变量?使用 in 列表,将其分配给一些内存,然后再访问它?而且,最重要的是,如何将变量分配给 sse 寄存器?到目前为止我失败了......
    • 这是一个巨大的秘密;但变量只是一种幻觉——它们不存在。确实存在的是地址和这些地址处的内存内容;并注册。您可能想告诉 GCC,您的程序集的输入是某物的地址(例如“变量”),以便您的内联程序集可以将该地址处的数据加载到 SSE 寄存器中。您可能想告诉 GCC 一个 SSE 寄存器也是一个输入(尽管我不确定 GCC 是否以这种方式正确支持 SSE)。
    【解决方案2】:

    一种方法可以是将变量包装到一个结构中,使用足够的虚拟变量强制干净对齐到 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 ");
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-25
      • 2014-03-25
      • 2011-10-16
      • 1970-01-01
      • 2019-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多