arch/arm64/include/asm/atomic.h的atomic_add函数问题

问题描述

我对基于 Linux 内核的 C 编码风格非常陌生。我试图从“arch/arm64/include/asm/atomic.h”文件here 的第 112-124 行)中理解“atomic_add”函数的以下实现。

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,%2\n"
        "   cbnz    %w1,1b"
        : "=&r" (result),"=&r" (tmp),"+Q" (v->counter)
        : "Ir" (I));

}

请帮助我理解以下问题。

  1. %w0 或 %w3 是什么意思?我知道 %2 指的是计数器值。

  2. %w0 是指(结果)变量还是通用寄存器?

  3. 约束字符串“Ir”是否代表“立即注册”?

解决方法

  1. w 是一个模板修饰符。它导致内联汇编包含寄存器的 32 位名称(w0 等),而不是其默认的 64 位名称 (x0)。请参阅 David Wohlferd 链接的 documentation。您还可以 try it 并注意,如果您编写 %0 而不是 %w0,则生成的指令使用 64 位 x 寄存器。这不是您想要的,因为这些应该是 32 位加载和存储。

  2. 两者都有。对于 GCC 样式的扩展 asm,%w0 指代内联 asm 的操作数编号 0(如上所述,使用 w 修饰符以使用其 32 位名称)。这是用 "=&r" (result) 声明的那个。由于约束是 r,这个操作数将被分配一个通用寄存器,并且所有在 asm 代码中提到的 %0(分别是 %w0)都将被替换为该操作数的名称登记。在上面的 Godbolt 示例中,编译器选择了 x9(分别为 w9)。

    (result) 表示在 asm 语句之后,编译器应该将 w9 中剩下的任何内容存储在变量 result 中。它可以通过存储到内存来实现,或者对用于 mov 的任何寄存器使用 result,或者它可以只在该变量本身中分配 result。幸运的是,优化器应该选择后者;并且由于 result 不用于 asm 之后的任何内容,因此不应对该寄存器执行任何进一步操作。因此,实际上,输出操作数带有一个以后不使用的变量是一种告诉编译器“请选择一个我可以用作临时寄存器的寄存器”的方式。

  3. 这是两个约束条件,Ir。 GCC 记录了约束:simplemachine-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 语句的第二行将作为立即 {{1} } 指令,您的常量直接编码到指令中:add 等等。但是,如果您编写 add w9,w9,#1atomic_add(4097,&c),编译器将在 atomic_add(my_variable,&c) 之前生成附加代码,以将适当的值加载到某个寄存器(例如 asm)中并发出 {{1 }} 在您的 w13 中。这让编译器在可能的情况下生成更高效的立即 add w9,w13,同时总体上仍能获得正确的代码。