Должен ли я после передачи адреса массива в функцию в MIPs сохранять значения массива в стек?

#mips

#mips

Вопрос:

Я должен перевести эту функцию C в MIPS.

 void updatePoint(int *arg)
    {
      int distance;

      distance = findDistance(arg[0],arg[1],0,-1);
      if ((distance < 1) amp;amp; (arg[2] < 0))
        arg[2] = - arg[2];
      distance = findDistance(arg[0],arg[1],10,-1);
      if ((distance < 1) amp;amp; (arg[2] > 0))
        arg[2] = - arg[2];
      distance = findDistance(arg[0],arg[1],-1,0);
      if ((distance < 1) amp;amp; (arg[3] < 0))
        arg[3] = - arg[3];
      distance = findDistance(arg[0],arg[1],-1,10);
      if ((distance < 1) amp;amp; (arg[3] > 0))
        arg[3] = - arg[3];
       arg[0] = arg[0]   arg[2];
      arg[1] = arg[1]   arg[3];
      return;
}
 

Вот код MIPS, ведущий к вызову функции.

         .data
redData:    .word   0:4
greenData:      .word   0:4
prmpt1:  .asciiz "Enter x-coordinate for red particle (0 to 10):"
prmpt2:  .asciiz "Enter y-coordinate for red particle (0 to 10):"
prmpt3:  .asciiz "Enter x-coordinate for green particle (0 to 10):"
prmpt4:  .asciiz "Enter y-coordinate for green particle (0 to 10):"
prmpt5:  .asciiz "cycle "
prmpt6:  .asciiz "red particle (x,y,xVel,yVel): "
prmpt7:  .asciiz "green particle (x,y,xVel,yVel): "
prmpt8:  .asciiz "Collison: oops, end of simulation!n"
space:   .asciiz " "
endl:    .asciiz "n"

# i     $s0
# cycle $s1 = 0
# dist  $s2

.text

main:   li      $s1,0

la      $s3,redData     #  redData[2] = 1 ;
li      $s4,1
sw      $s4,8($s3)
sw      $s4,12($s3)     #  redData[3] = 1 ;
la      $s3,greenData   #  greenData[2] = -1 ;
li      $s4,-1
sw      $s4,8($s3)
sw      $s4,12($s3)     #  greenData[3] = -1 ;

la      $a0,prmpt1      #  cout << prmpt1 ;
li      $v0,4
syscall
la      $s3,redData
li      $v0,5           #  cin >> redData[0] ;
syscall
sw      $v0,($s3)
la      $a0,prmpt2      #  cout << prmpt2 ;
li      $v0,4
syscall
li      $v0,5           #  cin >> redData[1] ;
syscall
sw      $v0,4($s3)
la      $a0,prmpt3      #  cout << prmpt3 ;
li      $v0,4
syscall
la      $s3,greenData   #  cin >> greenData[0] ;
li      $v0,5
syscall
sw      $v0,($s3)
la      $a0,prmpt4      #  cout << prmpt4 ;
li      $v0,4
syscall
li      $v0,5           #  cin >> greenData[1] ;
syscall
sw      $v0,4($s3)

loop:                           #  do {
la      $a0,prmpt5      #    cout << "cycle " << cycle << endl ;
li      $v0,4
syscall
move    $a0,$s1
li      $v0,1
syscall
la      $a0,endl
li      $v0,4
syscall
la      $a0,prmpt6      #    cout << "red particle (x,y,xVel,yVel): "
li      $v0,4
syscall
la      $s3, redData
lw      $a0,($s3)       #       << redData[0]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,4($s3)      #       << redData[1]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,8($s3)      #       << redData[2]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,12($s3)     #       << redData[3]
li      $v0,1
syscall
la      $a0,endl        #       << endl ;
li      $v0,4
syscall
la      $a0,prmpt7      #    cout << "green particle (x,y,xVel,yVel): "
li      $v0,4
syscall
la      $s3, greenData
lw      $a0,($s3)       #       << greenData[0]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,4($s3)      #       << greenData[1]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,8($s3)      #       << greenData[2]
li      $v0,1
syscall
la      $a0,space       #       << " "
li      $v0,4
syscall
lw      $a0,12($s3)     #       << greenData[3]
li      $v0,1
syscall
la      $a0,endl        #       << endl ;
li      $v0,4
syscall
la      $a0,endl        #       << endl ;
li      $v0,4
syscall

la      $a0,redData     #    updatePoint(redData) ;
jal     updatePoint
 

Для точки обновления я должен сохранять значения arg[0] — arg[2] в стек? Для этого я должен сделать

 lw      $s0, 0($a0) 
lw      $s1, 4($a0)
lw      $s2, 8($a0)
lw      $s3, 12($a0)
addi    $sp, $sp, -20
lw      $s0, 0($sp) 
lw      $s1, 4($sp)
lw      $s2, 8($sp)
lw      $s3, 12($sp)
lw      $ra, 16($sp)
 

Ответ №1:

Для точки обновления я должен сохранять значения arg[0] — arg[2] в стек?

Нет, arg это массив. Он передается по ссылке (т. Е. Указатель / адрес arg находится внутри $a0 ). arg Ожидается, что при возврате значения в будут изменены на updatePoint . Он изменит arg[0] и arg[1] (т. Е. См. Последние две строки updatePoint ) и, условно, может изменять arg[2] и arg[3] по пути.

На каждом шаге updatePoint он должен использовать обновленные значения, arg и по возвращении вызывающий объект updatePoint должен получать обратно arg измененные значения.

Ничто в updatePoint не позволит arg изменять, findDistance потому что все аргументы findDistance передаются по значению.

Итак, вам нужно сохранить [значение указателя в] $a0 updatePoint , чтобы предотвратить потерю этого значения указателя arg при выполнении вызовов findDistance .

Вы могли бы сохранить его во фрейме стека для updatePoint , но вам пришлось бы перезагружать его после каждого вызова findDistance . Лучше сохранить его в сохраненном вызываемом регистре (например $s0 ), чтобы это findDistance не мешало ему.


Вот пример, который выполняет первый вызов findDistance . Вам должно быть легко заполнить остальные, а также последние две строки C . Обратите внимание, что больше ничего не нужно сохранять в updatePoint [это сильный намек]. Вы можете использовать столько других временных правил, сколько захотите.

 # void
# updatePoint(int *arg)
# {
#     int distance;
#
#     distance = findDistance(arg[0], arg[1], 0, -1);
#     if ((distance < 1) amp;amp; (arg[2] < 0))
#         arg[2] = -arg[2];
#
#     distance = findDistance(arg[0], arg[1], 10, -1);
#     if ((distance < 1) amp;amp; (arg[2] > 0))
#         arg[2] = -arg[2];
#
#     distance = findDistance(arg[0], arg[1], -1, 0);
#     if ((distance < 1) amp;amp; (arg[3] < 0))
#         arg[3] = -arg[3];
#
#     distance = findDistance(arg[0], arg[1], -1, 10);
#     if ((distance < 1) amp;amp; (arg[3] > 0))
#         arg[3] = -arg[3];
#
#     arg[0] = arg[0]   arg[2];
#     arg[1] = arg[1]   arg[3];
# }
updatePoint:
    subu    $sp,$sp,8
    sw      $ra,4($sp)
    sw      $s0,0($sp)

    # NOTE: now that we've preserved $s0 of our caller, we are free to use it
    # for whatever we want -- we use it to preserve the address of 'arg'
    move    $s0,$a0                 # preserve address of 'arg'

    #     distance = findDistance(arg[0], arg[1], 0, -1);
    lw      $a0,0($s0)              # get arg[0]
    lw      $a1,4($s0)              # get arg[1]
    li      $a2,0                   # set 0
    li      $a3,-1                  # set -1
    jal     findDistance

    #     if ((distance < 1) amp;amp; (arg[2] < 0))
    bge     $v0,1,noset1            # distance < 1? if no, fly
    lw      $v0,8($s0)              # get arg[2]
    bgez    $v0,noset1              # is arg[2] < 0? if no, fly

    #         arg[2] = -arg[2];
    neg     $v0,$v0                 # get -arg[2]
    sw      $v0,8($s0)              # set arg[2] = -arg[2]
noset1:

    # with adjusted parameters repeat the above for all three remaining calls

    # perform last two lines of function ...

    lw      $ra,4($sp)
    lw      $s0,0($sp)
    addu    $sp,$sp,8
    jr      $ra

findDistance:
 

Обратите внимание, что вызываемая функция должна только сохраняться $s0-$s7 , должна оставаться $sp такой, какой она ее нашла, и должна сохраняться $ra , чтобы она могла выполнять jr $ra возврат.

Например $ra , функция может сохранить его в (например) $t3 и позже сделать jr $t3 . Вызывающему абоненту все равно, $ra сохранен он или нет (т. Е. Выполняйте jr с любым reg, который вы пожелаете, идиоматическим $ra ). ). Просто убедитесь, что функция возвращается в правильное место.

В приведенном выше коде обратите внимание, что после вызова to findDistance проверяется возвращаемое значение [in $v0 ] . После этого возвращаемое значение не требуется, и код использует $v0 в качестве временного (против (например) $t0 )).

Аналогично, вызываемая функция может использовать регистры аргументов $a0-$a3 для всего, что пожелает (т. Е. Они могут использоваться в качестве временных регистров во время вычислений)


Вот ваша функция c , перекодированная в то, что я называю «простым C». Это гораздо больше похоже на прямой перевод в asm:

 void
updatePoint(int *arg)
{
    int distance;

    distance = findDistance(arg[0], arg[1], 0, -1);
    if (distance >= 1)
        goto noset1;
    if (arg[2] >= 0)
        goto noset1;
    arg[2] = -arg[2];
noset1:

    distance = findDistance(arg[0], arg[1], 10, -1);
    if (distance >= 1)
        goto noset2;
    if (arg[2] <= 0)
        goto noset2;
    arg[2] = -arg[2];
noset2:

    distance = findDistance(arg[0], arg[1], -1, 0);
    if (distance >= 1)
        goto noset3;
    if (arg[3] >= 0)
        goto noset3;
    arg[3] = -arg[3];
noset3:

    distance = findDistance(arg[0], arg[1], -1, 10);
    if (distance >= 1)
        goto noset4;
    if (arg[3] <= 0)
        goto noset4;
    arg[3] = -arg[3];
noset4:

    arg[0] = arg[0]   arg[2];
    arg[1] = arg[1]   arg[3];
}