liaozhicai 2024-07-02 15:31 采纳率: 20%
浏览 5
已结题

mips架构下为什么是先返回jr再退栈指针sp?

函数返回前栈顶sp指针为什么在返回指令jr之后?
在jr之后的指令还能执行吗?

如glibc的ld库__libc_csu_init函数,不应该是先把栈顶指针sp回移,再返回吗?
但反汇编结果却是先返回,再移sp,都返回了,还怎么执行移sp?

不应该是先addiu sp,sp,56,再jr ra吗?,但实际却是这样的:
jr ra
addiu sp,sp,56
(jr之后的指令还能执行到吗?真是令人困惑。)
全部反汇编代码如下:
(mips32架构)

Dump of assembler code for function __libc_csu_init:
   0x55610f60 <+0>:     lui     gp,0x3
   0x55610f64 <+4>:     addiu   gp,gp,-24288
   0x55610f68 <+8>:     addu    gp,gp,t9
   0x55610f6c <+12>:    addiu   sp,sp,-56
   0x55610f70 <+16>:    sw      ra,52(sp)
   0x55610f74 <+20>:    sw      s5,48(sp)
   0x55610f78 <+24>:    sw      s4,44(sp)
   0x55610f7c <+28>:    sw      s3,40(sp)
   0x55610f80 <+32>:    sw      s2,36(sp)
   0x55610f84 <+36>:    sw      s1,32(sp)
   0x55610f88 <+40>:    sw      s0,28(sp)
   0x55610f8c <+44>:    move    s3,a0
   0x55610f90 <+48>:    lw      t9,-29668(gp)
   0x55610f94 <+52>:    move    s4,a1
   0x55610f98 <+56>:    sw      gp,16(sp)
   0x55610f9c <+60>:    jalr    t9
   0x55610fa0 <+64>:    move    s5,a2
   0x55610fa4 <+68>:    lw      gp,16(sp)
   0x55610fa8 <+72>:    lw      s0,-29664(gp)
   0x55610fac <+76>:    lw      s2,-29660(gp)
   0x55610fb0 <+80>:    subu    s2,s2,s0
   0x55610fb4 <+84>:    sra     s2,s2,0x2
--Type <RET> for more, q to quit, c to continue without paging--
   0x55610fb8 <+88>:    beqz    s2,0x55610fe4 <__libc_csu_init+132>
   0x55610fbc <+92>:    move    s1,zero
   0x55610fc0 <+96>:    lw      t9,0(s0)
   0x55610fc4 <+100>:   move    a2,s5
   0x55610fc8 <+104>:   move    a1,s4
   0x55610fcc <+108>:   addiu   s1,s1,1
   0x55610fd0 <+112>:   move    a0,s3
   0x55610fd4 <+116>:   jalr    t9
   0x55610fd8 <+120>:   addiu   s0,s0,4
   0x55610fdc <+124>:   bne     s2,s1,0x55610fc0 <__libc_csu_init+96>
   0x55610fe0 <+128>:   nop
   0x55610fe4 <+132>:   lw      ra,52(sp)
   0x55610fe8 <+136>:   lw      s5,48(sp)
   0x55610fec <+140>:   lw      s4,44(sp)
   0x55610ff0 <+144>:   lw      s3,40(sp)
   0x55610ff4 <+148>:   lw      s2,36(sp)
   0x55610ff8 <+152>:   lw      s1,32(sp)
   0x55610ffc <+156>:   lw      s0,28(sp)
=> 0x55611000 <+160>:   jr      ra
   0x55611004 <+164>:   addiu   sp,sp,56

  • 写回答

4条回答 默认 最新

  • 阿里嘎多学长 2024-07-02 15:32
    关注

    以下内容由CHATGPT及阿里嘎多学长共同生成、有用望采纳:


    在 MIPS 架构中,jr 指令用于跳转到寄存器 ra(返回地址寄存器)中存储的地址,从而返回到函数调用之前的代码继续执行。通常情况下,函数在返回之前需要进行一些清理工作,比如恢复寄存器状态、调整栈指针等。

    你提到的代码片段中,jr ra 指令后面跟着 addiu sp, sp, 56,看起来像是在函数返回之后执行的,这在直觉上确实有些令人困惑。但实际上,这里有一些底层的硬件和编译器优化的机制在起作用。

    1. 延迟槽(Delay Slot):MIPS 架构的很多指令都有一个延迟槽(也称为填充槽或空槽),这是指令执行流水线的一个特性。jr 指令是一个分支指令,它在跳转之前会留下一个延迟槽,在这个槽中可以放置一个指令,这个指令会在跳转发生之前执行。因此,jr ra 后面的 addiu sp, sp, 56 实际上就是在延迟槽中执行的。

    2. 编译器优化:编译器在生成代码时,会尽量优化指令的顺序和使用。在某些情况下,编译器可能会决定将一些清理工作放在 jr 指令的延迟槽中执行,以减少代码的大小或者提高执行效率。

    3. 硬件执行:即使 jr 指令已经将控制权转移,但在实际的硬件执行过程中,延迟槽中的指令仍然会被执行。这是因为在跳转发生之前,CPU 已经在执行流水线中预取了这条指令。

    4. 代码的可读性:虽然这种写法在汇编层面看起来有些不直观,但它是符合 MIPS 架构的执行规则的。编译器和硬件设计者已经考虑到了这种情况,确保了即使在 jr 后面放置指令,它们也能被正确执行。

    总结来说,jr 后面的指令能够执行是因为 MIPS 架构的延迟槽特性,以及编译器的优化策略。这种做法在 MIPS 架构中是合法且有效的,尽管它可能在初次接触时让人感到困惑。

    至于你提到的参考链接,由于我不能提供实时的网络搜索结果,我建议你可以查找关于 MIPS 架构的官方文档或者相关的计算机组成原理教材,这些资源通常会详细解释延迟槽和分支指令的执行机制。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 7月12日
  • 已采纳回答 7月4日
  • 创建了问题 7月2日