X86アセンブリでレジスタをゼロに設定する方法がいくつあるのか知りたいです。 1つの命令を使用します。誰かが、少なくとも10通りの方法を見つけたと私に言った。
私が考えることができるものは:
xor ax,ax
mov ax, 0
and ax, 0
IA32で0をaxに移動する方法はたくさんあります...
lea eax, [0]
mov eax, 0FFFF0000h //All constants form 0..0FFFFh << 16
shr eax, 16 //All constants form 16..31
shl eax, 16 //All constants form 16..31
そしておそらく最も奇妙な... :)
@movzx:
movzx eax, byte ptr[@movzx + 6] //Because the last byte of this instruction is 0
そして...
@movzx:
movzx ax, byte ptr[@movzx + 7]
編集:
また、16ビットx86 CPUモードの場合、テストされていません...:
lea ax, [0]
そして...
@movzx:
movzx ax, byte ptr cs:[@movzx + 7] //Check if 7 is right offset
cs:プレフィックスは、dsセグメントレジスタがcsセグメントレジスタと等しくない場合のオプションです。
レジスタをゼロにするための bestの方法についてのこの回答:xor eax,eax
(パフォーマンス上の利点、およびより小さいエンコーディング)を参照してください。
単一の命令がレジスターをゼロにできる方法だけを検討します。メモリからのゼロのロードを許可する方法は多すぎるため、メモリからロードする命令はほとんど除外します。
32ビットレジスター(つまり、ロングモードでは完全な64ビットレジスター)をゼロにし、他のメモリからの事前条件やロードがない、10の異なる単一命令を見つけました。これは、同じinsnの異なるエンコーディング、またはmov
の異なる形式をカウントしていません。ゼロを保持することがわかっているメモリ、またはセグメントレジスタなどからのロードをカウントする場合、さまざまな方法があります。ベクトルレジスタをゼロにする方法も無数にあります。
これらのほとんどの場合、eaxバージョンとraxバージョンは、同じ機能に対して別々のエンコーディングであり、64ビットレジスタ全体をゼロにする 上半分を暗黙的にゼロにする またはREXで明示的にフルレジスタを書き込むWプレフィックス。
# Works on any reg unless noted, usually of any size. eax/ax/al as placeholders
and eax, 0 ; three encodings: imm8, imm32, and eax-only imm32
andn eax, eax,eax ; BMI1 instruction set: dest = ~s1 & s2
imul eax, any,0 ; eax = something * 0. two encodings: imm8, imm32
lea eax, [0] ; absolute encoding (disp32 with no base or index). Use [abs 0] in NASM if you used DEFAULT REL
lea eax, [rel 0] ; YASM supports this, but NASM doesn't: use a RIP-relative encoding to address a specific absolute address, making position-dependent code
mov eax, 0 ; 5 bytes to encode (B8 imm32)
mov rax, strict dword 0 ; 7 bytes: REX mov r/m64, sign-extended-imm32. NASM optimizes mov rax,0 to the 5B version, but dword or strict dword stops it for some reason
mov rax, strict qword 0 ; 10 bytes to encode (REX B8 imm64). movabs mnemonic for AT&T. normally assemblers choose smaller encodings if the operand fits, but strict qword forces the imm64.
sub eax, eax ; recognized as a zeroing idiom on some but maybe not all CPUs
xor eax, eax ; Preferred idiom: recognized on all CPUs
@movzx:
movzx eax, byte ptr[@movzx + 6] //Because the last byte of this instruction is 0. neat hack from GJ.'s answer
.l: loop .l ; clears e/rcx... eventually. from I. J. Kennedy's answer. To operate on only ECX, use an address-size prefix.
; rep lodsb ; not counted because it's not safe (potential segfaults), but also zeros ecx
「すべてのビットを一方の端からシフトする」は、通常サイズのGPレジスタでは不可能であり、部分レジスタのみが可能です。 shl
およびshr
シフトカウントはマスクされます:count &= 31;
、count %= 32;
と同等。 (しかし、286以前は16ビットのみなので、ax
は「フル」レジスタです。命令のshr r/m16, imm8
変数カウント形式が286追加されたため、シフトがゼロになるCPUがありました完全な整数レジスター。)
また、ベクトルのシフトカウントは、ラッピングではなく飽和することにも注意してください。
# Zeroing methods that only work on 16bit or 8bit regs:
shl ax, 16 ; shift count is still masked to 0x1F for any operand size less than 64b. i.e. count %= 32
shr al, 16 ; so 8b and 16b shifts can zero registers.
# zeroing ah/bh/ch/dh: Low byte of the reg = whatever garbage was in the high16 reg
movxz eax, ah ; From Jerry Coffin's answer
他の既存の条件に応じて(別のレジストリにゼロがあること以外):
bextr eax, any, eax ; if al >= 32, or ah = 0. BMI1
BLSR eax, src ; if src only has one set bit
CDQ ; edx = sign-extend(eax)
sbb eax, eax ; if CF=0. (Only recognized on AMD CPUs as dependent only on flags (not eax))
setcc al ; with a condition that will produce a zero based on known state of flags
PSHUFB xmm0, all-ones ; xmm0 bytes are cleared when the mask bytes have their high bit set
これらのSSE2整数命令の一部は、MMXレジスタ(mm0
-mm7
)でも使用できます。繰り返しになりますが、最良の選択は何らかの形式のxorです。 PXOR
/VPXOR
、またはXORPS
/VXORPS
のいずれか。
AVX vxorps xmm0,xmm0,xmm0
は完全なymm0/zmm0をゼロにし、 AMD CPUではvxorps ymm0,ymm0,ymm0
よりも優れています です。これらのゼロ化命令には、レガシーSSE、AVX(VEXプレフィックス)、およびAVX512(EVEXプレフィックス)の3つのエンコーディングがあります。ただし、SSEバージョンは、完全なレジスタではない下部128のみをゼロにしますAVXまたはAVX512をサポートするCPU。とにかく、カウント方法に応じて、各エントリは3つの異なる命令になる可能性があります(ただし、同じオペコード、異なるプレフィックスのみ)。AVX512が変更しなかったvzeroall
を除きます(そしてゼロではありませんzmm16-31)。
ANDNPD xmm0, xmm0
ANDNPS xmm0, xmm0
PANDN xmm0, xmm0 ; dest = ~dest & src
PCMPGTB xmm0, xmm0 ; n > n is always false.
PCMPGTW xmm0, xmm0 ; similarly, pcmpeqd is a good way to do _mm_set1_epi32(-1)
PCMPGTD xmm0, xmm0
PCMPGTQ xmm0, xmm0 ; SSE4.2, and slower than byte/Word/dword
PSADBW xmm0, xmm0 ; sum of absolute differences
MPSADBW xmm0, xmm0, 0 ; SSE4.1. sum of absolute differences, register against itself with no offset. (imm8=0: same as PSADBW)
; shift-counts saturate and zero the reg, unlike for GP-register shifts
PSLLDQ xmm0, 16 ; left-shift the bytes in xmm0
PSRLDQ xmm0, 16 ; right-shift the bytes in xmm0
PSLLW xmm0, 16 ; left-shift the bits in each Word
PSLLD xmm0, 32 ; double-Word
PSLLQ xmm0, 64 ; quad-Word
PSRLW/PSRLD/PSRLQ ; same but right shift
PSUBB/W/D/Q xmm0, xmm0 ; subtract packed elements, byte/Word/dword/qword
PSUBSB/W xmm0, xmm0 ; sub with signed saturation
PSUBUSB/W xmm0, xmm0 ; sub with unsigned saturation
PXOR xmm0, xmm0
XORPD xmm0, xmm0
XORPS xmm0, xmm0
VZEROALL
# Can raise an exception on SNaN, so only usable if you know exceptions are masked
CMPLTPD xmm0, xmm0 # exception on QNaN or SNaN, or denormal
VCMPLT_OQPD xmm0, xmm0,xmm0 # exception only on SNaN or denormal
CMPLT_OQPS ditto
VCMPFALSE_OQPD xmm0, xmm0, xmm0 # This is really just another imm8 predicate value fro the same VCMPPD xmm,xmm,xmm, imm8 instruction. Same exception behaviour as LT_OQ.
SUBPS xmm0, xmm0
などは、NaN-NaN = NaN、ゼロではないため機能しません。
また、FP命令はNaN引数で例外を発生させる可能性があるため、CMPPS/PDでも安全であるのは、例外がマスクされていることがわかっていて、MXCSRで例外ビットを設定することについては気にしない場合です。述語の選択肢が拡張されたAVXバージョンでも、SNaNで#IA
が発生します。「静かな」述語は、QNaNの#IA
のみを抑制します。CMPPS/ PDは、非正規例外も発生させる可能性があります。
( CMPPDのinsn set refエントリ の表を参照してください。または、HTML抽出がそのテーブルをマングルするため、できればIntelのオリジナルのPDFを参照してください。)
ここにはおそらくいくつかのオプションがありますが、それらすべてを探すために命令セットのリストを掘り下げるのに十分なほど興味があるわけではありません。
ただし、注目に値する興味深い点が1つあります。 VPTERNLOGD/Q は、代わりにレジスタをall-onesに設定できます。 = 0xFF。 (しかし、現在の実装では、古い値に誤った依存関係があります)。比較命令はすべてマスクに比較されるため、テストではVPTERNLOGDがSkylake-AVX512でベクトルをすべて1に設定する最良の方法のようですが、 imm8 = 0xFFの場合は特別なケースではありません)誤った依存関係を回避するため 。
VPTERNLOGD zmm0, zmm0,zmm0, 0 ; inputs can be any registers you like.
選択肢は1つだけです(古い値が無限またはNaNの場合、subは機能しないため)。
FLDZ ; Push +0.0
さらにいくつかの可能性:
sub ax, ax
movxz, eax, ah
編集:movzx
はすべてのeax
をゼロ化しないことに注意してください-ah
をゼロ化するだけです(レジスタとしてアクセスできない上位16ビットに加えて)自体)。
最速であることに関しては、メモリが機能する場合、sub
とxor
は同等です。 CPU設計者が特別な最適化を追加するのに十分一般的であるため、他の(ほとんどの)よりも高速です。具体的には、通常のsub
またはxor
の場合、結果はレジスターの前の値に依存します。 CPUは、xor-with-selfとsubtract-from-selfを特別に認識するため、依存関係チェーンがそこで壊れていることがわかります。それ以降の命令は以前の値に依存しないため、名前変更レジスタを使用して前の命令と後続の命令を並行して実行できます。
特に古いプロセッサでは、「mov reg、0」は、16ビットのデータが余分にあるという理由だけで遅くなることが予想され、ほとんどの初期のプロセッサ(特に8088)は、主にメモリからストリームをロードする能力によって制限されていました- -実際、8088では、リファレンスシートを使用して実行時間をかなり正確に見積もることができ、関係するバイト数に注意するだけです。 div
命令とidiv
命令ではそれはうまくいきませんが、それだけです。 OTOH、8088は実際にはほとんどの人にとってほとんど関心がないので(少なくとも10年間は)、黙っておくべきです。
LOOP $
を使用して、CXレジスタを0に設定できます。
もちろん、特定のケースでは、レジスタを0に設定する追加の方法があります。 eax
を正の整数に設定している場合は、edx
をcdq/cltd
で0に設定できます(このトリックは、「安全でない」に表示される有名な24バイトのシェルコードで使用されます例によるプログラミング」)。
このスレッドは古いですが、他にいくつかの例があります。シンプルなもの:
xor eax,eax
sub eax,eax
and eax,0
lea eax,[0] ; it doesn't look "natural" in the binary
より複雑な組み合わせ:
; flip all those 1111... bits to 0000
or eax,-1 ; eax = 0FFFFFFFFh
not eax ; ~eax = 0
; XOR EAX,-1 works the same as NOT EAX instruction in this case, flipping 1 bits to 0
or eax,-1 ; eax = 0FFFFFFFFh
xor eax,-1 ; ~eax = 0
; -1 + 1 = 0
or eax,-1 ; eax = 0FFFFFFFFh or signed int = -1
not eax ;++eax = 0
mov eax,0
shl eax,32
shr eax,32
imul eax,0
sub eax,eax
xor eax,eax
and eax,0
andn eax,eax,eax
loop $ ;ecx only
pause ;ecx only (pause="rep nop" or better="rep xchg eax,eax")
;twogether:
Push dword 0
pop eax
or eax,0xFFFFFFFF
not eax
xor al,al ;("mov al,0","sub al,al",...)
movzx eax,al
...