MIPS:关于加载/存储字库+偏移的说明



我目前正在学习MIPS,并希望对存储/加载单词进行一些澄清。

是:

sw $t0, 4($s0) 

addi $s0, $s0, 4 # offsets are in bytes/word 8*4*4
sw   $t0, 0($s0)

此外,我知道偏移量是 16 位签名的即时偏移量。但是,如果它像 32 位即时一样更大呢?

sw $t0, x($s0) # x is a 32bits offset

如果它像 32 位即时一样大怎么办?

然后MIPS指令字必须更长,如48位,以便仍然有空间容纳操作码和寄存器号以及32位即时码。


但我想你在问,如果你有一个太大而无法立即使用的 32 位偏移量怎么办?

显而易见的方法是自己进行所有地址计算,将 32 位值具体化到具有lui/addiu的临时寄存器中,然后使用addu计算寄存器中的最终地址。 (因为 MIPS 没有 reg+reg 寻址模式,只有reg+imm16(。 但是,我们根本不会在负载中利用imm16。

我们可以通过将偏移的低部分用作负载中的直接部分来做得更好,只需使用 2 条额外指令而不是 3 条。 编译器知道这个技巧,所以很容易显示一个示例:

char foo(char *p) {
return p[0x123456];
}

在 Godbolt 编译器资源管理器上使用 MIPS gcc5.4 编译到这个 asm:

# gcc5.4 -O3 -fno-delayed-branch
foo:
li      $2,1179648              # 0x120000
addu    $4,$4,$2
lb      $2,13398($4)            # 0x3456($4)
j       $31
nop

$4$a0$2$v0。 所以这是用lui/addu做reg += %hi(0x123456),然后使用%lo(0x123456)作为偏移量。

如果您使用静态数组和寄存器索引,类似的技巧应该是可能的,但这不是 GCC 实际所做的。 可能是因为它不知道静态地址的低 16 是否会设置其高位,因此符号扩展为地址的上半部分创建了一个可能的 off-by-one。 也许没有重新定位类型可以解决这个问题并根据%lo()部分的高位调整%hi()部分,使这种优化变得不可能:/

我使用了char,所以 C 不会缩放我的索引:数组索引 = 字节偏移量,因为sizeof(char) = 1.lb是将符号扩展加载到字寄存器中,因为这就是MIPS的调用约定对窄参数的工作方式。

最新更新