はい、これらのシーケンスは FLAGS への影響を除いて正しく、もちろんpush %esp
clobber しません%edx
。代わりに、他の処理を行う前に入力 (ソース オペランド) のスナップショットを作成するプリミティブ操作を考えるのではなく、個別のステップに分割する場合は、内部の一時的な1を想像してください。push
(同様に/pop DST
としてモデル化することができ、 pop のすべての効果は、それがスタック ポインターであるか、スタック ポインターを含む場合でも、評価されて宛先に書き込まれる前に終了します。)pop %temp
mov %temp, DST
push
ESP の特殊なケースでも機能する同等のもの
(これらすべてにおいて、SS が正常に構成された 32 ビット互換モードまたは保護モードを想定しています。スタック アドレス サイズがモードと一致する場合、そうでない可能性さえあると想定しています。64 ビット モードと同等の%rsp
動作-8
/と同じように+8
. 16ビットモードでは(%sp)
アドレッシングモードが許可されないため、これを疑似コードと見なす必要があります.)
#push SRC for any source operand including %esp or 1234(%esp)
mov SRC, %temp
lea -4(%esp), %esp # esp-=4 without touching FLAGS
mov %temp, (%esp)
すなわちmov SRC, %temp
; push %temp
または、とにかく中断できないトランザクション (単一のpush
命令)
を記述しているため、保存する前に ESP を移動する必要はありません。
#push %REG # or immediate, but not memory source
mov %REG, -4(%esp)
lea -4(%esp), %esp
(この単純なバージョンは、メモリ ソースを使用して実際にアセンブルすることはなく、レジスタまたはイミディエイトのみであり、割り込みまたはシグナル ハンドラが mov と LEA の間で実行される場合は安全ではありません。実際のアセンブリではmov mem, mem
、2 つの明示的なアドレッシング モードでは安全ではありません。 t はエンコードpush (%eax)
できませんが、メモリの宛先が暗黙的であるためです. メモリ ソースの場合でも疑似コードと見なすことができます. しかし、一時的なスナップショットは、最初のブロックやmov SRC, %temp
/のように、内部で発生することのより現実的なモデルですpush %temp
.)
実際のプログラムでそのようなシーケンスを実際に使用することについて話している場合、一時レジスタ (最初のバージョン)、または (2 番目のバージョン) 割り込みを無効にするか、赤の ABI を使用せずに正確に複製する方法はないと思いますpush %esp
-ゾーン。(非カーネル コードの x86-64 System V のように、複製することができますpush %rsp
。)
pop
同等のもの:
#pop DST works for any operand
mov (%esp), %temp
lea 4(%esp), %esp # esp += 4 without touching FLAGS
mov %temp, DST # even if DST is %esp or 1234(%esp)
すなわちpop %temp
/ mov %temp, DST
。これは、ESP を含むメモリ アドレッシング モードの場合を正確に反映しています。インクリメント後DST
の ESP の値が使用されます。これに関する Intel のドキュメントを;で確認しました。. これにより、Skylake CPU の GDB でシングルステップしたときに書き込まれた dword のすぐ下にある dword にdword がコピーされました。その命令が実行される前に ESP を使用してアドレス計算が行われた場合、4 バイトのギャップが生じます。push $5
pop -8(%esp)
5
push
-8(%esp)
の特殊なケースではpop %esp
、yes はインクリメントを踏んで、次のように単純化します。
#pop %esp # 3 uops on Skylake, 1 byte
mov (%esp), %esp # 1 uop on Skylake. 3 bytes of machine-code size
Intel のマニュアルには、誤解を招く疑似コードがあります
Intel の命令セット マニュアル エントリ (SDM vol.2) の Operation セクションにある疑似コードは、スタック ポインタの特殊なケースを正確に反映していません。説明セクションの余分な段落 ( @nrz の回答で引用) のみがその権利を取得します。
https://www.felixcloutier.com/x86/popは、(StackAddrSize = 32 および OperandSize = 32 の場合) DEST へのロードと、 ESP のインクリメントを示しています。
DEST ← SS:ESP; (* Copy a doubleword *)
ESP ← ESP + 4;
しかし、pop %esp
これは ESP = load(SS:ESP) の後に ESP += 4 が発生することを意味するため、誤解を招く可能性があります。正しい疑似コードは使用します
if ... operand size etc.
TEMP ← SS:ESP; (* Copy a doubleword *)
ESP ← ESP + 4;
..
// after all the if / else size blocks:
DEST ← TEMP
インテルは、読み取り/書き込み宛先オペランドの元の状態のスナップショットpshufb
を作成するために擬似コードが開始する場所など、他の命令に対してこの権利を取得します。TEMP ← DEST
同様に、https://www.felixcloutier.com/x86/push#operationは、 RSP が最初にデクリメントされることを示しており、src
オペランドがその前にスナップショットされていることは示していません。テキストの説明セクションの余分な段落のみが、その特殊なケースを正しく処理します。
AMD のマニュアルVolume 3: General-Purpose and System Instructions (2021 年 3 月)は、これについて同様に間違っています (私の強調):
スタック ポインタ (SS:rSP) が指す値を指定されたレジスタまたはメモリ位置
にコピーし、rSP を 16 ビット ポップの場合は 2、32 ビット ポップの場合は 4、64 ビット ポップの場合は 8 ずつインクリメントします。ポップ。
Intel とは異なり、スタック ポインター自体または rSP を含むメモリ オペランドを使用してポップする特殊なケースについても文書化していません。少なくともここにはありません。検索してpush rsp
もpush esp
何も見つかりませんでした。
(AMD はrSP
、SS によって選択された現在のスタックサイズ属性に応じて、SP / ESP / RSP を意味します。)
AMD には、Intel のような疑似コード セクションがありません。少なくとも、プッシュ/ポップのような単純と思われる命令には対応していません。( 用がありpusha
ます。)
脚注 1 : それは一部の CPU で発生する可能性さえあります (私はそうは思いませんが)。たとえば、Skylake では、 Agner Fogは、フロントエンドで 2 uops と測定されましたが、他のレジスタをプッシュするためのマイクロ融合ストアは 1 でした。 push %esp
Intel CPU には、アーキテクチャ レジスタのように名前が変更されるが、マイクロコードによってのみアクセス可能なレジスタがいくつかあることはわかっています。たとえば、 https://blog.stuffedcow.net/2013/05/measuring-rob-capacity/は、「内部使用のためのいくつかの追加のアーキテクチャレジスタ」について言及しています。だからmov %esp, %temp
/push %temp
理論的には、それがどのようにデコードされたかである可能性があります。
しかし、より可能性の高い説明は、プッシュ/ポップ操作の後に OoO バックエンドが明示的に ESP を読み取るたびに得られるように、命令の長いシーケンスで余分に測定された uops は、push %esp
単なるスタック同期 uopsであるということです。たとえば、 push %eax
/mov %esp, %edx
もスタック同期 uop を引き起こします。(「スタックエンジンesp -= 4
」は、の部分に余分なuopを必要としないようにするものですpush
)
push %esp
たとえば、予約したばかりのスタック空間のアドレスをプッシュする場合などに便利です。
sub $8, %esp
push %esp
push $fmt # "%lf"
call scanf
movsd 8(%esp), %xmm0
# add $8, %esp # balance out the pushes at some point, or just keep using that allocated space for something. Or clean it up just before returning along with the space for your local var.
pop %esp
Skylake で 3 uops、1 つのロード (p23)、任意の整数 ALU ポート (2p0156) で 2 つの ALU が必要です。そのため、効率はさらに低下しますが、基本的にユースケースはありません。スタック上のスタック ポインターを有効に保存/復元することはできません。保存した場所に移動する方法がわかっている場合は、 で復元できますadd
。