web-dev-qa-db-ja.com

レジスタをゼロに設定する方法はいくつありますか?

X86アセンブリでレジスタをゼロに設定する方法がいくつあるのか知りたいです。 1つの命令を使用します。誰かが、少なくとも10通りの方法を見つけたと私に言った。

私が考えることができるものは:

xor ax,ax
mov ax, 0
and ax, 0
31
user173973

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セグメントレジスタと等しくない場合のオプションです。

13
GJ.

レジスタをゼロにするための 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

ベクトルreg:

これらの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を参照してください。)

AVX512:

ここにはおそらくいくつかのオプションがありますが、それらすべてを探すために命令セットのリストを掘り下げるのに十分なほど興味があるわけではありません。

ただし、注目に値する興味深い点が1つあります。 VPTERNLOGD/Q は、代わりにレジスタをall-onesに設定できます。 = 0xFF。 (しかし、現在の実装では、古い値に誤った依存関係があります)。比較命令はすべてマスクに比較されるため、テストではVPTERNLOGDがSkylake-AVX512でベクトルをすべて1に設定する最良の方法のようですが、 imm8 = 0xFFの場合は特別なケースではありません)誤った依存関係を回避するため

VPTERNLOGD zmm0, zmm0,zmm0, 0     ; inputs can be any registers you like.

x87 FP:

選択肢は1つだけです(古い値が無限またはNaNの場合、subは機能しないため)。

FLDZ    ; Push +0.0
7
Peter Cordes

さらにいくつかの可能性:

sub ax, ax

movxz, eax, ah

編集:movzxはすべてのeaxをゼロ化しないことに注意してください-ahをゼロ化するだけです(レジスタとしてアクセスできない上位16ビットに加えて)自体)。

最速であることに関しては、メモリが機能する場合、subxorは同等です。 CPU設計者が特別な最適化を追加するのに十分一般的であるため、他の(ほとんどの)よりも高速です。具体的には、通常のsubまたはxorの場合、結果はレジスターの前の値に依存します。 CPUは、xor-with-selfとsubtract-from-selfを特別に認識するため、依存関係チェーンがそこで壊れていることがわかります。それ以降の命令は以前の値に依存しないため、名前変更レジスタを使用して前の命令と後続の命令を並行して実行できます。

特に古いプロセッサでは、「mov reg、0」は、16ビットのデータが余分にあるという理由だけで遅くなることが予想され、ほとんどの初期のプロセッサ(特に8088)は、主にメモリからストリームをロードする能力によって制限されていました- -実際、8088では、リファレンスシートを使用して実行時間をかなり正確に見積もることができ、関係するバイト数に注意するだけです。 div命令とidiv命令ではそれはうまくいきませんが、それだけです。 OTOH、8088は実際にはほとんどの人にとってほとんど関心がないので(少なくとも10年間は​​)、黙っておくべきです。

4
Jerry Coffin

LOOP $を使用して、CXレジスタを0に設定できます。

3
I. J. Kennedy

もちろん、特定のケースでは、レジスタを0に設定する追加の方法があります。 eaxを正の整数に設定している場合は、edxcdq/cltdで0に設定できます(このトリックは、「安全でない」に表示される有名な24バイトのシェルコードで使用されます例によるプログラミング」)。

1
ninjalj

このスレッドは古いですが、他にいくつかの例があります。シンプルなもの:

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
1
Bartosz Wójcik
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
...
0
ARISTOS