为什么MIPS GCC在函数调用(GOT指针)后从16($fp)重新加载$28,而没有先存储到16($fp)



我认为我需要粘贴完整的代码,尽管它看起来很长。

我写了一个简单的测试代码。

#include <stdio.h>
int funadd(int a, int b){
int x = 0;
x = a + b;
return x;
}
int fun(int a, int b){
int y = 17;
int returnvalue = 0;
returnvalue = funadd(a, b);
returnvalue = returnvalue - y;
return returnvalue;
}
int main(){
int a = 32;
int b = 24;
int c = 0;
c = fun(a, b);
printf("%dn", c);
return c;
}

组装后:

.file   1 "testfuncall.c"
.section .mdebug.abi32
.previous
.nan    legacy
.module fp=xx
.module nooddspreg
.abicalls
.text
.align  2
.globl  funadd
.set    nomips16
.set    nomicromips
.ent    funadd
.type   funadd, @function
funadd:
.frame  $fp,24,$31      # vars= 8, regs= 1/0, args= 0, gp= 8
.mask   0x40000000,-4
.fmask  0x00000000,0
.set    noreorder
.set    nomacro
addiu   $sp,$sp,-24
sw  $fp,20($sp)
move    $fp,$sp
sw  $4,24($fp)
sw  $5,28($fp)
sw  $0,8($fp)
lw  $3,24($fp)
lw  $2,28($fp)
addu    $2,$3,$2
sw  $2,8($fp)
lw  $2,8($fp)
move    $sp,$fp
lw  $fp,20($sp)
addiu   $sp,$sp,24
jr  $31
nop
.set    macro
.set    reorder
.end    funadd
.size   funadd, .-funadd
.align  2
.globl  fun
.set    nomips16
.set    nomicromips
.ent    fun
.type   fun, @function
fun:
.frame  $fp,40,$31      # vars= 8, regs= 2/0, args= 16, gp= 8
.mask   0xc0000000,-4
.fmask  0x00000000,0
.set    noreorder
.cpload $25
.set    nomacro
addiu   $sp,$sp,-40
sw  $31,36($sp)
sw  $fp,32($sp)
move    $fp,$sp
.cprestore  16
sw  $4,40($fp)
sw  $5,44($fp)
li  $2,17           # 0x11
sw  $2,24($fp)
sw  $0,28($fp)
lw  $5,44($fp)
lw  $4,40($fp)
lw  $2,%got(funadd)($28)
move    $25,$2
.reloc  1f,R_MIPS_JALR,funadd
1:  jalr    $25
nop
lw  $28,16($fp)
sw  $2,28($fp)
lw  $3,28($fp)
lw  $2,24($fp)
subu    $2,$3,$2
sw  $2,28($fp)
lw  $2,28($fp)
move    $sp,$fp
lw  $31,36($sp)
lw  $fp,32($sp)
addiu   $sp,$sp,40
jr  $31
nop
.set    macro
.set    reorder
.end    fun
.size   fun, .-fun
.rdata
.align  2
$LC0:
.ascii  "%d1200"
.text
.align  2
.globl  main
.set    nomips16
.set    nomicromips
.ent    main
.type   main, @function
main:
.frame  $fp,48,$31      # vars= 16, regs= 2/0, args= 16, gp= 8
.mask   0xc0000000,-4
.fmask  0x00000000,0
.set    noreorder
.cpload $25
.set    nomacro
addiu   $sp,$sp,-48
sw  $31,44($sp)
sw  $fp,40($sp)
move    $fp,$sp
.cprestore  16
li  $2,32           # 0x20
sw  $2,24($fp)
li  $2,24           # 0x18
sw  $2,28($fp)
sw  $0,32($fp)
lw  $5,28($fp)
lw  $4,24($fp)
lw  $2,%got(fun)($28)
move    $25,$2
.reloc  1f,R_MIPS_JALR,fun
1:  jalr    $25
nop
lw  $28,16($fp)
sw  $2,32($fp)
lw  $5,32($fp)
lw  $2,%got($LC0)($28)
addiu   $4,$2,%lo($LC0)
lw  $2,%call16(printf)($28)
move    $25,$2
.reloc  1f,R_MIPS_JALR,printf
1:  jalr    $25
nop
lw  $28,16($fp)
lw  $2,32($fp)
move    $sp,$fp
lw  $31,44($sp)
lw  $fp,40($sp)
addiu   $sp,$sp,48
jr  $31
nop
.set    macro
.set    reorder
.end    main
.size   main, .-main
.ident  "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"

我意识到,在每次函数调用之后,都有一条lw $28,16($fp)指令。但我没有看到任何代码会先在调用者或被调用者中存储值。

我可以阅读MIPS程序集。我知道lw是加载字,$fp和$sp是帧指针和堆栈指针。

我只是不明白从16($fp)加载任何内容有什么意义;似乎有一个未初始化的空间。

我知道$28$gp,可以看到它被用作GOT指针,在调用之前加载函数地址,但在函数中使用之前,似乎没有任何东西初始化该寄存器。

MIPS调用约定是否要求$28已经指向函数项上的GOT?

lw $28,16($fp)

lw是一条"加载字"指令——它将一个字(4字节或32位(从内存加载到寄存器中。$28是目的寄存器(有时也称为$gp(,16($fp)是要从-16字节加载到帧中的地址($fp是帧指针寄存器,16被添加到其中以获得要加载的地址(。

"帧"通常用于保存函数的局部变量——当函数启动时,它通过从$sp中减去一个常数来在堆栈上分配一个帧,然后将调用方的$fp值存储在其中的某个位置,并将$sp复制到$fp,使其指向这个新分配的帧。然后,它通过加载(l(和存储(s(指令将本地数据读取到帧中/从帧中写入本地数据。

如果您使用优化进行编译,GCC会在可能的情况下将本地存储在寄存器中,而不是浪费大量的指令将它们存储/重新加载到堆栈中。并且将访问相对于堆栈指针的堆栈内存,而不是花费指令将$fp设置为传统的帧指针。未优化的代码看起来不像人类手工编写的代码,但优化的代码有时确实如此。

我想我知道为什么。

我自动忽略了.cprestore 16,因为我认为它毫无用处。但事实证明,它发出的是与$gp寄存器相关的实际指令,而不仅仅是元数据。

CCD_ 21将扩展到CCD_ 22。它与.cpload $25和其他必要的代码一起使用。具体来说,您可以阅读链接https://www.linux-mips.org/wiki/PIC_code

最新更新