MIPS 寄存器 $0 可以用于存储和检索值吗?

Joh*_*lén 4 assembly glibc mips

当我了解 MIPS 处理器时,我的脑海里猛然浮现出对 $0 寄存器的读取总是返回 0,而对 $0 的写入总是被丢弃。来自 MIPS 程序员手册:

2.13.4.1 CPU 通用寄存器 [...] r0 被硬连接到零值,并且可以用作其结果将被丢弃的任何指令的目标寄存器。当需要零值时,r0 也可以用作源。

因此,该说明or $0,$r31,$0是无操作的。

想象一下,当我在 ELF MIPS 二进制文件的启动代码中摸索时,看到以下指令序列时的惊讶:

00000610 03 E0 00 25   or     $0,$ra,$0
00000614 04 11 00 01   bgezal $0,0000061C
00000618 00 00 00 00   nop
0000061C 3C 1C 00 02   lui    $28,+0002
00000620 27 9C 84 64   addiu  $28,$28,-00007B9C
00000624 03 9F E0 21   addu   $28,$28,$ra
00000628 00 00 F8 25   or     $ra,$0,$0
Run Code Online (Sandbox Code Playgroud)

地址 0x610 处的指令是将 $ra 的值复制到 $r0 中,根据上面的段落,这相当于丢弃它。然后,地址 0x628 处的指令从 $0 读回值,但由于 $0 硬连线为 0,因此将 $ra 设置为 0。

这一切似乎毫无意义:为什么只执行 0x628 就足够了,为什么要执行语句 0x610。glibc 人员在编写此代码时显然有一些意图。似乎 $0 毕竟是可写和可读的!

那么在什么情况下程序可以读/写 $0 寄存器,就像它是其他通用寄存器中的任何一个一样?

编辑:查看 glibc 源代码没有帮助。__start 使用宏的代码:

https://github.com/bminor/glibc/blob/master/sysdeps/mips/start.S#L80

ENTRY_POINT:
# ifdef __PIC__
    SETUP_GPX($0)
...
Run Code Online (Sandbox Code Playgroud)

注意这里是如何故意指定 $0 的。SETUP_GPX 宏在此处定义:

https://github.com/bminor/glibc/blob/master/sysdeps/mips/sys/asm.h#L75

# define SETUP_GPX(r)                           \
        .set noreorder;                         \
        move r, $31;     /* Save old ra.  */     \
        bal 10f; /* Find addr of cpload.  */    \
        nop;                                    \
10:                                             \
        .cpload $31;                             \
        move $31, r;                             \
        .set reorder
Run Code Online (Sandbox Code Playgroud)

“Save old ra”清楚地表明了保存寄存器的意图,但为什么是 $0?

Jes*_*ter 5

它正在使用$0是因为在入口点没有理由 save $ra,所以它只是被丢弃。由于它是来自宏的手写 asm 代码,因此不会像通常情况那样进行优化。