使用-arch i386进行内联汇编时的%q

时间:2012-06-09 21:59:47

标签: c assembly cross-compiling inline-assembly

我正在使用一个库(pulseaudio,src / pulsecore / svolume_mmx.c),其代码类似于以下虚拟代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

int main(int argc, char *argv[]) {
  int32_t x = 5;
  int32_t *p_x = &x;
#if defined(__i386__)
  int32_t tmp;
#elif defined(__amd64__)
  int64_t tmp;
#endif

  __asm__ __volatile__ (
    " xor %1, %1                    \n\t"
    " movd (%q0, %1, 4), %%mm0      \n\t"
    " emms                          \n\t"
    : "+r" (p_x), "+r" (tmp)
  );
  printf("%"PRId32"\n", x);
  return 0;
}

我正在尝试将其编译为64位mac osx上的32位库。当我正常编译时,一切正常,但当我使用-arch标志将其编译为所需的32位库时,会发生这种情况:

$ gcc -std=c99 -arch i386 -o main main.c
/var/folders/random_stuff_here.s:22:bad register name `%rcx, %edx,4)'

在读取gcc的汇编输出后,问题出在movd行中。 %q0寄存器以%rcx填充,这是一个64位寄存器。汇编程序正在尝试从此创建一个32位输出,并且失败。

我无法找到%q0中'q'的含义,但我最终找到了different compiler(第194页)的文档,其中描述了q为含义“生成四字寄存器名称如果目标支持四字,则为操作数。否则,它产生一个字寄存器名称。(例如,如果操作数0在寄存器'a'中,则%q0在x86_64上产生%rax或在x86上产生%eax。)“它似乎asm块的代码生成输出64位寄存器,如果你用'q'标志请求它,即使-arch标志指定32位输出。

除-arch i386标志外,使用-m32标志根本没有帮助。如何告诉asm代码生成器仅对%qx符号使用32位寄存器?我更愿意为gcc提供额外的标志,而不是修改这个库的来源。

1 个答案:

答案 0 :(得分:1)

看起来你所拥有的gcc不喜欢在寻址表达式中明确混合%q普通寄存器,和/或评估%q到a 64位注册名称,即使您明确编译为32位(并且它不存在)。

但是,在你/你的库特定__asm__表达式中使用它是相当虚假的,因为你在寻址表达式中(错误)匹配使用(非)指针数据类型。你可以相对容易地纠正它:

#include <stdint.h>    // has [u]intptr_t and "sized types" [u]int(8|16|32|64)_t
...
int myintval = 0;
int tmp = 0;

__asm__("mov (%0, %1, 4), ..."
    : : "r"((void*)(intptr_t)myintval), "r"((void*)(intptr_t)tmp));

即。首先手动强制数据类型为[u]intptr_t(相同的底层大小的整数类型,无论您是在32位还是64位平台上的指针,然后是实际指针(void*)) ,您将其传递到输入寄存器约束。

这确保编译器将整数变量分配给可用于寻址操作的寄存器;代码将在32位和64位x86中正常工作,并且不需要使用显式寄存器宽度说明符。

成本/劣势?那么,在64位中,如果你使用寄存器工作而不是仅使用它们进行寻址,那么例如xor %...,%...变为显式xorq %r...,%r...(带有REX前缀),即使这不是严格要求的。如果您无法接受,请使用#ifdef / #else创建32位和64位代码块。

另外,如果您不能/不想修改库源,那么请尝试获取不同的gcc版本(下载更新的XCode)。我无法用gcc 3.4.5和各种4. [14567] .x重现你的问题,但手头没有任何4.2.x。