1*7ebb78a8SAlex Crichton# Conditional Compilation in Wasmtime 2*7ebb78a8SAlex Crichton 3*7ebb78a8SAlex CrichtonThe `wasmtime` crate and workspace is both quite configurable in terms of 4*7ebb78a8SAlex Crichtonruntime configuration (e.g. `Config::*`) and compile-time configuration (Cargo 5*7ebb78a8SAlex Crichtonfeatures). Wasmtime also wants to take advantage of native hardware features on 6*7ebb78a8SAlex Crichtonspecific CPUs and operating systems to implement optimizations for executing 7*7ebb78a8SAlex CrichtonWebAssembly. This overall leads to the state where the source code for Wasmtime 8*7ebb78a8SAlex Crichtonhas quite a lot of `#[cfg]` directives and is trying to wrangle the 9*7ebb78a8SAlex Crichtoncombinatorial explosion of: 10*7ebb78a8SAlex Crichton 11*7ebb78a8SAlex Crichton1. All possible CPU architectures that Wasmtime (or Rust) supports. 12*7ebb78a8SAlex Crichton2. All possible operating systems that Wasmtime (or Rust) supports. 13*7ebb78a8SAlex Crichton3. All possible feature combinations of the `wasmtime` crate. 14*7ebb78a8SAlex Crichton 15*7ebb78a8SAlex CrichtonLike any open source project one of the goals of Wasmtime is to have readable 16*7ebb78a8SAlex Crichtonand understandable code and to that effect we ideally don't have `#[cfg]` 17*7ebb78a8SAlex Crichtoneverywhere throughout the codebase in confusing combinations. The goal of this 18*7ebb78a8SAlex Crichtondocument is to explain the various guidelines we have for conditional 19*7ebb78a8SAlex Crichtoncompilation in Rust and some recommended styles for working with `#[cfg]` in a 20*7ebb78a8SAlex Crichtonmaintainable and scalable manner. 21*7ebb78a8SAlex Crichton 22*7ebb78a8SAlex Crichton## Rust's `#[cfg]` attribute 23*7ebb78a8SAlex Crichton 24*7ebb78a8SAlex CrichtonIf you haven't worked with Rust much before or if you'd like a refresher, Rust's 25*7ebb78a8SAlex Crichtonmain ability to handle conditional compilation is the `#[cfg]` attribute. This 26*7ebb78a8SAlex Crichtonis semantically and structurally different from `#ifdef` in C/C++ and gives rise 27*7ebb78a8SAlex Crichtonto alternative patterns which look quite different as well. 28*7ebb78a8SAlex Crichton 29*7ebb78a8SAlex CrichtonOne of the more common conditional compilation attributes in Rust is 30*7ebb78a8SAlex Crichton`#[cfg(test)]` which enables including a module or a piece of code only when 31*7ebb78a8SAlex Crichtoncompiled with `cargo test` (or `rustc`'s `--test` flag). There are many other 32*7ebb78a8SAlex Crichtondirectives you can put in `#[cfg]`, however, for example: 33*7ebb78a8SAlex Crichton 34*7ebb78a8SAlex Crichton* `#[cfg(target_arch = "...")]` - this can be used to detect the architecture 35*7ebb78a8SAlex Crichton that the code was compiled for. 36*7ebb78a8SAlex Crichton* `#[cfg(target_os = "...")]` - this can be used to detect the operating system 37*7ebb78a8SAlex Crichton that the code was compiled for. 38*7ebb78a8SAlex Crichton* `#[cfg(feature = "...")]` - these correspond to [Cargo features][cargo] and 39*7ebb78a8SAlex Crichton are enabled when depending on crates in `Cargo.toml`. 40*7ebb78a8SAlex Crichton* `#[cfg(has_foo)]` - completely custom directives can be emitted by build 41*7ebb78a8SAlex Crichton scripts such as `crates/wasmtime/build.rs`. 42*7ebb78a8SAlex Crichton 43*7ebb78a8SAlex Crichton[cargo]: https://doc.rust-lang.org/cargo/reference/features.html 44*7ebb78a8SAlex Crichton 45*7ebb78a8SAlex CrichtonTo explore built-in `#[cfg]` directives you can use `rustc --print cfg` for your 46*7ebb78a8SAlex Crichtonhost target. This also supports `rustc --print cfg --target ...`. 47*7ebb78a8SAlex Crichton 48*7ebb78a8SAlex CrichtonFinally, `#[cfg]` directive support internal "functions" such as `all(...)`, 49*7ebb78a8SAlex Crichton`any(...)`, and `not(..)`. 50*7ebb78a8SAlex Crichton 51*7ebb78a8SAlex CrichtonAttributes in Rust can be applied to syntactic items in Rust, not fragments of 52*7ebb78a8SAlex Crichtonlexical tokens like C/C++. This means that conditional compilation happens at 53*7ebb78a8SAlex Crichtonthe AST level rather than the lexical level. For example: 54*7ebb78a8SAlex Crichton 55*7ebb78a8SAlex Crichton```rust,ignore 56*7ebb78a8SAlex Crichton#[cfg(foo)] 57*7ebb78a8SAlex Crichtonfn the_function() { /* ... */ } 58*7ebb78a8SAlex Crichton 59*7ebb78a8SAlex Crichton#[cfg(not(foo))] 60*7ebb78a8SAlex Crichtonfn the_function() { /* ... */ } 61*7ebb78a8SAlex Crichton``` 62*7ebb78a8SAlex Crichton 63*7ebb78a8SAlex CrichtonThis can additionally be applied to entire expressions in Rust too: 64*7ebb78a8SAlex Crichton 65*7ebb78a8SAlex Crichton```rust,ignore 66*7ebb78a8SAlex Crichtonfn the_function() { 67*7ebb78a8SAlex Crichton #[cfg(foo)] 68*7ebb78a8SAlex Crichton { 69*7ebb78a8SAlex Crichton // ... 70*7ebb78a8SAlex Crichton } 71*7ebb78a8SAlex Crichton #[cfg(not(foo))] 72*7ebb78a8SAlex Crichton { 73*7ebb78a8SAlex Crichton // ... 74*7ebb78a8SAlex Crichton } 75*7ebb78a8SAlex Crichton} 76*7ebb78a8SAlex Crichton``` 77*7ebb78a8SAlex Crichton 78*7ebb78a8SAlex CrichtonThe Rust compiler doesn't type-check or analyze anything in 79*7ebb78a8SAlex Crichtonconditionally-omitted code. It is only required to be syntactically valid. 80*7ebb78a8SAlex Crichton 81*7ebb78a8SAlex Crichton## Hazards with `#[cfg]` 82*7ebb78a8SAlex Crichton 83*7ebb78a8SAlex CrichtonConditional compilation in any language can get hairy quickly and Rust is no 84*7ebb78a8SAlex Crichtonexception. The venerable "`#ifdef` soup" one might have seen in C/C++ is very 85*7ebb78a8SAlex Crichtonmuch possible to have in Rust too in the sense that it won't look the same but 86*7ebb78a8SAlex Crichtonit'll still taste just as bad. In that sense it's worth going over some of the 87*7ebb78a8SAlex Crichtondownsides of `#[cfg]` in Rust and some hazards to watch out for. 88*7ebb78a8SAlex Crichton 89*7ebb78a8SAlex Crichton**Unused Imports** 90*7ebb78a8SAlex Crichton 91*7ebb78a8SAlex CrichtonConditional compilation can be great for quickly excluding an entire function in 92*7ebb78a8SAlex Crichtonone line, but this might have ramifications if that function was the only use of 93*7ebb78a8SAlex Crichtonan imported type for example: 94*7ebb78a8SAlex Crichton 95*7ebb78a8SAlex Crichton```rust,ignore 96*7ebb78a8SAlex Crichtonuse std::ptr::NonNull; //~ WARNING: unused import when `foo` is turned off 97*7ebb78a8SAlex Crichton 98*7ebb78a8SAlex Crichton#[cfg(foo)] 99*7ebb78a8SAlex Crichtonfn my_function() -> NonNull<u8> { 100*7ebb78a8SAlex Crichton // ... 101*7ebb78a8SAlex Crichton} 102*7ebb78a8SAlex Crichton``` 103*7ebb78a8SAlex Crichton 104*7ebb78a8SAlex Crichton**Repetitive Attributes** 105*7ebb78a8SAlex Crichton 106*7ebb78a8SAlex CrichtonEnabling a Cargo feature can add features to existing types which means it can 107*7ebb78a8SAlex Crichtonlead to repetitive `#[cfg]` annotations such as: 108*7ebb78a8SAlex Crichton 109*7ebb78a8SAlex Crichton```rust,ignore 110*7ebb78a8SAlex Crichton#[cfg(feature = "async")] 111*7ebb78a8SAlex Crichtonuse std::future::Future; 112*7ebb78a8SAlex Crichton 113*7ebb78a8SAlex Crichtonimpl<T> Store<T> { 114*7ebb78a8SAlex Crichton #[cfg(feature = "async")] 115*7ebb78a8SAlex Crichton async fn some_new_async_api(&mut self) { 116*7ebb78a8SAlex Crichton // ... 117*7ebb78a8SAlex Crichton } 118*7ebb78a8SAlex Crichton 119*7ebb78a8SAlex Crichton #[cfg(feature = "async")] 120*7ebb78a8SAlex Crichton async fn some_other_new_async_api(&mut self) { 121*7ebb78a8SAlex Crichton // ... 122*7ebb78a8SAlex Crichton } 123*7ebb78a8SAlex Crichton} 124*7ebb78a8SAlex Crichton 125*7ebb78a8SAlex Crichton#[cfg(feature = "async")] 126*7ebb78a8SAlex Crichtonstruct SomeAsyncHelperType { 127*7ebb78a8SAlex Crichton // ... 128*7ebb78a8SAlex Crichton} 129*7ebb78a8SAlex Crichton 130*7ebb78a8SAlex Crichton#[cfg(feature = "async")] 131*7ebb78a8SAlex Crichtonimpl SomeAsyncHelperType { 132*7ebb78a8SAlex Crichton // ... 133*7ebb78a8SAlex Crichton} 134*7ebb78a8SAlex Crichton``` 135*7ebb78a8SAlex Crichton 136*7ebb78a8SAlex Crichton**Boilerplate throughout an implementation** 137*7ebb78a8SAlex Crichton 138*7ebb78a8SAlex CrichtonIn addition to being repetitive when defining conditionally compiled code 139*7ebb78a8SAlex Crichtonthere's also a risk of being quite repetitive when using conditionally compiled 140*7ebb78a8SAlex Crichtoncode as well. In its most basic form any usage of a conditionally compiled piece 141*7ebb78a8SAlex Crichtonof code must additionally be gated as well. 142*7ebb78a8SAlex Crichton 143*7ebb78a8SAlex Crichton```rust,ignore 144*7ebb78a8SAlex Crichton#[cfg(feature = "gc")] 145*7ebb78a8SAlex Crichtonfn gc() { 146*7ebb78a8SAlex Crichton // ... 147*7ebb78a8SAlex Crichton} 148*7ebb78a8SAlex Crichton 149*7ebb78a8SAlex Crichtonfn call_wasm() { 150*7ebb78a8SAlex Crichton #[cfg(feature = "gc")] 151*7ebb78a8SAlex Crichton gc(); 152*7ebb78a8SAlex Crichton 153*7ebb78a8SAlex Crichton // do the call ... 154*7ebb78a8SAlex Crichton 155*7ebb78a8SAlex Crichton #[cfg(feature = "gc")] 156*7ebb78a8SAlex Crichton gc(); 157*7ebb78a8SAlex Crichton} 158*7ebb78a8SAlex Crichton``` 159*7ebb78a8SAlex Crichton 160*7ebb78a8SAlex Crichton**Interactions with ecosystem tooling** 161*7ebb78a8SAlex Crichton 162*7ebb78a8SAlex CrichtonConditionally compiled code does not always interact well with ecosystem tooling 163*7ebb78a8SAlex Crichtonin Rust. An example of this is the `cfg_if!` macro where `rustfmt` is unable to 164*7ebb78a8SAlex Crichtonformat the contents of the macro. If there are conditionally defined modules in 165*7ebb78a8SAlex Crichtonthe macro then it means `rustfmt` won't format any modules internally in the 166*7ebb78a8SAlex Crichtonmacro either. Not a great experience! 167*7ebb78a8SAlex Crichton 168*7ebb78a8SAlex CrichtonHere neither `gc.rs` nor `gc_disabled.rs` will be formatted by `cargo fmt`. 169*7ebb78a8SAlex Crichton 170*7ebb78a8SAlex Crichton```rust,ignore 171*7ebb78a8SAlex Crichtoncfg_if::cfg_if! { 172*7ebb78a8SAlex Crichton if #[cfg(feature = "gc")] { 173*7ebb78a8SAlex Crichton mod gc; 174*7ebb78a8SAlex Crichton use gc::*; 175*7ebb78a8SAlex Crichton } else { 176*7ebb78a8SAlex Crichton mod gc_disabled; 177*7ebb78a8SAlex Crichton use gc_disabled::*; 178*7ebb78a8SAlex Crichton } 179*7ebb78a8SAlex Crichton} 180*7ebb78a8SAlex Crichton``` 181*7ebb78a8SAlex Crichton 182*7ebb78a8SAlex Crichton**Combinatorial explosion in testing complexity** 183*7ebb78a8SAlex Crichton 184*7ebb78a8SAlex CrichtonEach crate feature can be turned on and off. Wasmtime supports a range of 185*7ebb78a8SAlex Crichtonplatforms and architectures. It's practically infeasible to test every single 186*7ebb78a8SAlex Crichtonpossible combination of these. This means that inevitably there are going to be 187*7ebb78a8SAlex Crichtonuntested configurations as well as bugs within these configurations. 188*7ebb78a8SAlex Crichton 189*7ebb78a8SAlex Crichton## Conditional Compilation Style Guide 190*7ebb78a8SAlex Crichton 191*7ebb78a8SAlex CrichtonWith some of the basics out of the way, this is intended to document the rough 192*7ebb78a8SAlex Crichtoncurrent state of Wasmtime and some various principles for writing conditionally 193*7ebb78a8SAlex Crichtoncompiled code. Much of these are meant to address some of the hazards above. 194*7ebb78a8SAlex CrichtonThese guidelines are not always religiously followed throughout Wasmtime's 195*7ebb78a8SAlex Crichtonrepository but PRs to improve things are always welcome! 196*7ebb78a8SAlex Crichton 197*7ebb78a8SAlex CrichtonThe main takeaway is that the main goal is **to minimize the number of `#[cfg]` 198*7ebb78a8SAlex Crichtonattributes necessary in the repository**. Conditional compilation is required no 199*7ebb78a8SAlex Crichtonmatter what so this number can never be zero, but that doesn't mean every other 200*7ebb78a8SAlex Crichtonline should have `#[cfg]` on it. Otherwise these guidelines need to be applied 201*7ebb78a8SAlex Crichtonwith some understanding that each one is fallible. There's no always-right 202*7ebb78a8SAlex Crichtonanswer unfortunately and style will still differ from person to person. 203*7ebb78a8SAlex Crichton 204*7ebb78a8SAlex Crichton1. **Separate files** - try to put conditionally compiled code into separate 205*7ebb78a8SAlex Crichton files. By placing `#[cfg]` at the module level you can drastically cut down 206*7ebb78a8SAlex Crichton on annotations by removing the entire module at once. An example of this is 207*7ebb78a8SAlex Crichton that Wasmtime's internal `runtime` module is [feature gated][file-gate] at 208*7ebb78a8SAlex Crichton the top-level. 209*7ebb78a8SAlex Crichton 210*7ebb78a8SAlex Crichton2. **Only `#[cfg]` definitions, not uses** - try to only use `#[cfg]` when a 211*7ebb78a8SAlex Crichton type or function is defined, not when it's used. Functions and types can be 212*7ebb78a8SAlex Crichton used all over the place and putting a `#[cfg]` everywhere can be quite 213*7ebb78a8SAlex Crichton annoying an brittle to maintain. 214*7ebb78a8SAlex Crichton 215*7ebb78a8SAlex Crichton * This isn't a problem if a use-site is already contained in a 216*7ebb78a8SAlex Crichton `#[cfg]` item, such as a module. This can be assisted by lifting `#[cfg]` 217*7ebb78a8SAlex Crichton up "as high as possible". An example of this is Wasmtime's `component` 218*7ebb78a8SAlex Crichton module which uses `#[cfg(feature = "component-model")]` at the root of all 219*7ebb78a8SAlex Crichton component-related functionality. That means that conditionally included 220*7ebb78a8SAlex Crichton dependencies used within `component::*` don't need extra `#[cfg]` annotations. 221*7ebb78a8SAlex Crichton 222*7ebb78a8SAlex Crichton * Another common pattern for this is to conditionally define a "dummy" shim 223*7ebb78a8SAlex Crichton interface. The real implementation would live in `foo.rs` while the dummy 224*7ebb78a8SAlex Crichton implementation would live in `foo_disabled.rs`. That means that "foo" is 225*7ebb78a8SAlex Crichton always available but the dummy implementation doesn't do anything. This 226*7ebb78a8SAlex Crichton makes heavy use of zero-sized-types (e.g. `struct Foo;`) and uninhabited 227*7ebb78a8SAlex Crichton types (e.g. `enum Foo {}`) to ensure there is no runtime overhead. An 228*7ebb78a8SAlex Crichton example of this is [`shared_memory.rs`][dummy-enabled] and 229*7ebb78a8SAlex Crichton [`shared_memory_disabled.rs`][dummy-disabled] where the disabled version 230*7ebb78a8SAlex Crichton returns an error on construction and otherwise has trivial implementations 231*7ebb78a8SAlex Crichton of each method. 232*7ebb78a8SAlex Crichton 233*7ebb78a8SAlex Crichton3. **Off-by-default code should be trivial** - described above it's not possible 234*7ebb78a8SAlex Crichton to test Wasmtime in every possible configuration of `#[cfg]`, so to help 235*7ebb78a8SAlex Crichton reduce the risk of lurking bugs try to ensure that all off-by-default code is 236*7ebb78a8SAlex Crichton trivially correct-by-construction. For "dummy" shims described above this 237*7ebb78a8SAlex Crichton means that methods do nothing or return an error. If off-by-default code is 238*7ebb78a8SAlex Crichton nontrivial then it should have a dedicated CI job to ensure that all 239*7ebb78a8SAlex Crichton conditionally compiled parts are tested one way or another. 240*7ebb78a8SAlex Crichton 241*7ebb78a8SAlex Crichton4. **Absolute paths are useful, but noisy** - described above it's easy to get 242*7ebb78a8SAlex Crichton into a situation where a conditionally compiled piece of code is the only 243*7ebb78a8SAlex Crichton users of a `use` statement. One easy fix is to remove the `use` and use the 244*7ebb78a8SAlex Crichton fully qualified path (e.g. `param: core::ptr::NonNull`) in the function 245*7ebb78a8SAlex Crichton instead. This reduces the `#[cfg]` to one, just the function in question, as 246*7ebb78a8SAlex Crichton opposed to one on the function and one on the `use`. Beware though that this 247*7ebb78a8SAlex Crichton can make function signatures very long very quickly, so if that ends up 248*7ebb78a8SAlex Crichton happening one of the above points may help instead. 249*7ebb78a8SAlex Crichton 250*7ebb78a8SAlex Crichton5. **Use `#[cfg]` for anything that requires a new runtime dependency** - one of 251*7ebb78a8SAlex Crichton the primary use cases for `#[cfg]` in Wasmtime is to conditionally remove 252*7ebb78a8SAlex Crichton dependencies at runtime on pieces of functionality. For example if the 253*7ebb78a8SAlex Crichton `async` feature is disabled then stack switching is not necessary to 254*7ebb78a8SAlex Crichton implement. This is a lynchpin of Wasmtime's portability story where we don't 255*7ebb78a8SAlex Crichton guarantee all features compile on all platforms, but the "major" features 256*7ebb78a8SAlex Crichton should compile on all platforms. An example of this is that `threads` 257*7ebb78a8SAlex Crichton requires the standard library, but `runtime` does not. 258*7ebb78a8SAlex Crichton 259*7ebb78a8SAlex Crichton6. **Don't use `#[cfg]` for compiler features** - in contrast to the previous 260*7ebb78a8SAlex Crichton point it's generally not necessary to plumb `#[cfg]` features to Wasmtime's 261*7ebb78a8SAlex Crichton integration with Cranelift. The runtime size or runtime features required to 262*7ebb78a8SAlex Crichton compile WebAssembly code is generally much larger than just running code 263*7ebb78a8SAlex Crichton itself. This means that conditionally compiled compiler features can just add 264*7ebb78a8SAlex Crichton lots of boilerplate to manage internally without much benefit. Ideally 265*7ebb78a8SAlex Crichton `#[cfg]` is only use for WebAssembly runtime features, not compilation of 266*7ebb78a8SAlex Crichton WebAssembly features. 267*7ebb78a8SAlex Crichton 268*7ebb78a8SAlex CrichtonNote that it's intentional that these guidelines are not 100% comprehensive. 269*7ebb78a8SAlex CrichtonAdditionally they're not hard-and-fast rules in the sense that they're checked 270*7ebb78a8SAlex Crichtonin CI somewhere. Instead try to follow them if you can, but if you have any 271*7ebb78a8SAlex Crichtonquestions or feel that `#[cfg]` is overwhelming feel free to reach out on Zulip 272*7ebb78a8SAlex Crichtonor on GitHub. 273*7ebb78a8SAlex Crichton 274*7ebb78a8SAlex Crichton[file-gate]: https://github.com/bytecodealliance/wasmtime/blob/24620d9ff4cfd3a2a5f681181119eb8b0edaeab5/crates/wasmtime/src/lib.rs#L380-L383 275*7ebb78a8SAlex Crichton[high-gate]: https://github.com/bytecodealliance/wasmtime/blob/24620d9ff4cfd3a2a5f681181119eb8b0edaeab5/crates/wasmtime/src/runtime.rs#L55-L56 276*7ebb78a8SAlex Crichton[dummy-enabled]: https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasmtime/src/runtime/vm/memory/shared_memory.rs 277*7ebb78a8SAlex Crichton[dummy-disabled]: https://github.com/bytecodealliance/wasmtime/blob/24620d9ff4cfd3a2a5f681181119eb8b0edaeab5/crates/wasmtime/src/runtime/vm/memory/shared_memory_disabled.rs 278