是否可以从 C++ 中的程序集调用内置函数

Is it possible to call a built in function from assembly in C++

考虑以下汇编代码循环:

#include <iostream>

#define ADD_LOOP(i, n, v)       \
asm volatile (                  \
    "movw %1, %%cx      ;"      \
    "movq %2, %%rax     ;"      \
    "movq [=10=], %%rbx     ;"      \
    "for:               ;"      \
    "addq %%rax, %%rbx  ;"      \
    "decw %%cx          ;"      \
    "jnz for            ;"      \
    "movq %%rbx, %0     ;"      \
    : "=x"(v)                   \
    : "n"(i), "x"(n)            \
    : "%cx", "%rax", "%rbx"     \
);

int main() {
    uint16_t iter(10000);
    uint64_t num(5);
    uint64_t val;

    ADD_LOOP(iter, num, val)

    std::cout << val << std::endl;

    return 0;
}

是否可以从上面指定的循环中调用 C 函数(或其机器代码输出)?

例如:

#include <wmmintrin.h>

int main() {
    __m128i x, y;

    for(int i = 0; i < 10; i++) {
        x = __builtin_ia32_aesenc128(x, y);
    }

    return 0;
}

谢谢

没有。内置函数不是您可以使用 call 调用的真实函数。在 C / C++ 中使用时,它们总是内联。

例如,如果您希望 int __builtin_popcount (unsigned int x) 为带有 -mpopcnt 的目标获取 popcnt 指令,或者为不带 table 的目标获取按字节查找 table ' 支持 popcnt 指令,你倒霉了。您必须自己 #ifdef 并使用 popcnt 或其他指令序列。


你说的这个函数,__builtin_ia32_aesenc128只是the aesenc assembly instruction的包装,如果用asm写就可以直接使用。


如果您正在编写 asm 而不是使用 C++ 内在函数(如 #include <immintrin.h>)来提高性能,则需要查看 http://agner.org/optimize/ 以编写更高效的 asm。例如使用 %ecx 作为循环计数器,而不是 %cx。使用 16 位部分寄存器对您没有任何好处。

您还可以编写更高效的内联汇编约束,例如movq %%rbx, %0 是浪费一条指令。您可以一直使用 %0 而不是显式的 %rbx。如果你的内联 asm 以复制 to/from 一个 output/input 操作数的 mov 指令开始或结束,通常你做错了。让编译器为你分配寄存器。请参阅 标签 wiki。

或者更好,https://gcc.gnu.org/wiki/DontUseInlineAsm. Code with intrinsics typically compiles well for x86. See Intel's intrinsics guide#include <immintrin.h> 并使用 __m128i _mm_aesenc_si128 (__m128i a, __m128i RoundKey)。 (在 gcc 中,这只是 __builtin_ia32_aesenc128 的包装器,但它使您的代码 portable 到其他 x86 编译器。)

您的问题的答案可能分为两部分。

从汇编中调用 C 函数绝对是可能的。为此,您需要遵循调用约定(在 ABI 文档中进行了描述),该约定指定了如何传递参数和获取 return 值。请记住,您有寄存器、堆栈和内存来移动数据。

尽管内部函数看起来像 C 函数,但它们实际上并不是函数。您可能会将 C 语言视为一种适用于各种体系结构的高级程序集。在某些情况下,您想利用特定的体系结构指令集,因此编译器为您提供了通过内在函数的方式来实现这一点的方法。每个内在函数都映射到一些特定于体系结构的汇编指令。所以最终你不需要从汇编中调用它们,而是需要找到指令本身,例如我希望 __builtin_ia32_aesenc128AESENC 指令替换。