如何在 MIPS 中找到数组的最小值

How to find the minimum value of an array in MIPS

这是我的代码,我无法获得正确的输出。我哪里出错了?我最初将 min 设置为零,然后检查数组是否小于或等于该值,然后如果是我跳转到一个标签并将 min 的值设为数组的值,然后跳回迭代数组。

xyz:    .word   -8, 16, -32, 64, -128, 256 

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2  
#   int total   $s3
#   
    .text
    .globl  main
main:
    la  $s0, xyz                # p = foo
    addi    $s1, $s0, 24        # end = p + 6
    add $s3, $zero, $zero       # total = 0 
    add     $s2, $zero, $zero   # min = 0
L1:
    beq $s0, $s1, L2    # if (p == end) goto L2
    lw  $t0, ($s0)      # $t0 = *p
    lw  $t1, ($s2)      # $t1 = min
    slt $t2, $t1, $t0       # check if min is less than p
    add $s3, $s3, $t0       # total += $t0
    bne     $t2, $zero, L3      # if min is less than p, go to L3 
    addi    $s0, $s0, 4     # p++
    j   L1
L2:     
    add $v0, $zero, $zero   # return value from main = 0
    jr  $ra

L3:
    move    $s2, $t0
    j   L1

好的,我发现了几个错误。我已经创建了三个版本并添加了输出系统调用,因此您可以看到结果[请原谅不必要的样式清理]:


这是带有错误注释的原始代码:

    .data
xyz:        .word       -8,16,-32,64,-128,256

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    # NOTE/BUG: to find minimum, you want to init this to the first array
    # element
    # also, initializing with a minimum value (e.g. 0), or more correctly, the
    # largest possible negative number (e.g. 0x80000000) implies a search for
    # maximum
    add     $s2,$zero,$zero         # min = 0

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2
    lw      $t0,($s0)               # $t0 = *p

    # NOTE/BUG: s2 is a register variable that contains "min" (e.g. int min)
    # and is _not_ a pointer to a "min" variable in memory (e.g. int *min)
    lw      $t1,($s2)               # $t1 = min

    # NOTE/BUG: the the check should be reversed:
    slt     $t2,$t1,$t0             # check if min is less than p
    add     $s3,$s3,$t0             # total += $t0

    bne     $t2,$zero,L3            # if min is less than p, go to L3

    # NOTE/BUG: this pointer increment is out of place (i.e. it does not
    # get incremented if there is a jump to L3)
    # this won't affect the min value, but it will double count the value in
    # the total
    addi    $s0,$s0,4               # p++
    j       L1

L2:
    add     $v0,$zero,$zero         # return value from main = 0
    jr      $ra

L3:
    move    $s2,$t0
    j       L1

这里是固定版本:

    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$t0,$s2             # *p < min?
    bne     $t2,$zero,L3            # yes, fly

    j       L1

L2:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

L3:
    move    $s2,$t0                 # set new/better min value
    j       L1

这是一个稍微更简洁的版本,我颠倒了分支的意义,并且能够稍微收紧循环。此外,我将标签更改为更能描述 role/function:

    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$s2,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    move    $s2,$t0                 # set new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

更新:

thanks that helped a lot, but lw $t1,($s2) doesnt work because lw will only work on pointers?

没错。请注意您是如何使用 s3 来保持 total 的。这就是代码使用 s2 来保存最小值的方式。我这样做[部分]是因为最高评论:

#   int min     $s2

要使用 lw,置顶评论应该是:

#   int *min    $s2

要以原始方式使用 s2,您需要如下内容:

min:    .word    0

而且,您需要(在循环开始之前):

la    $s2,min

而且,您必须 lwsw,这只会减慢速度。因此,除了已有的内容之外,您还需要添加一个额外的 lw 和一个额外的 sw

mips 有很多 寄存器[它的强项]。因此,它加快了速度,以在其中保留自动的、函数范围的变量。

但是,为了完整起见,这里有一个允许 lw 的版本。注意额外的内存访问。这很像用 -O0:

编译的 C 代码
    .data
min:        .word       0
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int *min    $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    la      $s2,min                 # point to min
    lw      $t4,0($s0)              # fetch xyz[0]
    sw      $t4,0($s2)              # store in min

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    lw      $t4,0($s2)              # fetch min
    slt     $t2,$t4,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    sw      $t0,0($s2)              # store new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    lw      $a0,0($s2)              # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall