gcc-O3删除int-to-string转换器中的活动代码

gcc-O3删除int-to-string转换器中的活动代码,c,gcc,assembly,x86-64,C,Gcc,Assembly,X86 64,我是64位izing,它需要一个char*,必须指向至少20个字符和一个数字的缓冲区,我创建了这个: #define LOOP_WORK(number, shift) high *= 5; low *= 5; buf[number] = (high >> shift) + '0'; \ buf[number+10] = (low >> shift) + '0'; high &= andop; low &= andop; andop >>

我是64位izing,它需要一个
char*
,必须指向至少20个字符和一个数字的缓冲区,我创建了这个:

#define LOOP_WORK(number, shift) high *= 5; low *= 5; buf[number] = (high >> shift) + '0'; \
    buf[number+10] = (low >> shift) + '0'; high &= andop; low &= andop; andop >>= 1

#define uint128_t __uint128_t
#define uint64_t  unsigned long

void u2s(char* buf, unsigned long num) {
    // Split number into low/high pair.
    uint128_t split = num * 7922816251426433760;
    split += num >> 1;
    uint64_t high = split >> 96;
    uint64_t low = num - (high * 10000000000);
    // Transform numbers into usable decimal fractions.
    split = high * 18446744074;
    buf[0]  = (split >> 64) + '0';
    high = (uint64_t)split;
    split = low  * 18446744074;
    buf[10] = (split >> 64) + '0';
    low  = (uint64_t)split;
    // Adjust numbers and multiply by 2 (so we don't have to multiply by 10 later)
    high = (high + 7) >> 3;
    low  = (low  + 7) >> 3;
    // Store special and number
    uint64_t andop = 0x0fffffffffffffff;
    LOOP_WORK(1, 60);
    LOOP_WORK(2, 59);
    LOOP_WORK(3, 58);
    LOOP_WORK(4, 57);
    LOOP_WORK(5, 56);
    LOOP_WORK(6, 55);
    LOOP_WORK(7, 54);
    LOOP_WORK(8, 53);
    // Final loop, without extra stuffs
    high *= 5;
    low  *= 5;
    buf[9]  = (high >> 52) + '0';
    buf[19] = (low  >> 52) + '0';
}

#undef LOOP_WORK
以下是汇编中的等效版本(用AT&T手写):

当我在GCC中编译带有优化的C版本时,
-O3
,我发现其中一个高/低变量的代码被优化掉了,它到处都有
和op
的硬编码值,开始加载的代码在
buf[0]
buf[10]
中输入硬编码值(
48
3472328296227680304
)当我用
-fverbose asm-S
运行GCC时,我发现GCC优化了
high
!我猜我的C代码就是问题所在(我对C不是太在行),但我不知道为什么。它有自己的C版本,但它不包括那里给出的手写程序集优化。为什么GCC会把我搞得一团糟

B.T.W,这里是来自gcc(Gentoo 7.3.0-r1 p1.1)7.3.0的代码(使用标志:
-cilk+cxx-debug-doc+fortran-go-graphite-mpx-nls+nptl-objc-objc++-objc-gc+openmp+pch-pgo+pie-回归测试+sanitize+ssp-vanilla+vtv
)和标志
-O3-c
(带有标志的objdump
-d
):


是的,当编译器向您显示某些函数输出出乎意料地不依赖于输入时,这通常表明您的C源代码并不是您所认为的那样

在本例中,问题似乎出在
uint128_t split=num*7922816251426433760;
上。
num
是一个
无符号长的
uint64_t
在您编译的x86-64系统ABI中)因此,
*
运算符生成64位结果,该结果作为
uint128\u t split
的初始值设定项进行零扩展

uint128_t split = (unsigned __int128) num * 7922816251426433760;
在乘法之前将
num
强制转换为128位整数,这样就可以使用
mulq
获得完整的128位结果()

我没有研究函数其余部分的全部细节;可能还有其他问题,但这是我看到的第一个问题


回复:更新:


split=high*18446744074;
应该是
split=high*(无符号)18446744074;
?看起来完全是同一个bug。检查C代码的其余部分是否还有将计算结果分配给更广泛变量的情况。

谢谢!它解决了被优化掉的
high
的主要问题!但是启动代码仍然混乱。非常感谢!顺便说一句,GCC不应该对此抱怨吗?我试着用
-Wall-Wextra-Wpedantic
编译我的代码,但什么也没出现。为什么?@ARaspiK:你可以在无符号类型上使用算术溢出,而不会产生任何后果,所以64位无符号乘法和溢出是合法的。编译器认为你说的是真的。@ARaspiK:你希望gcc会这样做吗警告
uint64\u t high=split>>96;
是一种过于复杂的编写
high=0
的方式。在本例中,这是一个错误,但在其他情况下,特别是在内联之后,当两个单独的函数最终以这种方式交互时,相同的逻辑可能是一种理想的优化。或者它可能来自一个宏,而编译器本身就是这样做的无法看到,因此即使代码在同一个函数中,也可能不适合发出警告。我看到的最接近的东西是
-Wtype limits
,它将警告
未签名的u=…;if(u<0)
if(u>UINT\u MAX)
。在这种情况下,这样的警告选项对于本地化调试可能很有用,但gcc没有这样的选项()。我没有检查clang。通常情况下,您只需使用调试器单步执行,并注意到
high=0
,然后立即返回问题行。查看asm输出并向后查看
high=0
是编译时常量,这是另一种查看相同内容的方式,这就是我所做的。是
split=high*18吗446744074;
假定为
split=high*(无符号\uu int128)18446744074;
?看起来完全一样。检查C代码的其余部分是否还有将计算结果分配给更大变量的情况。你说得对!它是固定的!谢谢。你能将该注释作为一个答案,或者添加到你以前的答案中吗?顺便说一句,关于你的第二条注释,我们稍后会忽略
split 。我们只使用低位。测试程序!在我的测试中,我只得到0的输出,因为传递的是
strtoull
argv[0]
而不是
argv[1]
facepalm。好了,现在已经解决了!完成了,尽管我的回答已经指出了您存在的bug。在您发布的代码中查找该bug的每个实例只是一个小细节(对于将来在不同代码中遇到相同问题的读者来说)
u2s.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <u2s>:
   0:   48 b8 0a fa 82 4b 04    movabs $0x44b82fa0a,%rax
   7:   00 00 00 
   a:   48 b9 30 30 30 30 30    movabs $0x3030303030303030,%rcx
  11:   30 30 30 
  14:   c6 47 0a 30             movb   $0x30,0xa(%rdi) // What?
  18:   48 0f af f0             imul   %rax,%rsi
  1c:   48 89 0f                mov    %rcx,(%rdi) // What?
  1f:   48 83 c6 07             add    $0x7,%rsi
  23:   48 c1 ee 03             shr    $0x3,%rsi
  27:   48 8d 04 b6             lea    (%rsi,%rsi,4),%rax
  2b:   48 89 c2                mov    %rax,%rdx
  2e:   48 c1 ea 3c             shr    $0x3c,%rdx
  32:   83 c2 30                add    $0x30,%edx
  35:   88 57 0b                mov    %dl,0xb(%rdi)
  38:   48 ba ff ff ff ff ff    movabs $0xfffffffffffffff,%rdx
  3f:   ff ff 0f 
  42:   48 21 d0                and    %rdx,%rax // There should be another AND
  45:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  49:   48 89 c2                mov    %rax,%rdx
  4c:   48 c1 ea 3b             shr    $0x3b,%rdx
  50:   83 c2 30                add    $0x30,%edx
  53:   88 57 0c                mov    %dl,0xc(%rdi)
  56:   48 ba ff ff ff ff ff    movabs $0x7ffffffffffffff,%rdx
  5d:   ff ff 07 
  60:   48 21 d0                and    %rdx,%rax
  63:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  67:   48 89 c2                mov    %rax,%rdx
  6a:   48 c1 ea 3a             shr    $0x3a,%rdx
  6e:   83 c2 30                add    $0x30,%edx
  71:   88 57 0d                mov    %dl,0xd(%rdi)
  74:   48 ba ff ff ff ff ff    movabs $0x3ffffffffffffff,%rdx
  7b:   ff ff 03 
  7e:   48 21 d0                and    %rdx,%rax
  81:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  85:   48 89 c2                mov    %rax,%rdx
  88:   48 c1 ea 39             shr    $0x39,%rdx
  8c:   83 c2 30                add    $0x30,%edx
  8f:   88 57 0e                mov    %dl,0xe(%rdi)
  92:   48 ba ff ff ff ff ff    movabs $0x1ffffffffffffff,%rdx
  99:   ff ff 01 
  9c:   48 21 d0                and    %rdx,%rax
  9f:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  a3:   48 89 c2                mov    %rax,%rdx
  a6:   48 c1 ea 38             shr    $0x38,%rdx
  aa:   83 c2 30                add    $0x30,%edx
  ad:   88 57 0f                mov    %dl,0xf(%rdi)
  b0:   48 ba ff ff ff ff ff    movabs $0xffffffffffffff,%rdx
  b7:   ff ff 00 
  ba:   48 21 d0                and    %rdx,%rax
  bd:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  c1:   48 89 c2                mov    %rax,%rdx
  c4:   48 c1 ea 37             shr    $0x37,%rdx
  c8:   83 c2 30                add    $0x30,%edx
  cb:   88 57 10                mov    %dl,0x10(%rdi)
  ce:   48 ba ff ff ff ff ff    movabs $0x7fffffffffffff,%rdx
  d5:   ff 7f 00 
  d8:   48 21 d0                and    %rdx,%rax
  db:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  df:   48 89 c2                mov    %rax,%rdx
  e2:   48 c1 ea 36             shr    $0x36,%rdx
  e6:   83 c2 30                add    $0x30,%edx
  e9:   88 57 11                mov    %dl,0x11(%rdi)
  ec:   48 ba ff ff ff ff ff    movabs $0x3fffffffffffff,%rdx
  f3:   ff 3f 00 
  f6:   48 21 d0                and    %rdx,%rax
  f9:   48 8d 04 80             lea    (%rax,%rax,4),%rax
  fd:   48 89 c2                mov    %rax,%rdx
 100:   48 c1 ea 35             shr    $0x35,%rdx
 104:   83 c2 30                add    $0x30,%edx
 107:   88 57 12                mov    %dl,0x12(%rdi)
 10a:   48 ba ff ff ff ff ff    movabs $0x1fffffffffffff,%rdx
 111:   ff 1f 00 
 114:   48 21 d0                and    %rdx,%rax
 117:   ba 30 30 00 00          mov    $0x3030,%edx
 11c:   48 8d 04 80             lea    (%rax,%rax,4),%rax
 120:   66 89 57 08             mov    %dx,0x8(%rdi)
 124:   48 c1 e8 34             shr    $0x34,%rax
 128:   83 c0 30                add    $0x30,%eax
 12b:   88 47 13                mov    %al,0x13(%rdi)
 12e:   c3                      retq
   0:   48 b9 e0 ea f6 5e 67    movabs $0x6df37f675ef6eae0,%rcx
   7:   7f f3 6d 
   a:   49 b8 00 e4 0b 54 02    movabs $0x2540be400,%r8
  11:   00 00 00 
  14:   c6 07 30                movb   $0x30,(%rdi) // NOT GOOD
  17:   48 89 c8                mov    %rcx,%rax
  1a:   48 89 f1                mov    %rsi,%rcx
  1d:   c6 47 0a 30             movb   $0x30,0xa(%rdi) // NOT GOOD
  21:   48 f7 e6                mul    %rsi
  24:   48 d1 e9                shr    %rcx
  27:   49 89 c1                mov    %rax,%r9
  2a:   48 89 c8                mov    %rcx,%rax
  2d:   49 89 d2                mov    %rdx,%r10
  30:   31 d2                   xor    %edx,%edx
  32:   4c 01 c8                add    %r9,%rax
  35:   48 b9 0a fa 82 4b 04    movabs $0x44b82fa0a,%rcx
  3c:   00 00 00 
  3f:   4c 11 d2                adc    %r10,%rdx
  42:   48 c1 ea 20             shr    $0x20,%rdx
  46:   48 89 d0                mov    %rdx,%rax
  49:   49 0f af d0             imul   %r8,%rdx
  4d:   48 0f af c1             imul   %rcx,%rax
  51:   48 29 d6                sub    %rdx,%rsi
  54:   48 0f af f1             imul   %rcx,%rsi
  58:   48 83 c0 07             add    $0x7,%rax
  5c:   48 c1 e8 03             shr    $0x3,%rax
uint128_t split = (unsigned __int128) num * 7922816251426433760;