Skip to main content

wasmtime/
compile.rs

1//! Wasm compilation orchestration.
2//!
3//! It works roughly like this:
4//!
5//! * We walk over the Wasm module/component and make a list of all the things
6//!   we need to compile. This is a `CompileInputs`.
7//!
8//! * The `CompileInputs::compile` method compiles each of these in parallel,
9//!   producing a `UnlinkedCompileOutputs`. This is an unlinked set of compiled
10//!   functions, bucketed by type of function.
11//!
12//! * The `UnlinkedCompileOutputs::pre_link` method re-arranges the compiled
13//!   functions into a flat list. This is the order we will place them within
14//!   the ELF file, so we must also keep track of all the functions' indices
15//!   within this list, because we will need them for resolving
16//!   relocations. These indices are kept track of in the resulting
17//!   `FunctionIndices`.
18//!
19//! * The `FunctionIndices::link_and_append_code` method appends the functions
20//!   to the given ELF file and resolves relocations. It produces an `Artifacts`
21//!   which contains the data needed at runtime to find and call Wasm
22//!   functions. It is up to the caller to serialize the relevant parts of the
23//!   `Artifacts` into the ELF file.
24
25use crate::Engine;
26use crate::hash_map::HashMap;
27use crate::hash_set::HashSet;
28use crate::prelude::*;
29use std::{any::Any, borrow::Cow, collections::BTreeMap, mem, ops::Range};
30use wasmtime_environ::{
31    Abi, CompiledFunctionBody, CompiledFunctionsTable, CompiledFunctionsTableBuilder,
32    CompiledModuleInfo, Compiler, DefinedFuncIndex, FilePos, FinishedObject, FuncKey,
33    FunctionBodyData, Inlining, InliningCompiler, ModuleEnvironment, ModuleTranslation,
34    ModuleTypes, ModuleTypesBuilder, ObjectKind, PrimaryMap, StaticModuleIndex, Tunables,
35    graphs::{EntityGraph, Graph as _},
36};
37#[cfg(feature = "component-model")]
38use wasmtime_environ::{WasmChecksum, component::Translator};
39
40mod stratify;
41
42mod code_builder;
43pub use self::code_builder::{CodeBuilder, CodeHint, HashedEngineCompileEnv};
44
45#[cfg(feature = "runtime")]
46mod runtime;
47
48/// Converts an input binary-encoded WebAssembly module to compilation
49/// artifacts and type information.
50///
51/// This is where compilation actually happens of WebAssembly modules and
52/// translation/parsing/validation of the binary input occurs. The binary
53/// artifact represented in the `MmapVec` returned here is an in-memory ELF
54/// file in an owned area of virtual linear memory where permissions (such
55/// as the executable bit) can be applied.
56///
57/// Additionally compilation returns an `Option` here which is always
58/// `Some`, notably compiled metadata about the module in addition to the
59/// type information found within.
60pub(crate) fn build_module_artifacts<T: FinishedObject>(
61    engine: &Engine,
62    wasm: &[u8],
63    dwarf_package: Option<&[u8]>,
64    obj_state: &T::State,
65) -> Result<(
66    T,
67    Option<(CompiledModuleInfo, CompiledFunctionsTable, ModuleTypes)>,
68)> {
69    let compiler = engine.try_compiler()?;
70    let tunables = engine.tunables();
71
72    // First a `ModuleEnvironment` is created which records type information
73    // about the wasm module. This is where the WebAssembly is parsed and
74    // validated. Afterwards `types` will have all the type information for
75    // this module.
76    let mut parser = wasmparser::Parser::new(0);
77    let mut validator = wasmparser::Validator::new_with_features(engine.features());
78    parser.set_features(*validator.features());
79    let mut types = ModuleTypesBuilder::new(&validator);
80    let mut translation = ModuleEnvironment::new(
81        tunables,
82        &mut validator,
83        &mut types,
84        StaticModuleIndex::from_u32(0),
85    )
86    .translate(parser, wasm)
87    .context("failed to parse WebAssembly module")?;
88    prepare_translation(engine, compiler, &mut translation, &mut types);
89    let functions = mem::take(&mut translation.function_body_inputs);
90
91    let compile_inputs = CompileInputs::for_module(&types, &translation, functions);
92    let unlinked_compile_outputs = compile_inputs.compile(engine, &types)?;
93    let PreLinkOutput {
94        needs_gc_heap,
95        compiled_funcs,
96        indices,
97    } = unlinked_compile_outputs.pre_link();
98    translation.module.needs_gc_heap |= needs_gc_heap;
99
100    // Emplace all compiled functions into the object file with any other
101    // sections associated with code as well.
102    let mut object = compiler.object(ObjectKind::Module)?;
103    // Insert `Engine` and type-level information into the compiled
104    // artifact so if this module is deserialized later it contains all
105    // information necessary.
106    //
107    // Note that `append_compiler_info` and `append_types` here in theory
108    // can both be skipped if this module will never get serialized.
109    // They're only used during deserialization and not during runtime for
110    // the module itself. Currently there's no need for that, however, so
111    // it's left as an exercise for later.
112    engine.append_compiler_info(&mut object)?;
113    engine.append_bti(&mut object);
114
115    let (mut object, compilation_artifacts) = indices.link_and_append_code(
116        object,
117        engine,
118        compiled_funcs,
119        std::iter::once(translation).collect(),
120        dwarf_package,
121    )?;
122
123    if tunables.debug_guest {
124        object.append_wasm_bytecode(std::iter::once(wasm));
125    }
126
127    let (info, index) = compilation_artifacts.unwrap_as_module_info();
128    let types = types.finish();
129    object.serialize_info(&(&info, &index, &types));
130    let result = T::finish_object(object, obj_state)?;
131
132    Ok((result, Some((info, index, types))))
133}
134
135/// Performs the compilation phase for a component, translating and
136/// validating the provided wasm binary to machine code.
137///
138/// This method will compile all nested core wasm binaries in addition to
139/// any necessary extra functions required for operation with components.
140/// The output artifact here is the serialized object file contained within
141/// an owned mmap along with metadata about the compilation itself.
142#[cfg(feature = "component-model")]
143pub(crate) fn build_component_artifacts<T: FinishedObject>(
144    engine: &Engine,
145    binary: &[u8],
146    _dwarf_package: Option<&[u8]>,
147    unsafe_intrinsics_import: Option<&str>,
148    obj_state: &T::State,
149) -> Result<(T, Option<wasmtime_environ::component::ComponentArtifacts>)> {
150    use wasmtime_environ::ScopeVec;
151    use wasmtime_environ::component::{
152        CompiledComponentInfo, ComponentArtifacts, ComponentTypesBuilder,
153    };
154
155    let compiler = engine.try_compiler()?;
156    let tunables = engine.tunables();
157
158    let scope = ScopeVec::new();
159    let mut validator = wasmparser::Validator::new_with_features(engine.features());
160    let mut types = ComponentTypesBuilder::new(&validator);
161    let mut translator = Translator::new(tunables, &mut validator, &mut types, &scope);
162    if let Some(name) = unsafe_intrinsics_import {
163        translator.expose_unsafe_intrinsics(name);
164    }
165    let (component, mut module_translations) = translator
166        .translate(binary)
167        .context("failed to parse WebAssembly module")?;
168
169    for (_, translation) in module_translations.iter_mut() {
170        prepare_translation(
171            engine,
172            compiler,
173            translation,
174            types.module_types_builder_mut(),
175        );
176    }
177
178    let compile_inputs = CompileInputs::for_component(
179        engine,
180        &types,
181        &component,
182        module_translations.iter_mut().map(|(i, translation)| {
183            let functions = mem::take(&mut translation.function_body_inputs);
184            (i, &*translation, functions)
185        }),
186    );
187    let unlinked_compile_outputs = compile_inputs.compile(&engine, types.module_types_builder())?;
188
189    let PreLinkOutput {
190        needs_gc_heap,
191        compiled_funcs,
192        indices,
193    } = unlinked_compile_outputs.pre_link();
194    for (_, t) in &mut module_translations {
195        t.module.needs_gc_heap |= needs_gc_heap
196    }
197
198    // Collect bytecode slices here before moving `module_translations` below.
199    let module_wasms = if tunables.debug_guest {
200        module_translations
201            .values()
202            .map(|t| t.wasm)
203            .collect::<Vec<_>>()
204    } else {
205        vec![]
206    };
207
208    let mut object = compiler.object(ObjectKind::Component)?;
209    engine.append_compiler_info(&mut object)?;
210    engine.append_bti(&mut object);
211
212    let (mut object, compilation_artifacts) = indices.link_and_append_code(
213        object,
214        engine,
215        compiled_funcs,
216        module_translations,
217        None, // TODO: Support dwarf packages for components.
218    )?;
219
220    if tunables.debug_guest {
221        object.append_wasm_bytecode(module_wasms);
222    }
223
224    let (types, ty) = types.finish(&component.component);
225
226    let info = CompiledComponentInfo {
227        component: component.component,
228    };
229    let artifacts = ComponentArtifacts {
230        info,
231        table: compilation_artifacts.table,
232        ty,
233        types,
234        static_modules: compilation_artifacts.modules,
235        checksum: WasmChecksum::from_binary(binary, tunables.recording),
236    };
237    object.serialize_info(&artifacts);
238
239    let result = T::finish_object(object, obj_state)?;
240    Ok((result, Some(artifacts)))
241}
242
243fn prepare_translation(
244    engine: &Engine,
245    compiler: &dyn Compiler,
246    translation: &mut ModuleTranslation<'_>,
247    types: &mut ModuleTypesBuilder,
248) {
249    // If configured attempt to use static memory initialization
250    // which can either at runtime be implemented as a single memcpy
251    // to initialize memory or otherwise enabling
252    // virtual-memory-tricks such as mmap'ing from a file to get
253    // copy-on-write.
254    let align = compiler.page_size_align();
255    let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
256    translation.finalize_memory_init(engine.tunables(), align, max_always_allowed, types);
257
258    // Attempt to convert table initializer segments to FuncTable
259    // representation where possible, to enable table lazy init.
260    translation.finalize_table_init(engine.tunables(), types);
261}
262
263type CompileInput<'a> = Box<dyn FnOnce(&dyn Compiler) -> Result<CompileOutput<'a>> + Send + 'a>;
264
265struct CompileOutput<'a> {
266    key: FuncKey,
267    symbol: String,
268    function: CompiledFunctionBody,
269    start_srcloc: FilePos,
270
271    // Only present when `self.key` is a `FuncKey::DefinedWasmFunction(..)`.
272    translation: Option<&'a ModuleTranslation<'a>>,
273
274    // Only present when `self.key` is a `FuncKey::DefinedWasmFunction(..)`.
275    func_body: Option<wasmparser::FunctionBody<'a>>,
276}
277
278/// Inputs to our inlining heuristics.
279struct InlineHeuristicParams<'a> {
280    tunables: &'a Tunables,
281    caller_size: u32,
282    caller_key: FuncKey,
283    caller_needs_gc_heap: bool,
284    callee_size: u32,
285    callee_key: FuncKey,
286    callee_needs_gc_heap: bool,
287}
288
289/// The collection of things we need to compile for a Wasm module or component.
290#[derive(Default)]
291struct CompileInputs<'a> {
292    inputs: Vec<CompileInput<'a>>,
293}
294
295impl<'a> CompileInputs<'a> {
296    fn push_input(
297        &mut self,
298        f: impl FnOnce(&dyn Compiler) -> Result<CompileOutput<'a>> + Send + 'a,
299    ) {
300        self.inputs.push(Box::new(f));
301    }
302
303    /// Create the `CompileInputs` for a core Wasm module.
304    fn for_module(
305        types: &'a ModuleTypesBuilder,
306        translation: &'a ModuleTranslation<'a>,
307        functions: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
308    ) -> Self {
309        let mut ret = CompileInputs { inputs: vec![] };
310
311        let module_index = StaticModuleIndex::from_u32(0);
312        ret.collect_inputs_in_translations(types, [(module_index, translation, functions)]);
313
314        ret
315    }
316
317    /// Create a `CompileInputs` for a component.
318    #[cfg(feature = "component-model")]
319    fn for_component(
320        engine: &'a Engine,
321        types: &'a wasmtime_environ::component::ComponentTypesBuilder,
322        component: &'a wasmtime_environ::component::ComponentTranslation,
323        module_translations: impl IntoIterator<
324            Item = (
325                StaticModuleIndex,
326                &'a ModuleTranslation<'a>,
327                PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
328            ),
329        >,
330    ) -> Self {
331        use wasmtime_environ::Abi;
332        use wasmtime_environ::component::UnsafeIntrinsic;
333
334        let mut ret = CompileInputs { inputs: vec![] };
335
336        ret.collect_inputs_in_translations(types.module_types_builder(), module_translations);
337        let tunables = engine.tunables();
338
339        for i in component
340            .component
341            .unsafe_intrinsics
342            .iter()
343            .enumerate()
344            .filter_map(|(i, ty)| if ty.is_some() { Some(i) } else { None })
345        {
346            let i = u32::try_from(i).unwrap();
347            let intrinsic = UnsafeIntrinsic::from_u32(i);
348            for abi in [Abi::Wasm, Abi::Array] {
349                ret.push_input(move |compiler| {
350                    let symbol = format!(
351                        "unsafe-intrinsics-{}-{}",
352                        match abi {
353                            Abi::Wasm => "wasm-call",
354                            Abi::Array => "array-call",
355                            Abi::Patchable => "patchable-call",
356                        },
357                        intrinsic.name(),
358                    );
359                    Ok(CompileOutput {
360                        key: FuncKey::UnsafeIntrinsic(abi, intrinsic),
361                        function: compiler
362                            .component_compiler()
363                            .compile_intrinsic(tunables, component, types, intrinsic, abi, &symbol)
364                            .with_context(|| format!("failed to compile `{symbol}`"))?,
365                        symbol,
366                        start_srcloc: FilePos::default(),
367                        translation: None,
368                        func_body: None,
369                    })
370                });
371            }
372        }
373
374        for (idx, trampoline) in component.trampolines.iter() {
375            for abi in [Abi::Wasm, Abi::Array] {
376                ret.push_input(move |compiler| {
377                    let key = FuncKey::ComponentTrampoline(abi, idx);
378                    let symbol = format!(
379                        "component-trampolines[{}]-{}-{}",
380                        idx.as_u32(),
381                        match abi {
382                            Abi::Wasm => "wasm-call",
383                            Abi::Array => "array-call",
384                            Abi::Patchable => "patchable-call",
385                        },
386                        trampoline.symbol_name(),
387                    );
388                    let function = compiler
389                        .component_compiler()
390                        .compile_component_trampoline(component, types, key, abi, tunables, &symbol)
391                        .with_context(|| format!("failed to compile {symbol}"))?;
392                    Ok(CompileOutput {
393                        key,
394                        function,
395                        symbol,
396                        start_srcloc: FilePos::default(),
397                        translation: None,
398                        func_body: None,
399                    })
400                });
401            }
402        }
403
404        // If there are any resources defined within this component, the
405        // signature for `resource.drop` is mentioned somewhere, and the
406        // wasm-to-native trampoline for `resource.drop` hasn't been created yet
407        // then insert that here. This is possibly required by destruction of
408        // resources from the embedder and otherwise won't be explicitly
409        // requested through initializers above or such.
410        if component.component.num_resources > 0 {
411            if types
412                .module_types_builder()
413                .find_resource_drop_signature()
414                .is_some()
415            {
416                ret.push_input(move |compiler| {
417                    let key = FuncKey::ResourceDropTrampoline;
418                    let symbol = "resource_drop_trampoline".to_string();
419                    let function = compiler
420                        .compile_trampoline(None, key, types.module_types_builder(), &symbol)
421                        .with_context(|| format!("failed to compile `{symbol}`"))?;
422                    Ok(CompileOutput {
423                        key,
424                        function,
425                        symbol,
426                        start_srcloc: FilePos::default(),
427                        translation: None,
428                        func_body: None,
429                    })
430                });
431            }
432        }
433
434        ret
435    }
436
437    fn clean_symbol(name: &str) -> Cow<'_, str> {
438        /// Maximum length of symbols generated in objects.
439        const MAX_SYMBOL_LEN: usize = 96;
440
441        // Just to be on the safe side, filter out characters that could
442        // pose issues to tools such as "perf" or "objdump".  To avoid
443        // having to update a list of allowed characters for each different
444        // language that compiles to Wasm, allows only graphic ASCII
445        // characters; replace runs of everything else with a "?".
446        let bad_char = |c: char| !c.is_ascii_graphic();
447        if name.chars().any(bad_char) {
448            let mut last_char_seen = '\u{0000}';
449            Cow::Owned(
450                name.chars()
451                    .map(|c| if bad_char(c) { '?' } else { c })
452                    .filter(|c| {
453                        let skip = last_char_seen == '?' && *c == '?';
454                        last_char_seen = *c;
455                        !skip
456                    })
457                    .take(MAX_SYMBOL_LEN)
458                    .collect::<String>(),
459            )
460        } else if name.len() <= MAX_SYMBOL_LEN {
461            Cow::Borrowed(&name[..])
462        } else {
463            Cow::Borrowed(&name[..MAX_SYMBOL_LEN])
464        }
465    }
466
467    fn collect_inputs_in_translations(
468        &mut self,
469        types: &'a ModuleTypesBuilder,
470        translations: impl IntoIterator<
471            Item = (
472                StaticModuleIndex,
473                &'a ModuleTranslation<'a>,
474                PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
475            ),
476        >,
477    ) {
478        for (module, translation, functions) in translations {
479            for (def_func_index, func_body_data) in functions {
480                self.push_input(move |compiler| {
481                    let key = FuncKey::DefinedWasmFunction(module, def_func_index);
482                    let func_index = translation.module.func_index(def_func_index);
483                    let symbol = match translation
484                        .debuginfo
485                        .name_section
486                        .func_names
487                        .get(&func_index)
488                    {
489                        Some(name) => format!(
490                            "wasm[{}]::function[{}]::{}",
491                            module.as_u32(),
492                            func_index.as_u32(),
493                            Self::clean_symbol(&name)
494                        ),
495                        None => format!(
496                            "wasm[{}]::function[{}]",
497                            module.as_u32(),
498                            func_index.as_u32()
499                        ),
500                    };
501                    let func_body = func_body_data.body.clone();
502                    let data = func_body.get_binary_reader();
503                    let offset = data.original_position();
504                    let start_srcloc = FilePos::new(u32::try_from(offset).unwrap());
505                    let function = compiler
506                        .compile_function(translation, key, func_body_data, types, &symbol)
507                        .with_context(|| format!("failed to compile: {symbol}"))?;
508
509                    Ok(CompileOutput {
510                        key,
511                        symbol,
512                        function,
513                        start_srcloc,
514                        translation: Some(translation),
515                        func_body: Some(func_body),
516                    })
517                });
518
519                let func_index = translation.module.func_index(def_func_index);
520                if translation.module.functions[func_index].is_escaping() {
521                    self.push_input(move |compiler| {
522                        let key = FuncKey::ArrayToWasmTrampoline(module, def_func_index);
523                        let func_index = translation.module.func_index(def_func_index);
524                        let symbol = format!(
525                            "wasm[{}]::array_to_wasm_trampoline[{}]",
526                            module.as_u32(),
527                            func_index.as_u32()
528                        );
529                        let function = compiler
530                            .compile_trampoline(Some(translation), key, types, &symbol)
531                            .with_context(|| format!("failed to compile: {symbol}"))?;
532                        Ok(CompileOutput {
533                            key,
534                            symbol,
535                            function,
536                            start_srcloc: FilePos::default(),
537                            translation: None,
538                            func_body: None,
539                        })
540                    });
541                }
542            }
543
544            if !translation.module.startup.is_none() {
545                for abi in [Abi::Wasm, Abi::Array] {
546                    self.push_input(move |compiler| {
547                        let key = FuncKey::ModuleStartup(abi, module);
548                        let symbol = format!("module_start[{}]::{abi:?}", module.as_u32());
549                        let function = compiler
550                            .compile_trampoline(Some(translation), key, types, &symbol)
551                            .with_context(|| format!("failed to compile: {symbol}"))?;
552                        Ok(CompileOutput {
553                            key,
554                            function,
555                            symbol,
556                            start_srcloc: FilePos::default(),
557                            translation: None,
558                            func_body: None,
559                        })
560                    });
561                }
562            }
563        }
564
565        let mut trampoline_types_seen = HashSet::new();
566        for (_func_type_index, trampoline_type_index) in types.trampoline_types() {
567            let is_new = trampoline_types_seen.insert(trampoline_type_index);
568            if !is_new {
569                continue;
570            }
571            self.push_input(move |compiler| {
572                let key = FuncKey::WasmToArrayTrampoline(trampoline_type_index);
573                let symbol = format!(
574                    "signatures[{}]::wasm_to_array_trampoline",
575                    trampoline_type_index.as_u32()
576                );
577                let function = compiler
578                    .compile_trampoline(None, key, types, &symbol)
579                    .with_context(|| format!("failed to compile: {symbol}"))?;
580                Ok(CompileOutput {
581                    key,
582                    function,
583                    symbol,
584                    start_srcloc: FilePos::default(),
585                    translation: None,
586                    func_body: None,
587                })
588            });
589        }
590    }
591
592    /// Compile these `CompileInput`s (maybe in parallel) and return the
593    /// resulting `UnlinkedCompileOutput`s.
594    fn compile(
595        self,
596        engine: &Engine,
597        types: &'a ModuleTypesBuilder,
598    ) -> Result<UnlinkedCompileOutputs<'a>> {
599        let compiler = engine.try_compiler()?;
600
601        if self.inputs.len() > 0 && cfg!(miri) {
602            bail!(
603                "\
604You are attempting to compile a WebAssembly module or component that contains
605functions in Miri. Running Cranelift through Miri is known to take quite a long
606time and isn't what we want in CI at least. If this is a mistake then you should
607ignore this test in Miri with:
608
609    #[cfg_attr(miri, ignore)]
610
611If this is not a mistake then try to edit the `pulley_provenance_test` test
612which runs Cranelift outside of Miri. If you still feel this is a mistake then
613please open an issue or a topic on Zulip to talk about how best to accommodate
614the use case.
615"
616            );
617        }
618
619        let mut raw_outputs = if let Some(inlining_compiler) = compiler.inlining_compiler() {
620            if engine.tunables().inlining != Inlining::No {
621                self.compile_with_inlining(engine, compiler, inlining_compiler)?
622            } else {
623                // Inlining compiler but inlining is disabled: compile each
624                // input and immediately finish its output in parallel, skipping
625                // call graph computation and all that.
626                engine.run_maybe_parallel::<_, _, Error, _>(self.inputs, |f| {
627                    let mut compiled = f(compiler)?;
628                    inlining_compiler.finish_compiling(
629                        &mut compiled.function,
630                        compiled.func_body.take(),
631                        &compiled.symbol,
632                    )?;
633                    Ok(compiled)
634                })?
635            }
636        } else {
637            // No inlining: just compile each individual input in parallel.
638            engine.run_maybe_parallel(self.inputs, |f| f(compiler))?
639        };
640
641        if cfg!(debug_assertions) {
642            let mut symbols: Vec<_> = raw_outputs.iter().map(|i| &i.symbol).collect();
643            symbols.sort();
644            for [a, b] in symbols.array_windows() {
645                assert_ne!(
646                    a, b,
647                    "should never have duplicate symbols, but found two functions with the symbol `{a}`",
648                );
649            }
650        }
651
652        // Now that all functions have been compiled see if any
653        // wasmtime-builtin functions are necessary. If so those need to be
654        // collected and then those trampolines additionally need to be
655        // compiled.
656        compile_required_builtins(engine, types, &mut raw_outputs)?;
657
658        // Bucket the outputs by kind.
659        let mut outputs: BTreeMap<FuncKey, CompileOutput> = BTreeMap::new();
660        for output in raw_outputs {
661            outputs.insert(output.key, output);
662        }
663
664        Ok(UnlinkedCompileOutputs { outputs })
665    }
666
667    fn compile_with_inlining(
668        self,
669        engine: &Engine,
670        compiler: &dyn Compiler,
671        inlining_compiler: &dyn InliningCompiler,
672    ) -> Result<Vec<CompileOutput<'a>>, Error> {
673        // Our list of unlinked outputs.
674        let mut outputs = PrimaryMap::<OutputIndex, Option<CompileOutput<'_>>>::from(
675            engine.run_maybe_parallel(self.inputs, |f| f(compiler).map(Some))?,
676        );
677
678        // A map from a `FuncKey` to its index in our unlinked outputs.
679        //
680        // We will generally just be working with `OutputIndex`es, but
681        // occasionally we must translate from keys back to our index space, for
682        // example when we know that one module's function import is always
683        // satisfied with a particular `FuncKey::DefinedWasmFunction`. This map
684        // enables that translation.
685        let key_to_output: HashMap<FuncKey, OutputIndex> = inlining_functions(&outputs)
686            .map(|output_index| {
687                let output = outputs[output_index].as_ref().unwrap();
688                (output.key, output_index)
689            })
690            .collect();
691
692        // Construct the call graph for inlining.
693        //
694        // We only inline Wasm functions, not trampolines, because we rely on
695        // trampolines being in their own stack frame when we save the entry and
696        // exit SP, FP, and PC for backtraces in trampolines.
697        let call_graph = EntityGraph::<OutputIndex>::new(inlining_functions(&outputs), {
698            let mut func_keys = IndexSet::default();
699            let outputs = &outputs;
700            let key_to_output = &key_to_output;
701            move |output_index, calls| {
702                let output = outputs[output_index].as_ref().unwrap();
703                debug_assert!(is_inlining_function(output.key));
704
705                // Get this function's call graph edges as `FuncKey`s.
706                func_keys.clear();
707                inlining_compiler.calls(&output.function, &mut func_keys)?;
708
709                // Translate each of those to keys to output indices, which is
710                // what we actually need.
711                debug_assert!(calls.is_empty());
712                calls.extend(
713                    func_keys
714                        .iter()
715                        .copied()
716                        .filter_map(|key| key_to_output.get(&key).copied()),
717                );
718
719                log::trace!(
720                    "call graph edges for {output_index:?} = {:?}: {calls:?}",
721                    output.key
722                );
723
724                crate::error::Ok(())
725            }
726        })?;
727
728        // Stratify the call graph into a sequence of layers. We process each
729        // layer in order, but process functions within a layer in parallel
730        // (because they either do not call each other or are part of a
731        // mutual-recursion cycle; either way we won't inline members of the
732        // same layer into each other).
733        let strata = stratify::Strata::<OutputIndex>::new(&call_graph.filter_nodes(|f| {
734            let key = outputs[*f].as_ref().unwrap().key;
735            is_inlining_function(key)
736        }));
737        let mut layer_outputs = vec![];
738        for layer in strata.layers() {
739            // Temporarily take this layer's outputs out of our unlinked outputs
740            // list so that we can mutate these outputs (by inlining callee
741            // functions into them) while also accessing shared borrows of the
742            // unlinked outputs list (finding the callee functions we will
743            // inline).
744            debug_assert!(layer_outputs.is_empty());
745            layer_outputs.extend(layer.iter().map(|f| outputs[*f].take().unwrap()));
746
747            // Process this layer's members in parallel.
748            engine.run_maybe_parallel_mut(
749                &mut layer_outputs,
750                |output: &mut CompileOutput<'_>| {
751                    log::trace!("processing inlining for {:?}", output.key);
752                    debug_assert!(is_inlining_function(output.key));
753
754                    let caller_key = output.key;
755                    let caller_needs_gc_heap =
756                        output.translation.is_some_and(|t| t.module.needs_gc_heap);
757                    let caller = &mut output.function;
758
759                    let mut caller_size = inlining_compiler.size(caller);
760
761                    inlining_compiler.inline(caller, &mut |callee_key: FuncKey| {
762                        log::trace!("  --> considering call to {callee_key:?}");
763                        let callee_output_index: OutputIndex = key_to_output[&callee_key];
764
765                        // NB: If the callee is not inside `outputs`, then it is
766                        // in the same `Strata` layer as the caller (and
767                        // therefore is in the same strongly-connected component
768                        // as the caller, and they mutually recursive). In this
769                        // case, we do not do any inlining; communicate this
770                        // command via `?`-propagation.
771                        let callee_output = outputs[callee_output_index].as_ref()?;
772
773                        debug_assert_eq!(callee_output.key, callee_key);
774
775                        let callee = &callee_output.function;
776                        let callee_size = inlining_compiler.size(callee);
777
778                        let callee_needs_gc_heap = callee_output
779                            .translation
780                            .is_some_and(|t| t.module.needs_gc_heap);
781
782                        if Self::should_inline(InlineHeuristicParams {
783                            tunables: engine.tunables(),
784                            caller_size,
785                            caller_key,
786                            caller_needs_gc_heap,
787                            callee_size,
788                            callee_key,
789                            callee_needs_gc_heap,
790                        }) {
791                            caller_size = caller_size.saturating_add(callee_size);
792                            Some(callee)
793                        } else {
794                            None
795                        }
796                    })
797                },
798            )?;
799
800            for (f, func) in layer.iter().zip(layer_outputs.drain(..)) {
801                debug_assert!(outputs[*f].is_none());
802                outputs[*f] = Some(func);
803            }
804        }
805
806        // Fan out in parallel again and finish compiling each function.
807        engine.run_maybe_parallel(outputs.into(), |output| {
808            let mut output = output.unwrap();
809            inlining_compiler.finish_compiling(
810                &mut output.function,
811                output.func_body.take(),
812                &output.symbol,
813            )?;
814            Ok(output)
815        })
816    }
817
818    /// Implementation of our inlining heuristics.
819    ///
820    /// TODO: We should improve our heuristics:
821    ///
822    /// * One potentially promising hint that we don't currently make use of is
823    ///   how many times a function appears as the callee in call sites. For
824    ///   example, a function that appears in only a single call site, and does
825    ///   not otherwise escape, is often beneficial to inline regardless of its
826    ///   size (assuming we can then GC away the non-inlined version of the
827    ///   function, which we do not currently attempt to do).
828    ///
829    /// * Another potentially promising hint would be whether any of the call
830    ///   site's actual arguments are constants.
831    ///
832    /// * A general improvement would be removing the decision-tree style of
833    ///   control flow below and replacing it with (1) a pure estimated-benefit
834    ///   formula and (2) a benefit threshold. Whenever the estimated benefit
835    ///   reaches the threshold, we would inline the call. Both the formula and
836    ///   the threshold would be parameterized by tunables. This would
837    ///   effectively allow reprioritizing the relative importance of different
838    ///   hint sources, rather than being stuck with the sequence hard-coded in
839    ///   the decision tree below.
840    fn should_inline(
841        InlineHeuristicParams {
842            tunables,
843            caller_size,
844            caller_key,
845            caller_needs_gc_heap,
846            callee_size,
847            callee_key,
848            callee_needs_gc_heap,
849        }: InlineHeuristicParams,
850    ) -> bool {
851        log::trace!(
852            "considering inlining:\n\
853             \tcaller = {caller_key:?}\n\
854             \t\tsize = {caller_size}\n\
855             \t\tneeds_gc_heap = {caller_needs_gc_heap}\n\
856             \tcallee = {callee_key:?}\n\
857             \t\tsize = {callee_size}\n\
858             \t\tneeds_gc_heap = {callee_needs_gc_heap}"
859        );
860
861        debug_assert!(
862            tunables.inlining != Inlining::No,
863            "shouldn't even call this method if we aren't configured for inlining"
864        );
865        debug_assert_ne!(caller_key, callee_key, "we never inline recursion");
866
867        // Put a limit on how large we can make a function via inlining to cap
868        // code bloat.
869        let sum_size = caller_size.saturating_add(callee_size);
870        if sum_size > tunables.inlining_sum_size_threshold {
871            log::trace!(
872                "  --> not inlining: the sum of the caller's and callee's sizes is greater than \
873                 the inlining-sum-size threshold: {callee_size} + {caller_size} > {}",
874                tunables.inlining_sum_size_threshold
875            );
876            return false;
877        }
878
879        // Skip inlining into array-abi functions which are entry
880        // trampolines into wasm. ABI-wise it's required that these have a
881        // single `try_call` into the module and it doesn't work if multiple
882        // get inlined or if the `try_call` goes away. Prevent all inlining
883        // to guarantee the structure of entry trampolines.
884        if caller_key.abi() == Abi::Array {
885            log::trace!("  --> not inlining: not inlining into array-abi caller");
886            return false;
887        }
888
889        // Consider whether this is an intra-module call.
890        //
891        // Inlining within a single core module has most often already been done
892        // by the toolchain that produced the module, e.g. LLVM, and any extant
893        // function calls to small callees were presumably annotated with the
894        // equivalent of `#[inline(never)]` or `#[cold]` but we don't have that
895        // information anymore.
896        match (caller_key, callee_key) {
897            (
898                FuncKey::DefinedWasmFunction(caller_module, _),
899                FuncKey::DefinedWasmFunction(callee_module, _),
900            ) => match tunables.inlining {
901                Inlining::Yes => {}
902
903                Inlining::InterModuleAndIntraGc => {
904                    if caller_module == callee_module && !caller_needs_gc_heap {
905                        log::trace!("  --> not inlining: intra-module call where GC is not used");
906                        return false;
907                    }
908                }
909
910                Inlining::InterModule => {
911                    if caller_module == callee_module {
912                        log::trace!("  --> not inlining: only inter-module calls inlined");
913                        return false;
914                    }
915                }
916
917                Inlining::Intrinsics => {
918                    log::trace!("  --> not inlining: only inlining intrinsics");
919                    return false;
920                }
921
922                Inlining::No => unreachable!(),
923            },
924            _ => {}
925        }
926
927        // Small callees are often worth inlining regardless of the size of the
928        // caller.
929        if callee_size <= tunables.inlining_small_callee_size {
930            log::trace!(
931                "  --> inlining: callee's size is less than the small-callee size: \
932                 {callee_size} <= {}",
933                tunables.inlining_small_callee_size
934            );
935            return true;
936        }
937
938        log::trace!("  --> inlining: did not find a reason we should not");
939        true
940    }
941}
942
943/// The index of a function (of any kind: Wasm function, trampoline, or
944/// etc...) in our list of unlinked outputs.
945#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
946struct OutputIndex(u32);
947wasmtime_environ::entity_impl!(OutputIndex);
948
949/// Whether a function (as described by the given `FuncKey`) can
950/// participate in inlining or not (either as a candidate for being
951/// inlined into a caller or having a callee inlined into a callsite
952/// within itself).
953fn is_inlining_function(key: FuncKey) -> bool {
954    match key {
955        // Wasm functions can both be inlined into other functions and
956        // have other functions inlined into them.
957        FuncKey::DefinedWasmFunction(..) => true,
958
959        // Intrinsics can be inlined into other functions.
960        FuncKey::UnsafeIntrinsic(..) => true,
961
962        // Trampolines cannot participate in inlining since our
963        // unwinding and exceptions infrastructure relies on them being
964        // in their own call frames.
965        FuncKey::ArrayToWasmTrampoline(..)
966        | FuncKey::WasmToArrayTrampoline(..)
967        | FuncKey::WasmToBuiltinTrampoline(..)
968        | FuncKey::PatchableToBuiltinTrampoline(..)
969        | FuncKey::ModuleStartup(..) => false,
970        FuncKey::ComponentTrampoline(..) | FuncKey::ResourceDropTrampoline => false,
971
972        FuncKey::PulleyHostCall(_) => {
973            unreachable!("we don't compile artifacts for Pulley host calls")
974        }
975    }
976}
977
978/// Get just the output indices of the functions that can participate in
979/// inlining from our unlinked outputs.
980fn inlining_functions<'a>(
981    outputs: &'a PrimaryMap<OutputIndex, Option<CompileOutput<'_>>>,
982) -> impl Iterator<Item = OutputIndex> + 'a {
983    outputs.iter().filter_map(|(index, output)| {
984        if is_inlining_function(output.as_ref().unwrap().key) {
985            Some(index)
986        } else {
987            None
988        }
989    })
990}
991
992fn compile_required_builtins<'a>(
993    engine: &Engine,
994    types: &'a ModuleTypesBuilder,
995    raw_outputs: &mut Vec<CompileOutput<'a>>,
996) -> Result<()> {
997    let compiler = engine.try_compiler()?;
998    let mut builtins = HashSet::new();
999    let mut new_inputs: Vec<CompileInput<'_>> = Vec::new();
1000
1001    let compile_builtin = |key: FuncKey| {
1002        Box::new(move |compiler: &dyn Compiler| {
1003            let symbol = match key {
1004                FuncKey::WasmToBuiltinTrampoline(builtin) => {
1005                    format!("wasmtime_builtin_{}", builtin.name())
1006                }
1007                FuncKey::PatchableToBuiltinTrampoline(builtin) => {
1008                    format!("wasmtime_patchable_builtin_{}", builtin.name())
1009                }
1010                _ => unreachable!(),
1011            };
1012            let mut function = compiler
1013                .compile_trampoline(None, key, types, &symbol)
1014                .with_context(|| format!("failed to compile `{symbol}`"))?;
1015            if let Some(compiler) = compiler.inlining_compiler() {
1016                compiler.finish_compiling(&mut function, None, &symbol)?;
1017            }
1018            Ok(CompileOutput {
1019                key,
1020                function,
1021                symbol,
1022                start_srcloc: FilePos::default(),
1023                translation: None,
1024                func_body: None,
1025            })
1026        })
1027    };
1028
1029    for output in raw_outputs.iter() {
1030        for reloc in compiler.compiled_function_relocation_targets(&*output.function.code) {
1031            match reloc {
1032                FuncKey::WasmToBuiltinTrampoline(builtin)
1033                | FuncKey::PatchableToBuiltinTrampoline(builtin) => {
1034                    if builtins.insert(builtin) {
1035                        new_inputs.push(compile_builtin(reloc));
1036                    }
1037                }
1038                _ => {}
1039            }
1040        }
1041    }
1042    raw_outputs.extend(engine.run_maybe_parallel(new_inputs, |c| c(compiler))?);
1043    Ok(())
1044}
1045
1046#[derive(Default)]
1047struct UnlinkedCompileOutputs<'a> {
1048    // A map from kind to `CompileOutput`.
1049    outputs: BTreeMap<FuncKey, CompileOutput<'a>>,
1050}
1051
1052impl UnlinkedCompileOutputs<'_> {
1053    /// Flatten all our functions into a single list and remember each of their
1054    /// indices within it.
1055    fn pre_link(self) -> PreLinkOutput {
1056        // We must ensure that `compiled_funcs` contains the function bodies
1057        // sorted by their `FuncKey`, as `CompiledFunctionsTable` relies on that
1058        // property.
1059        //
1060        // Furthermore, note that, because the order functions end up in
1061        // `compiled_funcs` is the order they will ultimately be laid out inside
1062        // the object file, we will group all trampolines together, all defined
1063        // Wasm functions from the same module together, and etc... This is a
1064        // nice property, because it means that (a) cold functions, like builtin
1065        // trampolines, are not interspersed between hot Wasm functions, and (b)
1066        // Wasm functions that are likely to call each other (i.e. are in the
1067        // same module together) are grouped together.
1068        let mut compiled_funcs = vec![];
1069
1070        let mut indices = FunctionIndices::default();
1071        let mut needs_gc_heap = false;
1072
1073        // NB: Iteration over this `BTreeMap` ensures that we uphold
1074        // `compiled_func`'s sorted property.
1075        for output in self.outputs.into_values() {
1076            needs_gc_heap |= output.function.needs_gc_heap;
1077
1078            let index = compiled_funcs.len();
1079            compiled_funcs.push((output.symbol, output.key, output.function.code));
1080
1081            if output.start_srcloc != FilePos::none() {
1082                indices
1083                    .start_srclocs
1084                    .insert(output.key, output.start_srcloc);
1085            }
1086
1087            indices.indices.insert(output.key, index);
1088        }
1089
1090        PreLinkOutput {
1091            needs_gc_heap,
1092            compiled_funcs,
1093            indices,
1094        }
1095    }
1096}
1097
1098/// Our pre-link functions that have been flattened into a single list.
1099struct PreLinkOutput {
1100    /// Whether or not any of these functions require a GC heap
1101    needs_gc_heap: bool,
1102    /// The flattened list of (symbol name, FuncKey, compiled
1103    /// function) triples, as they will be laid out in the object
1104    /// file.
1105    compiled_funcs: Vec<(String, FuncKey, Box<dyn Any + Send + Sync>)>,
1106    /// The `FunctionIndices` mapping our function keys to indices in that flat
1107    /// list.
1108    indices: FunctionIndices,
1109}
1110
1111#[derive(Default)]
1112struct FunctionIndices {
1113    // A map of wasm functions and where they're located in the original file.
1114    start_srclocs: HashMap<FuncKey, FilePos>,
1115
1116    // The index of each compiled function in `compiled_funcs`.
1117    indices: BTreeMap<FuncKey, usize>,
1118}
1119
1120impl FunctionIndices {
1121    /// Link the compiled functions together, resolving relocations, and append
1122    /// them to the given ELF file.
1123    fn link_and_append_code<'a>(
1124        self,
1125        mut obj: object::write::Object<'static>,
1126        engine: &'a Engine,
1127        compiled_funcs: Vec<(String, FuncKey, Box<dyn Any + Send + Sync>)>,
1128        translations: PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
1129        dwarf_package_bytes: Option<&[u8]>,
1130    ) -> Result<(wasmtime_environ::ObjectBuilder<'a>, Artifacts)> {
1131        // Append all the functions to the ELF file.
1132        //
1133        // The result is a vector parallel to `compiled_funcs` where
1134        // `symbol_ids_and_locs[i]` is the symbol ID and function location of
1135        // `compiled_funcs[i]`.
1136        let compiler = engine.try_compiler()?;
1137        let tunables = engine.tunables();
1138        let symbol_ids_and_locs = compiler.append_code(
1139            &mut obj,
1140            &compiled_funcs,
1141            &|_caller_index: usize, callee: FuncKey| {
1142                self.indices.get(&callee).copied().unwrap_or_else(|| {
1143                    panic!("cannot resolve relocation! no index for callee {callee:?}")
1144                })
1145            },
1146        )?;
1147
1148        // If requested, generate and add DWARF information.
1149        if tunables.debug_native {
1150            compiler.append_dwarf(
1151                &mut obj,
1152                &translations,
1153                &|module, func| {
1154                    let i = self.indices[&FuncKey::DefinedWasmFunction(module, func)];
1155                    let (symbol, _) = symbol_ids_and_locs[i];
1156                    let (_, _, compiled_func) = &compiled_funcs[i];
1157                    (symbol, &**compiled_func)
1158                },
1159                dwarf_package_bytes,
1160                tunables,
1161            )?;
1162        }
1163
1164        let mut table_builder = CompiledFunctionsTableBuilder::new();
1165        for (key, compiled_func_index) in &self.indices {
1166            let (_, func_loc) = symbol_ids_and_locs[*compiled_func_index];
1167            let src_loc = self
1168                .start_srclocs
1169                .get(key)
1170                .copied()
1171                .unwrap_or_else(FilePos::none);
1172            table_builder.push_func(*key, func_loc, src_loc);
1173        }
1174
1175        let mut obj = wasmtime_environ::ObjectBuilder::new(obj, tunables);
1176        let modules = translations
1177            .into_iter()
1178            .map(|(_, translation)| obj.append(translation))
1179            .collect::<Result<PrimaryMap<_, _>>>()?;
1180
1181        let artifacts = Artifacts {
1182            modules,
1183            table: table_builder.finish(),
1184        };
1185
1186        Ok((obj, artifacts))
1187    }
1188}
1189
1190/// The artifacts necessary for finding and calling Wasm functions at runtime,
1191/// to be serialized into an ELF file.
1192struct Artifacts {
1193    modules: PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
1194    table: CompiledFunctionsTable,
1195}
1196
1197impl Artifacts {
1198    /// Assuming this compilation was for a single core Wasm module, get the
1199    /// resulting `CompiledModuleInfo`.
1200    fn unwrap_as_module_info(self) -> (CompiledModuleInfo, CompiledFunctionsTable) {
1201        assert_eq!(self.modules.len(), 1);
1202        let info = self.modules.into_iter().next().unwrap().1;
1203        let table = self.table;
1204        (info, table)
1205    }
1206}
1207
1208/// Extend `dest` with `items` and return the range of indices in `dest` where
1209/// they ended up.
1210fn extend_with_range<T>(dest: &mut Vec<T>, items: impl IntoIterator<Item = T>) -> Range<u32> {
1211    let start = dest.len();
1212    let start = u32::try_from(start).unwrap();
1213
1214    dest.extend(items);
1215
1216    let end = dest.len();
1217    let end = u32::try_from(end).unwrap();
1218
1219    start..end
1220}