assembly 在这个有三个操作数的divu中发生了什么?

yzuktlbb  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(162)

我在C代码中有下面一行

i = low + (1664525*(unsigned)high + 22695477*(unsigned)low) % (high-low+1);

这一行的代码以MIPS为单位如下所示

lw      $3,40($fp)
li      $2,1638400              # 0x190000
ori     $2,$2,0x660d
mult    $3,$2
mflo    $2
lw      $4,36($fp)
li      $3,22675456       # 0x15a0000
ori     $3,$3,0x4e35
mult    $4,$3
mflo    $3
addu    $2,$2,$3
lw      $4,40($fp)
lw      $3,36($fp)
nop
subu    $3,$4,$3
addiu   $3,$3,1
divu    $0,$2,$3
bne     $3,$0,1f
nop
break   7
mfhi    $2
move    $3,$2
lw      $2,36($fp)
nop
addu    $2,$3,$2
sw      $2,8($fp)

这里,作为参考,“low”的值存储在36($fp),“high”的值存储在40($fp)。

divu $0, $2, $3

在这里,我对发生了什么有点困惑,因为我只看到了带有两个操作数的divu命令。另外,我后来看到他们正在获取除法的mod(在这一行中)

mfhi    $2

但是,我们不是应该使用“mflo”来得到商吗?有人能帮我弄清楚这部分代码是如何工作的吗?

ffscu2ro

ffscu2ro1#

对于当前的MIPS 64(~ C):
根据官方的MIPS manuals你正在寻找的汇编源代码为一个发布6 MIPS 64,其中增加了一个新的指令来处理除法和模运算(DIV/MOD,DIVU/MODU,DDIV/DMOD,DDIVU/DMODU).
例如,对于DIVU(摘自手册):

divu rd,rs,rt

DIVU:GPR[rd]〈- sign_extend.32(无符号除法)(GPR[rs],GPR[rt])
DIVU执行无符号32位整数除法,并将32位商结果放入目的寄存器.
发行版6之前的指令的双参数对应部分现在产生保留的指令异常
现在有趣的部分:
根据手册中关于可用性和兼容性的章节“一些汇编器接受伪指令语法DIV rd,rs,rt并将其扩展为DIV rs,rt;当需要消除歧义时,可以使用诸如“具有GPR输出的DIV”和“具有HI/LO输出的DIV”之类的短语。
您发布的代码确实使用MFLO $2来获得除法后的余数,因此我认为汇编程序使用的是MIPS 64第6版源代码,以面向第6版之前的版本。MFHI $0可能已被优化,现在您可以使用MFLO $2来获得商
因此,我相信您可能只看到了divu $2, $3指令,带有prerelease 6 HI/LO语义
这是我最初的回答,我认为这对MIPS 32模拟器来说最有意义:
divu r1, r2, r3是一个伪指令。在Mars中,它是这样展开的:

bne r3, $0, skip
  nop
  break
skip:
  divu r2, r3
  mflo r1

所以它主要检查除法是否可以完成(否则跳过break),然后执行divu并得到r1中的商
由于您使用$0作为目标寄存器,因此使用伪指令没有多大意义。它将发出mflo $0
如果您不介意$3为0,则可能只使用divu $2, $3
如注解中所述,然后使用mfhi $2按预期获得余数。

h4cxqtbf

h4cxqtbf2#

您看到的是GCC输出,使用的是GAS和clang可以接受的语法。汇编源语法由工具定义,可能与供应商在其伊萨手册中使用的语法不同。这些手册明确了二进制机器代码是如何工作的,但不同的汇编器/反汇编器可以根据自己的选择改变语法。
divu $zero, src1, src2是裸机指令的GAS / clang语法,隐式输出为LO(商)和HI(余数)。MARS/SPIM和其他具有内置汇编器的MIPS模拟器更像经典的MIPS汇编器,而不是GNU工具链。(TODO:测试MARS接受的内容,并检查机器代码。)

**如果第一个操作数不是$zero,则它是伪指令的目标。**Clang将其扩展为在被零和mflo除时捕获目标。(我没有MIPS Binutils可供测试。)

例如,(请记住,这是clang,因此它针对的是具有分支延迟插槽的真实的商业MIPS,而不是MARS默认的具有禁用分支延迟的简化MIPS。)

$ clang -Wall -target mips -march=mips32r5 -O2 -c mips-div.s
$ llvm-objdump -d mips-div.o
...
00000000 <quot>:              
# divu  $v0, $4, $5 in the asm source assembled to
       0: 14 a0 00 02   bnez    $5, 12 <quot+0xc>  # skip over the break 7 on $a1!=0
       4: 00 85 00 1b   divu    $zero, $4, $5      #  in the branch-delay slot of bnez
       8: 00 07 00 0d   break   7 <quot>
       c: 00 00 10 12   mflo    $2                 # $v0
 # end of pseudocode expansion for  divu  $v0, $4, $5
...

      34: 00 85 00 1b   divu    $zero, $4, $5     # same source syntax

奇怪的是,clang至少把div $4, $5当作div $4, $4, $5来处理,并对其进行扩展,而不是仅仅把它汇编成一条2操作数机器指令,但clang支持这种通用模式:addu $a0, $a1汇编为addu $4, $4, $5,就像您编写了addu $a0, $a0, $a1一样。或者,就像MIPS支持更新目标的双操作数“破坏性”形式一样。因此,对于divu的双操作数语法,这种扩展似乎优先于实际的机器指令。
对于clang来说,这似乎是一个奇怪的设计选择,或者更像是多年前的GNU汇编程序。但考虑到这一事实,他们确实需要一个明确指示裸机指令的语法。他们选择了$zero目标。
这是一条R型指令,对于divdivu,机器指令中通常作为目的地的字段必须为零,因此这可能会使汇编器和反汇编器中的文本解析/格式化更容易。
编译器知道如何布局分支本身,并且可以在适当的地方调度mflo,因此它使用divu $zero, src, src版本。

MIPS 32/64 r6有一条3操作数机器指令,取代旧的

这个答案的第一部分是关于在此之前的MIPS,从经典的MIPS I到MIPS 32/64 r5。

  • 在MIPS 32 r6/MIPS 64 r6中,divu是一个3操作数指令,操作码与以前不同。它将商写入第一个操作数。(LO和HI不存在,mflo/mfhi在R6中被删除。)
# clang -target mips -march=mips32r6 div-mips.s && llvm-objdump -d div-mips.o
00 85 00 9b   divu    $zero, $4, $5        # $a0 / $a1 discarding the result
  • 在MIPS的早期版本中,divu被记录为2操作数指令,其中隐式目的是LO(商)与HI(余数)。在发行版6中删除了这种形式。
# clang -target mips -march=mips32r5
00 85 00 1b   divu    $zero, $4, $5     # same in the asm source

2014年的MIPS 32/64 r6重新组织了一些操作码,并删除了一些很少使用的操作码,通常 * 不 * 二进制兼容。

当前使用的工具版本,比如GCC,都是在这个日期之后。但是MARS和SPIM还没有更新AFAIK,而且大多数人都不希望它们更新,除非它是一个可选特性。使用MIPS或类似MIPS的伊萨进行教学的典型大学课程更像是20世纪80年代中期到后期的MIPS I或II。
但我怀疑clang只是为了兼容GNU Binutils,而且早在2014年MIPS 32/64 r6新推出之前,它就已经使用3操作数$zero形式来表示裸指令了。但我很好奇是否有人有MIPS Binutils,尤其是旧版本,可以测试汇编/反汇编。
另请参阅MIPS 64 r6伊萨manual from mips.com。我正在查看v6.06,2016年的第194页,以获得divu的新形式的条目。(我不希望MIPS手册有任何进一步的更新;伊萨在商业上是没有出路的。)

可用性与兼容性:

这些说明由版本6引入,并且从版本6开始需要这些说明。
发行版6的除指令与发行版6之前的除指令(DIV、DIVU、DDIV、DDIVU)具有相同的操作码 * 助记符 *。指令编码不同,指令语义也不同:Release 6指令仅产生商,而Release 6之前的指令分别在HI/LO寄存器中产生商和余数,并且需要单独的模指令来获得余数。
汇编语法区分了Release 6与Release 6之前的除法指令。例如,Release 6“DIV rd,rs,rt“指定3个寄存器操作数,而Release 6之前的“DIV rs,rt“只有两个寄存器参数。一些汇编器接受伪指令语法“DIV rd,rs,rt“,并将其扩展为“DIV rs,rt;MFHI rd“。短语如“带GPR输出的DIV”并且当需要消除歧义时,可以使用“具有HI/LO输出的DIV”。
该文档与GCC和clang所做的并不完全匹配,但汇编语言文本源代码语法确实是该工具的选择,供应商文档和伊萨实际上只对机器代码的规则具有完全控制权。

在MIPS 32/64 r6上还有一个modu指令,您可以使用它来代替
我们不是应该用“mflo”来求商吗?
% (high-low+1)是一个余数运算,而不是一个需要求商的/运算符。

相关问题