是否可以从 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 指令开始或结束,通常你做错了。让编译器为你分配寄存器。请参阅 inline-assembly 标签 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_aesenc128
被 AESENC 指令替换。
考虑以下汇编代码循环:
#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 指令开始或结束,通常你做错了。让编译器为你分配寄存器。请参阅 inline-assembly 标签 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_aesenc128
被 AESENC 指令替换。