为什么16位寄存器在此代码片段中与BSR指令一起使用?

时间:2009-11-30 07:46:56

标签: assembly x86 bit-manipulation

this hardcore article中,有一个函数find_maskwidth(),基本上可以检测表示itemCount dictinct值所需的位数:

unsigned int find_maskwidth( unsigned int itemCount )
{
    unsigned int maskWidth, count = itemCount;
    __asm {
        mov eax, count
        mov ecx, 0
        mov maskWidth, ecx
        dec eax
        bsr cx, ax
        jz next
        inc cx
        mov maskWidth, ecx
    next:
    }
    return maskWidth; 
} 

问题是为什么他们使用axcx个注册代替eaxecx

5 个答案:

答案 0 :(得分:2)

我只能猜测他们希望只处理不超过16位的字段。由于它用于确定可用于报告包中核心数或逻辑处理器数等的位数,因此在溢出超过65535之前可能需要一段时间。

我希望有人不会决定将这个例程用于更一般的目的。

只是一个FYI - 如果你想做这样的事情而不是放弃到x86程序集(虽然我猜这篇文章的目的,非便携性几乎是给定的),Bit Twiddling Hacks page你已经覆盖了

答案 1 :(得分:1)

我猜原始数据只有16位宽。由于cx获得了一个位数,所以无论如何它都不可能大于一个非常小的数字。

值得注意的是,在一个级别上,除了前缀字节之外,16位和32位ia32指令的操作码是相同的,因此发出全32位或全16位指令更有效,具体取决于你在哪种模式。我想这就是你问这个问题的原因......

答案 2 :(得分:1)

鉴于这段代码真的写错了(例如,绝对不需要maskWidth和count变量 - 除了让代码混乱之外)我想你可以放心,它只是另一个“这个代码中的坏事。

答案 3 :(得分:1)

例程基本确定itemCount的二进制(基数2)对数。

如果itemCount>它将提供完全错误的值。 2 ^ 16。它没有饱和或什么,这是完全错误的。输入参数是“unsigned int”,这使得它更加错误。因此它将停止在更多65536核心工作。

我的猜测是,英特尔的某个人挖掘了一些非常古老的代码,可以追溯到16位,没有真正了解它,并且使用它,因为65536将永远足够,完全就像640k内存将永远是足够的或者两位数的年份数字就足够了,直到时间结束。

答案 4 :(得分:1)

我想这是因为这段代码的作者可能并不真正知道他在做什么:-)。这些指令的16位版本更长,而且速度更快。实际上,它们可能会在下一条使用ECX的指令(即MOV)上导致部分寄存器停顿。

另请注意,跳转可以安全地移动一条指令(在DEC之后),因为DEC在输出为零时已经设置了ZF。这可以简化代码。

所以这就是我写这段代码的方式:

  mov eax, [count]
  xor ecx, ecx
  dec eax
  jz next
  bsr ecx, eax
  inc ecx
next:
  mov [maskWidth], ecx

此外,在这里放弃组装的动机似乎是使用BSR指令,它在C语言或库中没有任何等价物。为此,您可以通过使用特定于编译器的内部函数来避免使用程序集。虽然这些本质上是不可移植的,但内联汇编也不是。

在GCC中,等效函数如下所示:

unsigned int find_maskwidth(unsigned int itemCount)
{
   if(itemCount <= 1)
      return 0;
   else
      return 32 - __builtin_clz(itemCount - 1);
}

更具可读性,不是吗?