Profiles are resolved before code generation, not via conditionals.
Each top-level item (function, block, impl, import) can be annotated with a profile (userland, kernel, baremetal). During parsing, everything is collected into the AST as usual.
During IR lowering, the compiler is invoked with exactly one active profile. At that point:
Nodes whose profile does not match are not lowered to IR at all
They are dropped during IR validation, not guarded or compiled
The resulting IR literally has no trace of the other profiles
So this is not like #ifdef or runtime flags. The non-selected code never reaches:
borrow checking
optimization
codegen
linking
From LLVM’s point of view, it’s as if the other code never existed.
That’s why there’s no runtime overhead: no branches, no checks, no dead code elimination required. The IR is profile-pure by construction.
This also lets the compiler enforce different rules per profile:
userland: heap allowed, panics allowed
kernel: no heap, no panic, stricter aliasing
baremetal: raw pointers, UB allowed
Invalid combinations simply fail IR validation.
Happy to clarify further once the repo is public.
Code that is #ifdef'd out doesn't even make it into the AST so traces of it aren't going to be found in the IR either.
I think I'm missing something really basic. The idea of three different subsets/dialects is interesting, but I would expect all three to be usable at the same time, like how unsafe blocks can be used in the performance-critical sections of a larger Rust program.
JhonPork|1 month ago
platinumrad|1 month ago
I think I'm missing something really basic. The idea of three different subsets/dialects is interesting, but I would expect all three to be usable at the same time, like how unsafe blocks can be used in the performance-critical sections of a larger Rust program.