how Rust compiles

i promise it's actually doing something useful while you wait

the Rust compilation model has surprising effects

introduction to myself

Noratrieb (she/her)
contributing to the compiler since 2021

what does rustc like, do?

a quick overview of the compilation phases

the frontend and the backend

              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph compiler
                  frontend --> backend
                end
                source --> frontend
                backend --> binary
            

it all starts at the source


              pub fn add(a: u8, b: u8) -> u8 {
                a.wrapping_add(b)
              }
            

until it doesn't even look like Rust anymore

MIR

a crate - the compilation unit

quite big

in C it's just a single file

a codegen unit

LLVM is single-threaded

rustc: hi LLVM, look we are like a C file, now be fast

~1-256 depending on size and configuration

              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                crate

                crate --> cgu1["Codegen-Unit 1"]
                crate --> cgu2["Codegen-Unit 2"]
                crate --> cgu3["Codegen-Unit 3"]
            

codegen units


fn main() {}
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                mainmir["main (MIR)"]

                subgraph mycgu1[my CGU 1]
                  mainll["main (LLVM IR)"]
                end

                mycgu1 --> |LLVM| mycgu1.rcgu.o

                mainmir --> mainll

                mycgu1.rcgu.o --> |link| my_binary
                std["std (and others)"] --> |link| my_binary
            

codegen units (but more)


fn main() {}
mod foos {
  fn foo1() {}
  fn foo2() {}
}
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                mainmir["main (MIR)"]
                foo1mir["foo1 (MIR)"]
                foo2mir["foo2 (MIR)"]

                subgraph mycgu1[my CGU 1]
                  mainll["main (LLVM IR)"]
                end
                subgraph mycgu2[my CGU 2]
                  foo1ll["foo1 (LLVM IR)"]
                  foo2ll["foo2 (LLVM IR)"]
                end
                
                mycgu1 --> mycgu1.rcgu.o
                mycgu2 --> mycgu2.rcgu.o

                mainmir --> mainll
                foo1mir --> foo1ll
                foo2mir --> foo2ll

                mycgu1.rcgu.o --> my_binary
                mycgu2.rcgu.o --> my_binary
                std["std (and others)"] --> my_binary
            

codegen units (cross-crate)


fn add() {}
          

fn main() { math::add() }
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph crate math
                  addmir["add (MIR)"]

                  subgraph mathcgu1[math CGU 1]
                    addll["add (LLVM IR)"]
                  end

                  addmir --> addll

                  mathcgu1 --> mathcgu1.rcgu.o

                  mathcgu1.rcgu.o --> libmath.rlib
                end

                subgraph my crate
                  mainmir["main (MIR)"]

                  subgraph mycgu1[my CGU 1]
                    mainll["main (LLVM IR)"]
                  end

                  mycgu1 --> mycgu1.rcgu.o

                  mainmir --> mainll

                  mycgu1.rcgu.o --> my_binary
                  libmath.rlib --> my_binary
                  std["std (and others)"] --> my_binary
                end

                style mainmir fill:purple
                style mycgu1 fill:purple
                style mainll fill:purple
                
                style addmir fill:darkgreen
                style mathcgu1 fill:darkgreen
                style addll fill:darkgreen
                style mathcgu1.rcgu.o fill:darkgreen
                style libmath.rlib fill:darkgreen
            

instantiating generics


            fn add<T: Add>(a: T, b: T) -> T::Output { a + b }
            fn main() {
              add(0_u16, 0_u16);
              add(0_u32, 0_u32);
            }
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                addmir["add (MIR)"]
                useitmir["main (MIR)"]

                subgraph mycgu1[my CGU 1]
                  addu16ll["add_u16 (LLVM IR)"]
                  addu32ll["add_u32 (LLVM IR)"]
                  useitll["main (LLVM IR)"]
                end

                mycgu1 --> mycgu1.rcgu.o

                addmir -->|instantiate with T=u16| addu16ll
                addmir -->|instantiate with T=u32| addu32ll
                useitmir --> useitll

                mycgu1.rcgu.o --> my_binary
                std["std (and others)"] --> my_binary
            

generics (cross-crate)


            pub fn add<T: Add>(a: T, b: T) -> T::Output {
              a + b
            }
          

            fn main() {
              math::add(0_u16, 0_u16);
              math::add(0_u32, 0_u32);
            }
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph crate math
                  addmir["add (MIR)"]
                end

                subgraph my crate
                  mainmir["main (MIR)"]
                end

                subgraph my crate
                  subgraph mycgu1[my CGU 1]
                    addu16ll["add_u16 (LLVM IR)"]
                    addu32ll["add_u32 (LLVM IR)"]
                    mainll["main (LLVM IR)"]
                  end

                  mainmir --> mainll
                  addmir --> addu16ll
                  addmir --> addu32ll
                  mycgu1 --> mycgu1.rcgu.o
                  mycgu1.rcgu.o --> my_binary
                  std["std (and others)"] --> my_binary
                end

                style addmir fill:darkgreen
                style mainmir fill:purple
                style addu16ll fill:darkgreen
                style addu32ll fill:darkgreen
                style mainll fill:purple
            

            #[inline]
            pub fn add(a: u8, b: u8) -> u8 {
              a + b
            }
          

            fn main() {
              let x = math::add(1, 4);
              println!("{x}");
            }
          
              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph crate math
                  addmir["add (MIR)"]
                end

                subgraph my crate
                  mainmir["main (MIR)"]
                end

                subgraph my crate
                  subgraph mycgu1[my CGU 1]
                    addll["add (LLVM IR)"]
                    mainll["main (LLVM IR)"]
                  end

                  mainmir --> mainll
                  addmir --> addll
                  mycgu1 --> mycgu1.rcgu.o
                  mycgu1.rcgu.o --> my_binary
                  std["std (and others)"] --> my_binary
                end

                style addmir fill:purple
                style mainmir fill:darkgreen
                style addll fill:purple
                style mainll fill:darkgreen
            

lto = "fat" (monolithic)

              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph crate math
                  addmir["add (LLVM IR)"]
                end
                subgraph crate math2
                  submir["sub (LLVM IR)"]
                end

                subgraph my crate
                  mainmir["main (LLVM IR)"]
                end

                subgraph my crate
                  subgraph fatlto[fat LTO]
                    addll["add (LLVM IR)"]
                    subll["sub (LLVM IR)"]
                    mainll["main (LLVM IR)"]
                  end

                  mainmir --> mainll
                  addmir --> addll
                  submir --> subll

                  fatlto --> my_binary
                end

                style addmir fill:purple
                style addll fill:purple
                style submir fill:darkblue
                style subll fill:darkblue
                style mainmir fill:darkgreen
                style mainll fill:darkgreen
            

easily compiles 2-4x more slowly

lto = "thin" (sharded)

              %%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': true, 'fontSize': '25px' }}}%%
              flowchart LR
                subgraph crate math
                  addmir["add (LLVM IR)"]
                end
                subgraph crate math2
                  submir["sub (LLVM IR)"]
                end

                subgraph my crate
                  mainmir["main (LLVM IR)"]
                end

                subgraph my crate
                  subgraph thinltosummary[ThinLTO Index]
                  end
                  
                  subgraph thinlto1[ThinLTO 1]
                    addll["add (LLVM IR)"]
                  end
                  subgraph thinlto2[ThinLTO 2]
                    subll["sub (LLVM IR)"]
                  end
                  subgraph thinlto3[ThinLTO 3]
                    mainll["main (LLVM IR)"]
                  end

                  mainmir --> thinltosummary
                  addmir --> thinltosummary
                  submir --> thinltosummary

                  thinltosummary --> mainll
                  thinltosummary --> addll
                  thinltosummary --> subll

                  thinlto1 --> my_binary
                  thinlto2 --> my_binary
                  thinlto3 --> my_binary
                end

                style mainmir fill:purple
                style addmir fill:darkgreen
                style submir fill:darkblue
                style addll fill:darkgreen
                style subll fill:darkblue
                style mainll fill:purple
            

compiles ~1.1x-1.2x more slowly | ThinLTO Talk