我尝试使用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架构?
答案 0 :(得分:1)
您不需要(and shouldn't use)内联汇编。
为此使用gcc builtin:type __atomic_exchange_n (type *ptr, type val, int memorder)
或C ++ 11 std::atomic
,因此编译器可以根据-mcpu=
命令行为目标CPU使用最佳指令序列选项以及是否为64位或32位ARM(或x86)等构建。此外,编译器了解您正在做什么并可以相应地进行优化。
// 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 compiler explorer):
.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
,则会调用库函数,因为它不知道要用于交换的内容。< / p>
我不确定SEQ_CST
关于非原子事件的__atomic_exchange
内置命令asm volatile("":::"memory")
是否atomic_signal_fence
;如果不是,您可能需要如下面针对C ++ 11 #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;
}
所述的围栏。
或使用这个可移植的C ++ 11版本,它编译为相同的asm:
atomic_signal_fence
asm("":::"memory")
用作atomic
的等价物,用于阻止非atomic_signal_fence
加载/存储的编译时重新排序(但不发出任何指令)。这就是gcc如何实现它,但IDK只是gcc中实现细节标准所要求的。
至少在gcc中,atomic_thread_fence
命令&#34;正常&#34;变量,但atomic
仅对atomic
变量的操作进行排序。 (来自多个线程的非signal_fence
变量的共享访问将是未定义的行为数据竞争,因此gcc认为它没有发生。这里的问题是标准是否需要atomic
来订购非atomic
操作以及volatile
和exchange()
次访问,因为对信号处理程序中可以安全访问的内容的保证非常弱。)
无论如何,由于signal_fence编译为无指令,并且仅阻止我们希望exchange()
阻止重新排序,因此没有任何伤害。 (除非您没有希望swp
订购您的非共享变量,在这种情况下您不应该使用signal_fence)。
swp
但在ARMv6和ARMv7 中已弃用。 ARM's docs say它会增加中断延迟(因为{{1}}本身不可中断)。此外,
在多核系统中,在交换指令期间阻止所有处理器访问主存储器会降低整体系统性能。