如何使用“asm volatile”编写 btr 指令
Posted
技术标签:
【中文标题】如何使用“asm volatile”编写 btr 指令【英文标题】:How to write the btr instruction with 'asm volatile' 【发布时间】:2018-08-28 23:05:06 【问题描述】:在 C++ 中使用扩展程序集 (asm volatile
) 语法使用 btr 重置整数位的正确实现是什么?我需要在重置之前返回位中的值。
这是我的实现,这对于 16 位整数是否正确?
std::uint16_t reset(std::uint16_t& integer, std::uint32_t bit)
auto success = false;
asm volatile("lock btrw %1, (%2); setnc %0"
: "=r"(success)
: "i"(bit), "r"(&integer)
: "memory", "flags");
return !success;
这个实现是否正确?我错过了任何细节吗?我对asm()
语法或x86 程序集不太熟悉。
【问题讨论】:
关于近距离投票,我不相信我在问为什么我的代码行为不端,而是我是否错过了任何可能使该代码在不可预见的情况下表现不佳的细节。 *** 是我在该领域获得可靠信息的唯一来源。 x86 文档不能很好地转换为此处的语法。 纯粹出于兴趣,您正在创建什么软件产品需要这么多(在您的情况下)深奥的操作?而且我还没有在这里投票关闭或否决你,我真的很感兴趣。 我个人建议不要为此使用内联汇编。我相信编译器具有您可以调用来执行此操作的内在函数。 MSVC,"r"(&integer)
应该成为一个输出约束。 "=m"(integer)
。这也将捕获您正在修改该地址处的值这一事实。然后您可以删除%2
周围的括号您的编译器应该已经支持这个内在函数。
你应该使用"m" (integer)
而不是"r" (&integer)
。它也应该是一个读写操作数,您应该删除memory
约束。另请注意,i
约束是编译时的,因此如果该值仅在运行时已知(但您可以使用 btr r/m16, r16
),它将不起作用。最后,success
是一个误导性名称(并且具有误导性类型),如果您使用 setc
,您也可以省略否定。
【参考方案1】:
这是一个用正确的读写操作数替换memory
clobber 的版本,而不是在寄存器中传递地址,并且还去掉了setnc
(需要gcc 6+)。添加了r
以涵盖bit
在编译时未知的情况。使用bool
类型将success
更改为更易读的was_set
。请注意,如果您希望它是原子的,您还需要添加 lock
前缀。对于编译器内存屏障,您可能需要放回 memory
约束。
bool reset(std::uint16_t& integer, std::uint32_t bit)
bool was_set;
asm volatile("btrw %w2, %1"
: "=@ccc"(was_set), "+mr"(integer)
: "ri"(bit)
: "cc");
return was_set;
原子版本可能如下所示:
bool reset(std::uint16_t& integer, std::uint32_t bit)
bool was_set;
asm volatile("lock btrw %w2, %1"
: "=@ccc"(was_set), "+m"(integer)
: "ri"(bit)
: "cc", "memory");
return was_set;
【讨论】:
谢谢,你能澄清一下内存限制在这里有什么不同吗?我确实打算为此添加一个锁定前缀。我相应地更新了问题。memory
强加什么顺序(由 std::memory_order
定义)?
请注意,lock
不适用于寄存器操作数,因此您可能希望将 +mr
更改为 m
。 memory
约束将强制编译器刷新并重新加载可能保存在寄存器中的任何其他内容。
那么memory
约束和std::atomic_thread_fence(std::memory_order_seq_cst)
(MFENCE
) 是一样的吗?
@Curious 这是一个编译器障碍。它对内存访问的 CPU 重新排序没有影响,只是编译器的重新排序。
i
仅适用于编译时常量。你可以像我一样使用ri
。以上是关于如何使用“asm volatile”编写 btr 指令的主要内容,如果未能解决你的问题,请参考以下文章