gcc -O3优化移除有效代码:64位化整数转字符串函数遇问题
这问题我之前在优化高性能itoa实现时也碰到过!gcc的-O3优化确实会对这类依赖乘法逆元的间接计算下手,把它认为“无用”的代码删掉——毕竟编译器没法直观理解你通过乘法逆元拆分数字的逻辑,很容易误判某些步骤是死代码。我给你几个针对性的解决方案,分C和汇编两种情况来说:
针对你的C实现的解决方案
1. 添加内存屏障(推荐)
最温和的方式是在函数末尾加一个轻量级内存屏障,明确告诉编译器:buf的内存已经被修改了,不能优化掉之前的写入操作。这种方式不会过度限制编译器的其他优化,只是阻止它误删有效代码:
void u2s(char* buf, unsigned long num) { // 你的原代码完全保留 // 新增内存屏障,强制编译器保留所有buf写入 __asm__ __volatile__("" : : "m"(buf[0]) : "memory"); }
这里的"m"(buf[0])指定buf的内存是被修改的,"memory"告诉编译器这个操作会改变内存状态,必须保留所有相关的内存读写。
2. 标记buf为volatile
如果内存屏障不起作用,可以直接把buf参数声明为volatile char*,强制编译器不对buf的写入做任何优化:
void u2s(volatile char* buf, unsigned long num) { // 原代码不变 }
这种方式简单直接,但会轻微影响性能(编译器没法合并相邻的buf写入),不过对于itoa这种短函数来说,影响几乎可以忽略。
3. 把宏改成静态inline函数
你的LOOP_WORK宏展开后,编译器可能因为代码结构的原因,误判某些操作无意义。把宏改成静态inline函数,让编译器更好地追踪变量依赖:
#define uint128_t __uint128_t #define uint64_t unsigned long static inline void loop_work(char* buf, int number, int shift, uint64_t* high, uint64_t* low, uint64_t* andop) { *high *= 5; *low *= 5; buf[number] = (*high >> shift) + '0'; buf[number+10] = (*low >> shift) + '0'; *high &= *andop; *low &= *andop; *andop >>= 1; } 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(buf, 1, 60, &high, &low, &andop); loop_work(buf, 2, 59, &high, &low, &andop); loop_work(buf, 3, 58, &high, &low, &andop); loop_work(buf, 4, 57, &high, &low, &andop); loop_work(buf, 5, 56, &high, &low, &andop); loop_work(buf, 6, 55, &high, &low, &andop); loop_work(buf, 7, 54, &high, &low, &andop); loop_work(buf, 8, 53, &high, &low, &andop); // Final loop, without extra stuffs high *= 5; low *= 5; buf[9] = (high >> 52) + '0'; buf[19] = (low >> 52) + '0'; // 可选:加上内存屏障 __asm__ __volatile__("" : : "m"(buf[0]) : "memory"); }
针对手写AT&T汇编的解决方案
如果你的汇编代码是放在C文件的__asm__块里,gcc -O3可能会尝试优化这个块。解决方式是给asm块加上__volatile__关键字,同时正确声明输入输出操作数和被修改的寄存器/内存:
void u2s(char* buf, unsigned long num) { __asm__ __volatile__( " // tmp128(rax:rdx) = num * 7922816251426433760\n" " movq $7922816251426433760, %%rax\n" " mulq %%rsi\n" " // tmp128(rax:rdx) += num >> 1\n" " movq %%rsi, %%rcx\n" " shrq $0x1, %%rcx\n" " addq %%rcx, %%rax\n" " adcq $0x0, %%rdx\n" " // high(rdx) = tmp128(rax:rdx) >> 96\n" " shrq $32, %%rdx\n" " // high(rcx); low(rsi) = num - (high * 10^10)\n" " movq %%rdx, %%rcx\n" " movq $10000000000, %%rax\n" " mulq %%rdx\n" " subq %%rax, %%rsi\n" " // high2(rax:rdx) = high * 18446744074\n" " movq $18446744074, %%rax\n" " mulq %%rcx\n" " // buf[0] = (high2 >> 64) + '0'\n" " addb $'0', %%dl\n" " movb %%dl, (%%rdi)\n" " // low2(rax:rdx) = low * 18446744074\n" " movq %%rax, %%rcx\n" " movq $18446744074, %%rax\n" " mulq %%rsi\n" " // buf[10] = (low2 >> 64) + '0'\n" " addb $'0', %%dl\n" " movb %%dl, 10(%%rdi)\n" " // high(rcx) = (u64)high2\n" " // low(rax) = (u64)low2\n" " // high = (high + 7) >> 3\n" " addb $0x7, %%cl\n" " shrq $0x3, %%rcx\n" " // low = (low + 7) >> 3\n" " addb $0x7, %%al\n" " shrq $0x3, %%rax\n" " // low (rax) *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // high(rcx) *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // buf[1] = (high >> 60) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $60, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 1(%%rdi)\n" " // buf[11] = (low >> 60) + '0'\n" " movq %%rax, %%rdx\n" " shrq $60, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 11(%%rdi)\n" " // Store number 0x0fffffffffffffff\n" " movq $0x0fffffffffffffff, %%rsi\n" " // high &= 0x0fffffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x0fffffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // buf[2] = (high >> 59) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $59, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 2(%%rdi)\n" " // buf[12] = (low >> 59) + '0'\n" " movq %%rax, %%rdx\n" " shrq $59, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 12(%%rdi)\n" " // update the `and` number\n" " shrq $0x1, %%rsi\n" " // high &= 0x07ffffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x07ffffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // buf[3] = (high >> 58) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $58, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 3(%%rdi)\n" " // buf[13] = (low >> 58) + '0'\n" " movq %%rax, %%rdx\n" " shrq $58, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 13(%%rdi)\n" " // update the `and` number\n" " shrq $0x1, %%rsi\n" " // high &= 0x03ffffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x03ffffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // buf[4] = (high >> 57) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $57, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 4(%%rdi)\n" " // buf[14] = (low >> 57) + '0'\n" " movq %%rax, %%rdx\n" " shrq $57, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 14(%%rdi)\n" " // update the `and` number\n" " shrq $0x1, %%rsi\n" " // high &= 0x01ffffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x01ffffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // buf[5] = (high >> 56) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $56, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 5(%%rdi)\n" " // buf[15] = (low >> 56) + '0'\n" " movq %%rax, %%rdx\n" " shrq $56, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 15(%%rdi)\n" " // update the `and` number\n" " shrq $0x1, %%rsi\n" " // high &= 0x00ffffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x00ffffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" " // buf[6] = (high >> 55) + '0'\n" " movq %%rcx, %%rdx\n" " shrq $55, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 6(%%rdi)\n" " // buf[16] = (low >> 55) + '0'\n" " movq %%rax, %%rdx\n" " shrq $55, %%rdx\n" " addb $'0', %%dl\n" " movb %%dl, 16(%%rdi)\n" " // update the `and` number\n" " shrq $0x1, %%rsi\n" " // high &= 0x007fffffffffffff\n" " andq %%rsi, %%rcx\n" " // low &= 0x007fffffffffffff\n" " andq %%rsi, %%rax\n" " // high *= 5\n" " leaq (%%rcx,%%rcx,4), %%rcx\n" " // low *= 5\n" " leaq (%%rax,%%rax,4), %%rax\n" // 注意:你提供的汇编代码未完成,需要补全剩余的循环步骤 : : "D"(buf), "S"(num) : "rax", "rcx", "rdx", "memory" ); }
这里的关键点:
__volatile__告诉编译器不要优化这个汇编块;- 输入操作数
"D"(buf), "S"(num)把buf和num绑定到rdi和rsi寄存器,符合x86-64的调用约定; - 被修改的寄存器列表
"rax", "rcx", "rdx"告诉编译器这些寄存器会被汇编代码修改; "memory"
相关产品推荐
相关产品推荐

