1use 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
48pub(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 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 let mut object = compiler.object(ObjectKind::Module)?;
103 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#[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 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, )?;
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 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 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 translation: Option<&'a ModuleTranslation<'a>>,
273
274 func_body: Option<wasmparser::FunctionBody<'a>>,
276}
277
278struct 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#[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 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 #[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 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 const MAX_SYMBOL_LEN: usize = 96;
440
441 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 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 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 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 compile_required_builtins(engine, types, &mut raw_outputs)?;
657
658 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 let mut outputs = PrimaryMap::<OutputIndex, Option<CompileOutput<'_>>>::from(
675 engine.run_maybe_parallel(self.inputs, |f| f(compiler).map(Some))?,
676 );
677
678 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 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 func_keys.clear();
707 inlining_compiler.calls(&output.function, &mut func_keys)?;
708
709 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 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 debug_assert!(layer_outputs.is_empty());
745 layer_outputs.extend(layer.iter().map(|f| outputs[*f].take().unwrap()));
746
747 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 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 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 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 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 if caller_key.abi() == Abi::Array {
885 log::trace!(" --> not inlining: not inlining into array-abi caller");
886 return false;
887 }
888
889 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 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
946struct OutputIndex(u32);
947wasmtime_environ::entity_impl!(OutputIndex);
948
949fn is_inlining_function(key: FuncKey) -> bool {
954 match key {
955 FuncKey::DefinedWasmFunction(..) => true,
958
959 FuncKey::UnsafeIntrinsic(..) => true,
961
962 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
978fn 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 outputs: BTreeMap<FuncKey, CompileOutput<'a>>,
1050}
1051
1052impl UnlinkedCompileOutputs<'_> {
1053 fn pre_link(self) -> PreLinkOutput {
1056 let mut compiled_funcs = vec![];
1069
1070 let mut indices = FunctionIndices::default();
1071 let mut needs_gc_heap = false;
1072
1073 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
1098struct PreLinkOutput {
1100 needs_gc_heap: bool,
1102 compiled_funcs: Vec<(String, FuncKey, Box<dyn Any + Send + Sync>)>,
1106 indices: FunctionIndices,
1109}
1110
1111#[derive(Default)]
1112struct FunctionIndices {
1113 start_srclocs: HashMap<FuncKey, FilePos>,
1115
1116 indices: BTreeMap<FuncKey, usize>,
1118}
1119
1120impl FunctionIndices {
1121 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 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 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
1190struct Artifacts {
1193 modules: PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
1194 table: CompiledFunctionsTable,
1195}
1196
1197impl Artifacts {
1198 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
1208fn 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}