我可以摆脱CTZ和指针之间的符号扩展吗?

时间:2018-02-06 02:10:34

标签: gcc assembly x86

对于这样的代码:

#include <stdint.h>

char* ptrAdd(char* ptr, uint32_t x)
{
    return ptr + (uint32_t)__builtin_ctz(x);
}

GCC生成符号扩展名:(godbolt link

xor eax, eax
rep bsf eax, esi
cdqe ; sign-extend eax into rax
add rax, rdi
ret

这当然是完全冗余的 - 这是公然符号扩展无符号整数。我可以说服海湾合作委员会不要这样做吗?

自GCC 4.9.0以来存在问题,但在此之前它曾经是一个明确的零扩展,这也是多余的。

1 个答案:

答案 0 :(得分:4)

部分解决方案是使用64位版本的ctz以及-march参数,以便使用tzcnt代替bsf,如下所示:

char* ptrAdd(char* ptr, uint32_t x)
{
    return ptr + __builtin_ctzl(x);
}

This results没有符号扩展名:

ptrAdd(char*, unsigned int):
  mov eax, esi
  tzcnt rax, rax
  add rax, rdi
  ret

它有mov(做32到64位的零扩展),取代了32位版本中的归零xor(可以在{{1}处工作} false-dependency-on-destination issue)。这些费用大致相同,但内联后tzcnt更有可能消失。 64位mov的结果与32位的结果相同,除了零输入未定义的情况(就tzcnt内在而言,而不是{{1} })。

不幸的是,如果没有允许编译器使用gcc的{​​{1}}参数,它将使用tzcnt,并且在这种情况下仍会执行符号扩展。

似乎-marchtzcnt之间不同行为的起源是,在使用bsf版本的情况下,指令行为未定义为零。因此原则上,指令可以返回任何,甚至是我们通常期望的0到63范围之外的值。结合返回值声明为bsf这一事实,简单地省略符号扩展名可能会导致“不可能”的情况,例如tzcnt

现在根据gcc文档,bsf的零输入有一个“未定义的结果” - 但不清楚这是否与C / C ++相同“未定义的行为”任何事情都可能发生(这将允许不可能发生的事情),或者只是意味着“一些未指明的价值”。

您可以在问题已开放约7年的gcc bugzilla上阅读此内容。