与编译器聊指令集:你未理解位运算
Redshift_Shine · · 科技·工程
标题从分析学爱好者那里抄的。
这篇文章只是以最直观的方式揭示了问题的来源,对于其具体原理,受作者水平所限,无法解释。
前情提要:警示后人:编译器 Bug
chen_zhe 说编译器有问题。刚补完 NOIP2024 最后一题的我立刻无可救药地被吸引了过来一探究竟。
想要知道编译器干了什么的最好办法仍然是汇编。Compiler Explorer,启动!
首先我们从工具箱里找来 Executor From This,然后使用 GCC 15.1,编译运行。
Program stdout:
19 21
非常正常,没有问题。
此时,我使用的是我惯常使用的编译参数:
-std=c++23 -O2
思考一会之后,我发现应该从洛谷那里搬来评测机用的编译指令。修改一下后得到:
-x c++ -std=c++23 -O2 -fPIC -DONLINE_JUDGE -Wall -fno-asm -lm
这里一个重要的区别是把 -march=native 删掉了,因为之前我用这个网站的时候网站明确提示不建议使用 -march=native,会使得编译出来的汇编代码不具备参考性。
然后再次运行。
Program stdout:
19 21
还是没有问题。此时我把代码搬到我的 Arch Linux (GCC 15.2.1) 电脑上运行,还是正常输出结果。
怎么办?想了一分钟之后,我想:要不把 -march=native 加回去试试?
随后,我们得到了这个:
Program stdout:
3 5
OHHHHHHHH!
原来问题出在 march=native 上!立刻返回汇编界面,在汇编代码里面找到与平常 C++ 代码得到的汇编长得不太一样的地方。找到了,在这里!
:::warning[指令集优化来袭!]
.L32:
mov DWORD PTR [r12+r9*8], edx
movsx rdx, eax
inc r9
mov DWORD PTR 0[r13+rdx*4], r10d
cmp r14, r9
jne .L27
mov rbx, QWORD PTR top@GOTPCREL[rip]
mov rdi, QWORD PTR ans2@GOTPCREL[rip]
mov r8, QWORD PTR ans1@GOTPCREL[rip]
mov DWORD PTR [rbx], eax
lea eax, -1[r11]
mov rdx, QWORD PTR [rdi]
mov rsi, QWORD PTR [r8]
cmp eax, 2
jbe .L41
vmovq xmm7, rdx
mov edx, r11d
mov ecx, 4
mov rax, r12
vpcmpeqd ymm6, ymm6, ymm6
shr edx, 2
vmovd xmm5, ecx
vpxor xmm4, xmm4, xmm4
vpinsrq xmm3, xmm7, rsi, 1
sal rdx, 5
vpsrld ymm6, ymm6, 31
vmovdqa ymm1, YMMWORD PTR .LC0[rip]
vmovdqa xmm3, xmm3
add rdx, r12
vpbroadcastd ymm5, xmm5
.L36:
vpshufd ymm0, YMMWORD PTR 8[rax], 177
vpaddd ymm0, ymm0, ymm6
add rax, 32
vpmuldq ymm2, ymm1, ymm0
vpsrlq ymm0, ymm0, 32
vpxor ymm3, ymm3, ymm2
vpsrlq ymm2, ymm1, 32
vpaddd ymm1, ymm1, ymm5
vpmuldq ymm0, ymm2, ymm0
vpxor ymm4, ymm4, ymm0
cmp rax, rdx
jne .L36
vpxor ymm3, ymm3, ymm4
vmovdqa xmm0, xmm3
vextracti128 xmm3, ymm3, 0x1
vpxor xmm0, xmm0, xmm3
vmovq rdx, xmm0
vpextrq rsi, xmm0, 1
test r11b, 3
je .L48
mov eax, r11d
and eax, -4
inc eax
vzeroupper
.L26:
cdqe
:::
由此,我们也许可以下一个初步结论:编译器对于某些位运算的理解仍有偏差,使得最终看似通过指令集大幅优化了的代码会给出不符于预期的结果。
而好消息是,NOI 系列比赛的官方评测机是不使用 -march=native 的,也就意味着这类危险的激进指令集优化不会在重要的正式比赛中发生。
不幸的是,使用 -march=native 编译选项,对于客户量庞大的公开 OJ 来说可以一定程度上更为高效地利用评测资源,所以这个问题是很难在用户方与评测方都满意的情况下解决的。