MIPS-как MIPS выделяет память для массивов в стеке?

Я совершенно новичок в языке сборки MIPS и в настоящее время беру класс по компьютерной архитектуре, который имеет большой раздел по кодированию MIPS. В прошлом я изучал несколько других языков программирования высокого уровня (C, C#, Python), поэтому у меня есть некоторые основы программирования.

мой вопрос здесь конкретно спрашивает: Как MIPS выделяет память для массивов в стеке? Я надеюсь, что ответ на этот вопрос, надеюсь, даст мне лучшее полное понимание MIPS, поскольку я еще немного о концептуализации идеи языка MIPS и его архитектуры. Я тоже не совсем понимаю, как работают указатели во всем этом отношении...

было бы блестяще, если бы кто-то мог найти время, чтобы помочь этому запутавшемуся студенту! :)

2 ответов


хорошо.. вы должны знать, что MIPS, как и C, по существу имеет три разных способа выделения памяти.

рассмотрим следующий код C:

int arr[2]; //global variable, allocated in the data segment

int main() {
    int arr2[2]; //local variable, allocated on the stack
    int *arr3 = malloc(sizeof(int) * 2); //local variable, allocated on the heap
}

сборка MIPS поддерживает все эти типы данных.

чтобы выделить массив int в сегменте данных, вы можете использовать:

.data

arr: .word 0, 0 #enough space for two words, initialized to 0, arr label points to the first element 

чтобы выделить массив int в стеке, вы можете использовать:

#save $ra
addi $sp $sp -4  #give 4 bytes to the stack to store the frame pointer
sw   $fp 0($sp)  #store the old frame pointer
move $fp $sp     #exchange the frame and stack pointers
addi $sp $sp -12 #allocate 12 more bytes of storage, 4 for $ra and 8 for our array
sw   $ra  -4($fp)

# at this point we have allocated space for our array at the address -8($fp)

выделить место в куче, требуется системный вызов. В spim симулятор это системный вызов 9:

li $a0 8 #enough space for two integers
li $v0 9 #syscall 9 (sbrk)
syscall
# address of the allocated space is now in $v0

MIPS в отличие от других archs не имеет push или pop-регистра/немедленной инструкции. Таким образом, вы полагаетесь на управление стеком самостоятельно. Это фактически отмечается в большинстве арок за пределами mul / div, где ваши регистры не имеют конкретного использования, просто предлагаемый способ его использования. Теперь, если вы используете его так, как хотите, вы сломаете что-то, если попытаетесь интегрировать С C, например.

для того, чтобы подтолкнуть что-то к стеку, вам нужно использовать инструкцию магазина. Это sb, sh, sw, swl, swr. байт, половина, слово, слово влево, слово вправо соответственно.

addiu $sp, $sp, -4   # push stack 1 word
sw $t0, 0($sp)       # place item on newly pushed space

для того, чтобы поп что-то из стека, вам просто нужно deincrement его с addiu тоже. Тем не менее, вы можете загрузить данные из него с помощью lb, lh, lw, lwl, lwr.

lw $t0, 0($sp)
addiu $sp, $sp, 4   # pop stack 1 word

вот пример использования его с двумя словами push.

addiu $sp, $sp, -8  # allocate two words
sw $t0, 0($sp)      # push two registers t0 t1
sw $t1, 4($sp)

lw $t1, 4($sp)      # pop two registers t0 t1
lw $t0, 0($sp)
addiu $sp, $sp, 8   # deallocate two words

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

# grab us a quick string
.data
example_str: .asciiz "hello world :^)"

# grab us a function
.text
    .globl example
    .type test, @function
test:
    addiu $sp, $sp, -4  # push stack for 1 word
    sw $ra, 0($sp)      # save return address

    la $a0, example_str # call puts and give it a string
    jal puts
    nop

    lw $ra, 0($sp)      # load return address
    addiu $sp, $sp, 4   # pop stack for 1 word

    jr $ra              # return from function to caller
    nop

вот пример толкать множественные элементы в ряд. Хлопает обратный курс.

.data
example_arr: .word 0, 0, 0, 0

.text
addiu $sp, $sp, -16
la $t0, example_arr
lw $t1, 0($t0)
sw $t1, 0($sp)
lw $t1, 0($t0)
sw $t1, 4($sp)
lw $t1, 0($t0)
sw $t1, 8($sp)
sw $t1, 12($sp)

вот пример использования malloc / calloc из них.

# grab us a function
.text
    .globl example
    .type test, @function
test:
    addiu $sp, $sp, -4  # push stack for 1 word
    sw $ra, 0($sp)      # save return address

    li $a0, 4           # allocate 4*4 bytes (16)
    li $a1, 4
    jal calloc
    nop

    addiu $sp, $sp, -4  # push stack for 1 word
    sw $v0, 0($sp)      # save calloc'd buffer

    move $t0, $v0       # get the buffer into a temp
    li $t1, 1           # fill some temps with numbers
    li $t2, 2
    li $t3, 3
    li $t4, 4
    sw $t1, 0($t0)      # save some temps to buffer
    sw $t2, 4($t0)
    sw $t3, 8($t0)
    sw $t4, 12($t0)

    ... do stuff with the buffer ...

    lw $a0, 0($sp)      # pop buffer from stack
    jal free            # run it through free
    nop
    addiu $sp, $sp, 4   # don't forget to decrement

    lw $ra, 0($sp)      # load return address
    addiu $sp, $sp, 4   # pop stack for 1 word

    jr $ra              # return from function to caller
    nop

как я уже упоминал ранее, ничто не имеет строго определенного конкретного использования, поэтому вы также можете использовать свой собственный стек и забыть об использовании $sp, если хотите. Я показал примеры, где я использовал $t * как $s*. Это работает в случае принуждения каждой функции иметь свой собственный стек, например, или какой-либо другой usecase, о котором вы можете думать. В одно например, Lua (https://lua.org) делает это в некоторой степени. Однако, не пом. Несколько стеков прекрасны, особенно при работе с несколькими задачами.