这个比较交换函数中的内联汇编是如何工作的? (ARM 上的 %H 修饰符)
How does the inline assembly in this compare-exchange function work? (%H modifier on ARM)
static inline unsigned long long __cmpxchg64(unsigned long long *ptr,unsigned long long old,unsigned long long new)
{
unsigned long long oldval;
unsigned long res;
prefetchw(ptr);
__asm__ __volatile__(
"1: ldrexd %1, %H1, [%3]\n"
" teq %1, %4\n"
" teqeq %H1, %H4\n"
" bne 2f\n"
" strexd %0, %5, %H5, [%3]\n"
" teq %0, #0\n"
" bne 1b\n"
"2:"
: "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
: "r" (ptr), "r" (old), "r" (new)
: "cc");
return oldval;
}
我在 gnu 手册(扩展 extended-asm)中发现 '%H1' 中的 'H' 表示 'Add 8 bytes to an offsettable memory reference'。
但我认为如果我想将双字长数据加载到oldval(一个long long值),应该将4个字节添加到'%1',即oldval的低32位作为高32位有点旧。那么我的错误是什么?
I find in gnu manual(extend extended-asm) that 'H' in '%H1' means 'Add 8 bytes to an offsettable memory reference'.
That table of template modifiers 仅适用于 x86。不适用于ARM。
不幸的是,ARM 的模板修饰符没有记录在 GCC 手册中,但它们在 armclang manual 中定义,据我所知,GCC 符合这些定义。所以这里的H
模板修饰符的正确含义是:
The operand must use the r constraint, and must be a 64-bit integer or floating-point type. The operand is printed as the highest-numbered register holding half of the value.
现在这是有道理的。 inline asm 的操作数 1 是 oldval
,类型为 unsigned long long
,64 位,因此编译器会为其分配两个连续的 32 位通用寄存器。假设它们是 r4
和 r5
,如 this compiled output. Then %1
will expand to r4
, and %H1
will expand to r5
, which is exactly what the ldrexd
instruction needs. Likewise, %4, %H4
expanded to r2, r3
, and %5, %H5
expanded to fp, ip
, which are alternative names for r11, r12
.
frant 的回答解释了 compare-exchange 应该做什么。 (拼写 cmpxchg
可能来自 x86 compare-exchange 指令的助记符。)如果您现在通读代码,您应该会发现它确实是这样做的。如果 old
和 *ptr
不相等,ldrexd
和 strexd
之间的 teq; teqeq; bne
将中止存储。如果独占存储失败,strexd
之后的 teq; bne
将导致重试,如果有对 *ptr
的干预访问(由另一个核心、中断处理程序等),就会发生这种情况。这样就保证了原子性。
static inline unsigned long long __cmpxchg64(unsigned long long *ptr,unsigned long long old,unsigned long long new)
{
unsigned long long oldval;
unsigned long res;
prefetchw(ptr);
__asm__ __volatile__(
"1: ldrexd %1, %H1, [%3]\n"
" teq %1, %4\n"
" teqeq %H1, %H4\n"
" bne 2f\n"
" strexd %0, %5, %H5, [%3]\n"
" teq %0, #0\n"
" bne 1b\n"
"2:"
: "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
: "r" (ptr), "r" (old), "r" (new)
: "cc");
return oldval;
}
我在 gnu 手册(扩展 extended-asm)中发现 '%H1' 中的 'H' 表示 'Add 8 bytes to an offsettable memory reference'。
但我认为如果我想将双字长数据加载到oldval(一个long long值),应该将4个字节添加到'%1',即oldval的低32位作为高32位有点旧。那么我的错误是什么?
I find in gnu manual(extend extended-asm) that 'H' in '%H1' means 'Add 8 bytes to an offsettable memory reference'.
That table of template modifiers 仅适用于 x86。不适用于ARM。
不幸的是,ARM 的模板修饰符没有记录在 GCC 手册中,但它们在 armclang manual 中定义,据我所知,GCC 符合这些定义。所以这里的H
模板修饰符的正确含义是:
The operand must use the r constraint, and must be a 64-bit integer or floating-point type. The operand is printed as the highest-numbered register holding half of the value.
现在这是有道理的。 inline asm 的操作数 1 是 oldval
,类型为 unsigned long long
,64 位,因此编译器会为其分配两个连续的 32 位通用寄存器。假设它们是 r4
和 r5
,如 this compiled output. Then %1
will expand to r4
, and %H1
will expand to r5
, which is exactly what the ldrexd
instruction needs. Likewise, %4, %H4
expanded to r2, r3
, and %5, %H5
expanded to fp, ip
, which are alternative names for r11, r12
.
frant 的回答解释了 compare-exchange 应该做什么。 (拼写 cmpxchg
可能来自 x86 compare-exchange 指令的助记符。)如果您现在通读代码,您应该会发现它确实是这样做的。如果 old
和 *ptr
不相等,ldrexd
和 strexd
之间的 teq; teqeq; bne
将中止存储。如果独占存储失败,strexd
之后的 teq; bne
将导致重试,如果有对 *ptr
的干预访问(由另一个核心、中断处理程序等),就会发生这种情况。这样就保证了原子性。