关于arch/arm64/include/asm/atomic.h的atomic_add函数的问题
Questions on atomic_add function of arch/arm64/include/asm/atomic.h
我对 Linux 基于内核的 C 编码风格还很陌生。我试图从“arch/arm64/include/asm/atomic.h”文件中理解“atomic_add”函数的以下实现(here.
的第 112-124 行)
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
asm volatile("// atomic_add\n"
"1: ldxr %w0, %2\n"
" add %w0, %w0, %w3\n"
" stxr %w1, %w0, %2\n"
" cbnz %w1, 1b"
: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
: "Ir" (I));
}
请帮助我理解以下问题。
%w0 或 %w3 是什么意思?我知道 %2 指的是计数器值。
%w0 是指(结果)变量还是通用寄存器?
约束字符串“Ir”是否代表“立即注册”?
w
是一个模板修饰符。它导致内联汇编包含寄存器的 32 位名称(w0
等),而不是默认的 64 位名称(x0
)。请参阅 documentation linked by David Wohlferd. You can also try it 并注意,如果您编写 %0
而不是 %w0
,则生成的指令使用 64 位 x
寄存器。这不是您想要的,因为这些应该是 32 位加载和存储。
两者都有。与 GCC 风格的扩展汇编一样,%w0
指的是内联汇编的操作数 0(如前所述,w
修饰符使用其 32 位名称)。这是用 "=&r" (result)
声明的那个。由于约束是 r
,这个操作数将被分配一个通用寄存器,并且在 asm 代码中所有提及的 %0
(分别是 %w0
)将被替换为该寄存器的名称登记。在上面的 Godbolt 示例中,编译器选择了 x9
(分别为 w9
)。
(result)
的意思是在asm语句之后,编译器应该把w9
中剩下的任何东西存入变量result
中。它可以通过存储到内存或将 mov
存储到用于 result
的任何寄存器来执行此操作,或者它可以只在该变量本身中分配 result
。幸运的话,优化器应该选择后者;并且由于 result
在 asm
之后没有被用于任何东西,它不应该对该寄存器做任何进一步的事情。因此,实际上,带有以后未使用的变量的输出操作数是一种告诉编译器“请选择一个我可以用作临时寄存器的寄存器”的方式。
这是两个约束,I
和r
。约束由 GCC 记录:simple and machine-specific,当给出多个约束时,编译器可以选择满足其中任何一个。
I
要求一个适合在 AArch64 add
指令中使用的立即数,即一个 12 位零扩展数,可选择移动 12 位,这是一个编译时常量。如您所知,r
要求通用寄存器。因此,如果您编写 atomic_add(1, &c)
或 atomic_add(1+1+1, &c)
或 atomic_add(4095, &c)
或 atomic_add(4096, &c)
中的任何一个,asm
语句的第二行将作为立即数 add
指令,将您的常量直接编码到指令中: add w9, w9, #1
等等。但是如果你写 atomic_add(4097, &c)
或 atomic_add(my_variable, &c)
,编译器将在 asm
之前生成额外的代码来将适当的值加载到某个寄存器(比如 w13
)并发出 add w9, w9, w13
在你的 asm
里面。这让编译器尽可能地生成更高效的立即数 add
,同时通常仍能获得正确的代码。
我对 Linux 基于内核的 C 编码风格还很陌生。我试图从“arch/arm64/include/asm/atomic.h”文件中理解“atomic_add”函数的以下实现(here.
的第 112-124 行)static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
asm volatile("// atomic_add\n"
"1: ldxr %w0, %2\n"
" add %w0, %w0, %w3\n"
" stxr %w1, %w0, %2\n"
" cbnz %w1, 1b"
: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
: "Ir" (I));
}
请帮助我理解以下问题。
%w0 或 %w3 是什么意思?我知道 %2 指的是计数器值。
%w0 是指(结果)变量还是通用寄存器?
约束字符串“Ir”是否代表“立即注册”?
w
是一个模板修饰符。它导致内联汇编包含寄存器的 32 位名称(w0
等),而不是默认的 64 位名称(x0
)。请参阅 documentation linked by David Wohlferd. You can also try it 并注意,如果您编写%0
而不是%w0
,则生成的指令使用 64 位x
寄存器。这不是您想要的,因为这些应该是 32 位加载和存储。两者都有。与 GCC 风格的扩展汇编一样,
%w0
指的是内联汇编的操作数 0(如前所述,w
修饰符使用其 32 位名称)。这是用"=&r" (result)
声明的那个。由于约束是r
,这个操作数将被分配一个通用寄存器,并且在 asm 代码中所有提及的%0
(分别是%w0
)将被替换为该寄存器的名称登记。在上面的 Godbolt 示例中,编译器选择了x9
(分别为w9
)。(result)
的意思是在asm语句之后,编译器应该把w9
中剩下的任何东西存入变量result
中。它可以通过存储到内存或将mov
存储到用于result
的任何寄存器来执行此操作,或者它可以只在该变量本身中分配result
。幸运的话,优化器应该选择后者;并且由于result
在asm
之后没有被用于任何东西,它不应该对该寄存器做任何进一步的事情。因此,实际上,带有以后未使用的变量的输出操作数是一种告诉编译器“请选择一个我可以用作临时寄存器的寄存器”的方式。这是两个约束,
I
和r
。约束由 GCC 记录:simple and machine-specific,当给出多个约束时,编译器可以选择满足其中任何一个。I
要求一个适合在 AArch64add
指令中使用的立即数,即一个 12 位零扩展数,可选择移动 12 位,这是一个编译时常量。如您所知,r
要求通用寄存器。因此,如果您编写atomic_add(1, &c)
或atomic_add(1+1+1, &c)
或atomic_add(4095, &c)
或atomic_add(4096, &c)
中的任何一个,asm
语句的第二行将作为立即数add
指令,将您的常量直接编码到指令中:add w9, w9, #1
等等。但是如果你写atomic_add(4097, &c)
或atomic_add(my_variable, &c)
,编译器将在asm
之前生成额外的代码来将适当的值加载到某个寄存器(比如w13
)并发出add w9, w9, w13
在你的asm
里面。这让编译器尽可能地生成更高效的立即数add
,同时通常仍能获得正确的代码。