Фреймы, локальные переменные, рекурсия

Ранее рассмотренная конвенция не обеспечивает некоторые возможности языков высокого уровня. Добавим некоторые из этих функций в новой конвенции о вызове процедур на основе стека.

  1. Сохранение регистров в стеке
    • Сохранение адреса возврата в стеке($ra)
    • Сохранение и восстановление регистров $s0―$s7 в/из стеке/стека
  2. Конвенция о вызове процедур на основе стека.
    • Это не официальное соглашение. Однако, оно может быть использовано для небольшого проекта на языке ассемблера. Конвенция не очень сложна и делает почти все, что может быть нужно. Если вы хотите использовать процедуры из программы на языке "C", использовать процедуры из библиотек, написанных на других ЯП или вызывать вашу процедуру из программы на ЯВУ, то необходимо использовать официальные правила в полном объеме. (Что в эмуляторе MARS невозможно.)
    • Правила:
      1. Вызов подпрограммы (делает вызывающая процедура):
        • Сохранить в стеке регистры "$t0 - $t9", которые должны быть сохранены(будут использованы вызывающей процедурой после вызова подпрограммы). Подпрограмма может изменить эти регистры.
        • Загрузить значения аргументов в регистры "$a0 - $a3".
        • Вызов подпрограммы с помощью "jal".
      2. Пролог подпрограммы:
        • Сохранить регистр "$ra" в стек.
        • Сохранить в стек регистры "$s0 - $s7"(подпрограмма может изменять их).
      3. Тело подпрограммы:
        • Подпрограмма может изменять регистры "$t0 - $t9", или те из регистров "$s0 - $s7", которые были сохранены в прологе.
        • Из подпрограммы можно вызывать другую подпрограмму, следуя этим правилам.
      4. Эпилог подпрограммы (непосредственно перед возвращением):
        • Загрузить возвращаемые значения в регистры "$v0 - $v1".
        • Извлечь из стека (в обратном порядке) сохраненые в прологе регистры "$s0 - $s7".
        • Извлечь из стека в регистр '$ra" адрес возврата.
        • Вернуться в вызывающую процедуру, используя "jr $ra".
      5. Восстановление состояния при выходе из подпрограммы:
        • Извлечь из стека (в обратном порядке) сохраненые регистры "$t0 - $t9".
  3. Пролог и эпилог вызываемой подпрограммы:
  4. Вызов и возврат.
  5. Вложенные вызовы подпрограмм и цепь активации.
  6. О реальных конвенциях(ABI).
  7. Пример программы:
    •    1 ## Driver --  main program for the application
         2 
         3          .text
         4          .globl main
         5  
         6 main:
         7          sub     $sp,$sp,4        # push the return address
         8          sw      $ra,($sp)
         9          sub     $sp,$sp,4        # push $s0
        10          sw      $s0,($sp)
        11          
        12          la      $a0,xprompt      # prompt the user
        13          li      $v0,4            # service 4
        14          syscall
        15          li      $v0,5            # service 5 -- read int
        16          syscall                  # $v0 = integer
        17          move    $s0,$v0          # save x
        18          
        19          la      $a0,yprompt      # prompt the user
        20          li      $v0,4            # service 4
        21          syscall
        22          li      $v0,5            # service 5 -- read int
        23          syscall                  # $v0 = integer
        24          
        25                                   # prepare arguments
        26          move    $a0,$s0          # x         
        27          move    $a1,$v0          # y
        28          jal     maxExp           # maximum expression
        29          nop                      # returned in $v0
        30          move    $s0,$v0          # keep it safe
        31 
        32          la      $a0,rprompt      # output title
        33          li      $v0,4            # service 4
        34          syscall
        35 
        36          move    $a0,$s0          # get maximum
        37          li      $v0,1            # print it out
        38          syscall
        39                                                       
        40          lw      $ra,($sp)        # pop $s0
        41          add     $s0,$sp,4
        42          lw      $ra,($sp)        # pop return address
        43          add     $sp,$sp,4
        44          
        45          li      $s0,0              # return to OS
        46          li      $v0,10
        47          syscall
        48          
        49          
        50          .data
        51 xprompt: .asciiz  "Enter a value for x --> "
        52 yprompt: .asciiz  "Enter a value for y --> "
        53 rprompt: .asciiz  "The maximum expression is: "
        54 
        55 ## maxInt -- compute the maximum of two integer arguments
        56 ##
        57 ## Input:
        58 ## $a0 -- a signed integer
        59 ## $a1 -- a signed integer
        60 ##
        61 ## Returns:
        62 ## $v0 -- maximum
        63 
        64          .text
        65          .globl maxInt
        66 
        67 maxInt:
        68          # body
        69          move   $v0,$a0          # max = $a0
        70          bgt    $a0,$a1,endif    # if $a1 > $a0  
        71          nop
        72          move   $v0,$a1          #    max = $a1
        73 endif:                           # endif 
        74          # epilog
        75          jr     $ra              # return to caller
        76          nop
        77           
        78 ## maxExp -- compute the maximum of three expressions
        79 ##
        80 ## Input:
        81 ## $a0 -- a signed integer, x
        82 ## $a1 -- a signed integer, y
        83 ##           
        84 ## Returns: 
        85 ## $v0 -- the maximum of x*x,  x*y, or 5*y
        86 ##
        87 ## Registers:
        88 ## $s0 --  x*x
        89 ## $s1 --  x*y
        90 ## $s2 --  5*y
        91 
        92          .text
        93          .globl maxExp
        94 
        95 maxExp:
        96          # prolog
        97          sub     $sp,$sp,4        # push the return address
        98          sw      $ra,($sp)
        99          sub     $sp,$sp,4        # push $s0
       100          sw      $s0,($sp)
       101          sub     $sp,$sp,4        # push $s1
       102          sw      $s1,($sp)
       103          sub     $sp,$sp,4        # push $s2
       104          sw      $s2,($sp)
       105 
       106          # body
       107          mul     $s0,$a0,$a0      # x*x
       108          mul     $s1,$a0,$a1      # x*y
       109          li      $t0,5
       110          mul     $s2,$t0,$a1      # 5*y
       111          
       112          move    $a0,$s0          # compute max of x*x
       113          move    $a1,$s1          # and x*y
       114          jal     maxInt           # current max in $v0
       115          nop
       116 
       117          move    $a0,$v0          # compute max of
       118          move    $a1,$s2          # current max, and 5*y 
       119          jal     maxInt           # total max will be in $v0
       120          nop
       121          
       122          # epilog
       123          lw      $s2,($sp)        # pop $s2 
       124          add     $sp,$sp,4                                    
       125          lw      $s1,($sp)        # pop $s1 
       126          add     $sp,$sp,4                                    
       127          lw      $s0,($sp)        # pop $s0 
       128          add     $sp,$sp,4                                    
       129          lw      $ra,($sp)        # pop return address
       130          add     $sp,$sp,4         
       131          jr      $ra              # return to caller 
       132          nop         
      
  8. Локальные переменные.
  9. Кадр стека.
  10. Конвенция о вызове процедур с использованием указателя кадра стека.
    • Это не официальное соглашение. В большинстве реальных MIPS-ABI использование указателя кадра стека факультативно. В широко распространенной архитектуре "intel32/64" указателя кадра стека активно используется.
    1. Вызов подпрограммы (делает вызывающая процедура):
      • Сохранить в стеке регистры "$t0 - $t9", которые должны быть сохранены(будут использованы вызывающей процедурой после вызова подпрограммы). Подпрограмма может изменить эти регистры.
      • Загрузить значения аргументов в регистры "$a0 - $a3".
      • Вызов подпрограммы с помощью "jal".
    2. Пролог подпрограммы:
      • Сохранить регистр "$ra" в стек.
      • Сохранить регистр "$fp" в стек.
      • Сохранить в стек регистры "$s0 - $s7"(подпрограмма может изменять их).
      • Инициализировать указатель кадра: $fp = ($sp - размер пространства для локальных переменных).
        • NB Помните, что вычитание из $sp увеличивает стек, что стек растет вниз, что размер переменной всегда четыре байта.

      • Инициализировать указателя стека: $sp = $fp.
    3. Тело подпрограммы:
      • Подпрограмма может изменять регистры "$t0 - $t9", или те из регистров "$s0 - $s7", которые были сохранены в прологе.
      • Из подпрограммы можно вызывать другую подпрограмму, следуя этим правилам.
      • Подпрограмма обращается к локальным переменным, как disp($fp).
      • Подпрограмма может работать со стеком, используя регистр "$sp".
    4. Эпилог подпрограммы (непосредственно перед возвращением):
      • Загрузить возвращаемые значения в регистры "$v0 - $v1".
      • $sp = ($fp + размер пространства для локальных переменных).
      • Извлечь из стека (в обратном порядке) сохраненные в прологе регистры "$s0 - $s7".
      • Извлечь из стека в регистр "$fp" адрес кадра стека.
      • Извлечь из стека в регистр '$ra" адрес возврата.
      • Вернуться в вызывающую процедуру, используя "jr $ra".
    5. Восстановление состояния при выходе из подпрограммы (делает вызывающая процедура):
      • Извлечь из стека (в обратном порядке) сохраненые регистры "$t0 - $t9".
  11. Пример программы:
       1 ## file variablesStack.asm
       2 
       3 #  int mysub( int arg )
       4 #  {
       5 #    int b,c;                     // b: 0($fp)
       6 #                                 // c: 4($fp)
       7 #    b = arg*2;
       8 #    c = b + 7;
       9 #    
      10 #    return c;  
      11 #  }
      12          .text
      13          .globl  mysub
      14 mysub:
      15                                   # prolog        
      16          sub     $sp,$sp,4        #   1. Push return address
      17          sw      $ra,($sp)
      18          sub     $sp,$sp,4        #   2. Push caller's frame pointer
      19          sw      $fp,($sp)
      20          sub     $sp,$sp,4        #   3. Push register $s1
      21          sw      $s1,($sp)
      22          sub     $fp,$sp,8        #   4. $fp = $sp - space_for_variables
      23          move    $sp,$fp          #   5. $sp = $fp
      24          
      25                                   # body of subroutine     
      26          mul     $s1,$a0,2        #     arg*2
      27          sw      $s1,0($fp)       # b = "   "
      28          
      29          lw      $t0,0($fp)       # get b
      30          add     $t0,$t0,7        #     b+7
      31          sw      $t0,4($fp)       # c = "  "
      32          
      33                                   # epilog
      34          lw      $v0,4($fp)       #   1. Put return value in $v0        
      35          add     $sp,$fp,8        #   2. $sp = $fp + space_for_variables
      36          lw      $s1,($sp)        #   3. Pop register $s1
      37          add     $sp,$sp,4        #          
      38          lw      $fp,($sp)        #   4. Pop $fp
      39          add     $sp,$sp,4        #           
      40          lw      $ra,($sp)        #   5. Pop $ra
      41          add     $sp,$sp,4        #            
      42          jr      $ra              #   6. return to caller 
      43 
      44 #  main()
      45 #  {
      46 #    int a;                      // a: 0($fp)
      47 #    a = mysub( 6 );
      48 #    print( a );
      49 #  }
      50          .text
      51          .globl  main
      52 main:
      53                                   # prolog        
      54          sub     $sp,$sp,4        #   1. Push return address
      55          sw      $ra,($sp)
      56          sub     $sp,$sp,4        #   2. Push caller's frame pointer
      57          sw      $fp,($sp)
      58                                   #   3. No S registers to push
      59          sub     $fp,$sp,4        #   4. $fp = $sp - space_for_variables
      60          move    $sp,$fp          #   5. $sp = $fp
      61 
      62                                   # subroutine call
      63                                   #   1. No T registers to push
      64          li      $a0,6            #   2. Put argument into $a0
      65          jal     mysub            #   3. Jump and link to subroutine
      66          
      67                                   # return from subroutine 
      68                                   #   1. No T registers to restore
      69                                   
      70          sw     $v0,0($fp)        # a = mysub( 6 )
      71         
      72                                   # print a
      73          lw     $a0,0($fp)        # load a into $a0
      74          li     $v0,1             # print integer service
      75          syscall                                  
      76                                   # epilog
      77                                   #   1. No return value         
      78          add     $sp,$fp,4        #   2. $sp = $fp + space_for_variables       
      79                                   #   3. No S registers to pop      
      80          lw      $fp,($sp)        #   4. Pop $fp
      81          add     $sp,$sp,4        #           
      82          lw      $ra,($sp)        #   5. Pop $ra
      83          add     $sp,$sp,4        #                                    
      84 
      85          li      $s0,0              # return to OS
      86          li      $v0,10
      87          syscall
    
  12. Приложения:
    • MIPS ABI

    MIPS ABI History

    MIPS ABIs Described

    SYSTEM V APPLICATION BINARY INTERFACE

    MIPSpro TM N32 ABI Handbook

    mips eabi documentation...

ArchitectureAssembler2015/13_FramesLocals (последним исправлял пользователь FrBrGeorge 2024-06-17 12:52:29)