【问题标题】:What is the use of matching constraints in inline assembly内联汇编中匹配约束有什么用
【发布时间】:2020-01-09 01:01:49
【问题描述】:

从以下链接, https://www.ibm.com/developerworks/library/l-ia/index.html

一个变量可以同时作为输入和输出操作数。

我写了以下代码:

#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));

【问题讨论】:

  • 基于标题,我认为问题将是“当您可以将"+r"(var) 用于读/写操作数而不是更复杂的匹配约束时,为什么存在匹配约束句法?”。那会很有趣。 IDK 为什么您会期望仅输出的操作数对 inc 有用。
  • 除了迈克尔在他的回答中所说的之外,还有 c = a + b aka asm ("add %2,%0" :"=a"(numC) :"0"(numA), "r" (numB)); 的想法。这会将输入的 numA 放入 eax,但指定输出的 eax 将是 numC。
  • @PeterCordes,这就是问题所在,我们为什么以及何时应该使用匹配约束

标签: c gcc assembly inline-assembly


【解决方案1】:

我们为什么以及何时应该使用匹配约束

这不是你问的问题;您问为什么需要输入,当您知道语法的实际含义时,这应该是相当明显的。 ("=r"(var) 是一个纯输出,独立于 C 变量之前的任何值,就像var = 123; 一样)。所以"=r" 带有inc 指令就像var = stale_garbage + 1;


但无论如何,正如我评论的那样,有趣的问题是“当您可以将"+r"(var) 用于读/写操作数而不是更复杂的匹配约束语法时,为什么存在匹配约束?强>”

它们很少有用;通常您可以将相同的变量用于输入和输出,尤其是当您的 asm 包含在 C 包装函数中时。但是,如果您不想对输入和输出使用相同的 C var,但仍需要它们选择相同的寄存器或内存,那么您需要一个匹配的约束。一个用例可能是包装系统调用是一个用例;您可能希望对索书号与返回值使用不同的 C 变量。 (除非您可以只使用 "=a""a" 而不是匹配约束;编译器别无选择。)或者比输入 var 更窄或不同类型的输出 var 可能是另一个用例.

IIRC,x87 是另一个用例;我似乎记得"+t" 不工作。

我认为"+r" RMW 约束在内部实现为具有“隐藏”匹配约束的输出。但是,虽然%1 通常在只有一个操作数的 asm 模板中出错,但如果该操作数是输入/输出 "+something",那么 GCC 不会拒绝 %1 操作数数字太高。如果您查看 asm 以了解它实际为该越界操作数编号选择了哪个寄存器或内存,它确实与输入/输出操作数匹配。

所以"+r" 基本上是匹配约束的语法糖。我不确定它是否在某个时候是新的,并且在 GCC 版本 x.y 之前您必须使用匹配约束?经常看到教程示例使用具有相同 var 的匹配约束作为输入和输出,使用 "+" RMW 约束更易于阅读。


基础知识:

使用 "a""=a" 这样的约束,您不需要匹配约束;无论如何,编译器只有 1 个选择。有用的地方是"=r",编译器可以在其中选择任何寄存器,您需要它为输入操作数选择 same 寄存器。

如果您只使用 "=r" 和单独的 "r" 输入,您会告诉编译器它可以将其用作复制和任何操作,而原始输入保持不变并在新注册。或者根据需要覆盖输入。这适用于lea 1(%[srcreg]), %[dstreg],但不适用于inc %0。后者会假设 %0 和 %1 是同一个寄存器,因此您需要做一些事情来确保这是真的!

【讨论】:

    【解决方案2】:

    这段代码:

    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

    它也可以被重写为使用输入/输出约束(这做同样的事情):

    asm volatile ("incl %0"
            :"+a"(num));
    

    这里也不需要volatile,因为所有副作用都在约束中捕获。不必要地添加volatile 会导致代码生成效率降低,但代码仍然可以工作。我会这样写的:

    asm ("incl %0"
         :"+a"(num));
    

    【讨论】:

    • 输出操作数的约束中不是=吗?
    • @md.jamal :输出约束必须是某种形式的output= 仅用于输出,+ 用于输出和输入。仅输出= 和输入和输出+ 约束都在输出部分中指定。输入部分中只有仅输入约束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多