如何在MIPS中实现if(condition1&& condition2)? [英] How can I implement if(condition1 && condition2) in MIPS?

查看:547
本文介绍了如何在MIPS中实现if(condition1&& condition2)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了以下函数来检查字符是否为数字:

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte
# return value:
#   $v0 =   1 - digit
#           0 - not a digit
IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1
condition1:
    ble $t0, $t2, condition2

    li $v0, 0
    j return

condition2:
    li $v0, 1

return:                                                                                 
    # return 
    jr $ra 

有没有更好的方法来编写或编写此代码?

编辑:以下是版本2

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1

    j zero

condition1: 

    ble $t0, $t2, condition2

zero:
    li $v0, 0
    j return

condition2:
    li $v0, 1
    j return

return:                                                                                 
    # return 
    jr $ra 

编辑2 :以下是版本3

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character


    bge $t0, $t1, con1_fulfilled #bigger tha or equal to 0  
    j con1_not_fulfilled

con1_fulfilled:
    ble $t0, $t2, con2_fullfilled #less than or equal to 9
    j con2_not_fulfilled

con2_fullfilled:
    li $v0, 1
    j return

con1_not_fulfilled:
con2_not_fulfilled:
    li $v0, 0

return:                                                                     
    # return 
    jr $ra 

解决方案

在一般情况下,您使用2个分支,这些分支经过if()主体.如果采用任何一种,则if主体将不运行.在汇编中,您通常要使用C条件的否定,因为您要跳过循环主体,因此它不会运行.您的新版本会向后执行此操作,因此也需要无条件的j指令,从而使您的代码更加复杂.

<=(le)的反面是>(>).对于编写为使用包含范围(le和ge)的C,使用相同数值的asm应该在使用排斥范围的相反条件下分支(不包括eq ual情况).或者,您可以调整常量和bge $t0, '9'+1或其他任何值,这可以在适合16位立即数的末尾使用.

# this does assemble with MARS or clang, handling pseudo-instructions
# and I think it's correct.
IsDigit:
    lb  $t0, ($a0)    # obtain the character

    blt   $t0, '0', too_low    # if(   $t0 >= '0'  
    bgt   $t0, '9', too_high   #    && $t0 <= '9')
      # fall through into the if body
    li    $v0, 1
    jr    $ra                    # return 1

too_low:
too_high:                    # } else {
    li    $v0, 0

#end_of_else:
    jr    $ra                # return 0

如果这不是在函数的末尾,则可以从if主体的末尾j end_of_else跳过else块.或者在这种情况下,我们可以将li $v0, 0放在第一个blt的前面,以填充负载延迟时隙,而不必使管道停滞. (当然,真正的MIPS也具有分支延迟插槽,并且您不能具有背对背分支.但是bgt无论如何都是伪指令,因此不会真正背对背-后分支.)

此外,我没有跳到常见的jr $ra,而是将jr $ra复制到了另一个返回路径中.如果您需要执行更多清理工作,则可以跳到一条常见的返回路径.否则,尾部重复对于简化分支是一件好事.


在这种特定情况下,您的条件与之相关:您正在进行范围检查,因此只需要1个sub,然后再对范围长度进行1个无符号比较. ^ = 32背后的想法是什么,将小写字母转换为大写字母,反之亦然?了解有关ASCII字符的范围检查的更多信息.

并且由于返回的是布尔值0/1,所以根本不希望分支,而是使用sltu将条件在寄存器中转换为0或1. . (这就是MIPS用来代替x86或ARM之类的FLAGS寄存器的功能).无论如何,两个寄存器之间的ble之类的指令都是slt + bne的伪指令. MIPS在硬件上确实具有blezbltz,并且在两个寄存器之间也具有bnebeq.


顺便说一句,IsDigit上的注释与代码不匹配:他们说$a0 一个字符,但实际上您是使用$a0作为指向的指针加载一个角色.因此,您无缘无故通过引用传递char ,或者传递字符串并采用第一个字符.

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte (must be zero-extended, or sign-extended which is the same thing for low ASCII bytes like '0'..'9')
# return value:
#   $v0 = boolean: 1 -> it is an ASCII decimal digit in [0-9]

IsDigit:
    addiu   $v0, $a0, -'0'            # wraps to a large unsigned value if below '0'
    sltiu   $v0, $v0, 10              # $v0 = bool($v0 < 10U)  (unsigned compare)
    jr      $ra

MARS的汇编器拒绝立即汇编-'0',您必须将其编写为-48-0x30. clang的汇编器对addiu $v0, $a0, -'0'没问题.

如果您编写subiu $v0, $a0, '0',则MARS会使用Braindead lui + ori构造'0',因为对于大多数汇编程序不支持的扩展伪指令而言,它非常简单. (MIPS没有subi指令,只有addi/addiu,它们都使用符号扩展的立即数.)

I have written the following function to check whether a character is a digit or not:

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte
# return value:
#   $v0 =   1 - digit
#           0 - not a digit
IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1
condition1:
    ble $t0, $t2, condition2

    li $v0, 0
    j return

condition2:
    li $v0, 1

return:                                                                                 
    # return 
    jr $ra 

Is there any better way to do or write this?

Edit: The following is the version-2

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1

    j zero

condition1: 

    ble $t0, $t2, condition2

zero:
    li $v0, 0
    j return

condition2:
    li $v0, 1
    j return

return:                                                                                 
    # return 
    jr $ra 

Edit-2: the following is version-3

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character


    bge $t0, $t1, con1_fulfilled #bigger tha or equal to 0  
    j con1_not_fulfilled

con1_fulfilled:
    ble $t0, $t2, con2_fullfilled #less than or equal to 9
    j con2_not_fulfilled

con2_fullfilled:
    li $v0, 1
    j return

con1_not_fulfilled:
con2_not_fulfilled:
    li $v0, 0

return:                                                                     
    # return 
    jr $ra 

解决方案

In the general case, you use 2 branches that go to past the if() body. If either one is taken, the if body doesn't run. In assembly, you usually want to use the negation of the C condition, because you're jumping past the loop body so it doesn't run. Your later version does it backwards so also need unconditional j instructions, making your code extra complicated.

The opposite of <= (le) is > (gt). For C written to use inclusive ranges (le and ge), asm using the same numerical values should branch on the opposite conditions using exclusive ranges (that exclude the equal case). Or you can adjust your constants and bge $t0, '9'+1 or whatever, which can be useful right at the end of what fits into a 16-bit immediate.

# this does assemble with MARS or clang, handling pseudo-instructions
# and I think it's correct.
IsDigit:
    lb  $t0, ($a0)    # obtain the character

    blt   $t0, '0', too_low    # if(   $t0 >= '0'  
    bgt   $t0, '9', too_high   #    && $t0 <= '9')
      # fall through into the if body
    li    $v0, 1
    jr    $ra                    # return 1

too_low:
too_high:                    # } else {
    li    $v0, 0

#end_of_else:
    jr    $ra                # return 0

If this wasn't at the end of a function, you could j end_of_else from the end of the if body to jump over the else block. Or in this case, we could have put the li $v0, 0 ahead of the first blt, to fill the load delay slot instead of stalling the pipeline. (Of course a real MIPS also has branch-delay slots, and you can't have back-to-back branches. But bgt is a pseudo-instruction anyway so there aren't wouldn't really be back-to-back branches.)

Also, instead of jumping to a common jr $ra, I simply duplicated the jr $ra into the other return path. If you had more cleanup to do, you might jump to one common return path. Otherwise tail duplication is a good thing to simplify the branching.


In this specific case, your conditions are related: you're doing a range-check so you only need 1 sub and then 1 unsigned-compare against the length of the range. See What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa? for more about range-checks on ASCII characters.

And since you're returning a boolean 0/1, you don't want to branch at all, but rather use sltu to turn a condition into a 0 or 1 in a registers. (This is what MIPS uses instead of a FLAGS register like x86 or ARM). Instructions like ble between two registers are pseudo-instructions for slt + bne anyway; MIPS does have blez and bltz in hardware, as well as bne and beq between two registers.


And BTW, the comments on your IsDigit don't match the code: they say that $a0 is a character, but actually you're using $a0 as a pointer to load a character. So you're passing a char by reference for no apparent reason, or passing a string and taking the first character.

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte (must be zero-extended, or sign-extended which is the same thing for low ASCII bytes like '0'..'9')
# return value:
#   $v0 = boolean: 1 -> it is an ASCII decimal digit in [0-9]

IsDigit:
    addiu   $v0, $a0, -'0'            # wraps to a large unsigned value if below '0'
    sltiu   $v0, $v0, 10              # $v0 = bool($v0 < 10U)  (unsigned compare)
    jr      $ra

MARS's assembler refuses to assemble -'0' as an immediate, you have to write it as -48 or -0x30. clang's assembler has no problem with addiu $v0, $a0, -'0'.

If you write subiu $v0, $a0, '0', MARS constructs '0' using a braindead lui+ori, because it's very simplistic for extended pseudo-instructions that most assemblers don't support. (MIPS doesn't have a subi instruction, only addi/addiu, both of which take sign-extended immediates.)

这篇关于如何在MIPS中实现if(condition1&amp;&amp; condition2)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆