将值加载到寄存器中,然后将右移后的值放入其中的目的是什么?

huangapple go评论47阅读模式
英文:

What is the purpose of loading a value into a register before putting a right-shifted value into it?

问题

我正在反编译一个最初用C编写的PS2游戏。为了做到这一点,我正在阅读MIPS汇编语言并编写相应的C语句。

有一个重复出现的模式我一直没有能够弄清楚。考虑以下汇编代码:

    li     v1,2
    sra    v1,s1,1
    bgez   s1,*+16
    nop    
    addiu  v1,s1,1
    sra    v1,v1,1
    daddu  s1,v1,$0

立即数2被加载到寄存器v1中,然后立即被寄存器s1的右移一位的值覆盖。接下来,它检查s1是否为正数,如果不是,则将s1加一加载到v1中,并像之前一样右移一位。在两种情况下,结果然后被复制回s1。

在右移更多位的情况下,相同的模式也出现了。在这里,它将8位向右移动,立即数也相应更改以匹配:

    li     v1,256
    sra    v1,s1,8
    bgez   s1,*+16
    nop    
    addiu  v1,s1,255
    sra    v1,v1,8
    daddu  s1,v1,$0

经过一些思考,这使我想起了二进制补码,所以我认为这是使用C标准库函数abs()获取绝对值的结果。这有道理,因为我总是在这个片段之前遇到过值被用作数组索引的情况,而数组索引不能为负数。

但现在,我不太确定了,因为自那时以来,我看到它在允许并考虑负数的情况下使用过。

有人对这段代码是做什么有任何想法吗?

英文:

I'm decompiling a PS2 game that was originally coded in C. To do this, I'm reading MIPS assembler and writing the corresponding C statements.

There's one reoccurring pattern that I haven't been able to figure out. Consider the following assembly:

li     v1,2
sra    v1,s1,1
bgez   s1,*+16
nop    
addiu  v1,s1,1
sra    v1,v1,1
daddu  s1,v1,$0

The immediate value 2 is loaded into register v1, and is immediately overwritten by the one-bit-right-shifted value of register s1. Next, it checks if s1 was a positive number, and if it wasn't it loads s1 plus one into v1, and shifts it one bit to the right like before. In both cases, the result is then copied back to s1.

The same pattern has also appeared when shifting more bits to the right. Here, it shifts 8 bits to the right, and the immediate values are changed to match:

li     v1,256
sra    v1,s1,8
bgez   s1,*+16
nop    
addiu  v1,s1,255
sra    v1,v1,8
daddu  s1,v1,$0

After some thinking, this reminded me of two's complement, so I thought it was a result of using the C standard library function abs() to get the absolute value. It made sense, because I always encountered this snippet before the value would be used as an array index, which can't be negative.

Now, I'm not so sure, because since then I've seen it used in situations where negative numbers are allowed and accounted for.

Does anyone have any idea of what this code is for?

答案1

得分: 1

如果 li 不在较早分支的分支延迟槽中,它就没用。你说得对,它的结果会被覆盖,因此没有效果。

li 之后,代码看起来像是对 2256 做有符号除法,向 0 舍入,类似于 C 中的 x /= 256,而不是向负无穷舍入,类似于 x >>= 8

我想知道它是否用于调试或其他什么,用于在反汇编或调试器中查找块?既然你说游戏附带了调试符号,可能是出于这个原因而有意为之。

在这两种情况下,加载到 v1 中的值是除数,它正在执行 v1 = s1 / v1,但实际上没有使用来自 v1 的值。

或者可能是一些手写的汇编代码,或者是一个笨拙的编译器,它在寄存器优化方面出了错误,留下了一个无用的指令?

英文:

If the li isn't in the branch delay slot of an earlier branch, it's useless. You're right that its result is overwritten and thus has no effect.

After the li, the code looks like it's doing signed division by 2 or 256, rounding toward 0 like C x /= 256 instead of rounding toward -Inf like x >>= 8.

I wonder if it was for debugging or something, to find blocks in disassembly or in the debugger? Since you say the game came with debug symbols, it could perhaps be intentional for that reason.

In both cases the value loaded into v1 is the divisor, and it's doing v1 = s1 / v1, but not actually using the value from v1.

Or perhaps some hand-written asm, or a clunky compiler that got its peephole optimization wrong and left in a useless instruction?

huangapple
  • 本文由 发表于 2023年7月6日 19:17:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76628250.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定