有没有办法在linux平台上编译microsoft风格的内联汇编代码?
正如标题中提到的,我想知道有没有办法在linux操作系统(如ubuntu)中编译微软风格的内联汇编代码(如下所示)有没有办法在linux平台上编译microsoft风格的内联汇编代码?,linux,gcc,visual-c++,inline-assembly,Linux,Gcc,Visual C++,Inline Assembly,正如标题中提到的,我想知道有没有办法在linux操作系统(如ubuntu)中编译微软风格的内联汇编代码(如下所示) 示例代码是内联程序集代码的一部分,可以使用cl.exe编译器在win10上成功编译该内联程序集代码。有没有办法在linux上编译它?我是否必须用GNU c/c++风格(即uu asm uu{;;})重写它?首先,通常应该替换内联asm(使用内部函数或纯c),而不是移植它 clang-fasm块主要与MSVC低效的内联asm语法兼容。但它不支持通过将值留在EAX中,然后从非void
示例代码是内联程序集代码的一部分,可以使用cl.exe编译器在win10上成功编译该内联程序集代码。有没有办法在linux上编译它?我是否必须用GNU c/c++风格(即uu asm uu{;;})重写它?首先,通常应该替换内联asm(使用内部函数或纯c),而不是移植它
clang-fasm块
主要与MSVC低效的内联asm语法兼容。但它不支持通过将值留在EAX中,然后从非void函数的末尾掉下来来返回值
因此,您必须编写内联asm,将该值放入命名的C变量中,然后返回
,这通常会导致额外的存储/重新加载,从而使MSVC语法更加糟糕。(非常糟糕,除非您在asm中编写一个完整的循环,以分摊将数据输入/输出asm块的存储/重新加载开销)。有关包装单个指令时MSVC内联asm效率的比较,请参阅。当那些函数不内联时,使用堆栈参数的函数内部就不那么愚蠢了,但只有当您已经使事情变得低效时才会发生这种情况(例如,使用传统的32位调用约定,不使用链接时间优化来内联小函数)
MSVC在内联到调用方时可以用立即的1
替换A
,但clang不能。两者都可以阻止常量传播,但MSVC至少可以避免通过存储/重新加载反弹常量输入。(只要只与支持直接源操作数的指令一起使用。)
Clang接受\u asm
、asm
或\u asm\u
来引入asm块。MSVC接受\uu asm
(2个下划线,如clang)或\u asm
(更常用,但clang不接受)
因此,对于现有的MSVC代码,您可能需要#定义_asm\u asm
,这样您的代码就可以同时使用MSVC和clang进行编译,除非您需要创建单独的版本。或者使用clang-D_asm=asm
在命令行上设置CPP宏
示例:使用MSVC或clang-fasm块编译
(不要忘了启用优化:clang-fasm blocks-O3-march=native-flto-Wall
。如果您希望二进制文件可以在编译主机以外的早期/其他CPU上运行,请忽略或修改-march=native
)
使用编译表明,clang可以内联包含内联asm的包装器函数,以及需要多少存储/重新加载MSVC语法(与GNU C内联asm相比,GNU C内联asm可以在寄存器中获取输入和输出)
我在Intel syntax asm输出模式下使用了clang,但在AT&T语法模式下输出时,它也编译Intel syntax asm块。(通常,clang直接编译为机器代码,这也是正确的。)
注意编译器是如何在asm源代码块中用[rsp-4]
和[rsp-8]
替换C局部变量A
和out
的。静态存储器中的一个变量获得RIP相对寻址。GNU C内联asm不这样做,您需要声明%[name]
操作数并告诉编译器将它们放在哪里
我们甚至可以看到将该函数两次内联到一个调用者中,并将符号扩展优化为64位,因为该函数只返回int
int caller() {
return foo(1, 2, nullptr) + foo(1, 2, nullptr);
}
因此,我们可以看到,仅从内联asm中读取A
会造成错过的优化:编译器再次存储1
,即使asm只读取该输入而不修改它
在asm语句之前/之间/之后,我没有做过分配或读取a_global
之类的测试,以确保编译器“知道”asm语句修改了该变量
我还没有测试过将指针传递到asm块并在指向数组上循环,看它是否像GNU C内联asm中的“内存”
clobber。我想是的
我的Godbolt链接还包括一个从非void函数末尾脱落的示例,该函数的值为EAX。MSVC支持这一点,但当内联到调用方时,它像往常一样发出叮当声和中断声。(奇怪的是,即使在-Wall
,也没有任何警告)。您可以在上面我的Godbolt链接上看到x86msvc是如何编译它的
将MSVC asm移植到GNU C内联asm几乎肯定是错误的选择编译器对优化内部函数的支持非常好,因此您通常可以让编译器为您生成高质量高效的asm
如果您打算对现有的手写asm做些什么,通常用纯C替换它们将是最有效的,当然也是最经得起未来考验的前进之路。将来可以自动矢量化到更宽矢量的代码总是好的。但是,如果您确实需要手动矢量化以进行一些复杂的洗牌,那么intrinics是一种方法,除非编译器以某种方式将其弄得一团糟
查看从intrinsics获得的编译器生成的asm,以确保它与原始版本一样好或更好
如果您正在使用MMXEMMS
,现在可能是用SSE2内部函数替换MMX代码的好时机。SSE2是x86-64的基准,很少有Linux系统运行过时的32位内核
有没有办法在linux平台上编译microsoft风格的内联汇编代码
是的,这是可能的。有点
对于GCC,您必须同时使用Intel和AT&T语法。由于问题24232和问题39895,它不适用于Clang
这是模式。汇编程序模板使用.intel\u语法
。然后,在asm模板的末尾,切换回int a_global;
inline
long foo(int A, int B, int *arr) {
int out;
// You can't assume A will be in RDI: after inlining it prob. won't be
__asm {
mov ecx, A // comment syntax
add dword ptr [a_global], 1
mov out, ecx
}
return out;
}
## The x86-64 System V ABI passes args in rdi, rsi, rdx, ...
# clang -O3 -fasm-blocks -Wall
foo(int, int, int*):
mov dword ptr [rsp - 4], edi # compiler-generated store of register arg to the stack
mov ecx, dword ptr [rsp - 4] # start of inline asm
add dword ptr [rip + a_global], 1
mov dword ptr [rsp - 8], ecx # end of inline asm
movsxd rax, dword ptr [rsp - 8] # reload `out` with sign-extension to long (64-bit) : compiler-generated
ret
int caller() {
return foo(1, 2, nullptr) + foo(1, 2, nullptr);
}
caller(): # @caller()
mov dword ptr [rsp - 4], 1
mov ecx, dword ptr [rsp - 4] # first inline asm
add dword ptr [rip + a_global], 1
mov dword ptr [rsp - 8], ecx
mov eax, dword ptr [rsp - 8] # compiler-generated reload
mov dword ptr [rsp - 4], 1 # and store of A=1 again
mov ecx, dword ptr [rsp - 4] # second inline asm
add dword ptr [rip + a_global], 1
mov dword ptr [rsp - 8], ecx
add eax, dword ptr [rsp - 8] # compiler-generated reload
ret
#include <cstddef>
int main(int argc, char* argv[])
{
size_t ret = 1, N = 0;
asm __volatile__
(
".intel_syntax noprefix ;\n"
"xor esi, esi ;\n" // zero RSI
"neg %1 ;\n" // %1 is replaced with the operand location chosen by the compiler, in this case RCX
"inc %1 ;\n"
"push %1 ;\n" // UNSAFE: steps on the red-zone
"pop rax ;\n"
".att_syntax prefix ;\n"
: "=a" (ret) // output-only operand in RAX
"+c" (N) // read-write operand in RCX
: // no read-only inputs
: "%rsi" // RSI is clobbered: input and output register constraints can't pick it
);
return (int)ret;
}
mov edi, A;
__asm__ __volatile__
(
".intel_syntax noprefix ;\n"
"mov edi, %0 \n"; // inefficient: use a "D" constraint instead of a mov
...
".att_syntax prefix ;\n"
: : "r" (A) : "%edi"
);
uint32_t Mask = 1234;
uint32_t Index;
asm ("bsfl %1, %0"
: "=r" (Index)
: "r" (Mask)
: "cc");
uint32_t Mask = 1234;
uint32_t Index;
asm ("bsfl %[aMask], %[aIndex]"
: [aIndex] "=r" (Index)
: [aMask] "r" (Mask)
: "cc");
#define _asm\
asm(".intel_syntax noprefix\n");\
asm\
clang++ -c -fasm-blocks source.cpp