【问题标题】:Does __asm{}; return the value of eax?__asm{};返回eax的值?
【发布时间】:2016-08-16 14:34:51
【问题描述】:

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

【问题讨论】:

  • 它取决于编译器,但 AFAIK 它不是一个函数,它不返回任何东西,所以 int i = __asm__() 只是一个无效的构造
  • 哪个编译器?哪个处理器?大多数处理器没有 eax 寄存器。
  • 是的。好吧,假设它是 Visual Studio 编译器(不确定是哪一个)和 32 位英特尔处理器。
  • __asm__(); 在 Visual Sudio 中根本不起作用,无论是什么处理器。它是 GCC 语法,也适用于 ICC 和 Clang,但不适用于 MSVC。
  • 有些 CPU 甚至没有 eax 寄存器(是的,真的!)。所以这不是 C 语言固有的标准特性。

标签: c visual-c++ x86 inline-assembly


【解决方案1】:

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

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

asm ( character-string-literal );

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

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

例如GCC 使用"=r"-type 约束语义来允许您在程序集中对变量进行写访问。但您要确保结果出现在正确的位置。

【讨论】:

    【解决方案2】:

    __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 语句并不重要。

    【讨论】:

    • 现在我才意识到为什么在 C/C++ 中缺少“return”并不总是错误...如果您的 1 个示例正确,Ofc ;))(去检查)
    • 请注意,clang 对 MSVC 样式 __asm { } 块的 -fasm-blocks 支持确实支持从非void 函数的末尾脱落,因为它返回的值是在 EAX/RAX 中留下了 asm。只有 MSVC 本身支持(包括在内联之后)。
    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 2010-09-13
    • 1970-01-01
    • 2022-01-15
    相关资源
    最近更新 更多