rsp 堆栈指针在返回函数调用的值时有任何用处吗?

问题描述 投票:0回答:1

我知道有多种方法可以在汇编中返回值,

  1. rax
    注册
  2. xmm0
    中,
    xmm1
    寄存器
  3. 在堆栈中(类似于c++中的返回优化),这有时会使用rdi寄存器来保存调用者的堆栈地址,然后直接在rdi指向的地址中返回值

我在汇编中有以下代码,该代码应该计算 3x3 矩阵乘以 3D 向量,然后返回浮点数形式的 3 维向量(每个 4 字节,总共 12 字节)。

一切都很容易理解,直到最后一段返回值的代码,太奇怪了,我不明白它的用途。

假设3维值最终在

xmm6
xmm7
xmm8
中,代码如下,

注意:这个函数的开头和结尾没有

sub  rsp, xxx
add  rsp, xxx
,实际上在矩阵和向量乘法的计算过程中根本没有使用堆栈。

movss  [rsp-28h],xmm6
movss  [rsp-24h],xmm7
movss  [rsp-20h],xmm8

mov    eax,[rsp-24h]
shl    eax,20h
mov    ebx,[rsp-28h]
or     rax,rbx
mov    [rsp-18h],rax
movd   rcx,xmm8
mov    [rsp-10h],rcx

mov    ebx,eax
shr    rax,20h
mov    [rsp-28h],eax
mov    [rsp-24h],ebx
mov    [rsp-20h],ecx

movss  xmm1,[rsp-10h]
movsd  xmm0,[rsp-28h]

我尝试用

asm()
在C程序中编译并运行这段代码,发现实际上它只是将
xmm6
xmm7
xmm8
复制到3个地方,一个在
[rsp-28h]
,一个在
 [rsp-18h]
,以及
xmm1
xmm0
中的一个,都是相同的浮点值。

我知道

xmm0
xmm1
最有可能是返回值,但是
[rsp-28h]
[rsp-18h]
在返回值方面有什么用处吗?为什么程序使用一种很奇怪的方法(复制到堆栈,然后复制到
rax
等寄存器,然后再复制回堆栈)来复制结果?

我问这个是因为调用者代码不使用

xmm0
xmm1
。我不确定我是否应该认为调用者只是丢弃返回值,或者返回值实际上位于调用者堆栈中的某个位置。

谢谢。

c++ c assembly
1个回答
0
投票

警告:这可能不完全是你所说的,但是......

如果函数返回

rsp/rbp

 
按值
,则在返回过程中使用 struct

  1. 调用者必须在其堆栈帧中保留一个[temp]区域用于返回值。
  2. 被调用函数的第一个参数是指向该区域的隐式/隐藏指针。
  3. 被调用的函数会将返回值复制到this指针指向的区域
  4. 调用者将从临时区域复制到最终变量。

考虑以下来源:

#include <stdio.h>

struct obj {
    int x[10];
};

struct obj
fill(int arg)
{
    struct obj obj = {
        .x = { 4, 5, 6 }
    };

    printf("fill: arg=%d\n",arg);

    return obj;
}

void
objprint(const struct obj *obj)
{

    for (int i = 0;  i < 10;  ++i)
        printf(" %d",obj->x[i]);
    printf("\n");
}

struct obj obj = {
    .x = { 1, 2, 3 }
};

int
main(void)
{

    objprint(&obj);
    obj = fill(37);
    objprint(&obj);

    return 0;
}

为了清楚起见,我们使用

-m32
进行编译,但
x86_64
的原理是相同的。我们编译:

cc \
-fno-inline-small-functions \
-fno-inline-functions-called-once \
-fno-inline-functions \
-fomit-frame-pointer \
-S -fverbose-asm -O2 -m32 retval2.c

这是[已编辑]汇编器输出。堆栈帧之间有相当多的移动。

    .file   "retval2.c"
    .text
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "fill: arg=%d\n"
    .text
    .p2align 4,,15
    .globl  fill
    .type   fill, @function
fill:
.LFB11:
    .cfi_startproc
    pushl   %ebx    #
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    subl    $16, %esp   #,
    .cfi_def_cfa_offset 24
# retval2.c:9: {
    movl    24(%esp), %ebx  # .result_ptr, .result_ptr
# retval2.c:14:     printf("fill: arg=%d\n",arg);
    pushl   28(%esp)    # arg
    .cfi_def_cfa_offset 28
    pushl   $.LC0   #
    .cfi_def_cfa_offset 32
    call    printf  #
# retval2.c:16:     return obj;
    movl    $4, (%ebx)  #, MEM[(struct obj *)&<retval>]
# retval2.c:17: }
    movl    %ebx, %eax  # .result_ptr,
# retval2.c:16:     return obj;
    movl    $5, 4(%ebx) #, MEM[(struct obj *)&<retval> + 4B]
    movl    $6, 8(%ebx) #, MEM[(struct obj *)&<retval> + 8B]
    movl    $0, 12(%ebx)    #, MEM[(struct obj *)&<retval> + 12B]
    movl    $0, 16(%ebx)    #, MEM[(struct obj *)&<retval> + 16B]
    movl    $0, 20(%ebx)    #, MEM[(struct obj *)&<retval> + 20B]
    movl    $0, 24(%ebx)    #, MEM[(struct obj *)&<retval> + 24B]
    movl    $0, 28(%ebx)    #, MEM[(struct obj *)&<retval> + 28B]
    movl    $0, 32(%ebx)    #, MEM[(struct obj *)&<retval> + 32B]
    movl    $0, 36(%ebx)    #, MEM[(struct obj *)&<retval> + 36B]
# retval2.c:17: }
    addl    $24, %esp   #,
    .cfi_def_cfa_offset 8
    popl    %ebx    #
    .cfi_restore 3
    .cfi_def_cfa_offset 4
    ret $4      #
    .cfi_endproc
.LFE11:
    .size   fill, .-fill
    .section    .rodata.str1.1
.LC1:
    .string " %d"
    .text
    .p2align 4,,15
    .globl  objprint
    .type   objprint, @function
objprint:
.LFB12:
    .cfi_startproc
    pushl   %esi    #
    .cfi_def_cfa_offset 8
    .cfi_offset 6, -8
    pushl   %ebx    #
    .cfi_def_cfa_offset 12
    .cfi_offset 3, -12
    subl    $4, %esp    #,
    .cfi_def_cfa_offset 16
# retval2.c:21: {
    movl    16(%esp), %ebx  # obj, ivtmp.15
    leal    40(%ebx), %esi  #, _16
    .p2align 4,,10
    .p2align 3
.L5:
# retval2.c:24:         printf(" %d",obj->x[i]);
    subl    $8, %esp    #,
    .cfi_def_cfa_offset 24
    pushl   (%ebx)  # MEM[base: _14, offset: 0B]
    .cfi_def_cfa_offset 28
    addl    $4, %ebx    #, ivtmp.15
    pushl   $.LC1   #
    .cfi_def_cfa_offset 32
    call    printf  #
# retval2.c:23:     for (int i = 0;  i < 10;  ++i)
    addl    $16, %esp   #,
    .cfi_def_cfa_offset 16
    cmpl    %esi, %ebx  # _16, ivtmp.15
    jne .L5 #,
# retval2.c:25:     printf("\n");
    movl    $10, 16(%esp)   #,
# retval2.c:26: }
    addl    $4, %esp    #,
    .cfi_def_cfa_offset 12
    popl    %ebx    #
    .cfi_restore 3
    .cfi_def_cfa_offset 8
    popl    %esi    #
    .cfi_restore 6
    .cfi_def_cfa_offset 4
# retval2.c:25:     printf("\n");
    jmp putchar #
    .cfi_endproc
.LFE12:
    .size   objprint, .-objprint
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB13:
    .cfi_startproc
    leal    4(%esp), %ecx   #,
    .cfi_def_cfa 1, 0
    andl    $-16, %esp  #,
    pushl   -4(%ecx)    #
    pushl   %ebp    #
    .cfi_escape 0x10,0x5,0x2,0x75,0
    movl    %esp, %ebp  #,
    pushl   %ecx    #
    .cfi_escape 0xf,0x3,0x75,0x7c,0x6
    subl    $64, %esp   #,
# retval2.c:36:     objprint(&obj);
    pushl   $obj    #
    call    objprint    #
# retval2.c:37:     obj = fill(37);
    leal    -56(%ebp), %eax #, tmp88
    popl    %edx    #
    popl    %ecx    #
    pushl   $37 #
    pushl   %eax    # tmp88
    call    fill    #
    movl    -56(%ebp), %eax #, tmp91
# retval2.c:38:     objprint(&obj);
    pushl   $obj    #
# retval2.c:37:     obj = fill(37);
    movl    %eax, obj   # tmp91, obj
    movl    -52(%ebp), %eax #, tmp93
    movl    %eax, obj+4 # tmp93, obj
    movl    -48(%ebp), %eax #, tmp95
    movl    %eax, obj+8 # tmp95, obj
    movl    -44(%ebp), %eax #, tmp97
    movl    %eax, obj+12    # tmp97, obj
    movl    -40(%ebp), %eax #, tmp99
    movl    %eax, obj+16    # tmp99, obj
    movl    -36(%ebp), %eax #, tmp101
    movl    %eax, obj+20    # tmp101, obj
    movl    -32(%ebp), %eax #, tmp103
    movl    %eax, obj+24    # tmp103, obj
    movl    -28(%ebp), %eax #, tmp105
    movl    %eax, obj+28    # tmp105, obj
    movl    -24(%ebp), %eax #, tmp107
    movl    %eax, obj+32    # tmp107, obj
    movl    -20(%ebp), %eax #, tmp109
    movl    %eax, obj+36    # tmp109, obj
# retval2.c:38:     objprint(&obj);
    call    objprint    #
# retval2.c:41: }
    movl    -4(%ebp), %ecx  #,
    .cfi_def_cfa 1, 0
    addl    $16, %esp   #,
    xorl    %eax, %eax  #
    leave
    .cfi_restore 5
    leal    -4(%ecx), %esp  #,
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE13:
    .size   main, .-main
    .globl  obj
    .data
    .align 32
    .type   obj, @object
    .size   obj, 40
obj:
# x:
    .long   1
    .long   2
    .long   3
    .zero   28
    .ident  "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
    .section    .note.GNU-stack,"",@progbits

注意:

即使

main
做了:
obj = fill(37);
,它传递了一个指向其堆栈帧上的temp区域的指针,而不是传递一个指向lvalue
obj
的指针。

调用者从临时区域手动复制到左值。

© www.soinside.com 2019 - 2024. All rights reserved.