__asm__ 本身不返回值。 C 标准没有定义__asm__ 应该如何处理返回值,因此编译器之间的行为可能会有所不同。您说 Visual Studio 示例是有效的,但 Visual Studio 使用 __asm。 __asm__ 至少被 GCC 使用。
Visual Studio
要在 C 程序中获取结果,您可以将返回值放入汇编代码中的eax,然后从函数中返回。调用者将收到eax 的内容作为返回值。即使启用了优化也支持这一点,即使编译器决定内联包含__asm{} 块的函数。
它避免了存储/重新加载,否则您会从 moving 值到 asm 中的 C 变量并返回该 C 变量,因为 MSVC 内联 asm 语法不支持寄存器中的输入/输出(除了对于这种返回值情况)。
Visual Studio 2015 documentation:
int power2( int num, int power )
{
__asm
{
mov eax, num ; Get first argument
mov ecx, power ; Get second argument
shl eax, cl ; EAX = EAX * ( 2 to the power of CL )
}
// Return with result in EAX
// by falling off the end of a non-void function
}
clang -fasm-blocks 支持相同的 inline-asm 语法,但 不 支持从非void 函数的末尾脱落,因为它返回 asm{} 块留在 EAX 中的值/RAX。如果将 MSVC 内联 asm 移植到 clang,请注意这一点。在启用优化(函数内联)的情况下编译时会严重崩溃。
海合会
GCC inline assembly HOWTO 不包含类似的示例。您不能像在 Visual Studio 中那样使用隐式返回,但幸运的是,您不需要这样做,因为 GNU C 内联 asm 语法允许在寄存器中指定输出。无需 hack 即可避免存储/重新加载输出值。
HOWTO 显示您可以将结果存储到汇编块内的 C 变量中,并在汇编块结束后返回该变量的值。你甚至可以使用 "=r"(var) 让编译器选择它的寄存器,以防 EAX 在内联后不是最方便的。
一个(低效)字符串复制函数的例子,返回值为dest:
static inline char * strcpy(char * dest,const char *src)
{
int d0, d1, d2;
__asm__ __volatile__( "1:\tlodsb\n\t"
"stosb\n\t"
"testb %%al,%%al\n\t"
"jne 1b"
: "=&S" (d0), "=&D" (d1), "=&a" (d2)
: "0" (src),"1" (dest)
: "memory");
return dest;
}
(请注意,dest 实际上并不是内联 asm 语句的输出。虚拟输出操作数的匹配约束告诉编译器内联 asm 破坏了该变量的副本,因此它需要在整个 asm 中保留它以某种方式独立声明。)
如果您在启用优化的非void 函数中省略return 语句,则会收到类似warning: no return statement in function returning non-void [-Wreturn-type] 的警告,并且最近的GCC/clang 甚至不会发出ret;它假定永远不会采用这种执行路径(因为那将是 UB)。函数是否包含asm 语句并不重要。