アセンブリ言語が初めてです。 [〜#〜] mips [〜#〜]アーキテクチャについて読んでいたのですが、Jump Target AddressおよびBranch Target Addressそれぞれの計算方法それら。
MIPSの分岐命令には、次の命令を決定するための16ビットオフセットしかない。次の命令を決定するために、この16ビット値にレジスタを追加する必要がありますが、このレジスタは実際にはアーキテクチャによって暗示されています。 PCはフェッチサイクル中に更新され(PC + 4)、次の命令のアドレスを保持するため、PCレジスタです。
(以下の図では、PC
は分岐命令自体ではなく、分岐遅延スロットのアドレスです。しかし、本文では、PC + 4と言います。)
また、分岐距離を()分岐命令の後の命令からの-2^15 to +2^15 - 1
命令に制限します。ただし、ほとんどのブランチはローカルであるため、これは実際の問題ではありません。
だからステップバイステップ:
ジャンプ命令の場合、Mipにはジャンプ位置を決定するための26ビットしかありません。また、ジャンプはMIPSのPCに関連しています。分岐と同様に、即時ジャンプ値はワード境界で整列させる必要があります。したがって、26ビットのアドレスに4を掛ける必要があります。
ステップバイステップ:
つまり、PC + 4の下位28ビットを、フェッチされた命令の下位26ビットを2ビット左にシフトして置き換えます。
ジャンプは、必ずしもブランチ自体ではなく、ブランチ遅延スロットに相対的な領域です。上の図では、ジャンプ計算の前に、PCがすでに分岐遅延スロットに進んでいます。 (クラシック-RISC 5ステージパイプラインでは、ジャンプがデコードされるのと同じサイクルでBDがフェッチされたため、PC + 4の次の命令アドレスは、ジャンプとブランチで既に使用可能であり、ジャンプ自身のアドレスとの相対計算はその住所を保存するために余分な作業が必要です。)
出典: Bilkent University CS 224コーススライド
通常、アセンブラー(またはリンカー)が計算を正しく行うので、それらの計算について心配する必要はありません。あなたには小さな機能があるとしましょう:
func:
slti $t0, $a0, 2
beq $t0, $zero, cont
ori $v0, $zero, 1
jr $ra
cont:
...
jal func
...
上記のコードを命令のバイナリストリームに変換するときに、アセンブラー(またはオブジェクトファイルに最初にアセンブルした場合はリンカー)は、関数がメモリ内のどこにあるかを決定します(ここでは、位置独立コードを無視しましょう)。メモリ内の場所は通常、ABIで指定されるか、シミュレータを使用している場合に与えられます( [〜#〜] spim [〜#〜] でコードをロードします- 0x400000
-リンクにはプロセスの適切な説明も含まれていることに注意してください)。
SPIMケースについて説明しており、関数が最初にメモリ内にあると仮定すると、slti
命令は0x400000
に、beq
は0x400004
に、というように続きます。 。これでもうすぐです! beq
命令の場合、分岐先アドレスはcont
(0x400010
)のアドレスです。 MIPS命令リファレンス 次の命令に関連する16ビット符号付き即値としてエンコードされていることがわかります(すべての命令は4バイト境界のアドレスに常駐する必要があるため、4で除算されます)。
あれは:
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
バイト以内で分岐できます。何らかの理由でさらにジャンプする必要がある場合は、トランポリンを使用できます(指定された制限内に配置された実際のターゲットへの無条件ジャンプ)。
ジャンプターゲットアドレスは、ブランチターゲットアドレスとは異なり、absoluteを使用してエンコードされますアドレス(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アセンブラー iに遭遇しました(たとえば、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にします。これにより、ほとんどの場合、余分な演算が大幅に節約されます。