c - GCC 内联汇编 - 在调用之前将 float 移动到 XMM0

标签 c gcc assembly sse

我目前正在尝试从 GCC 内联汇编调用通用 C 函数(我知道这是个坏主意,但我今天很无聊...)。

我的操作系统是 Mac OS X,64 位,所以调用约定是 System V,意思是参数 0-6 通过 rdi, rsi, rdxrcxr8r9 寄存器。其他参数被压入堆栈。

我知道函数签名,所以我可以猜出返回类型和参数类型。 有了这些信息,我可以将参数放入正确的寄存器中。

整数类型一切正常,但浮点值有问题。

float 需要通过xmm0-xmm7寄存器传递。

所以问题基本上是下面的。我有一个 float 类型的 C 变量。我需要使用 GCC 的内联汇编将该变量移入比方说 xmm0 寄存器。

想象一下下面的代码:

#include <stdio.h>

void foo( int x )
{
    printf( "X: %i\n", x );
}

int main( void )
{
    int x = 42;

    __asm__
    (
        "mov %[x], %%rdi;"
        "call _foo;"
        :
        : [ x ] "m" ( x )
    );

    return 0;
}

函数 foo 被调用,参数为 42。它有效...

现在我尝试使用浮点参数进行同样的操作。我只需要使用 movss 而不是 mov,就可以了。

当我尝试调用这两个函数时出现问题:

#include <stdio.h>

void foo( int a )
{
    printf( "A: %i\n", a );
}

void bar( float b )
{
    printf( "B: %f\n", b );
}

int main( void )
{
    int   a = 42;
    float b = 42;

    __asm__
    (
        "mov %[a], %%rdi;"
        "call _foo;"
        "movss %[b], %%xmm0;"
        "call _bar;"
        :
        : [ a ] "m" ( a ),
          [ b ] "m" ( b )
    );

    return 0;
}

接受 float 参数的函数接收到 0。我不明白为什么。 我不接触堆栈,所以没有清理工作要做...

如果我直接从 C 调用函数,GCC 会产生以下结果:

movl    $42, -4(%rbp)
movl    $0x42280000, %eax
movl    %eax, -8(%rbp)
movl    -4(%rbp), %edi
call    _foo
movss   -8(%rbp), %xmm0
call    _bar

我不明白其中的区别...任何帮助将不胜感激:)

祝大家有个愉快的一天

编辑

根据要求,这是使用内联汇编时的 ASM 输出:

 movl    $42, -4(%rbp)
 movl    $0x42280000, %eax
 movl    %eax, -8(%rbp)
 mov    -4(%rbp), %rdi;
 call    _foo;
 movl    -8(%rbp), %eax;
 movl    %eax, -4(%rbp);
 movss    -4(%rbp), %xmm0;
 call    _bar;

EDIT2

根据要求,这是 GDB 输出:

0x100000e9e <main+4>:   movl   $0x2a,-0x4(%rbp)
0x100000ea5 <main+11>:  mov    $0x42280000,%eax
0x100000eaa <main+16>:  mov    %eax,-0x8(%rbp)
0x100000ead <main+19>:  mov    -0x4(%rbp),%rdi
0x100000eb1 <main+23>:  callq  0x100000e54 <foo>
0x100000eb6 <main+28>:  movss  -0x8(%rbp),%xmm0
0x100000ebb <main+33>:  callq  0x100000e75 <bar>

最佳答案

我花了一段时间,但我弄明白了。在使用内联汇编的输出中,gcc 使用 rbp 的负偏移量来存储值。但是,由于它不知道内联汇编中的函数调用,因此它认为它没有调用任何函数。因此,它将变量放在红色区域,并且不更改 rsp 为变量腾出空间。当您调用 foo 时,返回地址被压入堆栈,覆盖您存储的变量并为您提供一个不正确的变量。

如果在程序集之外的主函数中的任何一点调用函数,则 gcc 将更改堆栈以保留变量。例如,如果您将 foo(-1); 添加到 main 的顶部,它将起作用。

关于c - GCC 内联汇编 - 在调用之前将 float 移动到 XMM0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5860219/

相关文章:

go - Go 内置函数使用泛型吗?

c - 从不兼容的指针类型获取初始化上下文

memory - 直接向内存写入数据和使用asm指令的区别

在程序集 x86 x87 中使用 float 调用函数

c++ - 在 128 位的小流中查找重复的对称位模式

c - 为什么 char [] 作为静态

c++ - 取回失败的输出

c - 通过检查核心和调用堆栈了解使用 GDB 的 C 指针

c - 在C中将字符串存储在数组中

c++ - 静态库中的 PIC 性能