2013-02-26 3 views
8

В настоящее время я изучаю среднюю среднюю компьютерную организацию, и я пытаюсь полностью понять указатель стека и стек. Я знаю, что эти следующие факты, которые окружают понятие:

  • Это следует за первый в последнем из принципа
  • и добавляя что-то в стек занимает два этапа:

    addi $sp, $sp, -4 
    sw $s0, 0($sp) 
    

То, что я думаю, мешает мне полностью понять, что я не могу придумать адекватную, явную ситуацию, в которой я нуждаюсь и/или хочу отслеживать данные с помощью указателя стека.

Может ли кто-нибудь подробно остановиться на концепции в целом и дать мне несколько полезных примеров кода?

+2

Вы должны использовать неподписанные add/sub для адресов, или вы рискуете исключением/ловушкой, если есть переполнение (пересечение границы между 0x7FFFFFFF и 0x80000000). –

+0

На практике да, но для целей теста я не думаю, что он требует от нас понимания этой тонкости. –

ответ

17

Важным применением стека является вызов подпрограмм вложенности.

Каждая подпрограмма может иметь набор переменных, локальных для этой подпрограммы. Эти переменные удобно хранить в стеке в стеке . Некоторые вызывающие соглашения также передают аргументы в стеке.

Использование подпрограмм также означает, что вам необходимо отслеживать вызывающего абонента, то есть обратный адрес. Некоторые архитектуры имеют выделенный стек для этой цели, в то время как другие неявно используют «нормальный» стек. MIPS по умолчанию использует только регистр, но в не-листовых функциях (т. Е. Функции, вызывающие другие функции), которые возвращают адрес, перезаписываются. Следовательно, вы должны сохранить исходное значение, как правило, в стеке среди ваших локальных переменных. Вызывающие соглашения могут также объявлять, что некоторые значения регистра должны быть сохранены во всех вызовах функций, вы также можете сохранить и восстановить их с помощью стека.

Предположим, у вас есть этот фрагмент C:

extern void foo(); 
extern int bar(); 
int baz() 
{ 
    int x = bar(); 
    foo(); 
    return x; 
} 

MIPS сборки затем может выглядеть следующим образом:

addiu $sp, $sp, -8 # allocate 2 words on the stack 
sw $ra, 4($sp)  # save $ra in the upper one 
jal bar    # this overwrites $ra 
sw $v0, ($sp)  # save returned value (x) 
jal foo    # this overwrites $ra and possibly $v0 
lw $v0, ($sp)  # reload x so we can return it 
lw $ra, 4($sp)  # reload $ra so we can return to caller 
addiu $sp, $sp, 8 # restore $sp, freeing the allocated space 
jr $ra    # return 
6

Вызывающий конвенции MIPS требует первых четырех функциональных параметров, чтобы быть в регистрах a0 через a3 а остальное, если их больше, в стеке. Более того, он также требует, чтобы вызывающий функции выделял четыре слота в стеке для первых четырех параметров, несмотря на то, что они передаются в регистры.

Итак, если вы хотите получить доступ к параметру пять (и дополнительным параметрам), вам необходимо использовать sp. Если функция, в свою очередь, вызывает другие функции и использует ее параметры после вызовов, она должна будет хранить a0 через a3 в этих четырех слотах в стеке, чтобы избежать их потери/перезаписи. Опять же, вы используете sp для записи этих регистров в стек.

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

Например, если у вас это:

int tst5(int x1, int x2, int x3, int x4, int x5) 
{ 
    return x1 + x2 + x3 + x4 + x5; 
} 

его разборка будет что-то вроде:

tst5: 
     lw  $2,16($sp) # r2 = x5; 4 slots are skipped 
     addu $4,$4,$5 # x1 += x2 
     addu $4,$4,$6 # x1 += x3 
     addu $4,$4,$7 # x1 += x4 
     j  $31  # return 
     addu $2,$4,$2 # r2 += x1 

Престола sp используются для доступа к x5.

И потом, если у вас есть код что-то вроде этого:

int binary(int a, int b) 
{ 
    return a + b; 
} 

void stk(void) 
{ 
    binary(binary(binary(1, 2), binary(3, 4)), binary(binary(5, 6), binary(7, 8))); 
} 

это то, что она выглядит в разборке после компиляции:

binary: 
     j  $31      # return 
     addu $2,$4,$5    # r2 = a + b 

stk: 
     subu $sp,$sp,32    # allocate space for local vars & 4 slots 
     li  $4,0x00000001   # 1 
     li  $5,0x00000002   # 2 
     sw  $31,24($sp)    # store return address on stack 
     sw  $17,20($sp)    # preserve r17 on stack 
     jal  binary     # call binary(1,2) 
     sw  $16,16($sp)    # preserve r16 on stack 

     li  $4,0x00000003   # 3 
     li  $5,0x00000004   # 4 
     jal  binary     # call binary(3,4) 
     move $16,$2     # r16 = binary(1,2) 

     move $4,$16     # r4 = binary(1,2) 
     jal  binary     # call binary(binary(1,2), binary(3,4)) 
     move $5,$2     # r5 = binary(3,4) 

     li  $4,0x00000005   # 5 
     li  $5,0x00000006   # 6 
     jal  binary     # call binary(5,6) 
     move $17,$2     # r17 = binary(binary(1,2), binary(3,4)) 

     li  $4,0x00000007   # 7 
     li  $5,0x00000008   # 8 
     jal  binary     # call binary(7,8) 
     move $16,$2     # r16 = binary(5,6) 

     move $4,$16     # r4 = binary(5,6) 
     jal  binary     # call binary(binary(5,6), binary(7,8)) 
     move $5,$2     # r5 = binary(7,8) 

     move $4,$17     # r4 = binary(binary(1,2), binary(3,4)) 
     jal  binary     # call binary(binary(binary(1,2), binary(3,4)), binary(binary(5,6), binary(7,8))) 
     move $5,$2     # r5 = binary(binary(5,6), binary(7,8)) 

     lw  $31,24($sp)    # restore return address from stack 
     lw  $17,20($sp)    # restore r17 from stack 
     lw  $16,16($sp)    # restore r16 from stack 
     addu $sp,$sp,32    # remove local vars and 4 slots 
     j  $31      # return 
     nop 

Я надеюсь, что я аннотированный код без ошибок.

Итак, обратите внимание, что компилятор предпочитает использовать r16 и r17 в функции, но сохраняет их в стеке. Поскольку функция вызывает другую, она также должна сохранять свой обратный адрес в стеке, а не просто хранить его в r31.

PS Помните, что все инструкции по переходу/переходу на MIPS эффективно выполняют сразу следующую команду перед фактической передачей управления новому местоположению. Это может показаться странным.

Смежные вопросы