【问题标题】:Is it possible to call a built in function from assembly in C++是否可以从 C++ 中的程序集调用内置函数
【发布时间】:2018-06-04 06:15:14
【问题描述】:

考虑以下汇编代码循环:

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

谢谢

【问题讨论】:

  • 当然可以。都是二进制指令代码,所以可以调用,(权限/权限等允许)。
  • 请详细说明您想要实现的目标。你想从汇编中调用 C 函数,还是想在汇编中调用和内在函数?要调用函数,您只需遵循所选平台的 C ABI。另一方面,内在函数不是函数,而是使编译器生成一些特定于平台的指令(如内存屏障、原子指令、各种向量扩展等)的方式,因此它们不是被调用而是被程序集替换本身。
  • 您正在使用 C++。也许它是非常 C 风格的 C++,但它仍然是 C++。
  • 我理解你的意思,但你仍然需要一个 c++ 编译器,它强制 c++ 并且有些东西是不同的。就像 c 具有来自 void 指针的隐式转换。
  • 这个编辑把它变成了一个完全不同的问题。现在是关于调试 GNU C 内联汇编约束,而不是关于如何使用内联汇编中的__builtin 函数。这应该是一个新问题。

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


【解决方案1】:

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

例如,如果您希望 int __builtin_popcount (unsigned int x) 为具有 -mpopcnt 的目标获取 popcnt 指令,或者为不支持 popcnt 指令的目标获取逐字节查找表,那么您就出局了运气。您必须自己 #ifdef 并使用 popcnt 或其他指令序列。


您所说的函数__builtin_ia32_aesenc128 只是the aesenc assembly instruction 的包装器,如果用 asm 编写,您可以直接使用它。


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

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

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

【讨论】:

  • 谢谢。我会阅读您发送的有关优化的链接。我不知道 %0,我会看看它,但我认为这对我来说不是问题,因为循环中有数百万条指令。你可能是对的,内在函数编译得很好,但我不介意编写程序集并执行与 C 相比的执行时间的实际测量。它可能不会更快,但我想看看测量比较。
  • @AbdulAhad:通常带有内在函数的 C++ 更易于维护。除非您是 asm 性能专家,否则您可能应该这样做。或者,如果您只是为了好玩而尝试使用 asm,并且想看看您是否可以击败编译器,那么请查看 Why is this C++ code faster than my hand-written assembly for testing the Collatz conjecture?。 (提示:从优化的编译器输出开始并改进。)
  • 请注意,此答案是在编辑之前编写的,该编辑将问题更改为关于获取约束和其他内容的不同问题,以便 OP 的内联 asm 将编译和组装。
【解决方案2】:

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

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

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

【讨论】:

猜你喜欢
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-04
  • 1970-01-01
  • 2010-10-28
相关资源
最近更新 更多