Не можете понять использование стека в программе? - PullRequest
1 голос
/ 22 октября 2011

Хорошо, поэтому моя задача состояла в том, чтобы изменить этот код для подсчета гласных как в верхнем, так и в нижнем регистре. Цель программы - продемонстрировать использование стека для сохранения данных при вызове функций:

##
## vowel.a - prints out number of vowels in  
##         - the string str
##
##  a0 - points to the string
##

#################################################
#                                               #
#                 text segment                  #
#                                               #
#################################################

            .text           
            .globl __start 
   __start:         # execution starts here


            la $a0,str
            jal vcount      # call vcount

            move $a0,$v0
            li $v0,1
            syscall         # print answer


            la $a0,endl
            li $v0,4
            syscall         # print newline

            li $v0,10
            syscall         # au revoir...

           #------------------------------------------------
           # vowelp - takes a single character as a
           # parameter and returns 1 if the character 
           # is a (lower case) vowel otherwise return 0.
           #        a0 - holds character
           #        v0 - returns 0 or 1
           #------------------------------------------------

           vowelp:  li $v0,0
           beq  $a0,'a',yes
           beq  $a0,'e',yes
           beq  $a0,'i',yes
           beq  $a0,'o',yes
           beq  $a0,'u',yes
           jr $ra
           yes:     li $v0,1
           jr $ra


           #------------------------------------------------
           # vcount - use vowelp to count the vowels in a
           # string.
           #        a0 - holds string address
           #        s0 - holds number of vowels
           #        v0 - returns number of vowels
           #------------------------------------------------

           vcount:  
           sub $sp,$sp,16   # save registers on stack
           sw $a0,0($sp)
           sw $s0,4($sp)
           sw $s1,8($sp)
           sw $ra,12($sp)

           li $s0,0 # count of vowels
           move $s1,$a0     # address of string

           nextc:   lb $a0,($s1)    # get each character
           beqz $a0,done    # zero marks end
           jal vowelp       # call vowelp 
           add $s0,$s0,$v0  # add 0 or 1 to count
           add $s1,$s1,1    # move along string
           b nextc
           done:    move $v0,$s0    # use $v0 for result

           lw $a0,0($sp)    # restore registers
           lw $s0,4($sp)
           lw $s1,8($sp)
           lw $ra,12($sp)
           add $sp,$sp,16
           jr $ra


    #################################################
    #                                               #
    #               data segment                    #
    #                                               #
    #################################################


           .data
     str:   .asciiz "long time ago in a galaxy far away"
     endl:  .asciiz "\n"

     ##
     ## end of file vowel.a

мой модифицированный код, который работает:

    ##
    ## vowel.a - prints out number of vowels in  
    ##         - the string str
    ##
    ##      a0 - points to the string
    ##

    #################################################
    #                                               #
    #               text segment                    #
    #                                               #
    #################################################

            .text           
            .globl __start 
    __start:                # execution starts here

            la $a0,str
            jal vcount      # call vcount

            move $a0,$v0
            li $v0,1
            syscall         # print answer

            la $a0,endl
            li $v0,4
            syscall         # print newline

            move $a0,$t0
            li $v0,1
            syscall

            la $a0,endl
            li $v0,4
            syscall

            li $v0,10
            syscall         # au revoir...

            vowell: li $v0,0
            beq  $a0,'a',yes
            beq  $a0,'e',yes
            beq  $a0,'i',yes
            beq  $a0,'o',yes
            beq  $a0,'u',yes
            jr $ra
            yes:    li $v0,1
                    jr $ra
            vowelu:
            li $v0,0
            beq $a0,'A',yes
            beq $a0,'E',yes
            beq $a0,'I',yes
            beq $a0,'O',yes
            beq $a0,'U',yes
                    jr $ra

            vcount:
            sub $sp,$sp,20
            sw $a0,0($sp)
            sw $s0,4($sp)
            sw $s1,8($sp)
            sw $ra,12($sp)
            sw $s2,16($sp)

            li $s0,0
            li $s2,0
            move $s1,$a0

            nextc:
            lb $a0,($s1)
            beqz $a0,done
            jal vowell
            add $s0,$s0,$v0
            jal vowelu
            add $s2,$s2,$v0
            add $s1,$s1,1
            b nextc
            done:
            move $v0,$s0
            move $t0,$s2

            lw $a0,0($sp)
            lw $s0,4($sp)
            lw $s1,8($sp)
            lw $ra,12($sp)
            lw $s2,16($sp)
            add $sp,$sp,20
            jr $ra

            .data
            str:    .asciiz "Long Time Ago in a Galaxy Far Far Away"
            endl:   .asciiz "\n"

Я не понимаю, для чего блок lw в конце. Программа хранит счет в s0 и t0 соответственно, так какой смысл? Похоже, это просто восстановление исходных значений в конце. Уууууууууууууууууууу, а это было просто, чтобы продемонстрировать, что это возможно?

Ответы [ 3 ]

1 голос
/ 22 октября 2011

Я не очень разбираюсь в MIPS, но я предполагаю, что идея похожа на x86.

Из того, что вы сказали, последний LW восстанавливает исходные значения.Это происходит потому, что вы можете вызывать функцию внутри другой функции (начальный стиль) и не беспокоиться о потере переменных и значений, помещенных в стек, которые не были выделены для памяти (такие как итераторы и т. Д.).

* 1004Например, предположим, что вы перебираете всю страницу текста, занимая по одной строке за раз.Вы сохраните итератор внешнего цикла (строки страницы) в регистре.Теперь, когда вы входите в функцию для подсчета количества гласных, вам не нужно беспокоиться о потере этого значения, чтобы оно помещалось в стек.Ваш счетчик гласных будет выполнять то, что он должен делать, использовать любые регистры, которые он хочет, а затем, когда он завершит работу, он (в зависимости от вашей методологии) восстановит значения из стека в их исходные места.Таким образом, внутренняя функция не связывается с внешней функцией, и ваши регистры не разбиваются функциями вызываемого метода.
0 голосов
/ 22 октября 2011

В наиболее распространенном соглашении о вызовах MIPS

  • временные регистры $t0,$t1,...,$t9 могут быть уничтожены (т.е. изменены и не восстановлены) при вызове функции.
  • the $s0,$s1,...,$s7 (сохраненный вызываемый) регистры должны оставаться неизменными после вызова функции.Функция, которую вы вызываете, обычно сохраняет значение сохраненных регистров вызываемого абонента в стеке, если ей необходимо использовать эти регистры.Сохраненные регистры вызываемого будут восстановлены из стека непосредственно перед возвратом функции.
0 голосов
/ 22 октября 2011

Я не знаком со сборкой MIPS, но в целом каждая платформа имеет соглашения о том, как должны вести себя подпрограммы.Одно из этих соглашений обычно заключается в том, какие регистры ЦП должны сохранять подпрограммы.Соглашения, взятые вместе, образуют ABI.

Подумайте об этом так: когда у вас есть программа с несколькими подпрограммами, достаточно просто отслеживать «да, эта процедура уничтожает регистр X»раз ты это называешь.Но по мере роста вашей программы это становится очень трудным.Представьте себе сложность изменения функции для использования нового регистра - вам нужно будет проверить каждую подпрограмму, которая вызывает эту процедуру, чтобы убедиться, что она не полагается на регистр во время вызова.И каждая подпрограмма, которая вызывает эти подпрограммы и т. Д. Измените часто используемую служебную функцию, и вам придется проверять всю программу;в этом и заключается безумие.

Существует два поддерживаемых решения: либо вызывающий абонент сохраняет все регистры, которые он использует, либо вызываемый абонент сохраняет все регистры, которые он изменяет.Обычно вы ожидаете, что код будет становиться менее сложным (и использовать меньше регистров) по мере продвижения в цепочке вызовов, которую вы получаете, поэтому вызываемый объект, вероятно, имеет меньший набор для сохранения.Кроме того, количество вызовов функций обычно превышает количество функций, поэтому сохранение вызываемого абонента также приводит к меньшему количеству кода.Похоже, MIPS следует этой логике и требует, чтобы вызываемый объект сохранял регистры.Иногда в архитектурах с большим количеством регистров (например, PowerPC) есть такие, которые считаются «временными», и, следовательно, вызываемый объект не обязан их сохранять;это комбинация двух подходов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...