アセンブリ言語は初めてです。私はMIPSアーキテクチャについて読んでいましたが、ジャンプターゲットアドレスと分岐ターゲットアドレス、およびそれぞれの計算方法に悩まされています。
4 に答える
(以下の図とテキストで、PC
は分岐命令自体のアドレスです。 PC+4
は分岐命令自体の終わりであり、分岐遅延スロットの始まりです。絶対ジャンプ図を除きます。)
1. 支店住所の計算
MIPS では、分岐命令には次の命令を決定するための 16 ビットのオフセットしかありません。次の命令を決定するには、この 16 ビット値にレジスタを追加する必要があり、このレジスタは実際にはアーキテクチャによって暗示されます。次の命令のアドレスを保持するためにフェッチ サイクル中に PC が更新されるため (PC+4)、これは PC レジスタです。
-2^15 to +2^15 - 1
また、分岐命令 (後の命令) から命令までの分岐距離を制限します。ただし、ほとんどのブランチはとにかくローカルであるため、これは実際の問題ではありません。
ステップバイステップ:
- 16 ビットのオフセット値を符号拡張して、その値を保持します。
- 結果の値を 4 で乗算します。これの背後にある理由は、あるアドレスを分岐しようとしていて、PC が既にワード境界で整列されている場合、即値もワード境界で整列する必要があるためです。ただし、下位 2 ビットを強制的に 00 にすると無駄になるため、即値をワード境界に揃えても意味がありません。
- これで、32 ビットの相対オフセットができました。この値を PC + 4 に加算すると、それが支店の住所になります。
2.ジャンプアドレスの計算
ジャンプ命令の場合、MIPS にはジャンプ位置を決定するための 26 ビットしかありません。ジャンプは、MIPS の PC に相対的です。分岐と同様に、即時ジャンプ値はワード境界で整列する必要があります。したがって、26 ビット アドレスを 4 で乗算する必要があります。
再びステップバイステップ:
- 26 ビット値を 4 で乗算します。
- PC+4 値に対して相対的にジャンプするため、PC+4 値の最初の 4 ビットをジャンプ アドレスの左側に連結します。
- 結果のアドレスはジャンプ値です。
つまり、PC + 4 の下位 28 ビットを、フェッチした命令の下位 26 ビットを 2 ビット左にシフトしたものに置き換えます。
ジャンプは、必ずしもブランチ自体ではなく、ブランチ遅延スロットに相対的なリージョンです。 上の図では、PC はジャンプ計算の前にすでに分岐遅延スロットに進んでいます。 (従来の RISC 5 ステージ パイプラインでは、ジャンプがデコードされるのと同じサイクルで BD がフェッチされるため、PC+4 の次の命令アドレスは分岐だけでなくジャンプにも既に使用可能であり、ジャンプ自体のアドレスを基準にして計算すると、そのアドレスを保存するために余分な作業が必要でした。)
出典:ビルケント大学 CS 224 コース スライド
通常、アセンブラ(またはリンカ)が計算を正しく行うために、それらを計算することを心配する必要はありません。あなたが小さな関数を持っているとしましょう:
func:
slti $t0, $a0, 2
beq $t0, $zero, cont
ori $v0, $zero, 1
jr $ra
cont:
...
jal func
...
上記のコードを命令のバイナリストリームに変換するとき、アセンブラ(または最初にオブジェクトファイルにアセンブルした場合はリンカ)は、関数がメモリ内のどこにあるかを決定します(ここでは位置に依存しないコードを無視しましょう)。メモリ内のどこに配置されるかは、通常、ABIで指定されるか、シミュレーターを使用している場合に提供されます(コードをロードするSPIM0x400000
など-リンクにはプロセスの適切な説明も含まれていることに注意してください)。
SPIMの場合について話していて、関数が最初にメモリ内にあると仮定すると、命令は、、 atなどにslti
常駐します。もうすぐです!命令の分岐先アドレスは、()のアドレスです。MIPS命令リファレンスを見ると、次の命令に対して16ビットの符号付きイミディエートとしてエンコードされていることがわかります(すべての命令は4バイトに存在する必要があるため、4で除算されます)。とにかく整列されたアドレス)。0x400000
beq
0x400004
beq
cont
0x400010
あれは:
Current address of instruction + 4 = 0x400004 + 4 = 0x400008
Branch target = 0x400010
Difference = 0x400010 - 0x400008 = 0x8
To encode = Difference / 4 = 0x8 / 4 = 0x2 = 0b10
のエンコーディングbeq $t0, $zero, cont
0001 00ss ssst tttt iiii iiii iiii iiii
---------------------------------------
0001 0001 0000 0000 0000 0000 0000 0010
-0x1fffc .. 0x20000
ご覧のとおり、バイト内に分岐できます。何らかの理由でさらにジャンプする必要がある場合は、トランポリンを使用できます(指定された制限内に配置された実際のターゲットへの無条件ジャンプ)。
ジャンプターゲットアドレスは、分岐ターゲットアドレスとは異なり、絶対アドレス(ここでも4で割った値)を使用してエンコードされます。命令エンコーディングはオペコードに6ビットを使用するため、アドレスには26ビットしか残されません(最後の2ビットが0になると事実上28ビット)。したがって、アドレスを形成するときにPCレジスタの最上位4ビットが使用されます( 256 MBの境界を飛び越えるつもりがない限り、問題にはなりません)。
上記の例に戻ると、のエンコーディングjal func
は次のとおりです。
Destination address = absolute address of func = 0x400000
Divided by 4 = 0x400000 / 4 = 0x100000
Lower 26 bits = 0x100000 & 0x03ffffff = 0x100000 = 0b100000000000000000000
0000 11ii iiii iiii iiii iiii iiii iiii
---------------------------------------
0000 1100 0001 0000 0000 0000 0000 0000
私が遭遇したこのオンラインMIPSアセンブラーslti
を使用して、これをすばやく確認し、さまざまな手順を試すことができます(たとえば、すべてのオペコードをサポートしているわけではないので、ここに変更しましslt
た)。
00400000: <func> ; <input:0> func:
00400000: 0000002a ; <input:1> slt $t0, $a0, 2
00400004: 11000002 ; <input:2> beq $t0, $zero, cont
00400008: 34020001 ; <input:3> ori $v0, $zero, 1
0040000c: 03e00008 ; <input:4> jr $ra
00400010: <cont> ; <input:5> cont:
00400010: 0c100000 ; <input:7> jal func
このような小さな関数の場合、分岐命令の下の命令から、ターゲットまでのホップ数を手で数えることができます。逆方向に分岐する場合は、そのホップ数を負にします。その数が 16 ビットすべてを必要としない場合は、ホップ数の最上位の左側にあるすべての数を 1 にします。ホップ数が正の場合はすべて 0 にします。ほとんどの分岐はそれらに近いため、これにより、ほとんどの場合、余分な計算を大幅に節約できます。
- クリス
分岐先アドレスは実行時に決定され、その予測はハードウェアで行われるため、それらを計算するのはかなり難しいと思います。問題をもう少し詳しく説明し、何をしようとしているのかを説明していただければ、手助けが少し簡単になります。(: