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, InliningCompiler, IntraModuleInlining, 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 let functions = mem::take(&mut translation.function_body_inputs);
89
90 let compile_inputs = CompileInputs::for_module(&types, &translation, functions);
91 let unlinked_compile_outputs = compile_inputs.compile(engine)?;
92 let PreLinkOutput {
93 needs_gc_heap,
94 compiled_funcs,
95 indices,
96 } = unlinked_compile_outputs.pre_link();
97 translation.module.needs_gc_heap |= needs_gc_heap;
98
99 let mut object = compiler.object(ObjectKind::Module)?;
102 engine.append_compiler_info(&mut object)?;
112 engine.append_bti(&mut object);
113
114 let (mut object, compilation_artifacts) = indices.link_and_append_code(
115 object,
116 engine,
117 compiled_funcs,
118 std::iter::once(translation).collect(),
119 dwarf_package,
120 )?;
121
122 if tunables.debug_guest {
123 object.append_wasm_bytecode(std::iter::once(wasm));
124 }
125
126 let (info, index) = compilation_artifacts.unwrap_as_module_info();
127 let types = types.finish();
128 object.serialize_info(&(&info, &index, &types));
129 let result = T::finish_object(object, obj_state)?;
130
131 Ok((result, Some((info, index, types))))
132}
133
134#[cfg(feature = "component-model")]
142pub(crate) fn build_component_artifacts<T: FinishedObject>(
143 engine: &Engine,
144 binary: &[u8],
145 _dwarf_package: Option<&[u8]>,
146 unsafe_intrinsics_import: Option<&str>,
147 obj_state: &T::State,
148) -> Result<(T, Option<wasmtime_environ::component::ComponentArtifacts>)> {
149 use wasmtime_environ::ScopeVec;
150 use wasmtime_environ::component::{
151 CompiledComponentInfo, ComponentArtifacts, ComponentTypesBuilder,
152 };
153
154 let compiler = engine.try_compiler()?;
155 let tunables = engine.tunables();
156
157 let scope = ScopeVec::new();
158 let mut validator = wasmparser::Validator::new_with_features(engine.features());
159 let mut types = ComponentTypesBuilder::new(&validator);
160 let mut translator = Translator::new(tunables, &mut validator, &mut types, &scope);
161 if let Some(name) = unsafe_intrinsics_import {
162 translator.expose_unsafe_intrinsics(name);
163 }
164 let (component, mut module_translations) = translator
165 .translate(binary)
166 .context("failed to parse WebAssembly module")?;
167
168 let compile_inputs = CompileInputs::for_component(
169 engine,
170 &types,
171 &component,
172 module_translations.iter_mut().map(|(i, translation)| {
173 let functions = mem::take(&mut translation.function_body_inputs);
174 (i, &*translation, functions)
175 }),
176 );
177 let unlinked_compile_outputs = compile_inputs.compile(&engine)?;
178
179 let PreLinkOutput {
180 needs_gc_heap,
181 compiled_funcs,
182 indices,
183 } = unlinked_compile_outputs.pre_link();
184 for (_, t) in &mut module_translations {
185 t.module.needs_gc_heap |= needs_gc_heap
186 }
187
188 let module_wasms = if tunables.debug_guest {
190 module_translations
191 .values()
192 .map(|t| t.wasm)
193 .collect::<Vec<_>>()
194 } else {
195 vec![]
196 };
197
198 let mut object = compiler.object(ObjectKind::Component)?;
199 engine.append_compiler_info(&mut object)?;
200 engine.append_bti(&mut object);
201
202 let (mut object, compilation_artifacts) = indices.link_and_append_code(
203 object,
204 engine,
205 compiled_funcs,
206 module_translations,
207 None, )?;
209
210 if tunables.debug_guest {
211 object.append_wasm_bytecode(module_wasms);
212 }
213
214 let (types, ty) = types.finish(&component.component);
215
216 let info = CompiledComponentInfo {
217 component: component.component,
218 };
219 let artifacts = ComponentArtifacts {
220 info,
221 table: compilation_artifacts.table,
222 ty,
223 types,
224 static_modules: compilation_artifacts.modules,
225 checksum: WasmChecksum::from_binary(binary, tunables.recording),
226 };
227 object.serialize_info(&artifacts);
228
229 let result = T::finish_object(object, obj_state)?;
230 Ok((result, Some(artifacts)))
231}
232
233type CompileInput<'a> = Box<dyn FnOnce(&dyn Compiler) -> Result<CompileOutput<'a>> + Send + 'a>;
234
235struct CompileOutput<'a> {
236 key: FuncKey,
237 symbol: String,
238 function: CompiledFunctionBody,
239 start_srcloc: FilePos,
240
241 translation: Option<&'a ModuleTranslation<'a>>,
243
244 func_body: Option<wasmparser::FunctionBody<'a>>,
246}
247
248struct InlineHeuristicParams<'a> {
250 tunables: &'a Tunables,
251 caller_size: u32,
252 caller_key: FuncKey,
253 caller_needs_gc_heap: bool,
254 callee_size: u32,
255 callee_key: FuncKey,
256 callee_needs_gc_heap: bool,
257}
258
259#[derive(Default)]
261struct CompileInputs<'a> {
262 inputs: Vec<CompileInput<'a>>,
263}
264
265impl<'a> CompileInputs<'a> {
266 fn push_input(
267 &mut self,
268 f: impl FnOnce(&dyn Compiler) -> Result<CompileOutput<'a>> + Send + 'a,
269 ) {
270 self.inputs.push(Box::new(f));
271 }
272
273 fn for_module(
275 types: &'a ModuleTypesBuilder,
276 translation: &'a ModuleTranslation<'a>,
277 functions: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
278 ) -> Self {
279 let mut ret = CompileInputs { inputs: vec![] };
280
281 let module_index = StaticModuleIndex::from_u32(0);
282 ret.collect_inputs_in_translations(types, [(module_index, translation, functions)]);
283
284 ret
285 }
286
287 #[cfg(feature = "component-model")]
289 fn for_component(
290 engine: &'a Engine,
291 types: &'a wasmtime_environ::component::ComponentTypesBuilder,
292 component: &'a wasmtime_environ::component::ComponentTranslation,
293 module_translations: impl IntoIterator<
294 Item = (
295 StaticModuleIndex,
296 &'a ModuleTranslation<'a>,
297 PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
298 ),
299 >,
300 ) -> Self {
301 use wasmtime_environ::Abi;
302 use wasmtime_environ::component::UnsafeIntrinsic;
303
304 let mut ret = CompileInputs { inputs: vec![] };
305
306 ret.collect_inputs_in_translations(types.module_types_builder(), module_translations);
307 let tunables = engine.tunables();
308
309 for i in component
310 .component
311 .unsafe_intrinsics
312 .iter()
313 .enumerate()
314 .filter_map(|(i, ty)| if ty.is_some() { Some(i) } else { None })
315 {
316 let i = u32::try_from(i).unwrap();
317 let intrinsic = UnsafeIntrinsic::from_u32(i);
318 for abi in [Abi::Wasm, Abi::Array] {
319 ret.push_input(move |compiler| {
320 let symbol = format!(
321 "unsafe-intrinsics-{}-{}",
322 match abi {
323 Abi::Wasm => "wasm-call",
324 Abi::Array => "array-call",
325 Abi::Patchable => "patchable-call",
326 },
327 intrinsic.name(),
328 );
329 Ok(CompileOutput {
330 key: FuncKey::UnsafeIntrinsic(abi, intrinsic),
331 function: compiler
332 .component_compiler()
333 .compile_intrinsic(tunables, component, types, intrinsic, abi, &symbol)
334 .with_context(|| format!("failed to compile `{symbol}`"))?,
335 symbol,
336 start_srcloc: FilePos::default(),
337 translation: None,
338 func_body: None,
339 })
340 });
341 }
342 }
343
344 for (idx, trampoline) in component.trampolines.iter() {
345 for abi in [Abi::Wasm, Abi::Array] {
346 ret.push_input(move |compiler| {
347 let key = FuncKey::ComponentTrampoline(abi, idx);
348 let symbol = format!(
349 "component-trampolines[{}]-{}-{}",
350 idx.as_u32(),
351 match abi {
352 Abi::Wasm => "wasm-call",
353 Abi::Array => "array-call",
354 Abi::Patchable => "patchable-call",
355 },
356 trampoline.symbol_name(),
357 );
358 let function = compiler
359 .component_compiler()
360 .compile_trampoline(component, types, key, abi, tunables, &symbol)
361 .with_context(|| format!("failed to compile {symbol}"))?;
362 Ok(CompileOutput {
363 key,
364 function,
365 symbol,
366 start_srcloc: FilePos::default(),
367 translation: None,
368 func_body: None,
369 })
370 });
371 }
372 }
373
374 if component.component.num_resources > 0 {
381 if let Some(sig) = types.find_resource_drop_signature() {
382 ret.push_input(move |compiler| {
383 let key = FuncKey::ResourceDropTrampoline;
384 let symbol = "resource_drop_trampoline".to_string();
385 let function = compiler
386 .compile_wasm_to_array_trampoline(types[sig].unwrap_func(), key, &symbol)
387 .with_context(|| format!("failed to compile `{symbol}`"))?;
388 Ok(CompileOutput {
389 key,
390 function,
391 symbol,
392 start_srcloc: FilePos::default(),
393 translation: None,
394 func_body: None,
395 })
396 });
397 }
398 }
399
400 ret
401 }
402
403 fn clean_symbol(name: &str) -> Cow<'_, str> {
404 const MAX_SYMBOL_LEN: usize = 96;
406
407 let bad_char = |c: char| !c.is_ascii_graphic();
413 if name.chars().any(bad_char) {
414 let mut last_char_seen = '\u{0000}';
415 Cow::Owned(
416 name.chars()
417 .map(|c| if bad_char(c) { '?' } else { c })
418 .filter(|c| {
419 let skip = last_char_seen == '?' && *c == '?';
420 last_char_seen = *c;
421 !skip
422 })
423 .take(MAX_SYMBOL_LEN)
424 .collect::<String>(),
425 )
426 } else if name.len() <= MAX_SYMBOL_LEN {
427 Cow::Borrowed(&name[..])
428 } else {
429 Cow::Borrowed(&name[..MAX_SYMBOL_LEN])
430 }
431 }
432
433 fn collect_inputs_in_translations(
434 &mut self,
435 types: &'a ModuleTypesBuilder,
436 translations: impl IntoIterator<
437 Item = (
438 StaticModuleIndex,
439 &'a ModuleTranslation<'a>,
440 PrimaryMap<DefinedFuncIndex, FunctionBodyData<'a>>,
441 ),
442 >,
443 ) {
444 for (module, translation, functions) in translations {
445 for (def_func_index, func_body_data) in functions {
446 self.push_input(move |compiler| {
447 let key = FuncKey::DefinedWasmFunction(module, def_func_index);
448 let func_index = translation.module.func_index(def_func_index);
449 let symbol = match translation
450 .debuginfo
451 .name_section
452 .func_names
453 .get(&func_index)
454 {
455 Some(name) => format!(
456 "wasm[{}]::function[{}]::{}",
457 module.as_u32(),
458 func_index.as_u32(),
459 Self::clean_symbol(&name)
460 ),
461 None => format!(
462 "wasm[{}]::function[{}]",
463 module.as_u32(),
464 func_index.as_u32()
465 ),
466 };
467 let func_body = func_body_data.body.clone();
468 let data = func_body.get_binary_reader();
469 let offset = data.original_position();
470 let start_srcloc = FilePos::new(u32::try_from(offset).unwrap());
471 let function = compiler
472 .compile_function(translation, key, func_body_data, types, &symbol)
473 .with_context(|| format!("failed to compile: {symbol}"))?;
474
475 Ok(CompileOutput {
476 key,
477 symbol,
478 function,
479 start_srcloc,
480 translation: Some(translation),
481 func_body: Some(func_body),
482 })
483 });
484
485 let func_index = translation.module.func_index(def_func_index);
486 if translation.module.functions[func_index].is_escaping() {
487 self.push_input(move |compiler| {
488 let key = FuncKey::ArrayToWasmTrampoline(module, def_func_index);
489 let func_index = translation.module.func_index(def_func_index);
490 let symbol = format!(
491 "wasm[{}]::array_to_wasm_trampoline[{}]",
492 module.as_u32(),
493 func_index.as_u32()
494 );
495 let function = compiler
496 .compile_array_to_wasm_trampoline(translation, types, key, &symbol)
497 .with_context(|| format!("failed to compile: {symbol}"))?;
498 Ok(CompileOutput {
499 key,
500 symbol,
501 function,
502 start_srcloc: FilePos::default(),
503 translation: None,
504 func_body: None,
505 })
506 });
507 }
508 }
509 }
510
511 let mut trampoline_types_seen = HashSet::new();
512 for (_func_type_index, trampoline_type_index) in types.trampoline_types() {
513 let is_new = trampoline_types_seen.insert(trampoline_type_index);
514 if !is_new {
515 continue;
516 }
517 let trampoline_func_ty = types[trampoline_type_index].unwrap_func();
518 self.push_input(move |compiler| {
519 let key = FuncKey::WasmToArrayTrampoline(trampoline_type_index);
520 let symbol = format!(
521 "signatures[{}]::wasm_to_array_trampoline",
522 trampoline_type_index.as_u32()
523 );
524 let function = compiler
525 .compile_wasm_to_array_trampoline(trampoline_func_ty, key, &symbol)
526 .with_context(|| format!("failed to compile: {symbol}"))?;
527 Ok(CompileOutput {
528 key,
529 function,
530 symbol,
531 start_srcloc: FilePos::default(),
532 translation: None,
533 func_body: None,
534 })
535 });
536 }
537 }
538
539 fn compile(self, engine: &Engine) -> Result<UnlinkedCompileOutputs<'a>> {
542 let compiler = engine.try_compiler()?;
543
544 if self.inputs.len() > 0 && cfg!(miri) {
545 bail!(
546 "\
547You are attempting to compile a WebAssembly module or component that contains
548functions in Miri. Running Cranelift through Miri is known to take quite a long
549time and isn't what we want in CI at least. If this is a mistake then you should
550ignore this test in Miri with:
551
552 #[cfg_attr(miri, ignore)]
553
554If this is not a mistake then try to edit the `pulley_provenance_test` test
555which runs Cranelift outside of Miri. If you still feel this is a mistake then
556please open an issue or a topic on Zulip to talk about how best to accommodate
557the use case.
558"
559 );
560 }
561
562 let mut raw_outputs = if let Some(inlining_compiler) = compiler.inlining_compiler() {
563 if engine.tunables().inlining {
564 self.compile_with_inlining(engine, compiler, inlining_compiler)?
565 } else {
566 engine.run_maybe_parallel::<_, _, Error, _>(self.inputs, |f| {
570 let mut compiled = f(compiler)?;
571 inlining_compiler.finish_compiling(
572 &mut compiled.function,
573 compiled.func_body.take(),
574 &compiled.symbol,
575 )?;
576 Ok(compiled)
577 })?
578 }
579 } else {
580 engine.run_maybe_parallel(self.inputs, |f| f(compiler))?
582 };
583
584 if cfg!(debug_assertions) {
585 let mut symbols: Vec<_> = raw_outputs.iter().map(|i| &i.symbol).collect();
586 symbols.sort();
587 for w in symbols.windows(2) {
588 assert_ne!(
589 w[0], w[1],
590 "should never have duplicate symbols, but found two functions with the symbol `{}`",
591 w[0]
592 );
593 }
594 }
595
596 compile_required_builtins(engine, &mut raw_outputs)?;
601
602 let mut outputs: BTreeMap<FuncKey, CompileOutput> = BTreeMap::new();
604 for output in raw_outputs {
605 outputs.insert(output.key, output);
606 }
607
608 Ok(UnlinkedCompileOutputs { outputs })
609 }
610
611 fn compile_with_inlining(
612 self,
613 engine: &Engine,
614 compiler: &dyn Compiler,
615 inlining_compiler: &dyn InliningCompiler,
616 ) -> Result<Vec<CompileOutput<'a>>, Error> {
617 let mut outputs = PrimaryMap::<OutputIndex, Option<CompileOutput<'_>>>::from(
619 engine.run_maybe_parallel(self.inputs, |f| f(compiler).map(Some))?,
620 );
621
622 let key_to_output: HashMap<FuncKey, OutputIndex> = inlining_functions(&outputs)
630 .map(|output_index| {
631 let output = outputs[output_index].as_ref().unwrap();
632 (output.key, output_index)
633 })
634 .collect();
635
636 let call_graph = EntityGraph::<OutputIndex>::new(inlining_functions(&outputs), {
642 let mut func_keys = IndexSet::default();
643 let outputs = &outputs;
644 let key_to_output = &key_to_output;
645 move |output_index, calls| {
646 let output = outputs[output_index].as_ref().unwrap();
647 debug_assert!(is_inlining_function(output.key));
648
649 func_keys.clear();
651 inlining_compiler.calls(&output.function, &mut func_keys)?;
652
653 debug_assert!(calls.is_empty());
656 calls.extend(
657 func_keys
658 .iter()
659 .copied()
660 .filter_map(|key| key_to_output.get(&key).copied()),
661 );
662
663 log::trace!(
664 "call graph edges for {output_index:?} = {:?}: {calls:?}",
665 output.key
666 );
667
668 crate::error::Ok(())
669 }
670 })?;
671
672 let strata = stratify::Strata::<OutputIndex>::new(&call_graph.filter_nodes(|f| {
678 let key = outputs[*f].as_ref().unwrap().key;
679 is_inlining_function(key)
680 }));
681 let mut layer_outputs = vec![];
682 for layer in strata.layers() {
683 debug_assert!(layer_outputs.is_empty());
689 layer_outputs.extend(layer.iter().map(|f| outputs[*f].take().unwrap()));
690
691 engine.run_maybe_parallel_mut(
693 &mut layer_outputs,
694 |output: &mut CompileOutput<'_>| {
695 log::trace!("processing inlining for {:?}", output.key);
696 debug_assert!(is_inlining_function(output.key));
697
698 let caller_key = output.key;
699 let caller_needs_gc_heap =
700 output.translation.is_some_and(|t| t.module.needs_gc_heap);
701 let caller = &mut output.function;
702
703 let mut caller_size = inlining_compiler.size(caller);
704
705 inlining_compiler.inline(caller, &mut |callee_key: FuncKey| {
706 log::trace!(" --> considering call to {callee_key:?}");
707 let callee_output_index: OutputIndex = key_to_output[&callee_key];
708
709 let callee_output = outputs[callee_output_index].as_ref()?;
716
717 debug_assert_eq!(callee_output.key, callee_key);
718
719 let callee = &callee_output.function;
720 let callee_size = inlining_compiler.size(callee);
721
722 let callee_needs_gc_heap = callee_output
723 .translation
724 .is_some_and(|t| t.module.needs_gc_heap);
725
726 if Self::should_inline(InlineHeuristicParams {
727 tunables: engine.tunables(),
728 caller_size,
729 caller_key,
730 caller_needs_gc_heap,
731 callee_size,
732 callee_key,
733 callee_needs_gc_heap,
734 }) {
735 caller_size = caller_size.saturating_add(callee_size);
736 Some(callee)
737 } else {
738 None
739 }
740 })
741 },
742 )?;
743
744 for (f, func) in layer.iter().zip(layer_outputs.drain(..)) {
745 debug_assert!(outputs[*f].is_none());
746 outputs[*f] = Some(func);
747 }
748 }
749
750 engine.run_maybe_parallel(outputs.into(), |output| {
752 let mut output = output.unwrap();
753 inlining_compiler.finish_compiling(
754 &mut output.function,
755 output.func_body.take(),
756 &output.symbol,
757 )?;
758 Ok(output)
759 })
760 }
761
762 fn should_inline(
785 InlineHeuristicParams {
786 tunables,
787 caller_size,
788 caller_key,
789 caller_needs_gc_heap,
790 callee_size,
791 callee_key,
792 callee_needs_gc_heap,
793 }: InlineHeuristicParams,
794 ) -> bool {
795 log::trace!(
796 "considering inlining:\n\
797 \tcaller = {caller_key:?}\n\
798 \t\tsize = {caller_size}\n\
799 \t\tneeds_gc_heap = {caller_needs_gc_heap}\n\
800 \tcallee = {callee_key:?}\n\
801 \t\tsize = {callee_size}\n\
802 \t\tneeds_gc_heap = {callee_needs_gc_heap}"
803 );
804
805 debug_assert!(
806 tunables.inlining,
807 "shouldn't even call this method if we aren't configured for inlining"
808 );
809 debug_assert_ne!(caller_key, callee_key, "we never inline recursion");
810
811 let sum_size = caller_size.saturating_add(callee_size);
814 if sum_size > tunables.inlining_sum_size_threshold {
815 log::trace!(
816 " --> not inlining: the sum of the caller's and callee's sizes is greater than \
817 the inlining-sum-size threshold: {callee_size} + {caller_size} > {}",
818 tunables.inlining_sum_size_threshold
819 );
820 return false;
821 }
822
823 if caller_key.abi() == Abi::Array {
829 log::trace!(" --> not inlining: not inlining into array-abi caller");
830 return false;
831 }
832
833 match (caller_key, callee_key) {
841 (
842 FuncKey::DefinedWasmFunction(caller_module, _),
843 FuncKey::DefinedWasmFunction(callee_module, _),
844 ) => {
845 if caller_module == callee_module {
846 match tunables.inlining_intra_module {
847 IntraModuleInlining::Yes => {}
848
849 IntraModuleInlining::WhenUsingGc
850 if caller_needs_gc_heap || callee_needs_gc_heap => {}
851
852 IntraModuleInlining::WhenUsingGc => {
853 log::trace!(
854 " --> not inlining: intra-module call that does not use GC"
855 );
856 return false;
857 }
858
859 IntraModuleInlining::No => {
860 log::trace!(" --> not inlining: intra-module call");
861 return false;
862 }
863 }
864 }
865 }
866
867 _ => {}
868 }
869
870 if callee_size <= tunables.inlining_small_callee_size {
873 log::trace!(
874 " --> inlining: callee's size is less than the small-callee size: \
875 {callee_size} <= {}",
876 tunables.inlining_small_callee_size
877 );
878 return true;
879 }
880
881 log::trace!(" --> inlining: did not find a reason we should not");
882 true
883 }
884}
885
886#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
889struct OutputIndex(u32);
890wasmtime_environ::entity_impl!(OutputIndex);
891
892fn is_inlining_function(key: FuncKey) -> bool {
897 match key {
898 FuncKey::DefinedWasmFunction(..) => true,
901
902 #[cfg(feature = "component-model")]
904 FuncKey::UnsafeIntrinsic(..) => true,
905
906 FuncKey::ArrayToWasmTrampoline(..)
910 | FuncKey::WasmToArrayTrampoline(..)
911 | FuncKey::WasmToBuiltinTrampoline(..)
912 | FuncKey::PatchableToBuiltinTrampoline(..) => false,
913 #[cfg(feature = "component-model")]
914 FuncKey::ComponentTrampoline(..) | FuncKey::ResourceDropTrampoline => false,
915
916 FuncKey::PulleyHostCall(_) => {
917 unreachable!("we don't compile artifacts for Pulley host calls")
918 }
919 }
920}
921
922fn inlining_functions<'a>(
925 outputs: &'a PrimaryMap<OutputIndex, Option<CompileOutput<'_>>>,
926) -> impl Iterator<Item = OutputIndex> + 'a {
927 outputs.iter().filter_map(|(index, output)| {
928 if is_inlining_function(output.as_ref().unwrap().key) {
929 Some(index)
930 } else {
931 None
932 }
933 })
934}
935
936fn compile_required_builtins(engine: &Engine, raw_outputs: &mut Vec<CompileOutput>) -> Result<()> {
937 let compiler = engine.try_compiler()?;
938 let mut builtins = HashSet::new();
939 let mut new_inputs: Vec<CompileInput<'_>> = Vec::new();
940
941 let compile_builtin = |key: FuncKey| {
942 Box::new(move |compiler: &dyn Compiler| {
943 let symbol = match key {
944 FuncKey::WasmToBuiltinTrampoline(builtin) => {
945 format!("wasmtime_builtin_{}", builtin.name())
946 }
947 FuncKey::PatchableToBuiltinTrampoline(builtin) => {
948 format!("wasmtime_patchable_builtin_{}", builtin.name())
949 }
950 _ => unreachable!(),
951 };
952 let mut function = compiler
953 .compile_wasm_to_builtin(key, &symbol)
954 .with_context(|| format!("failed to compile `{symbol}`"))?;
955 if let Some(compiler) = compiler.inlining_compiler() {
956 compiler.finish_compiling(&mut function, None, &symbol)?;
957 }
958 Ok(CompileOutput {
959 key,
960 function,
961 symbol,
962 start_srcloc: FilePos::default(),
963 translation: None,
964 func_body: None,
965 })
966 })
967 };
968
969 for output in raw_outputs.iter() {
970 for reloc in compiler.compiled_function_relocation_targets(&*output.function.code) {
971 match reloc {
972 FuncKey::WasmToBuiltinTrampoline(builtin)
973 | FuncKey::PatchableToBuiltinTrampoline(builtin) => {
974 if builtins.insert(builtin) {
975 new_inputs.push(compile_builtin(reloc));
976 }
977 }
978 _ => {}
979 }
980 }
981 }
982 raw_outputs.extend(engine.run_maybe_parallel(new_inputs, |c| c(compiler))?);
983 Ok(())
984}
985
986#[derive(Default)]
987struct UnlinkedCompileOutputs<'a> {
988 outputs: BTreeMap<FuncKey, CompileOutput<'a>>,
990}
991
992impl UnlinkedCompileOutputs<'_> {
993 fn pre_link(self) -> PreLinkOutput {
996 let mut compiled_funcs = vec![];
1009
1010 let mut indices = FunctionIndices::default();
1011 let mut needs_gc_heap = false;
1012
1013 for output in self.outputs.into_values() {
1016 needs_gc_heap |= output.function.needs_gc_heap;
1017
1018 let index = compiled_funcs.len();
1019 compiled_funcs.push((output.symbol, output.key, output.function.code));
1020
1021 if output.start_srcloc != FilePos::none() {
1022 indices
1023 .start_srclocs
1024 .insert(output.key, output.start_srcloc);
1025 }
1026
1027 indices.indices.insert(output.key, index);
1028 }
1029
1030 PreLinkOutput {
1031 needs_gc_heap,
1032 compiled_funcs,
1033 indices,
1034 }
1035 }
1036}
1037
1038struct PreLinkOutput {
1040 needs_gc_heap: bool,
1042 compiled_funcs: Vec<(String, FuncKey, Box<dyn Any + Send + Sync>)>,
1046 indices: FunctionIndices,
1049}
1050
1051#[derive(Default)]
1052struct FunctionIndices {
1053 start_srclocs: HashMap<FuncKey, FilePos>,
1055
1056 indices: BTreeMap<FuncKey, usize>,
1058}
1059
1060impl FunctionIndices {
1061 fn link_and_append_code<'a>(
1064 self,
1065 mut obj: object::write::Object<'static>,
1066 engine: &'a Engine,
1067 compiled_funcs: Vec<(String, FuncKey, Box<dyn Any + Send + Sync>)>,
1068 translations: PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
1069 dwarf_package_bytes: Option<&[u8]>,
1070 ) -> Result<(wasmtime_environ::ObjectBuilder<'a>, Artifacts)> {
1071 let compiler = engine.try_compiler()?;
1077 let tunables = engine.tunables();
1078 let symbol_ids_and_locs = compiler.append_code(
1079 &mut obj,
1080 &compiled_funcs,
1081 &|_caller_index: usize, callee: FuncKey| {
1082 self.indices.get(&callee).copied().unwrap_or_else(|| {
1083 panic!("cannot resolve relocation! no index for callee {callee:?}")
1084 })
1085 },
1086 )?;
1087
1088 if tunables.debug_native {
1090 compiler.append_dwarf(
1091 &mut obj,
1092 &translations,
1093 &|module, func| {
1094 let i = self.indices[&FuncKey::DefinedWasmFunction(module, func)];
1095 let (symbol, _) = symbol_ids_and_locs[i];
1096 let (_, _, compiled_func) = &compiled_funcs[i];
1097 (symbol, &**compiled_func)
1098 },
1099 dwarf_package_bytes,
1100 tunables,
1101 )?;
1102 }
1103
1104 let mut table_builder = CompiledFunctionsTableBuilder::new();
1105 for (key, compiled_func_index) in &self.indices {
1106 let (_, func_loc) = symbol_ids_and_locs[*compiled_func_index];
1107 let src_loc = self
1108 .start_srclocs
1109 .get(key)
1110 .copied()
1111 .unwrap_or_else(FilePos::none);
1112 table_builder.push_func(*key, func_loc, src_loc);
1113 }
1114
1115 let mut obj = wasmtime_environ::ObjectBuilder::new(obj, tunables);
1116 let modules = translations
1117 .into_iter()
1118 .map(|(_, mut translation)| {
1119 if engine.tunables().memory_init_cow {
1125 let align = compiler.page_size_align();
1126 let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
1127 translation.try_static_init(align, max_always_allowed);
1128 }
1129
1130 if engine.tunables().table_lazy_init {
1133 translation.try_func_table_init();
1134 }
1135
1136 obj.append(translation)
1137 })
1138 .collect::<Result<PrimaryMap<_, _>>>()?;
1139
1140 let artifacts = Artifacts {
1141 modules,
1142 table: table_builder.finish(),
1143 };
1144
1145 Ok((obj, artifacts))
1146 }
1147}
1148
1149struct Artifacts {
1152 modules: PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
1153 table: CompiledFunctionsTable,
1154}
1155
1156impl Artifacts {
1157 fn unwrap_as_module_info(self) -> (CompiledModuleInfo, CompiledFunctionsTable) {
1160 assert_eq!(self.modules.len(), 1);
1161 let info = self.modules.into_iter().next().unwrap().1;
1162 let table = self.table;
1163 (info, table)
1164 }
1165}
1166
1167fn extend_with_range<T>(dest: &mut Vec<T>, items: impl IntoIterator<Item = T>) -> Range<u32> {
1170 let start = dest.len();
1171 let start = u32::try_from(start).unwrap();
1172
1173 dest.extend(items);
1174
1175 let end = dest.len();
1176 let end = u32::try_from(end).unwrap();
1177
1178 start..end
1179}