gcc-O3删除int-to-string转换器中的活动代码
我是64位izing,它需要一个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 >>
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;