xiv*_*r77 5 c x86 assembly compiler-optimization bigint
我正在尝试在不使用内联汇编的情况下有效地实现SHLD和SHRD说明x86。
uint32_t shld_UB_on_0(uint32_t a, uint32_t b, uint32_t c) {
return a << c | b >> 32 - c;
}
Run Code Online (Sandbox Code Playgroud)
似乎有效,但当c == 0第二个移位的操作数变为时会调用未定义的行为32。SHLD第三个操作数的实际指令0被明确定义为不执行任何操作。(https://www.felixcloutier.com/x86/shld)
uint32_t shld_broken_on_0(uint32_t a, uint32_t b, uint32_t c) {
return a << c | b >> (-c & 31);
}
Run Code Online (Sandbox Code Playgroud)
不会调用未定义的行为,但当c == 0结果是a | b而不是a.
uint32_t shld_safe(uint32_t a, uint32_t b, uint32_t c) {
if (c == 0) return a;
return a << c | b >> 32 - c;
}
Run Code Online (Sandbox Code Playgroud)
做了预期的事情,但gcc现在放了一个je. clang另一方面,它足够聪明,可以将其转换为单个shld指令。
有没有什么方法可以在不使用内联汇编的情况下正确高效地实现呢?
为什么gcc这么努力却不放shld呢?该shld_safe尝试被gcc11.2 -O3 翻译为(Godbolt):
shld_safe:
mov eax, edi
test edx, edx
je .L1
mov ecx, 32
sub ecx, edx
shr esi, cl
mov ecx, edx
sal eax, cl
or eax, esi
.L1:
ret
Run Code Online (Sandbox Code Playgroud)
当 clang 这样做时,
shld_safe:
mov ecx, edx
mov eax, edi
shld eax, esi, cl
ret
Run Code Online (Sandbox Code Playgroud)
小智 1
据我使用 gcc 9.3 (x86-64) 进行测试,它将以下代码转换为shldq和shrdq。
uint64_t shldq_x64(uint64_t low, uint64_t high, uint64_t count) {
return (uint64_t)(((((unsigned __int128)high << 64) | (unsigned __int128)low) << (count & 63)) >> 64);
}
uint64_t shrdq_x64(uint64_t low, uint64_t high, uint64_t count) {
return (uint64_t)((((unsigned __int128)high << 64) | (unsigned __int128)low) >> (count & 63));
}
Run Code Online (Sandbox Code Playgroud)
另外,gcc -m32 -O3将以下代码翻译为shld和shrd。(不过,我还没有使用 gcc (i386) 进行测试。)
uint32_t shld_x86(uint32_t low, uint32_t high, uint32_t count) {
return (uint32_t)(((((uint64_t)high << 32) | (uint64_t)low) << (count & 31)) >> 32);
}
uint32_t shrd_x86(uint32_t low, uint32_t high, uint32_t count) {
return (uint32_t)((((uint64_t)high << 32) | (uint64_t)low) >> (count & 31));
}
Run Code Online (Sandbox Code Playgroud)
(我刚刚阅读了 gcc 代码并编写了上述函数,即我不确定它们是您期望的函数。)