Assembly 试图理解这个MIPS函数,如果它的输入是负数,它就会分支

Assembly 试图理解这个MIPS函数,如果它的输入是负数,它就会分支,assembly,mips,Assembly,Mips,我正在阅读以下关于汇编语言代码的摘录: “标签可用于replace[sic]中的汇编,用于计算分支和跳转指令的精确值。以下是一个示例。” intisneg(inta0){ if(a0

我正在阅读以下关于汇编语言代码的摘录:

“标签可用于replace[sic]中的汇编,用于计算分支和跳转指令的精确值。以下是一个示例。”

intisneg(inta0){
if(a0<0){
返回1;
}否则{
返回0;
}
}
isNeg:
slt$t0$a0$0
beq$t0$0 ISPO
jr$ra
ISPO:
添加$v0$0$0
jr$ra
以下是我的解释(请纠正我的错误假设):

isNeg:
slt$t0$a0$0//以$t0存储($a0<0)
beq$t0$0 isPos//if($t0==0)分支0字节
jr$ra
ISPO:
添加$v0$0$0//在$v0中存储0+0=0
jr$ra
如果我在这里的假设是正确的,那么如果$a是负的,我们什么也不做,如果$a是正的,我们分支0,这相当于什么也不做。有人能解释一下吗

有人能解释一下吗

如果我理解正确的话,这篇课文来自一本书。该示例是为了显示某些指令的效果,而不是显示真正可以在实际程序中找到的“真实”代码(例如,在带有MIPS CPU的WLAN路由器上)

这本书的作者唯一想展示的是标签是如何工作的,所以他写了一个(愚蠢的)包含一些标签的例子

如果$a为负,我们什么也不做

正确(假设
jr$ra
与实际MIPS CPU中的不同)

我假设这本书的作者忘记了
addi$v0$0-1
指令

如果$a为正,则我们按0进行分支

如果
beq
为a,则这是正确的

在这种情况下,CPU将首先在执行
beq
jr
指令之后立即执行该指令,然后再实际进行分支/跳转

然而,在这种情况下,在大多数真正的MIPS CPU上不允许紧跟在
beq
指令之后的
jr
指令。因此,我认为这里的情况并非如此

如果使用不模拟延迟槽的仿真器,则
beq
指令将跳转4个字节(在
jr
指令上)


如果在每个跳转/分支指令(GNU中的
.set reorder
选项)之后使用真正的MIPS CPU和汇编器插入
nop
(例如
添加$0$0
)指令,则
beq
指令将跳转8字节(在
jr
指令和其后的
nop
指令之上)。在跳转之后,执行
add$v0$0$0
指令。

如果($t0==0),代码跳转到“isPos:”并执行add。为什么您认为
beq$t0$0 isPos
会分支0个字节?您省略了操作数之间的逗号。什么MIPS汇编器会真正解析这个?我想一定有一些,因为我们经常在很多问题中看到这样的asm。但当啷一声哽住了。注意,负数的对立面不是正数,这就排除了零。你想要的术语是“非负”。顺便说一句,这是一个非常低效的函数实现:
srl$v0,$a0,31
/
jr$ra
返回输入的符号位;一个好的优化编译器应该编译成这样。你在哪里找到这个asm或者那个不合语法的引用?对于非负输入,它没有修改
$v0
,因此它甚至不能正确实现C函数。该函数的另一个有效的单指令实现是
slt$v0、$a0、$0
。分支编码仍将使用
+1
指令(4字节)的偏移量,因为分支目标是相对于
beq
之后的指令开始进行编码的。(即相对于分支延迟槽中指令的开始)。是的,对于具有
.set noreorder
的真正MIPS,这显然不是asm,因为它具有背对背的分支。
int isNeg(int a0) {
  if (a0 < 0) {
    return 1;
  } else {
    return 0;
  }
}

isNeg:
  slt  $t0 $a0 $0
  beq $t0 $0 isPos
  jr $ra
isPos:
  add $v0 $0 $0
  jr $ra
isNeg:
  slt $t0 $a0 $0 // store ($a0 < 0) in $t0
  beq $t0 $0 isPos // if ($t0 == 0) branch by 0 bytes
  jr  $ra
isPos:
  add $v0 $0 $0 // store 0+0=0 in $v0
  jr  $ra
beq $t0 $0 isPos