【问题标题】:set a bit with inline asm without btsl instruction在没有 btsl 指令的情况下使用 inline asm 设置位
【发布时间】:2020-08-18 13:40:10
【问题描述】:

我想在位置 p 设置一个位,而不使用带有内联 asm gcc c 代码的 btsl 指令。使用 btsl 指令很简单:

int n,p;
scanf("%d%d",&n,&p);
asm("btsl %1, %0"
:"=m"(n)
:"Ir"(p)
:"cc");

我知道设置位是可以做到的:

n|=1<<p;

现在,我正在使用内联 asm:

  asm("movl %1, %%eax;"
      "movl %2, %%ebx;" 
      "shl $1, %%ebx;"
      "orl %%ebx, %%eax;"
      "movl %%eax, %0;"
      
  :"=b"(n)
  :"a"(n), "b"(p)
  :"cc");
  printf("%d\n",n);

在我看来 shl 指令不起作用。例如 n=20=10100。当我尝试设置第一位 p=1 时,结果是 22=10110 而不是 21=10101

【问题讨论】:

  • 另外,shl $1, %%ebx 不会像您认为的那样做。它将ebx 移动1 而不是1 移动ebx。 PS:不要在内联汇编中使用mov,只使用适当的约束。您已经为p 指定了b 作为输入,因此编译器已经将它放入ebx。所以movl %2, %%ebx 只是扩展为毫无意义的movl %ebx, %ebxn 也一样。您也忘记将%eax 指定为输出/clobber。
  • 请注意,您将eax 指定为输入寄存器,然后继续覆盖它。这是不允许的。
  • 如果您将其指定为输出(或一个clobber,但如果它已经是一个输入则不支持),您可以覆盖它。您的代码实际上应该只包含 shlor 并在输入和输出中使用约束。
  • 为什么除了shl 之外的每条指令都使用冗余的l 操作数大小后缀?无论如何,这并不重要,我认为使用内联汇编没有任何意义。只需使用较旧的 GCC 编译 x|=1&lt;&lt;p;(错过 BTS 窥孔优化或选择不使用它),然后查看 asm 输出 (How to remove "noise" from GCC/clang assembly output?),如果您好奇什么 asm 序列将实现这一点。
  • btsl 有什么问题?如果你的 CPU 坏了,你应该更换 CPU。

标签: c assembly gcc x86 inline-assembly


【解决方案1】:

按照 Michael 的示例,我已经完成了 clear、toggle 和 set:

unsigned long bitset(unsigned long value, uint8_t shift)
{
    unsigned long tmp;

    asm ("mov $0x1, %[tempreg]\n\t"
         "shl %[shift], %[tempreg]\n\t"
         "or %[tempreg], %[val]"
    : [val]"+r"(value),
      [tempreg]"=&r"(tmp)
    : [shift]"cN"(shift));

    return value;
}


unsigned long bitclear(unsigned long value, uint8_t shift)
{
    unsigned long tmp;

    asm ("mov $0x1, %[tempreg]\n\t"
         "shl %[shift], %[tempreg]\n\t"
         "not %[tempreg]\n\t"
         "and %[tempreg], %[val]\n\t"
    : [val]"+r"(value),
      [tempreg]"=&r"(tmp)
    : [shift]"cN"(shift));

    return value;
}
unsigned long toggle(unsigned long value, uint8_t shift)
{
    unsigned long tmp;

    asm ("mov $0x1, %[tempreg]\n\t"
         "shl %[shift], %[tempreg]\n\t"
         "xor %[tempreg], %[val]"
    : [val]"+r"(value),
      [tempreg]"=&r"(tmp)
    : [shift]"cN"(shift));

    return value;
}

【讨论】:

  • 你为什么要使用EBX?那是没有意义的。以mov 指令开始和结束也是没有意义的,只需告诉编译器您将内容放在哪里的正确寄存器,并在正确的寄存器中请求它以开始(即ECX 中的计数)。此外,这是错误的:您编写 ECX 时没有在其上声明一个 clobber。 (对该错误投反对票;内联 asm 很难在没有错误使用它的危险示例的情况下正确使用。如果您将其减少到仅“必要”的 3 条指令,或者如果您只是勉强做到的话,我很乐意将其删除安全但仍然低效的垃圾。)
  • 如果您真的打算使用这 3 条指令(mov/shl/or),那么您可以这样做:godbolt.org/z/MvKnsa
  • bitclear 仍然只需要 3 条指令;您可以旋转0xFFFFFFFE 将零放在您想要的位置。
  • @Peter Cordes,你能写这个例子吗?我从来没有见过这样的清除方法。
猜你喜欢
  • 1970-01-01
  • 2013-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 2018-02-04
  • 2014-04-04
相关资源
最近更新 更多