【问题标题】:Why won't simple inline assembly function work correctly in GCC?为什么简单的内联汇编函数不能在 GCC 中正常工作?
【发布时间】:2011-10-28 23:40:15
【问题描述】:

我有一个简单的内联汇编函数,它在 MSVC 中运行良好,但由于某种原因拒绝在 Apple GCC 4.2.1 下运行(i386 架构,强制 32位模式)。幸运的是,更复杂的汇编函数可以正常工作,但是我无法理解为什么这个不能工作......不幸的是,我无法调试它 - 从外观上看,XCode 中没有寄存器窗口4.0.2(在 3.2 版本中)。

我很肯定这个问题与英特尔风格的组件无关

int Convert(double value)
{
     _asm
     {
         fld value
         push eax
         fistp dword ptr [esp]
         pop eax
     }

// The returned value is insane
}

【问题讨论】:

  • 你为什么要在汇编中这样做?浮动 a = 3.14; int b = *(int *)&a;做同样的事情。此外,您传入一个双精度,但将其转换为 32 位。
  • @dascandy:那行代码是未定义的行为。
  • 这很重要。是否使用内联汇编定义的行为?
  • 开个大玩笑,达斯坎迪!
  • @dascandy:是的,GCC 和 clang 定义了内联 asm 的行为(只要约束实际上向优化器描述了 asm 模板的作用)。或者对于asm{} 块,由编译器来推断哪些寄存器被踩到以及哪些C++ 对象可以被读取或修改。但无论如何,你是对的,为此使用内联汇编是可怕的,而且效率非常低;使用 memcpy 将内联。 (不是指针转换技巧;g++ -fstrict-aliasing 是启用优化的默认设置。)

标签: c++ xcode gcc inline-assembly


【解决方案1】:

幸运的是,更复杂的汇编函数可以正常工作[...]

这些也是内联汇编函数吗?因为 GCC 对内联汇编器使用 completely different 语法。不过,您可以使语法看起来更熟悉,请参阅wikipedia

1     int Convert(double value) 
2     {   
3         int result; 
4         __asm__ __volatile__ ( 
5             "fist %0\n\t" 
6             : "=m" (result) 
7             : "t" (value) 
8             );          
9         return result; 
10     }   

我会怎么做。 =m 指定我们想要一个内存操作数来存储结果(我们不想要一个寄存器,因为fist 不适用于这些)。 t 指定在栈顶传递值,这也确保了我们的正确清理。

编辑:

假设 gcc 和 xcode 允许与 msvc 相同类型的内联汇编器,另一件要尝试的事情是:

 int Convert(double value)
 {
      int result;
      _asm
      {
          fld value
          push eax
          fistp dword ptr [esp]
          pop eax
          mov [result], eax
      }
      return result;
 } 

这也应该关闭您可能收到的有关缺少返回值的警告。可能只是允许您通过编写 eax 从汇编程序块返回值比 msvc 更严格。

【讨论】:

  • 旁注:GCC 有一个开关来允许不同的汇编语言,不是吗?它允许 AT&T 和 Intel 语法。
  • @Kerrek SB:你在考虑gcc -masm=intel -S吗?那(至少在我的机器上)只是在文件顶部放置一个.intel_syntax noprefix(如我帖子中的维基百科链接所述)。
  • 是的,就是那个。我只是认为了解您何时从其他代码库移植并且不想重写汇编程序部分可能很有用。
  • 是的,AT&T 起初非常令人困惑 :)(关于它的旧 SO question
  • 是的,这里的问题是 MSVC 支持从包含 asm 块的非 void 函数的末尾脱落,并使用 EAX 作为返回值,即使在内联包装函数时也是如此. Clang 的 -fasm-blocks 支持不支持。您必须将 EAX 存储到命名变量并返回它,从而引入另一个存储/重新加载,因为 MSVC 样式的 asm-blocks 语法非常低效。当然,实际上你应该只使用 memcpy(或 C++20 std::bit_cast),它将完全内联到任何必要的 asm。
【解决方案2】:

你的代码在 Apple gcc 4.2.1 上对我来说很好:

#include <stdio.h>

static int Convert(double value)
{
     _asm
     {
         fld value
         push eax
         fistp dword ptr [esp]
         pop eax
     }
}

int main(void)
{
    int i = Convert(42.0);

    printf("i = %d\n", i);

    return 0;
}


$ gcc -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5666.3~123/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5666) (dot 3)
$ gcc -Wall -m32 -O3 -fasm-blocks convert.c -o convert
convert.c: In function ‘Convert’:
convert.c:14: warning: no return statement in function returning non-void
$ ./convert
i = 42

我的猜测是您无意中为 64 位编译。查看 Xcode 中的构建记录,确保在编译此代码时可以看到 -m32 - 很容易在项目中的某处覆盖设置。您还可以尝试从命令行构建和运行我上面的代码示例,以确保它适用于您的工具链。

【讨论】:

【解决方案3】:

GCC 的语法要求您指定要在汇编编码部分中使用的变量。发布的代码并没有这样做,实际上可能是在随机内存而不是您期望的变量上运行。

此外,GCC 有一些使用调用约定的能力,而不是标准的“传递堆栈上的所有内容”(例如,fastcall 和尾递归调用)。发布的汇编代码假定所有内容都在堆栈上传递。您的汇编代码所期望的和 GCC 正在做的可能不匹配。

user786653 的答案中的汇编代码避免了这些问题。

【讨论】:

    【解决方案4】:

    它在 Xcode 4.0.2 中对我来说很好用(带有关于控制到达非 void 函数末尾的警告)。我在 3.2.6 中创建了这个项目,当我第一次将它加载到 Xcode 4.0.2 中时它不会编译。在将体系结构设置为 32 位、将有效体系结构设置为 i386 并将编译器设置为 GCC 4.2 之后,我终于可以编译它了。将编译器设置为 LLVM GCC 4.2,它会运行但函数返回 0。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-20
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-06
      相关资源
      最近更新 更多