x86 - 使用内联汇编设置一个位

时间:2015-11-24 08:53:30

标签: c assembly x86 inline-assembly

我正在编写一个函数,通过内联汇编使用bts指令在数字x中设置第n位。这是我的功能:

uint32_t set_bit_assembly(uint32_t x, uint32_t n)
{
    asm( "movl %1, %%eax; bts %0, %%eax;"
         :"=&r"(x)
         :"r"(n)
        );
    return x;
}

我想要变量' n'和' x'分别是movlbts的第一个操作数。但是当我编译时,它需要' x'对于movl并完全无视' n' (我尝试交换%0和%1,这没有帮助)。你能告诉我哪里出错了吗?下面是生成的汇编代码:

00000043 <set_bit_assembly>:
  43:   55                      push   %ebp
  44:   89 e5                   mov    %esp,%ebp
  46:   83 ec 10                sub    $0x10,%esp
  49:   8b 55 0c                mov    0xc(%ebp),%edx
  4c:   89 d0                   mov    %edx,%eax
  4e:   0f ab c0                bts    %eax,%eax
  51:   89 45 fc                mov    %eax,-0x4(%ebp)
  54:   8b 45 fc                mov    -0x4(%ebp),%eax
  57:   c9                      leave  
  58:   c3                      ret    

1 个答案:

答案 0 :(得分:4)

如何在asm中使用bts指令

在您的代码中,这一行:

bts %0, %%eax;  

应替换为:

bts %%eax, %0;

说明

给出一般形式asm(&#34;代码&#34;:输出:输入:clobbers)GCC用“冒号”后面的参数替换了“代码”中的%0,%1和%2。 BTS的定义是第一个操作数是位串,第二个是位索引。所以解决方案似乎是:bts%0,%1你已经完成了你的代码。然而,这不是bts如何工作:bts想要将地址作为第二个操作数,并将要设置为第一个的位:bts%1,%0。查看正确的用法here

更好的解决方案

虽然您的代码可以使用建议的更正,但有更好的选项,如下所示:

uint32_t set_bit_assembly2(uint32_t x, uint32_t n)
{
    asm( "bts %1,%0"
         :"+r"(x)
         :"r"(n)
        );
    return x;
}

正如@DavidWohlferd在评论中所指出的,我们应该使用&#34; + r&#34;因为x将由bts指令读写。

此外,可以使用符号 名称来提高可读性:

asm( "bts %[bit],%[value]"
     : [value] "+rm"(value) 
     : [bit] "r"(bit)
     :"cc");

另一种可能性是(see this post):

uint32_t set_bit_assembly3(uint32_t x, uint32_t n)
{
    asm( "bts %1,%0": "+rm"(x) : "r"(n));
    return x;
}

进一步阅读:

此页面可能对想要使用bts的人非常感兴趣:http://lxr.free-electrons.com/source/arch/x86/include/asm/bitops.h#L41

在此post中,Peter Cordes解释了为什么内存操作数上的bts对性能很糟糕。