gcc - 我可以摆脱 CTZ 和添加到指针之间的符号扩展吗?

标签 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 以来就存在,但在此之前它曾经是一个显式的零扩展,这也是多余的。

最佳答案

部分解决方案是使用 ctz 的 64 位版本,以及 -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 位零扩展)取代了归零 xor在 32 位版本中(用于解决 tzcnt false-dependency-on-destination issue )。这些成本大致相同,但 mov内联后更有可能消失。 64 位 tzcnt 的结果与 32 位相同,除了未定义的零输入的情况(就 gcc 内在函数而言,而不是 tzcnt )。

不幸的是,没有 -march允许编译器使用 tzcnt 的参数它将使用 bsf在这种情况下,仍然进行符号扩展。

似乎bsf 之间不同行为的起源和 tzcnt是在 bsf 的情况下使用版本,指令行为未定义为零。所以原则上,指令可以返回任何东西,甚至是我们通常期望的 0 到 63 范围之外的值。结合返回值声明为int ,简单地省略符号扩展可能会导致“不可能”的情况,例如 (__builtin_clzl (x) & 0xff) == 0xdeadbeef .

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

您可以在 gcc bugzilla 上阅读有关此内容的信息。 ,其中一个问题已经公开了大约 7 年。

关于gcc - 我可以摆脱 CTZ 和添加到指针之间的符号扩展吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48634422/

相关文章:

gcc - 使用 PPC 汇编器时为 "Error: operand out of range"

assembly - ASSUME dataregister : qualifiedtype [, dataregister : qualifiedtype]? 有什么示例

c - 通过 r15 访问 r8 的低 32 位

c - 为什么使用 -Os 编译会使这个函数变大?

assembly - x86 汇编器中 "data label"的功能是什么?

x86 - 进化算法是否有可能创建机器代码?

assembly - 为 x86 程序集绘制堆栈框架

c - 解密 x86 汇编函数

linux - 如何链接到旧版本的数学库?

linux - 目标代码文件 ctr1.o 在 gcc 编译器中有什么作用?