__asm{};会返回eax的值吗?

9

简单的问题。在C语言中,函数asm用于在代码中进行内联汇编,但它返回什么?它是否返回传统的eax,如果不是,它返回什么?


3
这取决于编译器,但据我所知,__asm__()不是一个函数且没有返回值,因此int i = __asm__()只是一个无效的语句。 - myaut
3
哪个编译器?哪个处理器?大多数处理器没有eax寄存器。 - atturri
1
真的。好的,假设它是Visual Studio编译器(不确定是哪一个)和32位英特尔处理器。 - Delupara
1
无论使用哪种处理器,__asm__(); 在 Visual Studio 中都无法正常工作。这是 GCC 语法,在 ICC 和 Clang 上也可以使用,但在 MSVC 上不行。 - Dietrich Epp
1
有一些CPU甚至没有eax寄存器(是的,真的!)。因此,这不是C语言固有的特性。 - tofro
2个回答

10

__asm__本身不返回值。C标准没有定义__asm__如何处理返回值,因此行为可能在编译器之间不同。您说Visual Studio的示例是有效的,但Visual Studio使用__asm。至少GCC使用__asm__

Visual Studio

要在C程序中获得结果,您可以将返回值放置在汇编代码中的eax中,然后从函数返回。调用者将接收eax的内容作为返回值。即使启用了优化,并且编译器决定内联包含__asm{}块的函数,也支持这一点。

这可以避免从mov将值移动到C变量并返回该C变量时,您将从中获取的存储/重新加载,因为MSVC内联asm语法不支持寄存器中的输入/输出(除了这种返回值情况)。

Visual Studio 2015文档

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支持相同的内联汇编语法,但不支持从非void函数末尾掉落并返回asm{}块在EAX/RAX中留下的值。如果将MSVC内联汇编移植到clang,请注意这一点。当启用优化(函数内联)时,它会严重破坏。

GCC

GCC内联汇编HOWTO没有类似的示例。您不能像Visual Studio那样使用隐式返回,但幸运的是,GNU C内联汇编语法允许在寄存器中指定输出。不需要任何技巧来避免存储/重新加载输出值。

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 实际上不是内联汇编语句的输出结果。虚假输出操作数的匹配约束告诉编译器,内联汇编破坏了该变量的副本,因此它需要自己在汇编语句中保留它。)
如果在启用优化的非 void 函数中省略了 return 语句,则会收到警告,例如 warning: no return statement in function returning non-void [-Wreturn-type],最近的 GCC/clang 甚至不会发出 ret;它假定这个执行路径永远不会被执行(因为那将是 UB)。无论函数是否包含 asm 语句都无关紧要。

现在我才意识到为什么在C/C++中缺少'return'并不总是错误...当然,如果你的第一个例子是正确的;)(我要去检查一下) - Gelldur
1
请注意,clang 对于 MSVC 风格的 __asm { } 块的 -fasm-blocks 支持 不支持 在非 void 函数结尾处掉落,以返回 asm 在 EAX/RAX 中留下的值。只有 MSVC 自身支持(包括内联后)。 - Peter Cordes

3

根据C99规范下的J3实现定义行为,这是不太可能的:

可以使用 asm 关键字将汇编语言直接插入翻译器输出中(6.8)。最常见的实现方式是通过以下形式的语句:

asm ( 字符串文字 );

因此,不太可能有一个实现者会想出一种方法,既将汇编语言插入翻译器输出,又生成一些额外的中间链接代码来将某个寄存器作为返回结果。

它是一个关键字,而不是一个函数。

例如,GCC使用 "=r" 类型的约束语义,允许您在汇编语言中具有对变量的写访问权限。但您需要确保结果最终落在正确的位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接