我在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”来得到商吗?有人能帮我弄清楚这部分代码是如何工作的吗?
2条答案
按热度按时间ffscu2ro1#
对于当前的MIPS 64(~ C):
根据官方的MIPS manuals你正在寻找的汇编源代码为一个发布6 MIPS 64,其中增加了一个新的指令来处理除法和模运算(DIV/MOD,DIVU/MODU,DDIV/DMOD,DDIVU/DMODU).
例如,对于DIVU(摘自手册):
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中,它是这样展开的:所以它主要检查除法是否可以完成(否则跳过
break
),然后执行divu
并得到r1
中的商由于您使用
$0
作为目标寄存器,因此使用伪指令没有多大意义。它将发出mflo $0
。如果您不介意
$3
为0,则可能只使用divu $2, $3
如注解中所述,然后使用
mfhi $2
按预期获得余数。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至少把
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型指令,对于
div
和divu
,机器指令中通常作为目的地的字段必须为零,因此这可能会使汇编器和反汇编器中的文本解析/格式化更容易。编译器知道如何布局分支本身,并且可以在适当的地方调度
mflo
,因此它使用divu $zero, src, src
版本。MIPS 32/64 r6有一条3操作数机器指令,取代旧的
这个答案的第一部分是关于在此之前的MIPS,从经典的MIPS I到MIPS 32/64 r5。
divu
是一个3操作数指令,操作码与以前不同。它将商写入第一个操作数。(LO和HI不存在,mflo
/mfhi
在R6中被删除。)divu
被记录为2操作数指令,其中隐式目的是LO(商)与HI(余数)。在发行版6中删除了这种形式。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)
是一个余数运算,而不是一个需要求商的/
运算符。