我目前正在学习 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
我目前正在学习 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 位偏移量怎么办?
lui
显而易见的方法是自己进行所有地址计算,用/将 32 位值具体化到一个临时寄存器中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
。所以这是reg += %hi(0x123456)
用 lui / addu 做的,然后%lo(0x123456)
用作它的偏移量。
如果您使用静态数组和寄存器索引,则应该可以使用类似的技巧,但这不是 GCC 实际所做的。可能是因为它不知道静态地址的低 16 位是否会设置其高位,因此符号扩展为地址的上半部分创建了一个可能的非一。也许没有重定位类型可以解决这个问题并%hi()
根据零件的高位调整%lo()
零件,使得这种优化不可能:/
我使用char
C 不会缩放我的索引:数组索引 = 字节偏移量,因为sizeof(char) = 1
. lb
是对字寄存器的符号扩展加载,因为这就是 MIPS 的调用约定对窄参数起作用的方式。