Gcc 装配中的钻头测试(BT)

Gcc 装配中的钻头测试(BT),gcc,assembly,x86,Gcc,Assembly,X86,我使用bt测试汇编中是否设置了某些位。我的代码如下所示: #define bv %eax //just renaming reg to make reading easier #define bit %edx //rename reg /* * bitIsSetBV(unsigned int *bv, unsigned int bit) * this function returns 0 if bit at location bit is not set * it returns

我使用bt测试汇编中是否设置了某些位。我的代码如下所示:

#define bv  %eax   //just renaming reg to make reading easier
#define bit %edx   //rename reg
/*
 * bitIsSetBV(unsigned int *bv, unsigned int bit)
 * this function returns 0 if bit at location bit is not set
 * it returns 1 if it is set
 */

.text
.global bitIsSetBV

bitIsSetBV:
movl 4(%esp), bv   //make room on stack for bv
movl 8(%esp), bit  //make room on stack for bit

bt bit, (bv)       //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet          // if CF is 1, jump to bitSet
jnc bitNotSet      // if CF is 0, jump to bitNotSet

bitNotSet:
movl $0, bv        //return 0, bit is not set
jmp Done           // jump to Done

bitSet:
movl $1, bv         // return 1, bit is set
jmp Done           // jump to Done

Done:
ret                //end of code
./bittest
测试人员:


我的代码适用于位=0-5,但在位=6时,它不适用。为什么呢?我肯定这是我对位级操作的误解。

这是对您问题的回答,但不一定是有解决方案的答案。虽然您的汇编代码效率很低,但当我第一次看到它时,我没有看到它的任何逻辑缺陷,因此我假设问题不在您的汇编代码中,而是在您的C代码中的某个地方。我昨晚确实要求你提供你的C代码,但没有后续行动

我从下面的汇编代码中提取了多余的跳转和分支,但是您的原始代码应该可以工作。我把它放在一个名为
isbitset.S
的程序集文件中:

#define bv  %eax   //just renaming reg to make reading easier
#define bit %edx   //rename reg
/*
 * bitIsSetBV(unsigned int *bv, unsigned int bit)
 * this function returns 0 if bit at location bit is not set
 * it returns 1 if it is set
 */

.text
.global bitIsSetBV

bitIsSetBV:
movl 4(%esp), bv   //make room on stack for bv
movl 8(%esp), bit  //make room on stack for bit

bt bit, (bv)       //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet          // if CF is 1, jump to bitSet

bitNotSet:
movl $0, bv        //return 0, bit is not set
jmp Done           // jump to Done

bitSet:
movl $1, bv         // return 1, bit is set

Done:
ret                //end of code
我推荐Peter Cordes在评论中提出的建议,以提高代码的效率。因为我认为问题在于测试程序,所以我更关心它是如何被调用的。我用C语言创建了一个简单的测试程序,名为
bitest.C

#include <assert.h>

extern int bitIsSetBV(unsigned int *bv, unsigned int bit);

int main()
{
  unsigned int a = 0xe1; /* which is binary 11100001 */
  unsigned int *bv = &a;

  assert(bitIsSetBV(bv, 0) == 1); /* 0001 */
  assert(bitIsSetBV(bv, 1) == 0);
  assert(bitIsSetBV(bv, 2) == 0);
  assert(bitIsSetBV(bv, 3) == 0);

  assert(bitIsSetBV(bv, 4) == 0); /* 1110 */
  assert(bitIsSetBV(bv, 5) == 1);
  assert(bitIsSetBV(bv, 6) == 1);
  assert(bitIsSetBV(bv, 7) == 1);

  return 0;
}
或者,您也可以这样做:

gcc -m32 bittest.c isbitset.S -o bittest
我是这样运行的:

#define bv  %eax   //just renaming reg to make reading easier
#define bit %edx   //rename reg
/*
 * bitIsSetBV(unsigned int *bv, unsigned int bit)
 * this function returns 0 if bit at location bit is not set
 * it returns 1 if it is set
 */

.text
.global bitIsSetBV

bitIsSetBV:
movl 4(%esp), bv   //make room on stack for bv
movl 8(%esp), bit  //make room on stack for bit

bt bit, (bv)       //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet          // if CF is 1, jump to bitSet
jnc bitNotSet      // if CF is 0, jump to bitNotSet

bitNotSet:
movl $0, bv        //return 0, bit is not set
jmp Done           // jump to Done

bitSet:
movl $1, bv         // return 1, bit is set
jmp Done           // jump to Done

Done:
ret                //end of code
./bittest
-m32
确保生成32位可执行文件。如果您是在默认为Linux使用的64位系统上编译此文件,那么这会产生很大的不同。您使用32位调用约定编写了汇编代码,因此,如果从编译为64位的C程序调用,它将无法工作

在本例中,代码不会生成任何断言,这意味着这些位与我预期的一致


这个答案还提供了一个例子,说明了如何创建一个人们可以测试的问题。

只需重命名reg以使阅读更容易。
这条评论很有趣。我甚至没有看那两行代码,我想知道
BT
是如何使用一对内存操作数的。也许我已经老了,但是这两个定义使得代码更难理解,因为你没有向我们展示定义
bv
的测试C代码,所以我们不知道它实际上是如何定义和分配的。尽管您的代码效率低下,但这种冗余是完全不必要的:
jnc bitNotSet
。删除该行,因为您之前已经在指令上设置了进位测试,因此,如果进位设置失败,则不得设置该行。它将直接进入下一条指令,而下一条指令恰好是
bitNotSet
的代码
jmp Done
在标签
Done
之前也可以删除,因为您要跳转到下一条指令。
movl 4(%esp),bv//在堆栈上为bv腾出空间
对该代码的注释不匹配。堆栈已经保存了这些值,您正在将它们复制到寄存器中。你没有在堆栈上为它们腾出空间。
bt位,(bv)//如果bv[bit]为0,将CF设置为0,否则,将CF设置为1
这是怎么回事?这只是解引用
bv
并在
位测试位
。。。如何在C代码中定义
bv
?使用
xor%eax,%eax/bt/setc%al
而不是在进位标志上进行分支。