MIPS 呼び出し規則では、最初の 4 つの関数パラメーターをレジスターに配置a0
しa3
、それ以上ある場合は残りをスタックに配置する必要があります。さらに、最初の 4 つのパラメーターがレジスターで渡されているにもかかわらず、関数の呼び出し元がスタックに 4 つのスロットを割り当てる必要があります。
したがって、パラメーター 5 (およびその他のパラメーター) にアクセスする場合は、 を使用する必要がありますsp
。関数が他の関数を順番に呼び出し、呼び出しの後にそのパラメーターを使用する場合、それらが失われたり上書きされたりしないように、スタック上のこれら 4 つのスロットに格納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
andを使用することを選択しますr17
が、それらをスタックに保持することに注意してください。関数は別の関数を呼び出すため、戻りアドレスを単純に に保持するのではなく、スタックに保持する必要もありますr31
。
PS MIPS のすべての分岐/ジャンプ命令は、実際に制御を新しい場所に移す前に、直後の命令を効果的に実行することに注意してください。これは紛らわしいかもしれません。