【问题标题】:Bit Test (BT) in assembly装配中的位测试 (BT)
【发布时间】:2016-02-29 05:30:42
【问题描述】:

我正在使用 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

测试人员:

  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);

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

【问题讨论】:

  • just renaming reg to make reading easier 这个评论很有趣。我什至没有看这两行,而是看了你的代码,想知道BT 是如何使用几个内存操作数的。也许我变老了,但是这两个定义使代码更难遵循 lol
  • 您没有向我们展示定义bv 的测试C 代码,因此我们不知道它是如何实际定义和分配的。尽管您的代码效率低下,但这种冗余是完全没有必要的:jnc bitNotSet。删除该行,因为您之前已经测试过是否在指令上设置了进位,因此如果它通过,则不得设置。它会一直执行到下一条指令,该指令恰好是bitNotSet 的代码。 jmp Done 之前的标签 Done 也可以删除,因为您正在跳转到下一条指令。
  • movl 4(%esp), bv //make room on stack for bv 该代码的注释不匹配。堆栈已经保存了这些值,您正在将它们复制到寄存器中。你不是 making room 在堆栈上的他们。
  • bt bit, (bv) //if bv[bit] is 0, set CF to 0, else, set CF to 1 这个bv[bit] 怎么样?这只是取消引用bv 并测试bit 的位...您如何在C 代码中定义bv
  • 不要在进位标志上分支,而是使用xor %eax,%eax / bt / setc %al

标签: gcc assembly x86


【解决方案1】:

这是对您问题的回答,但不一定是带有解决方案的答案。尽管您的汇编代码效率低下,但当我第一次看到它时,我并没有看到任何逻辑缺陷,所以我认为问题不在于您的汇编代码,而在于您的 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 中创建了这个简单的测试程序,名为 bittest.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;
}

我使用以下命令组装、编译并将代码链接到名为bittest 的程序:

gcc -m32 -c isbitset.S -o isbitset.o
gcc -m32 bittest.c isbitset.o -o bittest

或者你也可以这样做:

gcc -m32 bittest.c isbitset.S -o bittest

我是这样运行的:

./bittest

-m32 确保您生成的是 32 位可执行文件。如果您在 Linux 使用的默认为 64-bit System V ABI 的 64 位系统上编译它,这可能会产生很大的不同。您使用 32 位调用约定编写了汇编代码,因此如果从编译为 64 位的 C 程序调用,它将无法工作。

在这种情况下,代码不会生成任何断言,这确实意味着这些位与我预期的一样。

这个答案还提供了一个Minimal Complete Verifiable Example 并且是一个示例,说明如何创建一个人们可以测试的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 2019-07-16
    • 2016-10-10
    • 1970-01-01
    相关资源
    最近更新 更多