如何在内联汇编中实现这一点?

Posted

技术标签:

【中文标题】如何在内联汇编中实现这一点?【英文标题】:How to implement this in inline assembly? 【发布时间】:2014-06-03 02:45:23 【问题描述】:

我在理解 GNU 内联汇编语法方面非常糟糕,所以我希望一个实际的例子能有所帮助。鉴于以下程序集(x86-64,Clang 输出),我将如何使用相同的内联程序集构造函数? GCC 为同一个函数生成不同的代码,我想让它生成与 Clang (-O3) 输出相同的版本。

bittest(unsigned char, int):
    btl %esi, %edi
    setb    %al
    ret

这是 GCC (-O3) 产生的结果:

bittest(unsigned char, int):
    movzx    eax, dil
    mov    ecx, esi
    sar    eax, cl
    and    eax, 1
    ret

这是函数的 C 代码:

bool bittest(unsigned char byte, int index)

    return (byte >> index) & 1;

【问题讨论】:

我和帕特里克在一起,你在试图准确匹配 clang 时打错了电话。你可以简单地做 asm("btl %esi, %edi\nsetb %al\nret"),但你几乎肯定会后悔。如果你决定你愿意探索合理的替代方案,我有一些。另外,如果您要使用内联 asm,您是否查看了新文档?他们最近被重写了。它们组织得更好,并且包含许多示例:gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html 那么 GCC 生成了什么?你确定它至少没有那么好? 您在询问有关编程细节的问题,但没有暗示您的更大目标。你在优化性能吗?为了按位正确?只是为了学习?还是出于完全不同的原因?这可能会严重影响给出的答案 - 目前我不知道你的目标是什么,什么是可接受的答案。 @MatsPetersson 更新了我的问题。 @KlaasvanGend 我实际上无法访问 Clang atm 的本地副本,只有 MinGW。我想比较一下性能。 【参考方案1】:

嗯,上次我写了一个 32bit 的 bittest,它看起来像这样(64bit 看起来略有不同):

unsigned char _bittest(const long *Base, long Offset) 
 
   unsigned char old; 
   __asm__ ("btl %[Offset],%[Base] ; setc %[old]" : 
      [old] "=rm" (old) : 
      [Offset] "Ir" (Offset), [Base] "rm" (*Base) : 
      "cc"); 

   return old; 

虽然如果你想把它放在公共标题中,我有一个不同的版本。当我使用 -O2 时,它最终会内联整个内容以生成真正高效的代码。

我很惊讶 gcc 本身不会在此处生成 btl(请参阅 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36473),但你说得对,它不会。

【讨论】:

Base 中删除"m" 替代项将是一种优化。现代 CPU 上的 x86 内存位串指令比某些地址数学 + 负载 + bt r,r 慢得多(如 ~10 微秒)。 我想这取决于释放寄存器的成本。如果 gcc 有一个临时寄存器,那你可能是对的。如果它必须刷新/重新加载某些东西,也许不是。 IAC,我认为我从未见过 gcc 在指定“rm”时使用“m”。但是,如果我要更新这个旧答案,我会更倾向于添加 @cc 东西而不是使用 setc【参考方案2】:

我认为您不太可能在编译器中确定一个逐字节的等效版本,存在一些不值得担心的细微差异。在this question 之后,确保您使用正确的标志进行编译。试图让两个编译器产生相同的输出可能是徒劳的。

【讨论】:

【参考方案3】:

如果您想生成完全相同的代码,则可以执行以下操作

const char bittestfunction[] =  0xf, 0xa3, 0xf7, 0xf, 0x92, 0xc0, 0x3 ;
int (*bittest)( unsigned char, int ) = (int(*)(unsigned char, int))bittestfunction;

你可以用同样的方式调用它bittest( foo, bar )

来自 (gcc) 编译的可执行文件上的 objdump

00000000004006cc <bittestfunction>:
  4006cc:       0f a3 f7                bt     %esi,%edi
  4006cf:       0f 92 c0                setb   %al
  4006d2:       c3                      retq

【讨论】:

这没用,因为它没有内联。这个问题真正想要的是一个可以内联的 inline-asm 版本。更糟糕的是,这必须通过函数指针(使用间接call)调用,因为您将其编写为数据,而不是仅在 asm 中编写可以正常链接的独立函数。

以上是关于如何在内联汇编中实现这一点?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GCC (x86_64) 中使用内联汇编进行相对跳转/调用

x86汇编编程中如何表示FFFFFFBB等十六进制值?

x86汇编编程中如何表示FFFFFFBB等十六进制值?

在内联汇编中访问 C 结构成员

我将如何通过仿射变换在opencv中实现这一点?

我想在表 A 中提取一些在表 B 中没有条目的列。如何在 Hive 中实现这一点?