【发布时间】:2020-06-01 23:44:41
【问题描述】:
所以 lw 指令的格式如下:lw RegDest, Offset(RegSource)。为什么第二个参数同时包含偏移量和寄存器源?为什么不是只有一个(即只注册源)?
【问题讨论】:
标签: assembly mips cpu-architecture
所以 lw 指令的格式如下:lw RegDest, Offset(RegSource)。为什么第二个参数同时包含偏移量和寄存器源?为什么不是只有一个(即只注册源)?
【问题讨论】:
标签: assembly mips cpu-architecture
因为你还打算用剩下的 32 位指令字做什么? (假设您是设计 MIPS 指令集的 CPU 架构师)。
它允许 LUI + LW 在 2 条指令中从任意 32 位地址加载,而不是 3 条。对于循环展开或结构指针-> 成员访问,避免指针数学的 ADDIU 指令。即在 LW/SW 上花费这么多的编码空间可以让 MIPS 程序更有效率。 有时你只需要0($reg),但有时它会浪费指令来计算最终地址一个寄存器。
省略 16 位立即位移不能使指令更短。 MIPS 是具有固定长度指令字的 RISC。 (它可能是 R 类型而不是 I 类型,但在那种格式中你仍然有未使用的位。经典 MIPS 有很多未使用的编码空间,并且在 LW/SW、LB/LBU/SB 和等等,值得。)
MIPS 没有很多不同的操作码(尤其是没有任何 FPU 指令且没有 64 位指令的经典 MIPS)。它使用大量指令编码空间来支持大多数指令的立即数形式,具有很大的立即数。 (例如,与 ARM32 不同,它在每条指令中使用 4 位进行谓词执行,而更多位用于“灵活”源操作数(可选旋转或移位一个常数或另一个寄存器,或立即数)。但 ARM 立即数编码为 8带有旋转的位,允许许多在现实生活中常见的有用位模式。)
MIPS 只有一种寻址模式,imm16(reg) 可以节省大量的 addiu 指令,而不是 (reg)。
例如,考虑一个加载或存储到静态(或全局)变量的 C 函数。喜欢
unsigned rng(void) {
static unsigned seed = 1234;
return (seed = seed * 5678 + 0x1234);
}
编译器生成(或手写)的 asm 需要从 seed 加载和存储,因此您需要将它放在寄存器中。但它是一个 32 位常量,不适合单条指令。在手写 asm 中,您可能会使用像 la $t0, rng.seed 这样的伪指令,它将组装成 lui $t0, hi(rng.seed) / ori $t0, $t0, lo(rng.seed)。 (hi 和 lo 得到 32 位地址的一半)。
但你可以做得更好:
lui $t0, hi(rng.seed)
lw $t1, lo(rng.seed) ($t0)
即使用地址的低 16 位作为加载指令中的 16 位位移。这其实是compilers like gcc do:
rng: # gcc5.4 -O3
lui $5,%hi(seed.1482)
lw $4,%lo(seed.1482)($5)
nop ; classic MIPS has a 1-cycle "shadow" for loads before the result is usable, with no pipeline interlock
sll $3,$4,5 ; I should have picked a simpler multiply constant (with fewer bits set)
sll $2,$4,3
subu $2,$3,$2
sll $3,$2,3
subu $2,$3,$2
subu $2,$2,$4
sll $3,$2,4
addu $2,$2,$3
sll $2,$2,1
addiu $2,$2,4660
j $31
sw $2,%lo(seed.1482)($5) ; branch-delay slot
seed.1482:
.word 1234
寄存器中小的立即位移还有很多其他用途。例如:
struct 字段正如我所说,对于非常适合 MIPS 的额外 16 位指令字,您无能为力。您可以为置换保留少于 16 位,但 MIPS 不是 PowerPC(其中有很多很多操作码)。
【讨论】: