2016-12-15 496 views
5

我正在使用x86汇编和Irvine库。确定寄存器值是否等于零的最简单方法是什么?

什么是最简单的方法来检查寄存器值是否等于零?

我用cmp指令,但我正在寻找替代方法。 这是使用CMP指令我的代码和寄存器是EBX

cmp ebx,0 
    je equ1 
    mov ebx,0 
    jmp cont 
equ1: 
    mov ebx,1 
    jmp cont 
cont: 
    exit 

这种 “booleanizes” 的值,产生一个0或1像int ebx = !!ebx将在C.

+4

您至少使用80386条指令(在8086上不存在'ebx')。此外,答案取决于此代码前面的指令,零值的存在可能会按照前面的指令进行“泄漏”,从而为您节省更多的测试时间,否则常用的习惯用法是'test ebx,ebx'来设置ZF。然后把ZF变成0/1的值是ebx的另一个故事,那里的'测试'可能再次不是最佳解决方案的一部分。下定决心,无论你想确定寄存器是否为零,或者你想根据它设置一些寄存器为0/1。 #'mov ebx,0' ='xor ebx,ebx'当你可以摧毁旗帜时。 – Ped7g

+0

为了说明我的观点(细节的重要性),让我们假设在这之前的最后一个(标志和ebx)修改指令是'neg ebx'。然后'sbb ebx,ebx'会在'ebx'等于零时将'ebx'设置为-1,对于非零值则设为0(进一步'neg ebx'会将其转换为原来的0/1工作方式)。 – Ped7g

+0

没有与我一起工作。 –

回答

11

大概是“最简单”,或者简单地说,“不关心细节有关”回答如何确定是:

; here ebx is some value, flags are set to anything 
    test ebx,ebx ; CF=0, ZF=0/1 according to ebx 
    jz  whereToJumpWhenZero 
    ; "non-zero ebx" will go here 

    ; Or you can use the inverted "jnz" jump to take 
    ; a branch when value was not zero instead of "jz". 

有一个详细的answer from Peter Cordes to "testl eax against eax?" question关于设置标志的推理等。还有一个链接到另一个类似的答案,但推断为什么它是从性能角度来看最好的方式。 :)

如何设置一些其他的寄存器(我会选择eax)到1时ebx是零,而设置为0时ebx非零(非破坏性的方式为ebx本身):

xor eax,eax ; eax = 0 (upper 24 bits needed to complete "al" later) 
    test ebx,ebx ; test ebx, if it is zero (ZF=0/1) 
    setz al  ; al = 1/0 when ZF=1/0 (eax = 1/0 too) 

或如何ebx转换成自己当1/0为ebx零/非零:

neg ebx  ; ZF=1/0 for zero/non-zero, CF=not(ZF) 
    sbb ebx,ebx ; ebx = 0/-1 for CF=0/1 
    inc ebx  ; 1 when ebx was 0 at start, 0 otherwise 

或如何ebx本身转化为1/0时,ebx是零/非零的,其他的变种(快上“P6”为“Haswell的”芯):

test ebx,ebx ; ZF=1/0 for zero/non-zero ebx 
    setz bl  ; bl = 1/0 by ZF (SETcc can target only 8b r/m) 
    movzx ebx,bl ; ebx = bl extended to 32 bits by zeroes 

等等,等等...这取决于你在测试之前发生了什么,还你真正想要的作为测试的输出,存在多种可能的方式(对于不同的情况是最佳的,对于不同的目标CPU是最佳的)。


我会加几个非常常见的情况......一个反向环向下计数从N到零,循环N次:

mov ebx,5 ; loop 5 times 
exampleLoop: 
    ; ... doing something, preserving ebx 
    dec ebx 
    jnz exampleLoop ; loop 5 times till ebx is zero 

如何处理的word 5个元素( 16b)数组(在数组[0],数组[1]中访问它们,...顺序):

mov ebx,-5 
    lea esi,[array+5*2] 
exampleLoop: 
    mov ax,[esi+ebx*2] ; load value from array[i] 
    ; process it ... and preserve esi and ebx 
    inc ebx 
    jnz exampleLoop ; loop 5 times till ebx is zero 

再举一个例子,我不知何故像这样的很多:

如何设置目标寄存器(eax例子中)到〜0(-1)/ 0时ebx是零/非零并且已经有值1在某些寄存器(例如在ecx):

; ecx = 1, ebx = some value 
    cmp ebx,ecx ; cmp ebx,1 => CF=1/0 for ebx zero/non-zero 
    sbb eax,eax ; eax = -1 (~0)/0 for CF=1/0 ; ebx/ecx intact 

-1可能看起来尽可能的1(用于索引目的至少),但-1还可以作为完整位掩码用于进一步的操作,所以有时它更方便。

+0

感谢它正常工作:) 我感谢你的帮助。 –

+2

对于没有使用任何额外regs的同一个寄存器的情况:'test ebx,ebx' /'setz bl' /'movzx ebx,bl'应该在P6和Haswell上更高效(SBB是2c latency/2 uop)。 'neg/sbb/inc'在Pentium4(slow setcc)上效率更高,我认为它们在Broadwell及其后的版本和AMD(所有1c延迟)上都是相同的,并且代码量较小(特别是在32位)。 –

+1

test/setz/movzx是gcc6.2'-mtune = haswell'所做的,如果你给它一个它想在原地执行的情况:https://godbolt.org/g/076BSI。铛零一个单独的寄存器,然后使用一个mov。 –

-3

你可以使用:

or ebx, 0 ;This does nothing, just triggers the zero flag if ebx is zero 
jnz notZero 
or ebx, 1 ;ebx was zero, then ebx is 1 
notZero: 
+3

即使我已经在评论中发布了'or ebx,0',直接进行downvoting,常见的习惯用法是'test ebx,ebx'。您可以至少使用'或ebx,ebx'来避免立即执行'0',但'test'仍然更好,因为C/C++编译器广泛使用它,所以CPU可以进行额外的优化。 '或'可以被视为普通的算术运算。 – Ped7g

+1

是的,我发布我的东西后,我看到了你的答案,学到了一些东西!谢谢! – LeCalore

+3

@ Ped7g:更多关于为什么'或'是这个不好的选择:http://stackoverflow.com/a/33724806/224132。总结:除了明显的代码大小之外,'test ebx,ebx'可以与JCC宏观融合,并且它不会写入EBX,从而在依赖链中引入一个额外的延迟周期,以便下一次读取EBX。所以是的,CPU对TEST/JCC与OR/JCC有“额外的优化”,并且OR与其他OR相同。 –

2

使用标志卢克
您通过检查零标志来测试寄存器是否为零。
如果寄存器通过一些影响标志的操作(或者更确切地说是零标志)获得了它的值,那么你不必做任何事情,因为零标志已经反映了存储在该寄存器中的值。

测试仅在需要
如果你不能保证标志已经设置,你必须使用一个测试操作。
这些操作有两种类型:破坏性和非破坏性。

你可以看到的指令,并改变在标志列表:http://ref.x86asm.net - 更具体地说,在:http://ref.x86asm.net/coder32-abc.html

movlea指令从未改变的标志,因此需要援助。大多数其他指令至少设置一个标志。

如果你需要测试寄存器零不要创建虚假的依赖
,但不想改变它的价值,您使用test指令。
您不应该使用orand指令来检查寄存器,因为CPU可能不知道or/and可以非破坏性方式使用,并且可能无法应用某些优化。 这是一个'错误的依赖'的技术术语。该寄存器需要ebx,并且'认为'它最近被更改了,因此它等待结果完成。

test ebx, ebx ; <-- CPU knows ebx was not altered, no stalls on subsequent reads. 
or ebx, ebx ; <-- CPU 'thinks' ebx was changed, stall on subsequent read. 

如果你想零状态到另一个寄存器反映,你可以简单地mov EBX到另一个寄存器。

如果你想减少寄存器为布尔(真,如果不为零,否则返回False),您使用以下序列中的一个减少的值到布尔

; ebx holds the value to reduce to a boolean. 
; eax is an unused register. 
xor eax, eax ; eax = 0 
sub eax, ebx ; eax = 0 - ebx; CF (carry flag) = 1 if ebx <> 0 
sbb ebx, ebx ; ebx = ebx - ebx - CF 
       ; <<-- ebx = -1 if non zero, 0 if zero 
xor eax, eax ; eax = 0 
sub eax, ebx ; eax = - ebx; CF = 1 if ebx <> 0 
adc ebx, eax ; ebx = (ebx + -ebx) aka 0 + CF 
       ; <<== ebx = 1 if non zero, 0 if zero 
test ebx, ebx ; force ZF to be correct 
setnz al  ; Store 1 if non-zero, 0 otherwise in byte register AL. 

请注意,由于与“部分寄存器写入”相关的停顿,字节寄存器的使用可能会产生问题。

+0

Ped7g的回答指出,'neg ebx'将'0 - ebx'中的标志设置为1指令(但也会修改原始值)。 –

相关问题