Rust 的性能很大程度上来自 LLVM 的优化。这篇用 LLVM IR 把「优化」变成看得见的东西:栈槽怎么变成寄存器、循环怎么被向量化、函数调用怎么被内联,以及为什么 -O3 的代码并不一定最短。
O0:行数 40 alloca 3 load 5 store 5 phi 0 向量类型 <4 x i32> 0 add 0
O3:行数 89 alloca 0 load 6 store 0 phi 14 向量类型 <4 x i32> 21 add 15
O3 的 sum 里与向量相关的几行:
br i1 %min.iters.check, label %bb3.preheader, label %vector.main.loop.iter.checkMIR 是 LLVM IR 的前一层,见MIR 是什么。
start:
%_5 = alloca [8 x i8], align 8
%iter = alloca [16 x i8], align 8
%s = alloca [4 x i8], align 4
store i32 0, ptr %s, align 4
%0 = call { ptr, ptr } @_RNvXs_NtNtCsedRpiqSkYaQ_4core5slice4iterRSlNtNtNtNtB8_4iter6traits7collect12IntoIterator9into_iterCsdo2
%_3.0 = extractvalue { ptr, ptr } %0, 0
%_3.1 = extractvalue { ptr, ptr } %0, 1
store ptr %_3.0, ptr %iter, align 8
%1 = getelementptr inbounds i8, ptr %iter, i64 8
store ptr %_3.1, ptr %1, align 8
br label %bb2
bb2: ; preds = %bb7, %start
%2 = call align 4 ptr @_RNvXs2J_NtNtCsedRpiqSkYaQ_4core5slice4iterINtB6_4IterlENtNtNtNtBa_4iter6traits8iterator8Iterator4nextCsd
store ptr %2, ptr %_5, align 8
%3 = load ptr, ptr %_5, align 8
%4 = ptrtoint ptr %3 to i64
%5 = icmp eq i64 %4, 0O0:caller 的 IR 12 行,call 指令 4 条:['helper', 'helper', 'llvm.sadd.with.overflow.i32', 'panic_const_add_overflow'] O3:caller 的 IR 6 行,call 指令 0 条:[]
单态化生成的代码见Monomorphization 生成的代码。
opt-level=0:汇编指令 146 条 opt-level=1:汇编指令 17 条 opt-level=2:汇编指令 71 条 opt-level=3:汇编指令 71 条 opt-level=s:汇编指令 17 条 opt-level=z:汇编指令 15 条
LTO 对体积的影响见LTO 与二进制体积。指令数来自 aarch64 汇编,x86_64 会不同。