ASM volatile语法
基本语法
__asm__ volatile ("汇编指令"
: "输出操作数"
: "输入操作数"
: "clobber 列表");
解释:
汇编指令 - 这里填写具体的汇编指令。
输出操作数 - 表示汇编指令的结果。这些操作数必须出现在汇编指令中,并且必须以 = 或 + 前缀标记。
输入操作数 - 表示汇编指令的输入。这些操作数也必须出现在汇编指令中。
clobber 列表 - 这是可能被汇编指令修改的寄存器列表。这些寄存器的值在汇编指令执行后可能被改变,因此需要告诉编译器哪些寄存器被修改了。
示例
假设我们需要编写一个简单的汇编指令来交换两个寄存器的值:
void swap(int *a, int *b) {
__asm__ volatile (
"xchg %0, %1\n"
: "+r" (*a), "+r" (*b)
:
: "memory"
);
}
解释:
“xchg %0, %1\n” 是 x86 架构下的 xchg 指令,用于交换两个寄存器的值。
“+r” (*a), “+r” (*b) 表示这两个变量既可以作为输入也可以作为输出。+r 表示可以使用任何类型的寄存器(整数或指针),并且它的值在指令前后都有效。
: “memory” 指定内存可能被此汇编指令修改,这样编译器就不会对这部分代码进行优化,导致意外的行为。
注意事项:
使用 volatile 关键字可以确保编译器不会对这段代码做任何优化,这在处理 I/O 和中断服务例程时非常重要。
输入和输出操作数的顺序必须与汇编指令中的顺序一致。
如果不使用 volatile,编译器可能会对内联汇编进行优化,这可能导致意外行为。
如果你有具体的汇编需求或者想要了解更详细的用法,请告诉我,我可以提供进一步的帮助。
标记含义
输入操作数标记
r: 表示操作数可以被放置在任意类型的寄存器中,并且只能作为输入使用。
m: 表示操作数位于内存中。
a: 在 ARM 架构中,表示操作数可以被放置在支持寻址模式的寄存器中。
n: 在 ARM 架构中,表示操作数可以被放置在通用寄存器中。
d: 在 ARM 架构中,表示操作数可以被放置在数据寄存器中。
输出操作数标记
=: 表示操作数是输出操作数,它可以被放置在任意类型的寄存器中。
+: 表示操作数既是输入也是输出,并且可以被放置在任意类型的寄存器中。
组合标记
组合标记将输入和输出标记结合起来使用:
=r: 表示操作数是输出操作数,并且可以被放置在任意类型的寄存器中。
+r: 表示操作数既是输入也是输出,并且可以被放置在任意类型的寄存器中。
+m: 表示操作数既是输入也是输出,并且位于内存中。
+a, +n, +d: 类似地,这些标记在 ARM 架构中分别表示操作数可以被放置在支持寻址模式的寄存器中、通用寄存器中、数据寄存器中,并且它们既是输入也是输出。
=r 标记
=r 标记表示该操作数是输出操作数,它可以被放置在任意类型的寄存器中。这意味着操作数只能作为输出出现,不能作为输入。
+r 标记
+r 标记表示该操作数既是输入也是输出,并且可以被放置在任意类型的寄存器中。这意味着操作数可以同时作为输入和输出使用,并且它的值在指令执行前后都是有效的。
r 标记
r 标记表示该操作数仅作为输入,它可以被放置在任意类型的寄存器中。这意味着操作数只能作为输入使用,并且其值在指令执行后保持不变。
+A 标记
+A 标记并不是标准的 GCC 标记,但在某些文档中可能会出现。在 ARM 架构中,它可能被用来表示操作数可以放在任何类型的支持寻址模式的寄存器中。然而,在实际的 GCC 内联汇编语法中,+A 并不是一个标准的约束符。
注意事项
对于特定架构,你可能需要使用不同的标记符。例如,在 ARM 架构中,你可以使用 =d 和 d 来指定数据寄存器。
cc 在 clobber 列表中表示条件码寄存器可能被修改,这对于许多指令集来说是非常重要的,特别是当你使用条件指令时。
RISC-V标记符
输入操作数标记
r: 表示操作数可以被放置在任意类型的寄存器中,并且只能作为输入使用。
m: 表示操作数位于内存中。
n: 表示操作数可以被放置在整数寄存器中,并且只能作为输入使用。
i: 表示操作数可以是一个立即数。
输出操作数标记
=: 表示操作数是输出操作数,它可以被放置在任意类型的寄存器中。
+: 表示操作数既是输入也是输出,并且可以被放置在任意类型的寄存器中。
组合标记
组合标记将输入和输出标记结合起来使用:
=r: 表示操作数是输出操作数,并且可以被放置在任意类型的寄存器中。
+r: 表示操作数既是输入也是输出,并且可以被放置在任意类型的寄存器中。
+m: 表示操作数既是输入也是输出,并且位于内存中。