gcc 直接使用操作码而不是助记符的内联AT&T asm语法

m4pnthwp  于 2023-08-06  发布在  其他
关注(0)|答案(3)|浏览(135)

不幸的是,我不能进入的原因,我必须支持一个古老的汇编程序,没有一个Map的助记符,我需要。
我知道硬件支持它,但我似乎找不到任何文档在线如何使用操作码而不是助记符。
有没有人有一个参考如何在GCC上内联AT&T语法。

enyaitl3

enyaitl31#

试试这个:

long result;
char success = 0; /* make sure we don't get surprised by setc only writing 8 bits */

/* "rdrand %%rax ; setc %b1" */
asm volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %b1" : "=a"(result), "=qm"(success) :: "cc");

字符串
a约束强制编译器为result使用rax寄存器。这是一般的,因为它得到没有令人讨厌。我建议你添加一个configure测试来检查汇编器是否理解rdrand,并使用以下代码:

long result;
char success = 0;

#ifdef HAVE_RDRAND
asm volatile ("rdrand %0; setc %b1" : "=r"(result), "=qm"(success) :: "cc");
#else
asm volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %b1" : "=a"(result), "=qm"(success) :: "cc");
#endif


如果汇编器不理解rdrand,强制编译器使用rax寄存器可能会有很小的性能损失,但允许使用任何寄存器所需的复杂组件远远超过了这一点。

gk7wooem

gk7wooem2#

幸运的是,rdrand只接受一个参数,这是一个寄存器。因此,如果你想让编译器自由选择,你只需要覆盖几种情况。注意,它仍然很丑:)

inline int rdrand()
{
    int result;
    __asm__ __volatile__ (
        ".byte 0x0f, 0xc7\n\t"
        ".ifc %0, %%eax\n\t"
        ".byte 0xf0\n\t"
        ".else\n\t"
        ".ifc %0, %%ebx\n\t"
        ".byte 0xf3\n\t"
        ".else\n\t"
        ".ifc %0, %%ecx\n\t"
        ".byte 0xf1\n\t"
        ".else\n\t"
        ".ifc %0, %%edx\n\t"
        ".byte 0xf2\n\t"
        ".else\n\t"
        ".ifc %0, %%esi\n\t"
        ".byte 0xf6\n\t"
        ".else\n\t"
        ".ifc %0, %%edi\n\t"
        ".byte 0xf7\n\t"
        ".else\n\t"
        ".ifc %0, %%ebp\n\t"
        ".byte 0xf5\n\t"
        ".else\n\t"
        ".error \"uknown register\"\n\t"
        ".endif\n\t"
        ".endif\n\t"
        ".endif\n\t"
        ".endif\n\t"
        ".endif\n\t"
        ".endif\n\t"
        ".endif\n\t"
    : "=R" (result) : : "cc");

    // "=R" excludes r8d..r15d in 64-bit mode
    return result;
}

字符串
对于64位操作数大小,您将需要一个雷克斯.W(0x48)前缀,但the "=R" constraint而不是"=r"将避免在REX前缀中设置任何其他位。
请注意,rdrand也使用进位标志,其处理留给读者作为练习。gcc6可以使用标志输出操作数,这比setcc更有效。

1aaf6o9v

1aaf6o9v3#

binutils 2.41开始,新的directive .insn允许生成任意指令。虽然这可能对您和您的旧汇编程序没有特别的帮助,但它可能对将来遇到这个问题的读者有用。以rdrand为例,它可以是这样的:

#define rdrand(x) asm (".insn 0x0fc7/6, %0" : "=r"(x))

字符串
.insn指令根据给定的操作数自动计算出要使用的编码和指令大小,因此我们可以对所有操作数大小使用如上所述的宏,即使存在内存操作数(尽管对于rdrand来说,这些当然是不允许的)。比如说,

long long example(void)
{
    char foo;
    short bar;
    int baz;
    long long quux;

    rdrand(foo);
    rdrand(bar);
    rdrand(baz);
    rdrand(quux);

    return (foo + bar + baz + quux);
}


成为

0000000000000000 <example>:
   0:   0f c7 f0                rdrand %eax
   3:   66 0f c7 f2             rdrand %dx
   7:   0f be c0                movsbl %al,%eax
   a:   0f bf d2                movswl %dx,%edx
   d:   01 d0                   add    %edx,%eax
   f:   0f c7 f6                rdrand %esi
  12:   01 f0                   add    %esi,%eax
  14:   48 98                   cltq
  16:   48 0f c7 f1             rdrand %rcx
  1a:   48 01 c8                add    %rcx,%rax
  1d:   c3  

                ret


您可以看到.insn无法区分8位和32位操作数,因为x86指令编码使用单独的操作码而不是前缀来实现8位指令。这可能导致无效结果。如果需要,可以添加Assert以避免这种情况:

#define rdrand(x) do { \
    _Static_assert(sizeof (x) > sizeof (char)); \
    asm (".insn 0x0fc7/6, %0" : "=r"(x)); \
    } while (0)

相关问题