选定的处理器不支持"SWP X1,X1,[X0]"

selected processor does not support `swp x1,x1,[x0]'

本文关键字:X1 X0 不支持 SWP 处理器      更新时间:2023-10-16

我尝试使用swp指令来实现原子交换。

asm volatile ("swp %[newval], %[newval], [%[oldval]]"
: [newval] "+r" (newval), [oldval] "+p" (oldval)
:
: "memory");

当我编译代码时(使用g++ main.cpp -o main -march=armv8-a(。我收到以下错误消息。

/tmp/cc0MHTHA.s: Assembler messages:
/tmp/cc0MHTHA.s:20: Error: selected processor does not support `swp x1,x1,[x0]'

我使用的ARM机器是armv8的,/proc/cpuinfo是这样的(这是一台16核的SMP机器,除了第一行之外,其他处理器的信息是一样的。

processor   : 0
model name  : phytium FT1500a
flags   : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x70
CPU architecture: 8
CPU variant : 0x1
CPU part    : 0x660
bogomips    : 3590.55
CPU revision    : 1

g++ --version输出

g++ (Ubuntu/Linaro 4.9.1-16kord6) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

我在使用 ldrex/strex 指令时收到以下错误

/tmp/ccXxJgQH.s: Assembler messages:
/tmp/ccXxJgQH.s:19: Error: unknown mnemonic `ldrex' -- `ldrex x0,[x0]'

谁能解释我为什么以及在哪里出现此错误以及如何处理此错误?机器不支持SWP,或者我应该在编译命令上添加一些参数(也许是-march(来指示CPU架构?

您不需要(也不应该使用(内联程序集。

为此,请使用内置的 gcc:type __atomic_exchange_n (type *ptr, type val, int memorder)或 C++11std::atomic,因此编译器可以根据-mcpu=命令行选项以及您是针对 64 位还是 32 位 ARM(或 x86(等构建目标 CPU 的最佳指令序列。 此外,编译器了解您在做什么,并可以相应地进行优化。

// static inline
int xchg_gcc(int *p, int newval) {
int oldval = __atomic_exchange_n(p, newval, __ATOMIC_SEQ_CST);
//__atomic_signal_fence ( __ATOMIC_SEQ_CST);
return oldval;
}

对于带有 gcc5.4 的 ARM64 和 ARM(带-mcpu=cortex-a72的 32 位(,这可以编译到您想要的(Godbolt 编译器资源管理器(:

.L2:   ## This is the ARM64 version.
ldaxr   w2, [x0]
stlxr   w3, w1, [x0]
cbnz    w3, .L2
mov     w0, w2      # This insn will optimize away after inlining, leaving just the retry loop
ret

或者,如果您只想要原子性,但不需要对其他操作进行排序,请使用__ATOMIC_RELAXED而不是__ATOMIC_SEQ_CST。 然后它编译为ldxr/stxr,而不是 LL/SC 指令的获取/发布版本。

对于 32 位版本,如果未指定-mcpu-march,它将调用库函数,因为它不知道用于交换的内容。

我不确定SEQ_CSTasm volatile("":::"memory")方式用于非原子事物的__atomic_exchange内置命令; 如果不是,您可能需要如下所述的围栏 C++11atomic_signal_fence.


或者使用此可移植的 C++11 版本,该版本编译为相同的 ASM:

#include <atomic>
// static inline
int xchg_stdatomic(std::atomic<int> *p, int newval) {
atomic_signal_fence(std::memory_order_seq_cst);
int oldval = p->exchange(newval, std::memory_order_seq_cst);
atomic_signal_fence(std::memory_order_seq_cst);  // order WRT. non-atomic variables (on gcc/clang at least).
return oldval;
}

atomic_signal_fence被用作asm("":::"memory")的等价物,以阻止使用非atomic加载/存储(但不发出任何指令(的编译时重新排序。 这就是 gcc 实现它的方式,但如果 gcc 中只是一个实现细节的标准要求这样做,IDK。

至少在 gcc 中,atomic_signal_fence对"正常"变量的操作进行排序,但atomic_thread_fence只对atomic变量的操作进行排序。 (从多个线程共享对非atomic变量的访问将是一场未定义行为的数据竞赛,因此 gcc 假设它不会发生。 这里的问题是,该标准是否要求signal_fence订购非atomic操作以及atomicvolatile访问,因为关于信号处理程序中可以安全访问的内容的保证非常弱。

无论如何,由于signal_fence编译为没有指令,并且只是阻止我们希望exchange()阻止的重新排序,因此没有害处。 (除非您不希望exchange()对非共享变量进行排序,在这种情况下,您不应该使用 signal_fence(。


swp受支持,但在 ARMv6 和 ARMv7 中已弃用。 ARM的文档说它增加了中断延迟(因为swp本身是不可中断的(。 也

多核系统中,在交换指令期间阻止所有处理器访问主内存会降低整体系统性能。