1

ARM アセンブリで関数を作成するときは、通常、最初にLRレジスタの内容をプッシュr4-r5し、関数が終了した後に次のようにポップr4-r5PCます。

.global myfunc
.type   myfunc, %function

myfunc:
push {r4-r5,lr}
... do stuff...
pop {r4-r5,pc}

ただし、使用するstmfdldmfdパフォーマンスが向上する可能性があることを読みました。

myfunc:
stmfd sp!,{r4-r11,lr}
...do stuff...
ldmfd sp!,{r4-r11,pc}

正確には何spですか?r4-r11内部で実際に使用していない場合に備えて、すべてのレジスタを保存する価値はないと思いますよmyfuncね?その場合、プッシュポップバリアントの方が優れていますか?

4

4 に答える 4

6

PUSH {...}ARM 命令に相当する Thumb です。STMDB SP!,{...}

POP {...}ARM 命令に相当する Thumb です。LDMIA SP!,{...}

STMとは、STore Multiple を意味します。
DBは Decrement Before を意味します。つまり、この場合、各ストアの前に宛先アドレスをデクリメントします。
IAは Increment After を意味します。つまり、この場合、各ロード後にソース アドレスをインクリメントします。
! ソース/デスティネーション アドレス レジスタに最終アドレスを書き戻すことを意味します。たとえば、SP0x100 だった場合STMDB SP!,{R0-R2}、その後に 0xF4 が含まれSPます。
SPは のエイリアスでR13あり、ARM プロセッサのスタック ポインタとして使用されます。

于 2013-03-23T13:35:28.967 に答える
3

push と pop は、アセンブラに対する疑似命令であり、実際の命令ではありません。ベース レジスタが stm で更新されたストアを取得します。

push {r11}
stmdb r13!,{r11}

push {r10-r12}
stmdb r13!,{r10-r12}

同じ命令に対して stmfd よりも stmdb を好みますが、構文が異なるだけです。(stmdb と ldmia は私には理にかなっています。前に減少し、後に増加します)。

組み立ててから分解。

   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e92d0800    stmfd   sp!, {fp}
   8:   e92d1c00    push    {sl, fp, ip}
   c:   e92d1c00    push    {sl, fp, ip}

stmエンコーディングを調べるか、ビットを見て考えてみると、命令0xe92dの上位ビットはstmia/fdであり、下位ビットはどのレジスタを保存するかを示すフラグです。 11 を押すと、次に 8 と c でそのビットが r11 に設定され、次にその下のビットが r10 に設定され、その上のビットが r12 に設定されます。

push と pop は、 sp を使用して ! を使用することを覚えようとするよりも読みやすいです。レジスタの後に、ia/db/fd などのサフィックスなどを覚えておいてください。

親指には実際のプッシュ/ポップがあると思います。

arm の単一のレジスタ バリアントが単一のストアに変わった場合、stm を 1 つの命令で使用するか、str で使用するかに関係なく、操作は機能的に同等です。

操作後にr13を更新し、stmにdbまたはfdを使用する限り、疑似命令または実際の命令を使用できます。

複数のレジスタを保存/復元する場合は、それらを 1 つの命令で確実にリストします。複数のプッシュまたはポップのリストを作成しないでください。

no:
push {r10}
push {r11}
push {r12}
yes:
push {r10-r11}

r0-r7+r14 をプッシュし、r0-r7+r15 をポップしてより高いレジスタを保存することしかできないため、経験がない限り、それらをより低いレジスタにコピーしてからプッシュを使用する必要があります。そして、stmをプッシュする必要があり、r13を使用できません。(thumb2 は、アーキテクチャで使用できる拡張機能に応じて、腕のようなエクスペリエンスを提供します)。

あなたの質問を読み直して

sp はスタック ポインタ r13 です。疑似命令は正しい命令を選択するので、stm と str について心配する必要はありません。複数のレジスタを保存すると、最新のアーム システムで最適化を「行うことができます」が、保証はされません。amba/axi バスが 64 ビット幅の場合、一度に 32 ビットを書き込むよりも、一度に 64 ビットを書き込む方が 2 倍以上高速です。 32 ビットの書き込みですが、64 ビットの書き込みはそうではありません (キャッシュの動作は無視してください)。stm がアラインされたアドレスにある場合 (スタックを使用する場合、それを理解するのにコードが多すぎますが、心配しないでください)、2 つのレジスターのプッシュは、2 つの別々のプッシュよりも著しく高速です (コアがそれらを最適化しない限り) 1 バス サイクル)。4 つのレジスタをプッシュすると、アライメントされていない場合に 3 つのうちの 1 つが発生し、アライメントされていないアドレス (0x1004 など) で 32 ビットの転送が 3 回行われ、その後、アライメントされたアドレス (0x1008) で 64 ビットの転送が行われ、次に 32最後のレジスタ (0x1010) のビット転送。その 4 つのレジスタ プッシュがアラインされたアドレスで行われた場合、2 つの別々の 64 ビット転送のいずれかが発生します。つまり、2 つのレジスタを 0x2010 に転送し、2 つのレジスタを 0x2018 に転送するか、長さ 2 の転送 (1 回の転送で 2 つの 64 ビット項目)アラインされたベースアドレス、たとえば 0x2010。4 つの個別の 32 ビット転送という最悪のケースは発生しないため、stm/push を使用する価値があります。次に、最後のレジスタ (0x1010) の 32 ビット転送。その 4 つのレジスタ プッシュがアラインされたアドレスで行われた場合、2 つの別々の 64 ビット転送のいずれかが発生します。つまり、2 つのレジスタを 0x2010 に転送し、2 つのレジスタを 0x2018 に転送するか、長さ 2 の転送 (1 回の転送で 2 つの 64 ビット項目)アラインされたベースアドレス、たとえば 0x2010。4 つの個別の 32 ビット転送という最悪のケースは発生しないため、stm/push を使用する価値があります。次に、最後のレジスタ (0x1010) の 32 ビット転送。その 4 つのレジスタ プッシュがアラインされたアドレスで行われた場合、2 つの別々の 64 ビット転送のいずれかが発生します。つまり、2 つのレジスタを 0x2010 に転送し、2 つのレジスタを 0x2018 に転送するか、長さ 2 の転送 (1 回の転送で 2 つの 64 ビット項目)アラインされたベースアドレス、たとえば 0x2010。4 つの個別の 32 ビット転送という最悪のケースは発生しないため、stm/push を使用する価値があります。

于 2013-03-23T13:37:39.297 に答える
1

レジスタを使用しない場合は、レジスタをスタックにプッシュする必要はありません。そうは言っても、実際のパフォーマンス上の利点が追加されるかどうかを確認する必要があります。すべてをプッシュするのは簡単だと思います。後でコードを変更しても、レジスタやスタックが誤って破損することはありません。

ちなみに、これもできます。つまり、 を使用して r4 ~ r5 のみを保存しstmfdます。

myfunc:
stmfd sp!,{r4-r5,lr}
...do stuff...
ldmfd sp!,{r4-r5,pc}

また

myfunc:
stmfd r13!,{r4-r5,r14}
...do stuff...
ldmfd r13!,{r4-r5,pc}

spのエイリアスでr13あり、のlrエイリアスであることがわかりr14ます。ここで、spはスタック ポインターとlrリンク レジスタを表します。

于 2013-03-24T20:51:39.063 に答える
0

SPスタック ポインタ レジスタです。現在のスタックの先頭を示します。stmfdより高いレジスタを保存している場合にのみ使用する必要があると思います。いくつかの低音域のみを保存する必要がある場合は、プッシュ & ポップするだけです。

于 2013-03-23T13:05:45.917 に答える