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