iOS版本中的非法操作码

Fra*_*ank 2 c++ boost arm ios stdatomic

以下代码(从大型项目中最小化)在使用XCode 7.3.1,针对iOS的Boost 1.61构建时导致EXC_BAD_INSTRUCTION崩溃:

main.mm:

#include "stdio.h"
#include "boost/lockfree/queue.hpp"

int main(int argc, char * argv[]) {
    printf("Test1 in\n");
    boost::lockfree::queue<int*> q(100);
    printf("Test1 out\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

stacktrace似乎告诉我,问题来自c ++原子操作:

#0  0x0000000100047a78 in std::__1::__atomic_base<boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, false>::store(boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, std::__1::memory_order) [inlined] at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/atomic:842
#1  0x0000000100047a74 in boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::deallocate_impl_unsafe(boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node*) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/detail/freelist.hpp:251
#2  0x00000001000479e8 in boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_stack<std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >(std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> const&, unsigned long) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/detail/freelist.hpp:64
#3  0x00000001000478e0 in boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::queue(unsigned long) at /Users/deinzer/src/pipeline.ios/boost/boost/lockfree/queue.hpp:205
#4  0x0000000100047840 in main at /Users/deinzer/src/iostester/lockfreecrash/lockfree_crash/lockfree_crash/main.mm:7
#5  0x00000001821d68b8 in start ()
Run Code Online (Sandbox Code Playgroud)

反汇编输出显示非法操作码:

0x100047a5c <+72>:  mov    x20, x0
0x100047a60 <+76>:  add    x0, sp, #16               ; =16 
0x100047a64 <+80>:  bl     0x100047aac               ; boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>::get_ptr at tagged_ptr_dcas.hpp:78
0x100047a68 <+84>:  mov    x1, x0
0x100047a6c <+88>:  mov    x0, x20
0x100047a70 <+92>:  bl     0x100047aa4               ; boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>::set_ptr at tagged_ptr_dcas.hpp:83
0x100047a74 <+96>:  ldp    x9, x8, [sp]
->  0x100047a78 <+100>: .long  0xc87f7e7f                ; unknown opcode
0x100047a7c <+104>: stxp   w10, x9, x8, [x19]
0x100047a80 <+108>: cbnz   w10, 0x100047a78          ; <+100> [inlined] std::__1::__atomic_base<boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, false>::store(boost::lockfree::detail::tagged_ptr<boost::lockfree::detail::freelist_stack<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node, std::__1::allocator<boost::lockfree::queue<int*, boost::parameter::void_, boost::parameter::void_, boost::parameter::void_>::node> >::freelist_node>, std::__1::memory_order) + 4 at freelist.hpp:251
Run Code Online (Sandbox Code Playgroud)

问题只发生在什么时候

  • 打开优化(O1,O2和O3)
  • 目标体系结构设置为arm64

代码运行正常,如果

  • 优化已关闭(O0)
  • 目标体系结构设置为armv7

我想知道问题的根本原因.这是一个clang问题,一个std :: atomic或boost bug?可以做些什么来避免这个问题?

Not*_*hat 5

您的反汇编程序拒绝确认的指令是ldxp xzr, xzr, [x19]- 换句话说,一个负载来填充独占监视器,以便存储独占将成功(或失败并重新启动,如果确实有一些并发内存访问,所以商店的原子性可以是保证).因为在这种情况下,这是唯一重要的方面,即我们不关心实际加载的数据,所以使用零寄存器作为目标来简单地丢弃数据并避免必须分配临时寄存器来加载.

这里的问题是对于负载对的两个目标使用相同的寄存器在架构上是不可预测的.这必须是Boost或Clang中的错误,具体取决于违规指令是来自某些显式汇编代码还是来自编译器内部实现.从取消这些模板开始,我认为它是在std :: atomic中,但是随着我的知识在C++ 98之后停止,我不确定指向哪里.

从引用ldxp的部分的ARMv8 ARM不可预知的行为附件:

如果t == t2,则必须发生以下行为之一:

  • 该指令未定义.
  • 指令作为a执行NOP.
  • 该指令使用指定的寻址模式执行加载,基址寄存器[sic]设置为UNKNOWN值.

很可能在设计人员选择第三个选项的某些CPU上,这段代码最终会按预期工作(实际上可以进行测试).然而,苹果公司的CPU设计人员似乎已经采取了第一个选择,至少在他们的核心中有问题的设备,因此爆炸.

什么__atomic_base::store()方法应该能够做到整齐地修复它是简单地重用分配给商店的独家地位,而不是之一暂存寄存器xzrS,例如ldxp xzr, x10, [x19]在这个例子.这应该使指令定义良好而不影响任何其他代码(以下stxp将始终无条件地覆盖整个寄存器),并且不需要优化器分配额外的寄存器.可以想象,可以编写一个工具来对已编译的二进制文件进行后处理,扫描相关的指令对,从而修复加载操作数,但是提交相应的错误报告并将其修复到源代码可能更为明智 - 因为它事实证明,我怀疑已经针对上游LLVM报告了潜在的优化问题.