x86 - 使用内联汇编设置位
x86 - setting a bit using 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' 分别成为 movl
和 bts
的第一个操作数。但是,当我编译时,movl 需要 'x',而完全无视 'n'。 (我尝试交换 %0 和 %1,但没有帮助)。你能告诉我哪里出错了吗?下面是生成的汇编代码:
00000043 <set_bit_assembly>:
43: 55 push %ebp
44: 89 e5 mov %esp,%ebp
46: 83 ec 10 sub [=11=]x10,%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
如何在asm中使用bts指令
在您的代码中,这一行:
bts %0, %%eax;
应替换为:
bts %%eax, %0;
说明
给定一般形式 asm( "code" : outputs : inputs : clobbers) GCC 将“代码”中的 %0、%1 和 %2 替换为在冒号后保存参数的寄存器。 BTS 的定义是第一个操作数是位串,第二个是位索引。所以解决方案似乎是:bts %0, %1 has you have done you have in your code.然而,这不是 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 在评论中指出的那样,我们应该使用“+r”,因为 x 将由 bts 指令读取和写入。
此外,可以通过使用 symbolic names:
来提高可读性
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 对性能来说很糟糕。
我正在编写一个函数,通过内联汇编使用 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' 分别成为 movl
和 bts
的第一个操作数。但是,当我编译时,movl 需要 'x',而完全无视 'n'。 (我尝试交换 %0 和 %1,但没有帮助)。你能告诉我哪里出错了吗?下面是生成的汇编代码:
00000043 <set_bit_assembly>:
43: 55 push %ebp
44: 89 e5 mov %esp,%ebp
46: 83 ec 10 sub [=11=]x10,%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
如何在asm中使用bts指令
在您的代码中,这一行:
bts %0, %%eax;
应替换为:
bts %%eax, %0;
说明
给定一般形式 asm( "code" : outputs : inputs : clobbers) GCC 将“代码”中的 %0、%1 和 %2 替换为在冒号后保存参数的寄存器。 BTS 的定义是第一个操作数是位串,第二个是位索引。所以解决方案似乎是:bts %0, %1 has you have done you have in your code.然而,这不是 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 在评论中指出的那样,我们应该使用“+r”,因为 x 将由 bts 指令读取和写入。
此外,可以通过使用 symbolic names:
来提高可读性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 对性能来说很糟糕。