web-dev-qa-db-ja.com

MIPS-MIPSはスタック内のアレイにメモリをどのように割り当てますか?

私はMIPSアセンブリ言語にまったく慣れておらず、現在、MIPSコーディングに関する大きなセクションがあるコンピューターアーキテクチャのクラスを受講しています。私は過去に他のいくつかの高級プログラミング言語(C、C#、Python)を研究したことがあるので、プログラミングの基礎がいくつかあります。

ここでの私の質問は具体的に尋ねます:MIPSはスタック内の配列にメモリをどのように割り当てますか?私はまだMIPS言語とそのアーキテクチャの概念を概念化することに少し取り組んでいるので、この質問に答えることでMIPSをよりよく理解できるようになることを願っています。この点でも、ポインタがどのように機能するのかよくわかりません...

誰かがこの混乱した学生を助けるために時間をかけることができれば素晴らしいでしょう! :)

10
Sam O'Mahony

MIPSには、Cと同様に、基本的に3つの異なるメモリ割り当て方法があることに注意してください。

次の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
19

他のアーチとは異なり、MIPSにはプッシュまたはポップレジスタ/即時命令がありません。したがって、スタックを自分で管理することに依存します。これは、レジスタが特定の用途を持たないmul/div以外のほとんどのArchで実際に指摘されており、推奨される使用方法にすぎません。これで、好きなように使用した場合、たとえばCと統合しようとすると、何かが壊れてしまいます。

何かをスタックにプッシュするには、ストア命令を使用する必要があります。これらはsb, sh, sw, swl, swrです。それぞれバイト、ハーフ、ワード、ワード左、ワード右。

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

スタックから何かをポップするには、addiuでそれをデインクリメントする必要があります。ただし、lb, lh, lw, lwl, lwrを使用してデータをロードすることもできます。

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

これは、2つのワードプッシュで使用する例です。

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 *として使用した例を示しました。これは、たとえば、各関数に独自のスタックを強制する場合や、考えられる他のユースケースの場合に機能します。一例として、Lua( https://lua.org )はこれをある程度行います。ただし、MIPSではありません。複数のスタックは、特に複数の目的を扱うときに便利です。

0
user2262111