2

我正在使用一个库(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' 的含义,但我最终找到了不同编译器的文档(第 194 页),它将 q 描述为“如果目标为操作数,则为操作数生成四字寄存器名称支持四字。否则,它会产生一个字寄存器名称。(例如,如果操作数 0 在寄存器 'a' 中,则 %q0 在 x86_64 上产生 %rax 或在 x86 上产生 %eax。)如果您使用 'q' 标志请求 asm 块输出 64 位寄存器,即使 -arch 标志指定 32 位输出也是如此。

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

4

1 回答 1

1

看起来您拥有的 gcc 不喜欢在寻址表达式中显式混合 a%q普通寄存器,和/或评估%q为 64 位 reg 名称,即使您正在显式编译 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 位中,如果您使用寄存器而不是仅仅使用它们进行寻址,那么 egxor %...,%...就变成了显式的xorq %r...,%r...(带有 REX 前缀),即使这不是严格要求的。如果您不能接受,请使用#ifdef/#else创建一个 32 位和一个 64 位代码块。

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

于 2012-06-12T11:06:22.217 回答