内联汇编中匹配约束有什么用
What is the use of matching constraints in inline assembly
来自以下link,
https://www.ibm.com/developerworks/library/l-ia/index.html
a single variable may serve as both the input and the output operand.
我写了下面的代码:
#include <stdio.h>
int main()
{
int num = 1;
asm volatile ("incl %0"
:"=a"(num)
:"0"(num));
printf("num:%d\n", num);
return 0;
}
以上代码递增 num 的值。
匹配约束有什么用,如果我不使用匹配约束,代码不会按预期运行。
asm volatile ("incl %0"
:"=a"(num));
此代码:
asm volatile ("incl %0"
:"=a"(num));
不起作用,因为为了增加寄存器中的值(在本例中为 1),需要从寄存器中读取原始值; 1 添加到它;并将值写回寄存器。 =a
只是说寄存器 EAX 的输出在完成后将被移动到 num
,但编译器不会将 num
的原始值加载到寄存器 EAX。上面的代码只会将 1 添加到 EAX 中的任何内容(可以是任何内容)并在内联汇编完成时将其放入 num
。
asm volatile ("incl %0"
:"=a"(num)
:"0"(num));
另一方面,这表示 num
既被用作输入(因此 num
的值被移动到 EAX)并且它还在 EAX 中输出一个值,因此编译器当内联汇编完成时,会将 EAX 中的值移动到 num
。
它也可以被重写为使用 input/output 约束(这做同样的事情):
asm volatile ("incl %0"
:"+a"(num));
这里也不需要 volatile
,因为所有的副作用都包含在约束中。不必要地添加 volatile
会导致代码生成效率降低,但代码仍然有效。我会这样写:
asm ("incl %0"
:"+a"(num));
why and when should we use matching constraints
这不是你问的问题;你问为什么你需要一个输入,当你知道语法的实际含义时,这应该是相当明显的。 ("=r"(var)
是一个纯输出,独立于 C 变量之前的任何值,就像 var = 123;
一样)。所以 "=r"
和 inc
指令就像 var = stale_garbage + 1;
但是无论如何,正如我评论的那样,有趣的问题是“当您可以将 "+r"(var)
用于 read/write 操作数而不是更多时,为什么存在匹配约束复杂的匹配约束语法?"
它们很少有用;通常你可以对输入和输出使用相同的变量,特别是如果你的 asm 位于 C 包装函数 中。但是,如果您不想对输入和输出使用相同的 C var,但仍需要它们选择相同的寄存器或内存,那么您需要一个匹配约束。一个用例可能是包装系统调用是一个用例;您可能希望对索书号使用不同的 C 变量而不是 return 值。 (除了你可以只使用 "=a"
和 "a"
而不是匹配约束;编译器没有选择。)或者输出变量可能比输入变量更窄或类型不同另一个用例。
IIRC,x87 是另一个用例;我好像记得 "+t"
不工作。
我认为 "+r"
RMW 约束在内部实现为具有 "hidden" 匹配约束的输出。但是,虽然 %1
通常在只有一个操作数的 asm 模板中出错,但如果该操作数是 in/out "+something"
那么 GCC 不会拒绝 %1
因为它太高了操作数。如果您查看 asm 以查看它实际为该越界操作数选择了哪个寄存器或内存,它确实与 in/out 操作数匹配。
所以"+r"
基本上是匹配约束的语法糖。我不确定它在某个时候是否是新的,在 GCC 版本 x.y 之前你必须使用匹配约束?对于输入和输出使用具有相同 var 的匹配约束的教程示例并不少见,这些示例使用 "+"
RMW 约束更易于阅读。
基础知识:
使用 "a"
和 "=a"
等约束,您 不需要 匹配约束;无论如何,编译器只有一种选择。它有用的地方是 "=r"
,编译器可以在其中选择任何寄存器,而您需要它为输入操作数选择 相同的 寄存器。
如果您只使用 "=r"
和一个单独的 "r"
输入,您会告诉编译器它可以将其用作复制和任何操作,而不会修改原始输入并在新的寄存器中产生输出。或者,如果需要,可以覆盖输入。这适合 lea 1(%[srcreg]), %[dstreg]
但不适合 inc %0
。后者会假定 %0 和 %1 是同一个寄存器,因此您需要做一些事情来确保这是真的!
来自以下link, https://www.ibm.com/developerworks/library/l-ia/index.html
a single variable may serve as both the input and the output operand.
我写了下面的代码:
#include <stdio.h>
int main()
{
int num = 1;
asm volatile ("incl %0"
:"=a"(num)
:"0"(num));
printf("num:%d\n", num);
return 0;
}
以上代码递增 num 的值。
匹配约束有什么用,如果我不使用匹配约束,代码不会按预期运行。
asm volatile ("incl %0"
:"=a"(num));
此代码:
asm volatile ("incl %0"
:"=a"(num));
不起作用,因为为了增加寄存器中的值(在本例中为 1),需要从寄存器中读取原始值; 1 添加到它;并将值写回寄存器。 =a
只是说寄存器 EAX 的输出在完成后将被移动到 num
,但编译器不会将 num
的原始值加载到寄存器 EAX。上面的代码只会将 1 添加到 EAX 中的任何内容(可以是任何内容)并在内联汇编完成时将其放入 num
。
asm volatile ("incl %0"
:"=a"(num)
:"0"(num));
另一方面,这表示 num
既被用作输入(因此 num
的值被移动到 EAX)并且它还在 EAX 中输出一个值,因此编译器当内联汇编完成时,会将 EAX 中的值移动到 num
。
它也可以被重写为使用 input/output 约束(这做同样的事情):
asm volatile ("incl %0"
:"+a"(num));
这里也不需要 volatile
,因为所有的副作用都包含在约束中。不必要地添加 volatile
会导致代码生成效率降低,但代码仍然有效。我会这样写:
asm ("incl %0"
:"+a"(num));
why and when should we use matching constraints
这不是你问的问题;你问为什么你需要一个输入,当你知道语法的实际含义时,这应该是相当明显的。 ("=r"(var)
是一个纯输出,独立于 C 变量之前的任何值,就像 var = 123;
一样)。所以 "=r"
和 inc
指令就像 var = stale_garbage + 1;
但是无论如何,正如我评论的那样,有趣的问题是“当您可以将 "+r"(var)
用于 read/write 操作数而不是更多时,为什么存在匹配约束复杂的匹配约束语法?"
它们很少有用;通常你可以对输入和输出使用相同的变量,特别是如果你的 asm 位于 C 包装函数 中。但是,如果您不想对输入和输出使用相同的 C var,但仍需要它们选择相同的寄存器或内存,那么您需要一个匹配约束。一个用例可能是包装系统调用是一个用例;您可能希望对索书号使用不同的 C 变量而不是 return 值。 (除了你可以只使用 "=a"
和 "a"
而不是匹配约束;编译器没有选择。)或者输出变量可能比输入变量更窄或类型不同另一个用例。
IIRC,x87 是另一个用例;我好像记得 "+t"
不工作。
我认为 "+r"
RMW 约束在内部实现为具有 "hidden" 匹配约束的输出。但是,虽然 %1
通常在只有一个操作数的 asm 模板中出错,但如果该操作数是 in/out "+something"
那么 GCC 不会拒绝 %1
因为它太高了操作数。如果您查看 asm 以查看它实际为该越界操作数选择了哪个寄存器或内存,它确实与 in/out 操作数匹配。
所以"+r"
基本上是匹配约束的语法糖。我不确定它在某个时候是否是新的,在 GCC 版本 x.y 之前你必须使用匹配约束?对于输入和输出使用具有相同 var 的匹配约束的教程示例并不少见,这些示例使用 "+"
RMW 约束更易于阅读。
基础知识:
使用 "a"
和 "=a"
等约束,您 不需要 匹配约束;无论如何,编译器只有一种选择。它有用的地方是 "=r"
,编译器可以在其中选择任何寄存器,而您需要它为输入操作数选择 相同的 寄存器。
如果您只使用 "=r"
和一个单独的 "r"
输入,您会告诉编译器它可以将其用作复制和任何操作,而不会修改原始输入并在新的寄存器中产生输出。或者,如果需要,可以覆盖输入。这适合 lea 1(%[srcreg]), %[dstreg]
但不适合 inc %0
。后者会假定 %0 和 %1 是同一个寄存器,因此您需要做一些事情来确保这是真的!