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

时间:2017-12-21 14:36:09

标签: c++ gcc assembly inline-assembly built-in

考虑以下汇编代码循环:

#include <iostream>

#define ADD_LOOP(i, n, v)       \
asm volatile (                  \
    "movw %1, %%cx      ;"      \
    "movq %2, %%rax     ;"      \
    "movq $0, %%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;
}

由于

2 个答案:

答案 0 :(得分:10)

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

例如,如果您希望int __builtin_popcount (unsigned int x)popcnt的目标获取-mpopcnt指令,或者为不支持{popcnt的目标获取逐字节查找表1}}指示,你运气不好。您必须自己#ifdef并使用popcnt或其他指令序列。

您正在谈论的功能,__builtin_ia32_aesenc128只是the aesenc assembly instruction的包装,如果以asm身份书写,您可以直接使用它。

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

您还可以编写更有效的内联asm约束,例如: movq %%rbx, %0是浪费指令。您可以一直使用%0而不是明确%rbx。如果你的内联asm以一个mov指令开始或结束来复制到输出/输入操作数,那么通常你做错了。让编译器为您分配寄存器。请参阅代码wiki。

或者更好,https://gcc.gnu.org/wiki/DontUseInlineAsm。具有内在函数的代码通常可以很好地编译x86。请参阅Intel's intrinsics guide#include <immintrin.h>并使用__m128i _mm_aesenc_si128 (__m128i a, __m128i RoundKey)。 (在gcc中,它只是__builtin_ia32_aesenc128的包装器,但它使您的代码可以移植到其他x86编译器。)

答案 1 :(得分:3)

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

从Assembly中调用C函数是不可能的。为此,您需要遵循调用约定(在ABI文档中描述),该约定指定如何传递参数并获取返回值。请记住,您有寄存器,堆栈和内存来移动数据。

然而,内在函数虽然看起来像C函数不是函数。您可以将C看作一个有点高级别的程序集,适用于各种各样的体系结构。在某些情况下,您希望利用特定的体系结构指令集,因此编译器通过内在函数为您提供了这样做的方法。每个内在映射到一些体系结构特定的汇编指令。因此,在一天结束时,您不需要从汇编中调用它们,而是需要查找指令本身,例如,我希望__builtin_ia32_aesenc128替换为AESENC指令。