没错,
"=r"
意思是只写。寄存器在输入时失效。编译器不会在asm之前将任何特定的内容放入所选寄存器,因为它将被覆盖。编译器将进行优化,就像您编写
out1 = asm_result;
外部内联装配。
"+r"
是输入/输出操作数。如果它
可以
要修改,您需要编译器假定它始终是。
查看编译器为函数生成的asm,例如在Godbolt编译器资源管理器上。(
https://godbolt.org/
我也很关心
ptr1
. 指针本身并没有实际设置,但它指向的是。
"+r"(ptr1)
告诉编译器指针值已被修改,但是
不
意味着指向的值被修改。这个
"memory"
击打是一种沉重的方式,或者正如杰斯特所说,你应该使用
"=m"(*ptr1)
相反,约束让编译器选择一种寻址模式,并告诉它指向的内存是无条件写入的。
STREX没有之前的LDREX有意义吗?我不这么认为,但是如果我错了,那么对于这一条指令,您只需要内联asm,因为ARM编译器只是使用普通的
str
即使是原子能商店。
如果这个函数执行LL/SC的后半部分,那就很奇怪了。
你确定你不能用内置的
__atomic_store(ptr1, value, __ATOMIC_RELAXED)
+可选屏障或C11
atomic_store_explicit
?
#include <stdatomic.h>
int foo(int in1, int *ptr1) {
int out1=123;
if (in1 != 0) {
out1 = 0;
//asm("dmb" ::: "memory");
atomic_thread_fence(memory_order_release); // make the following stores release-stores wrt. earlier operations
}
atomic_store_explicit((_Atomic int*)ptr1, in1, memory_order_relaxed);
return out1;
}
与gcc6.3一起编译,
on the Godbolt compiler explorer
:
@ gcc6.3 -O3 -mcpu=cortex-a53 (ARM mode)
foo:
subs r3, r0, #0 @ copy in1 and set flags from it at the same time
moveq r0, #123 @ missed-optimization: since we still branch, no point hoisting this out of the if with predication
bne .L5
str r3, [r1] @ if()-not-taken path
bx lr
.L5:
dmb ish @ if()-taken path
mov r0, #0 @ makes the moveq doubly silly, because we do it again inside the branch.
str r3, [r1]
bx lr @ out1 return value in r0
因此,它运行与实现相同的指令(str而不是strex除外),但分支不同,使用尾部复制,可能会总体保存指令(代码大小可能更大,但更低
指令计数,因为我们使用
-O3
)与
-Os
dmb
).
叮当声使整件事失去了小枝,用
itte
(在拇指模式中)对
dmbne sy
请注意,如果要将其移植到AArch64,单独的屏障通常效率较低
. 您希望编译器能够使用AArch64
stlr
发布存储(即使它是顺序发布,而不是较弱的普通发布)。
dmb ish
是一个完整的记忆障碍。另外,用于ARMv8的32位代码可以使用
stl
.
dmb公司
意志命令
后期商店工作。早期的存储,因此这与AArch64(或32位,提供ARMv8指令)上的完全不同,后者编译器生成的代码不使用
dmb公司
.
我看到的一个遗漏优化是编译器无法将
dmb公司
从
,留下一个共同点
有条件的
. (对于必须使用的情况
dmb公司
).
// recommended version
int foo_ifelse(int in1, int *ptr1) {
int out1=123;
if (in1 != 0) {
out1 = 0;
atomic_store_explicit((_Atomic int*)ptr1, in1, memory_order_release);
} else {
atomic_store_explicit((_Atomic int*)ptr1, in1, memory_order_relaxed);
}
return out1;
}
Godbolt compiler explorer
):
foo_ifelse:
cbnz w0, .L9 @ compare-and-branch-non-zero
str wzr, [x1] @ plain (relaxed) store
mov w0, 123
ret
.L9:
stlr w0, [x1] @ release-store
mov w0, 0
ret
能够
使
order
参数化变量作为简化源代码的一种方法,但是gcc对它的处理非常糟糕。(叮当地把它变成一根树枝)。GCC将其增强到seq_cst,尽管在本例中只有两个选项是放松和释放。
// don't do this, gcc just strengthen variable-order to seq_cst
int foo_variable_order(int in1, int *ptr1) {
int out1=123;
memory_order order = memory_order_relaxed;
if (in1 != 0) {
out1 = 0;
order = memory_order_release;
}
// SLOW AND INEFFICIENT with gcc
// but clang distributes it over the branch
atomic_store_explicit((_Atomic int*)ptr1, in1, order);
return out1;
}
非常数
秩序
我们可以看到过度增强对x86的影响,gcc使用
mfence
为了这个,但只是简单的
mov
其他的(在x86 asm中有发布语义)。同样在ARM32 gcc输出中,我们可以看到
dmb公司
之前
和
@ gcc6.3 -Os -mcpu=cortex-m4 -mthumb
foo_variable_order:
dmb ish
str r0, [r1]
dmb ish @ barrier after for seq-cst
cmp r0, #0
ite eq @ branchless out1 = in1 ? 0 : 123
moveq r0, #123
movne r0, #0
bx lr