Комментарии 6
лучше не выдавать один гигантский крейт, в котором содержится всё и сразу. Вместо этого мы откорректировали наш компилятор, преобразующий SQL в Rust, так, чтобы он распределял вывод на несколько более мелких крейтов.
Это первое, что пришло в голову, даже недочитав до этого места, ведь хорошо известно, что проект с сотней-другой крейтов при компиляции после cargo clean занимает многоядерник на всю катушку:
У меня возник вопрос — если тут транспилятор на другой язык, то почему именно Раст? Ведь можно с таким же успехом и в Си перегонять, но вижу, что это просто перевод.
Это же нужно было создать такой язык который 100к строк фигачит 30 минут. Жуть.
Есть же пример Free Pascal. Компиляция быстрейшая. Нельзя было какие то идеи позаимствовать.
Причём тут язык если все время уходит на llvm? И причём тут Free Pascal? Тут уж надо сравнивать с С++ с большим количеством вычислений на шаблонах
Не, я думаю там не на llvm основное время уходит, им-то многие компиляторы пользуются, и там всё резвее. Тут дело в том, что ещё до llvm производится несколько проходов. Если вы в курсе основных фишек Раста, я имею ввиду владение, заимствование, мутабельность и время жизни, вот это вот всё, то вы поймёте почему так, там всё это чекается на предварительных проходах.
Вот смотрите, допустим такой примитивный код, тут восемь строчек всего:
fn main() {
let mut x = 10; // создаём изменяемую переменную x со значением 10
add_one(&mut x);// передаём изменяемую ссылку на x (mutable borrow)
println!("x after mod: {x}"); // выводим значение x после изменения
}
fn add_one(n: &mut i32) {
*n += 1; // разыменовываем ссылку и увеличиваем значение на 1
}Вначале при компиляции идёт стадия HIR (High‑Level IR) - преобразование в высоко-уровневое промежуточное представление. С некоторой натяжкой его можно назвать "препроцессор", тут разворачиваются макросы, код очищается от "сахара", вот что получится, это всё ещё Раст:
extern crate std;
#[attr = PreludeImport]
use std::prelude::rust_2024::*;
fn main() {
let mut x =
10; // создаём изменяемую переменную x со значением 10
add_one(&mut x); // передаём изменяемую ссылку на x (mutable borrow)
// выводим значение x после изменения
// разыменовываем ссылку и увеличиваем значение на 1
{
::std::io::_print({
super let args = (&x,);
super let args = [format_argument::new_display(args.0)];
unsafe {
format_arguments::new(b"\rx after mod: \xc0\x01\n\x00",
&args)
}
});
};
}
fn add_one(n: &'_ mut i32) { *n += 1; }Затем идёт вторая стадия, THIR (Typed High‑Level IR), тоже высокоуровневая, тут я только кусочек покажу:
DefId(0:4 ~ r_test[ee7e]::add_one):
params: [
Param {
ty: &'{erased} mut i32
ty_span: Some(src\main.rs:7:15: 7:23 (#0))
self_kind: None
hir_id: Some(HirId(DefId(0:4 ~ r_test[ee7e]::add_one).1))
param: Some(
Pat {
ty: &'{erased} mut i32
span: src\main.rs:7:12: 7:13 (#0)
kind: PatKind {
Binding {
name: "n"
mode: BindingMode(No, Not)Так-то там восемьсот(!) с лишним строк
DefId(0:3 ~ r_test[ee7e]::main):
params: [
]
body:
Expr {
ty: ()
temp_scope_id: 54
span: src\main.rs:1:11: 5:2 (#0)
kind:
Scope {
region_scope: Node(54)
hir_id: HirId(DefId(0:3 ~ r_test[ee7e]::main).54)
value:
Expr {
ty: ()
temp_scope_id: 54
span: src\main.rs:1:11: 5:2 (#0)
kind:
Block {
targeted_by_break: false
span: src\main.rs:1:11: 5:2 (#0)
region_scope: Node(1)
safety_mode: Safe
stmts: [
Stmt {
kind: Let {
remainder_scope: Remainder { block: 1, first_statement_index: 0}
init_scope: Node(2)
pattern:
Pat {
ty: i32
span: src\main.rs:2:9: 2:14 (#0)
kind: PatKind {
Binding {
name: "x"
mode: BindingMode(No, Mut)
var: LocalVarId(HirId(DefId(0:3 ~ r_test[ee7e]::main).5))
ty: i32
is_primary: true
is_shorthand: false
subpattern: None
}
}
}
,
initializer: Some(
Expr {
ty: i32
temp_scope_id: 3
span: src\main.rs:2:17: 2:19 (#0)
kind:
Scope {
region_scope: Node(3)
hir_id: HirId(DefId(0:3 ~ r_test[ee7e]::main).3)
value:
Expr {
ty: i32
temp_scope_id: 3
span: src\main.rs:2:17: 2:19 (#0)
kind:
Literal( lit: Spanned { node: Int(Pu128(10), Unsuffixed), span: src\main.rs:2:17: 2:19 (#0) }, neg: false)
}
}
}
)
else_block: None
hir_id: HirId(DefId(0:3 ~ r_test[ee7e]::main).4)
span: src\main.rs:2:5: 2:19 (#0)
}
}
Stmt {
kind: Expr {
scope: Node(12)
expr:
Expr {
ty: ()
temp_scope_id: 6
span: src\main.rs:3:5: 3:20 (#0)
kind:
Scope {
region_scope: Node(6)
hir_id: HirId(DefId(0:3 ~ r_test[ee7e]::main).6)
value:
Expr {
ty: ()
temp_scope_id: 6
... дальше Хабр не разрешаетСледом идёт средняя промежуточная стадия MIR (Mid‑Level Intermediate Representation), тут мы ближе к llvm, и как раз на этом этапе разбираются заимствования, владения, времена жизни, и Miri, кстати, работает как раз на этом уровне:
let _11: &[core::fmt::rt::Argument<'_>; 1];
let mut _12: &i32;
scope 1 {
debug x => _1;
let _6: (&i32,);
scope 2 {
debug args => _6;
let _8: [core::fmt::rt::Argument<'_>; 1];
scope 3 {
debug args => _8;
}
}
}
bb0: {
_1 = const 10_i32;
_3 = &mut _1;
_2 = add_one(copy _3) -> [return: bb1, unwind continue];
}
Полностью
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
// HINT: See also -Z dump-mir for MIR at specific points during compilation.
fn main() -> () {
let mut _0: ();
let mut _1: i32;
let _2: ();
let mut _3: &mut i32;
let _4: ();
let mut _5: std::fmt::Arguments<'_>;
let mut _7: &i32;
let mut _9: core::fmt::rt::Argument<'_>;
let mut _10: &[u8; 27];
let _11: &[core::fmt::rt::Argument<'_>; 1];
let mut _12: &i32;
scope 1 {
debug x => _1;
let _6: (&i32,);
scope 2 {
debug args => _6;
let _8: [core::fmt::rt::Argument<'_>; 1];
scope 3 {
debug args => _8;
}
}
}
bb0: {
_1 = const 10_i32;
_3 = &mut _1;
_2 = add_one(copy _3) -> [return: bb1, unwind continue];
}
bb1: {
_7 = &_1;
_6 = (move _7,);
_12 = no_retag copy (_6.0: &i32);
_9 = core::fmt::rt::Argument::<'_>::new_display::<i32>(copy _12) -> [return: bb2, unwind continue];
}
bb2: {
_8 = [move _9];
_10 = const b"\x16x after modification: \xc0\x01\n\x00";
_11 = &_8;
_5 = Arguments::<'_>::new::<27, 1>(move _10, copy _11) -> [return: bb3, unwind continue];
}
bb3: {
_4 = std::io::_print(move _5) -> [return: bb4, unwind continue];
}
bb4: {
return;
}
}
alloc1 (size: 27, align: 1) {
0x00 │ 16 78 20 61 66 74 65 72 20 6d 6f 64 69 66 69 63 │ .x after modific
0x10 │ 61 74 69 6f 6e 3a 20 c0 01 0a 00 │ ation: ....
}
fn add_one(_1: &mut i32) -> () {
debug n => _1;
let mut _0: ();
let mut _2: (i32, bool);
bb0: {
_2 = AddWithOverflow(copy (*_1), const 1_i32);
assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", copy (*_1), const 1_i32) -> [success: bb1, unwind continue];
}
bb1: {
(*_1) = move (_2.0: i32);
return;
}
}
И лишь затем идёт собственно llvm, это четвёртая стадия, там видно как наша константа 10 с инкрементом "сфолдилась" в 11:
call void @llvm.lifetime.start.p0(ptr nonnull %x)
store i32 11, ptr %x, align 4 ; 11
call void @llvm.lifetime.start.p0(ptr nonnull %args)
; ...
call void __print(ptr noundef, ....LLVM полностью:
LLVM
; ModuleID = 'r_test.decfb7566f1495f6-cgu.0'
source_filename = "r_test.decfb7566f1495f6-cgu.0"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
@vtable.0 = private unnamed_addr constant <{ [24 x i8], ptr, ptr, ptr }> <{ [24 x i8] c"\00\00\00\00\00\00\00\00\08\00\00\00\00\00\00\00\08\00\00\00\00\00\00\00", ptr @_RNSNvYNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0INtNtNtCsckEfzrs6Fp9_4core3ops8function6FnOnceuE9call_once6vtableCsj81e8EUiNGk_6r_test, ptr @_RNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0Csj81e8EUiNGk_6r_test, ptr @_RNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0Csj81e8EUiNGk_6r_test }>, align 8
@alloc_e5787bd0c3f1ea82bc1a90da9c1a53af = private unnamed_addr constant [27 x i8] c"\16x after modification: \C0\01\0A\00", align 1
; std::rt::lang_start::<()>
; Function Attrs: uwtable
define hidden noundef i64 @_RINvNtCscd1f6iWV66h_3std2rt10lang_startuECsj81e8EUiNGk_6r_test(ptr noundef nonnull %main, i64 noundef %argc, ptr noundef %argv, i8 noundef %sigpipe) unnamed_addr #0 {
start:
%_7 = alloca [8 x i8], align 8
call void @llvm.lifetime.start.p0(ptr nonnull %_7)
store ptr %main, ptr %_7, align 8
; call std::rt::lang_start_internal
%_0 = call noundef i64 @_RNvNtCscd1f6iWV66h_3std2rt19lang_start_internal(ptr noundef nonnull %_7, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(48) @vtable.0, i64 noundef %argc, ptr noundef %argv, i8 noundef %sigpipe)
call void @llvm.lifetime.end.p0(ptr nonnull %_7)
ret i64 %_0
}
; std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
; Function Attrs: noinline uwtable
define internal fastcc void @_RINvNtNtCscd1f6iWV66h_3std3sys9backtrace28___rust_begin_short_backtraceFEuuECsj81e8EUiNGk_6r_test(ptr noundef nonnull readonly captures(none) %f) unnamed_addr #1 {
start:
tail call void %f()
tail call void asm sideeffect "", "~{memory}"() #6, !srcloc !4
ret void
}
; std::rt::lang_start::<()>::{closure#0}
; Function Attrs: inlinehint uwtable
define internal noundef i32 @_RNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0Csj81e8EUiNGk_6r_test(ptr noalias nofree noundef readonly align 8 captures(none) dereferenceable(8) %_1) unnamed_addr #2 {
start:
%_4 = load ptr, ptr %_1, align 8, !nonnull !5, !noundef !5
; call std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
tail call fastcc void @_RINvNtNtCscd1f6iWV66h_3std3sys9backtrace28___rust_begin_short_backtraceFEuuECsj81e8EUiNGk_6r_test(ptr noundef nonnull %_4) #7
ret i32 0
}
; <std::rt::lang_start<()>::{closure#0} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
; Function Attrs: inlinehint uwtable
define internal noundef i32 @_RNSNvYNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0INtNtNtCsckEfzrs6Fp9_4core3ops8function6FnOnceuE9call_once6vtableCsj81e8EUiNGk_6r_test(ptr noundef readonly captures(none) %_1) unnamed_addr #2 personality ptr @__CxxFrameHandler3 {
start:
%0 = load ptr, ptr %_1, align 8, !nonnull !5, !noundef !5
; call std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
tail call fastcc void @_RINvNtNtCscd1f6iWV66h_3std3sys9backtrace28___rust_begin_short_backtraceFEuuECsj81e8EUiNGk_6r_test(ptr noundef nonnull readonly %0) #7, !noalias !6
ret i32 0
}
; r_test::main
; Function Attrs: uwtable
define hidden void @_RNvCsj81e8EUiNGk_6r_test4main() unnamed_addr #0 {
start:
%args = alloca [16 x i8], align 8
%x = alloca [4 x i8], align 4
call void @llvm.lifetime.start.p0(ptr nonnull %x)
store i32 11, ptr %x, align 4
call void @llvm.lifetime.start.p0(ptr nonnull %args)
store ptr %x, ptr %args, align 8
%_6.sroa.4.0..sroa_idx = getelementptr inbounds nuw i8, ptr %args, i64 8
store ptr @_RNvXs9_NtNtNtCsckEfzrs6Fp9_4core3fmt3num3implNtB9_7Display3fmt, ptr %_6.sroa.4.0..sroa_idx, align 8
; call std::io::stdio::_print
call void @_RNvNtNtCscd1f6iWV66h_3std2io5stdio6__print(ptr noundef nonnull @alloc_e5787bd0c3f1ea82bc1a90da9c1a53af, ptr noundef nonnull %args)
call void @llvm.lifetime.end.p0(ptr nonnull %args)
call void @llvm.lifetime.end.p0(ptr nonnull %x)
ret void
}
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(ptr captures(none)) #3
; std::rt::lang_start_internal
; Function Attrs: uwtable
declare noundef i64 @_RNvNtCscd1f6iWV66h_3std2rt19lang_start_internal(ptr noundef nonnull, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(48), i64 noundef, ptr noundef, i8 noundef) unnamed_addr #0
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(ptr captures(none)) #3
; <i32 as core::fmt::Display>::fmt
; Function Attrs: uwtable
declare noundef zeroext i1 @_RNvXs9_NtNtNtCsckEfzrs6Fp9_4core3fmt3num3implNtB9_7Display3fmt(ptr noalias nofree noundef readonly align 4 captures(address, read_provenance) dereferenceable(4), ptr noalias nofree noundef align 8 dereferenceable(24)) unnamed_addr #0
; std::io::stdio::_print
; Function Attrs: uwtable
declare void @_RNvNtNtCscd1f6iWV66h_3std2io5stdio6__print(ptr noundef nonnull, ptr noundef nonnull) unnamed_addr #0
declare i32 @__CxxFrameHandler3(...) unnamed_addr #4
define noundef i32 @main(i32 %0, ptr %1) unnamed_addr #5 {
top:
%_7.i = alloca [8 x i8], align 8
%2 = sext i32 %0 to i64
call void @llvm.lifetime.start.p0(ptr nonnull %_7.i)
store ptr @_RNvCsj81e8EUiNGk_6r_test4main, ptr %_7.i, align 8
; call std::rt::lang_start_internal
%_0.i = call noundef i64 @_RNvNtCscd1f6iWV66h_3std2rt19lang_start_internal(ptr noundef nonnull %_7.i, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(48) @vtable.0, i64 noundef %2, ptr noundef %1, i8 noundef 0)
call void @llvm.lifetime.end.p0(ptr nonnull %_7.i)
%3 = trunc i64 %_0.i to i32
ret i32 %3
}
attributes #0 = { uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse,+sse2,+sse3,+sahf" }
attributes #1 = { noinline uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse,+sse2,+sse3,+sahf" }
attributes #2 = { inlinehint uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse,+sse2,+sse3,+sahf" }
attributes #3 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { "target-cpu"="x86-64" }
attributes #5 = { "target-cpu"="x86-64" "target-features"="+cx16,+sse,+sse2,+sse3,+sahf" }
attributes #6 = { nounwind }
attributes #7 = { noinline }
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 7, !"PIE Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 2}
!3 = !{!"rustc version 1.99.0-nightly (375b1431b 2026-07-10)"}
!4 = !{i64 4735085480825446}
!5 = !{}
!6 = !{!7}
!7 = distinct !{!7, !8, !"_RNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0Csj81e8EUiNGk_6r_test: %_1"}
!8 = distinct !{!8, !"_RNCINvNtCscd1f6iWV66h_3std2rt10lang_startuE0Csj81e8EUiNGk_6r_test"}
На а дальше ассемблер, тут просто по итогу печатаем 11 в консоль, вот здесь:
mov dword ptr [rsp + 36], 11 загрузили 11
lea rax, [rsp + 36]
mov qword ptr [rsp + 40], rax
lea rax, [rip + Display3fmt]
mov qword ptr [rsp + 48], rax
call __print ; напечаталиАссемблер
полный листинг
.def @feat.00;
.scl 3;
.type 0;
.endef
.globl @feat.00
@feat.00 = 0
.intel_syntax noprefix
.file "r_test.f5a1b3f68eecb1a6-cgu.0"
.def _ZN3std2rt10lang_start17h28856b37936baaa7E;
.scl 2;
.type 32;
.endef
.section .text,"xr",one_only,_ZN3std2rt10lang_start17h28856b37936baaa7E,unique,0
.globl _ZN3std2rt10lang_start17h28856b37936baaa7E
.p2align 4
_ZN3std2rt10lang_start17h28856b37936baaa7E:
.seh_proc _ZN3std2rt10lang_start17h28856b37936baaa7E
sub rsp, 56
.seh_stackalloc 56
.seh_endprologue
mov rax, r8
mov r8, rdx
mov qword ptr [rsp + 48], rcx
mov byte ptr [rsp + 32], r9b
lea rdx, [rip + anon.e6415d161255c43dc2c05166e7cf7172.0]
lea rcx, [rsp + 48]
mov r9, rax
call _RNvNtCslk5dvTldH8g_3std2rt19lang_start_internal
nop
.seh_startepilogue
add rsp, 56
.seh_endepilogue
ret
.seh_endproc
.def _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E;
.scl 3;
.type 32;
.endef
.section .text,"xr",one_only,_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E,unique,1
.p2align 4
_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E:
.seh_proc _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
mov rcx, qword ptr [rcx]
call _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E
xor eax, eax
.seh_startepilogue
add rsp, 40
.seh_endepilogue
ret
.seh_endproc
.def _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E;
.scl 3;
.type 32;
.endef
.section .text,"xr",one_only,_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E,unique,2
.p2align 4
_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E:
.seh_proc _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call rcx
nop
#APP
#NO_APP
.seh_startepilogue
add rsp, 40
.seh_endepilogue
ret
.seh_endproc
.def _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hdd5ea432718ad0c1E;
.scl 3;
.type 32;
.endef
.section .text,"xr",one_only,_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hdd5ea432718ad0c1E,unique,3
.p2align 4
_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hdd5ea432718ad0c1E:
.seh_proc _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hdd5ea432718ad0c1E
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
mov rcx, qword ptr [rcx]
call _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h8d3554b6567cef64E
xor eax, eax
.seh_startepilogue
add rsp, 40
.seh_endepilogue
ret
.seh_endproc
.def _ZN6r_test4main17haba959300c4c510dE;
.scl 2;
.type 32;
.endef
.section .text,"xr",one_only,_ZN6r_test4main17haba959300c4c510dE,unique,4
.globl _ZN6r_test4main17haba959300c4c510dE
.p2align 4
_ZN6r_test4main17haba959300c4c510dE:
.seh_proc _ZN6r_test4main17haba959300c4c510dE
sub rsp, 56
.seh_stackalloc 56
.seh_endprologue
mov dword ptr [rsp + 36], 11
lea rax, [rsp + 36]
mov qword ptr [rsp + 40], rax
lea rax, [rip + _RNvXs9_NtNtNtCs9N2lWLRSIT9_4core3fmt3num3implNtB9_7Display3fmt]
mov qword ptr [rsp + 48], rax
lea rcx, [rip + anon.e6415d161255c43dc2c05166e7cf7172.1]
lea rdx, [rsp + 40]
call _RNvNtNtCslk5dvTldH8g_3std2io5stdio6__print
nop
.seh_startepilogue
add rsp, 56
.seh_endepilogue
ret
.seh_endproc
.def main;
.scl 2;
.type 32;
.endef
.section .text,"xr",one_only,main,unique,5
.globl main
.p2align 4
main:
.seh_proc main
sub rsp, 56
.seh_stackalloc 56
.seh_endprologue
mov r9, rdx
movsxd r8, ecx
lea rax, [rip + _ZN6r_test4main17haba959300c4c510dE]
mov qword ptr [rsp + 48], rax
mov byte ptr [rsp + 32], 0
lea rdx, [rip + anon.e6415d161255c43dc2c05166e7cf7172.0]
lea rcx, [rsp + 48]
call _RNvNtCslk5dvTldH8g_3std2rt19lang_start_internal
nop
.seh_startepilogue
add rsp, 56
.seh_endepilogue
ret
.seh_endproc
.section .rdata,"dr",one_only,anon.e6415d161255c43dc2c05166e7cf7172.0,unique,6
.p2align 3, 0x0
anon.e6415d161255c43dc2c05166e7cf7172.0:
.asciz "\000\000\000\000\000\000\000\000\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000"
.quad _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hdd5ea432718ad0c1E
.quad _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E
.quad _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h3c899e35677daf87E
.section .rdata,"dr",one_only,anon.e6415d161255c43dc2c05166e7cf7172.1,unique,7
anon.e6415d161255c43dc2c05166e7cf7172.1:
.asciz "\026x after modification: \300\001\n"
Вот что происходит всего с восемью строчками кода. Я полагаю, что основное время затрачивается на этапе THIR, но как-то руки не доходили проверить, да и не знаю как.
Я оказывается в блог Rust первый коммент написал. О чую прилетит:))

Сокращаем длительность компиляции проекта на Rust c 30 до 2 минут — пример с 1000 крейтов