Gcc 我可以去掉CTZ和指针加法之间的符号扩展吗?
对于此类代码:Gcc 我可以去掉CTZ和指针加法之间的符号扩展吗?,gcc,assembly,x86,Gcc,Assembly,X86,对于此类代码: #include <stdint.h> char* ptrAdd(char* ptr, uint32_t x) { return ptr + (uint32_t)__builtin_ctz(x); } 当然,这是完全冗余的——这是对无符号整数的明显符号扩展。我能说服GCC不要这样做吗 该问题自GCC 4.9.0以来就存在,但在此之前,它是一个显式的零扩展,也是冗余的。部分解决方案是使用64位版本的ctz,以及-march参数,以便使用tzcnt代替bsf,
#include <stdint.h>
char* ptrAdd(char* ptr, uint32_t x)
{
return ptr + (uint32_t)__builtin_ctz(x);
}
当然,这是完全冗余的——这是对无符号整数的明显符号扩展。我能说服GCC不要这样做吗
该问题自GCC 4.9.0以来就存在,但在此之前,它是一个显式的零扩展,也是冗余的。部分解决方案是使用64位版本的
ctz
,以及-march
参数,以便使用tzcnt
代替bsf
,如下所示:
char* ptrAdd(char* ptr, uint32_t x)
{
return ptr + __builtin_ctzl(x);
}
在无符号扩展中:
ptrAdd(char*, unsigned int):
mov eax, esi
tzcnt rax, rax
add rax, rdi
ret
它有一个mov
(执行32到64位的零扩展),它取代了32位版本中的一个零化xor
(该版本用于解决tzcnt
)。这些成本大致相同,但是mov
更可能在内联后消失。64位tzcnt
的结果与32位的结果相同,但零输入的情况除外,零输入未定义(就gcc
内部而言,不是tzcnt
)
不幸的是,如果没有允许编译器使用tzcnt
的-march
参数,它将使用bsf
,并且在这种情况下仍然执行符号扩展
似乎bsf
和tzcnt
之间不同行为的根源在于,在使用bsf
版本的情况下,指令行为未定义为零。因此,原则上,指令可以返回任何内容,甚至是超出我们通常期望的0到63范围的值。再加上返回值被声明为int
,简单地忽略符号扩展可能会导致“不可能”的情况,如(\uuuuu builtin\uclzl(x)&0xff)==0xdeadbeef
)
现在根据gcc文档,对\uuuu builtin\u ctzl
的零输入有一个“未定义的结果”——但不清楚这是否与C/C++“未定义的行为”相同,在C/C++“未定义的行为”中可能发生任何事情(这将允许不可能的事情),或者仅仅意味着“一些未指定的值”
您可以在上阅读此内容,其中一个问题已经公开了大约7年。您可以不安全地使用
asm(“:”=r”(ctz64):“0”(ctz))
告诉编译器64位输出与32位输入在同一寄存器中,没有指令()。但这是非常可怕的,如果某些优化导致不只是使用tzcnt结果寄存器,则无法保护您免受输入32位值中的高垃圾的影响。另外,我不确定bsf
如何处理x=0
;它甚至可能不会将EAX扩展到RAX。(我看到了符号扩展,即使在明确使用tzcnt的情况下,-march=haswell
,tzcnt总是编写EAX)。gcc有一些遗漏的优化错误,它对输出值没有足够的了解。这一个非常奇怪;我认为内部bsf
/tzcnt
必须定义为返回int
或long
,但是IDK为什么首先强制转换到uint32\u t
并不能解决它。我想知道\u内置的假设()
是否有帮助。@PeterCordes-这看起来像是优化器中一个实际的错误代码。正如我在回答中提到的,基本问题似乎是,bsf/bsr
的未定义行为使得您通常假设的内置返回值(即,它在0-31范围内)不一定是真的,因此编译器在扩展方面必须保守,但是这里生成的代码似乎是错误的-它可能导致从ptr
中减去一个值,忽略对unsigned的转换(除非我在这里混淆了我的C转换规则)。可能是因为这些本质是特殊情况?@BeeOnRope:从技术上讲,这不是一个bug,因为:如果x是0,那么结果是未定义的。这不是ffs
库函数或任何东西;内置代码似乎被定义为只使用bsf而不是tzcnt高效编译。(但不幸的是,这些天没有。)似乎gcc7.3-march=skylake
不知道skylake修复了tzcnt
/lzcnt
(但不是popcnt
)对目的地的错误依赖性。我问了一些稍微不同的问题,但答案实际上是这个问题的答案(链接到同一错误条目)。
ptrAdd(char*, unsigned int):
mov eax, esi
tzcnt rax, rax
add rax, rdi
ret