验证在一系列位中只有一个打开

时间:2018-02-16 04:27:54

标签: assembly x86 x86-16 micro-optimization

我已经编写了这个算法,其中评估了三个条件位,以确定如何根据给定地址对齐文本。

7 =向右对齐
6 =对齐中心
5 =向右对齐。

目前,没有实现默认值,因此如果没有定义任何内容,则返回ZF表示没有发生任何事情。屏幕上没有预期的文字也是指标。

        test    al, 111000000B
        jz      Done

现在这个解决方案适合我,因为作为ABI设计的一部分,CF表示错误。这解决了需求,并将适用于16/32/64位,但我想认为有一种更有效的方式或至少有一种不那么重要的方式。

    0  0FBC46FE          bsf ax,[bp-0x2]
    4  88C3              mov bl,al
    6  0FBD46FE          bsr ax,[bp-0x2]
    A  28C3              sub bl,al
    C = 12 bytes

由于对结尾的每个条件都有特殊处理,我可以通过将 AH 旋转到进位来捕获它,并一直这样做直到CY和ZF。如果有一个进位且非零,那么我知道有超过1位的设置或者我可以忽略多余的位然后函数将按位位置优先。

1 个答案:

答案 0 :(得分:1)

您可能需要重新设计此选项,以便在2位字段中使用3或4个值,而不是位图,如果它们完全相互排斥的话。 (See my comments关于这个问题)。

屏蔽掉非位图位后:

一个很好的方法是使用the (n & (n - 1)) == 0 bithack作为2的幂的整数。(或者实际上,测试它最多有1位设置,而不是1位设置,因为该表达式是n == 0时为true。您可以使用lea edx, [rax-1] / and edx, eax / add edx, -1实现该功能,如果eax设置的位数超过1,则会设置CF,否则CF清零。 (add edx, -1包含除0)之外的每个edx值。

VEX前缀在16位模式下不可用(为了兼容使用相同的先前非法位模式作为陷阱的一些旧的实模式约定),但仅在32/64位模式下,在具有BMI1的CPU上

使用blsr(重置最低设置位)实现该位操作是一种非常有效的方法,因为它以对此bithack有用的方式设置标志。几乎就像指令集架构师知道他们在做什么......:

and   eax, 11100000b     ; clear other bits

; requires BMI1
blsr  edx, eax           ; destination = a dummy register, also sets flags

jnz  multiple_bits_were_set       ; and thus edx still has a bit set
jc   input_was_zero

即。它可以在一条指令中完成您想要的一切,即使使用CF的方式适用于没有cmc的代码。您的multiple_bits_were_set分支目标可能只有stc并落入input_was_zero

如果(CF == 1或ZF == 0),只有ja(CF = 0和ZF = 0)和jbe(CF = 1),则没有JCC跳转或ZF = 1)。你可以cmc / jbe exactly_one_bit_set,但是你可能会有一个潜在的部分标志减速,所以最好只使用两个独立的分支。或者,因为您可能正在分支 设置的位,可能只使用blsr来检查多个位设置,然后使用cmp al, 01000000b / jae bits_1_or_2把事情分成最高或第二高与最低(前三名)或没有设定。

如果你想只分支一次没有位或太多位:

 AND   eax, mask

 blsr  edx, eax     ; edx = 0 iff no more than 1 bit was set in the input
 ; CF=1 iff eax=9
 adc   edx, 0
 jnz   not_exactly_one_bit_set

adc无法换算为零,因为根据定义,blsr始终保持低位清零。如果在BLSR之后edx = 0,并且edx没有添加任何内容,则adc只能为零(因此只能设置ZF)。

如果您的位掩码不包含寄存器的高位,adc edx,edx也可以工作,保存一个字节。但是,在adc通常为2 uop的某些Intel CPU上,adc具有立即0可以特殊地设置为1 uop。 (我想我曾经读过这篇文章,但没有在Haswell上测试过。)