未启用优化标志时出现 gcc 内联汇编编译错误?

gcc inline-assembly compilation error when optimization flags is not enabled?

我学习内联汇编快一个月了,作为另一个练习题,我尝试使用内联汇编将两个uint512加起来,一开始我得到了我想要的,代码可以编译,我可以用下面的代码得到正确的结果。虽然直到现在我只是用优化标志编译我的程序; -O2 和 -O3。

但是,当我尝试删除编译标志中的 -O1、-O2 或 -O3 时,它会产生编译错误 error: 'asm' operand has impossible constraints这可能是什么原因?

我假设是因为优化被禁用,程序实际上正在尝试使用比我的 CPU 拥有的更多的寄存器?。我仍然只使用 8 个寄存器(或者可能不再使用?),我的输入是否存储在另外 8 个寄存器中?

// uint512 += uint512
void uint512_add(unsigned long *sum, unsigned long *add) {
    asm volatile(
        "add %[adn0], %[sum0]\n\t"
        "adc %[adn1], %[sum1]\n\t"
        "adc %[adn2], %[sum2]\n\t"
        "adc %[adn3], %[sum3]\n\t"
        "adc %[adn4], %[sum4]\n\t"
        "adc %[adn5], %[sum5]\n\t"
        "adc %[adn6], %[sum6]\n\t"
        "adc %[adn7], %[sum7]"
        :
        [sum0]"+r"(sum[0]), [sum1]"+r"(sum[1]),
        [sum2]"+r"(sum[2]), [sum3]"+r"(sum[3]),
        [sum4]"+r"(sum[4]), [sum5]"+r"(sum[5]),
        [sum6]"+r"(sum[6]), [sum7]"+r"(sum[7])
        :
        [adn0]"m"(add[0]), [adn1]"m"(add[1]),
        [adn2]"m"(add[2]), [adn3]"m"(add[3]),
        [adn4]"m"(add[4]), [adn5]"m"(add[5]),
        [adn6]"m"(add[6]), [adn7]"m"(add[7])
        : "memory", "cc"
    );
}

如果你删除了一些操作数,比如一个 256 位的加法,你会注意到 在禁用优化的情况下,GCC 想要将一个指针直接指向每个内存操作数在一个单独的register,而不是为它们中的每一个发明相对于同一基的寻址模式。所以它用完了寄存器。 (请参阅 Strange 'asm' operand has impossible constraints error 的中间部分以获取对此进行演示的编译器输出。)

你可能想要 __attribute__((optimize("-O3"))) 这个函数或其他东西,这样它就不会阻止你的程序的其余部分编译。


此外,这不需要“内存”破坏;你不写任何内存,你只通过 "m" 操作数读取。它也不需要是易变的:除了编写 "+r" in/output regs 之外它没有副作用。除了 sum7,从技术上讲,这些应该是 "+&r" early-clobber 操作数,因为您在读取所有输入和 in-out 操作数之前编写它们,但是 GCC 基本上没有合理的方法在此处重叠指针和整数 in/out 之间的寄存器。

您还可以让编译器选择 "mre" 而不是强制内存源操作数,即使源操作数是 compile-time 常量或在寄存器中也是如此。但是,如果这会使它为您的实际 use-case 生成更糟糕的 asm(例如,将 mov 单独加载到 regs 而不是 adc 的内存源),那么可能只是 "me"。 ("e" constraint 类似于 "i",但只允许适合 32 位有符号整数的常量,即可以安全地用作 64 位 operand-size 的立即数,用于除 mov.)


顺便说一句,使用 clang(但不是 GCC)你根本不需要内联 asm:使用 typedef unsigned _ExtInt(512) uint512; - 参见