1use std::{
2 borrow::{self, Cow},
3 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
4 ops::Range,
5};
6
7use anyhow::{Context, Result, anyhow, bail};
8use index_safety::OutputFuncId;
9pub use memory_layout::{DataChunk, DataSegmentOutput, SegmentLayout, SymbolRelation};
10use modify::{ModifyContext, StoreType, init_each_store_var};
11use wamex_types::{BumpVersion, dylink0::Dylink0Section, map_vec::MiniSet};
12use wasm_encoder::{GlobalType, reencode::Reencode};
13use wasmparser::{RelocationEntry, TypeRef};
14
15use crate::{
16 analysis::{
17 self,
18 split_point::{
19 ModuleIdentifier, SharedModuleIdentifier, SplitModuleIdentifier, SplitPoint,
20 SplitProgramInfo,
21 },
22 symbols::SymbolKind,
23 },
24 emit::{
25 globals::{DefinedGlobal, GlobalImport},
26 index_safety::OutputGlobalId,
27 modify::{RelocateState, StartFnGen},
28 },
29 helpers::encoding_size,
30 index::{
31 AnySymbolId, DataSegmentId, FuncTypeId, Id, IdMap, IdVec, ImportsOrDefined, Indexed,
32 InputFuncId, InputGlobalId, MemoryId, SymbolId, WithOriginalIndex,
33 },
34};
35
36mod globals;
37mod memory_layout;
38
39mod index_safety;
40mod modify;
41mod names;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum LinkageType {
45 OriginalLayout,
52
53 DynamicLinking {
60 table_offset: u32,
62 table_num_entrypoints: u32,
64 },
65}
66
67trait ImportedEntity {
68 fn import_name(&self) -> Cow<'_, str>;
69 fn module_name(&self) -> Cow<'_, str>;
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73enum DefinedFunctionKind {
74 Copied {
75 modification_list: Vec<modify::CodeModifyEntry>,
77 },
78 IndirectTrampoline {
79 table_index_offset: u32,
81 },
82 Trampoline {},
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct DefinedFunction {
88 export: bool,
89 input_func_id: InputFuncId,
90 kind: DefinedFunctionKind,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
94pub struct ImportedFunction<'a> {
95 input_func_id: InputFuncId,
96 kind: ImportFunctionKind<'a>,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
100enum ImportFunctionKind<'a> {
101 Existing {
103 module_name: &'a str,
104 import_function_name: &'a str,
105 },
106 New {
108 link_module: usize,
109 output_function_index: usize,
110 mangled_function_name: &'a str,
111 },
112}
113impl ImportedEntity for ImportedFunction<'_> {
114 fn import_name(&self) -> Cow<'_, str> {
115 match self.kind {
116 ImportFunctionKind::Existing {
117 import_function_name,
118 ..
119 } => import_function_name.into(),
120 ImportFunctionKind::New {
121 mangled_function_name,
122 ..
123 } => format!("__wamex_{}", mangled_function_name).into(),
124 }
125 }
126
127 fn module_name(&self) -> Cow<'_, str> {
128 match self.kind {
129 ImportFunctionKind::Existing { module_name, .. } => module_name.into(),
130 ImportFunctionKind::New { .. } => {
131 "__wamex".into()
132 }
134 }
135 }
136}
137
138impl ImportedFunction<'_> {
139 pub fn input_func_id(&self) -> InputFuncId {
140 self.input_func_id
141 }
142}
143
144impl Ord for DefinedFunction {
146 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
147 let tag = match self.kind {
148 DefinedFunctionKind::Copied { .. } => 0,
149 DefinedFunctionKind::IndirectTrampoline { .. } => 1,
150 DefinedFunctionKind::Trampoline { .. } => 2,
151 };
152 let other_tag = match other.kind {
153 DefinedFunctionKind::Copied { .. } => 0,
154 DefinedFunctionKind::IndirectTrampoline { .. } => 1,
155 DefinedFunctionKind::Trampoline { .. } => 2,
156 };
157
158 match (tag, self.input_func_id).cmp(&(other_tag, other.input_func_id)) {
159 std::cmp::Ordering::Equal => self.export.cmp(&other.export),
160 ord => ord,
161 }
162 }
163}
164impl PartialOrd for DefinedFunction {
165 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
166 Some(self.cmp(other))
167 }
168}
169
170pub(crate) struct GotBase {
171 lib_base_id: OutputGlobalId,
172 table_base_id: OutputGlobalId,
173}
174
175struct SubModuleExtra {
176 self_base: GotBase,
177 entrypoints: Vec<InputFuncId>,
178 extern_modules: Vec<(SharedModuleIdentifier, GotBase)>,
179 export_got_with_id: Option<SharedModuleIdentifier>,
180}
181impl SubModuleExtra {
182 const MAIN_GLOBAL_EXPORTS: &[&str] = &["__stack_pointer"]; #[allow(dead_code)]
184 const MAIN_GLOBAL_EXPORTS_COUNT: u32 = Self::MAIN_GLOBAL_EXPORTS.len() as u32;
185}
186
187pub struct ModuleEmitState<'any, 'src> {
189 functions: WithOriginalIndex<'src, DefinedFunction>,
190
191 globals: WithOriginalIndex<'src, DefinedGlobal<'src>>,
197 pub global_tmp_store: BTreeMap<StoreType, OutputGlobalId>,
198 sub_module_extra: Option<SubModuleExtra>,
201
202 data: IdMap<DataSegmentId, memory_layout::DataSegmentOutput>,
204 data_relocations: IdMap<DataSegmentId, Vec<modify::DataModifyEntry>>,
206
207 pub src: &'any analysis::ModuleInfo<'src>,
209 pub indirect_functions: IndirectFunctionEmitInfo,
211 linkage_type: LinkageType,
212 pub linked_modules: Vec<SharedModuleIdentifier>,
213 pub incremental_version: BumpVersion,
214}
215
216const MEMORY_INDEX: u32 = 0; impl<'any, 'src> ModuleEmitState<'any, 'src> {
218 pub fn produce_state(
219 module_info: &'any analysis::ModuleInfo<'src>,
220 verbose: bool,
221 emit_info: &'any CommonEmitInfo,
222 (module_id, output_module_info): &(
223 SplitModuleIdentifier,
224 analysis::split_point::OutputModuleInfo,
225 ),
226 static_main: Option<&Self>,
229 shared_modules: &[SharedModuleIdentifier],
231 linkage_type: LinkageType,
232 is_nonexportable: impl Fn(SymbolId) -> bool,
233 version: BumpVersion,
234 ) -> ModuleEmitState<'any, 'src> {
235 log::debug!("output_module_info: {output_module_info:#?}");
236 log::debug!("shared_modules: {shared_modules:#?}");
238 let mut funcs_to_define = BTreeSet::new();
240 let mut import_functions = Vec::new();
241
242 let mut indirect_funcs_stubs = Vec::new();
243 let mut import_funcs_stubs = Vec::new();
244
245 let main_module = static_main.is_none();
246
247 let mut used_funcs = BTreeSet::new();
248 for (sym, func_id) in output_module_info.defined_symbols.iter().filter_map(|s| {
249 module_info
250 .symbols
251 .as_input_function(*s)
252 .map(|func_id| (*s, func_id))
253 }) {
254 if used_funcs.contains(&func_id) {
255 continue;
256 }
257 used_funcs.insert(func_id);
258
259 let need_export = {
260 let static_export = output_module_info.exports.contains(&sym);
262 let lazy_export = output_module_info
264 .split_points
265 .iter()
266 .any(|split_point| split_point.export_func() == func_id);
267 static_export || lazy_export
268 };
269
270 if emit_info.is_external_entrypoint(&func_id) {
271 indirect_funcs_stubs.push((func_id, need_export));
272 } else if let Some(import_id) = module_info.get_function_import_id(func_id) {
273 let import_fn = module_info.wasm.imports[import_id];
274
275 import_functions.push(ImportedFunction {
276 input_func_id: func_id,
277 kind: ImportFunctionKind::Existing {
278 module_name: import_fn.module,
279 import_function_name: import_fn.name,
280 },
281 });
282
283 if need_export {
284 import_funcs_stubs.push(func_id);
285 }
286 } else {
287 funcs_to_define.insert((func_id, need_export, sym));
288 }
289 }
290
291 if !main_module {
292 import_functions.extend(
294 output_module_info
295 .imports
296 .iter()
297 .inspect(|symbol| {
298 debug_assert!(!output_module_info.defined_symbols.contains(*symbol))
299 })
300 .filter_map(|s| module_info.symbols.as_input_function(*s))
301 .map(|func_id| ImportedFunction {
302 input_func_id: func_id,
303 kind: ImportFunctionKind::New {
304 link_module: 0,
306 output_function_index: 0,
307 mangled_function_name: module_info
308 .wasm
309 .names
310 .functions
311 .get(func_id)
312 .expect("Function name should be defined"),
313 },
314 }),
315 );
316 }
317
318 let imported_globals = if main_module {
319 module_info
320 .wasm
321 .imports
322 .iter()
323 .filter_map(|(_id, import)| {
324 if let TypeRef::Global(global_type) = &import.ty {
325 Some((
326 wasm_encoder::reencode::RoundtripReencoder
327 .global_type(*global_type)
328 .expect("failed to reencode global type"),
329 import,
330 ))
331 } else {
332 None
333 }
334 })
335 .enumerate()
336 .map(|(i, (ty, import))| GlobalImport::Existing {
337 global_name: import.name,
338 module_name: import.module,
339 input_global_id: Id::from_index(i),
340 global_type: ty,
341 })
342 .collect::<Vec<_>>()
343 } else {
344 SubModuleExtra::MAIN_GLOBAL_EXPORTS
345 .iter()
346 .map(|&name| {
347 let input_global_id =
348 module_info.find_global_id_by_name(name).unwrap_or_else(|| {
349 panic!(
350 "Globals {:?} should be defined in main module, {} is missing",
351 SubModuleExtra::MAIN_GLOBAL_EXPORTS,
352 name
353 )
354 });
355 GlobalImport::New {
356 input_global_id: Some(input_global_id),
357 global_name: Cow::Borrowed(name),
358 global_type: GlobalType {
359 val_type: wasm_encoder::ValType::I32,
360 mutable: true,
362 shared: false,
363 },
364 }
365 })
366 .collect::<Vec<_>>()
367 };
368
369 let defined_globals: Vec<_> = if main_module {
370 module_info
371 .wasm
372 .globals
373 .iter()
374 .map(|(id, global)| DefinedGlobal::PlainCopy {
375 global: global.clone(),
376 input_global_id: id,
377 })
378 .collect()
379 } else {
380 Vec::new()
381 };
382 let mut globals = ImportsOrDefined::new(imported_globals, defined_globals);
383
384 let lib_base_import = (!main_module).then(|| {
385 globals.push_import(GlobalImport::New {
386 input_global_id: None,
387 global_name: Cow::Borrowed("__lib_base"),
388 global_type: GlobalType {
389 val_type: wasm_encoder::ValType::I32,
390 mutable: false,
391 shared: false,
392 },
393 })
394 });
395
396 let mut data_to_define = BTreeMap::new();
397 for symbol_id in output_module_info.defined_symbols.iter() {
398 let symbol = module_info.symbols.get(*symbol_id).unwrap();
399 let SymbolKind::DataDefined { segment_id, .. } = symbol.kind else {
400 continue;
401 };
402 data_to_define
403 .entry(segment_id)
404 .or_insert_with(BTreeSet::new)
405 .insert(*symbol_id);
406 }
407
408 let data_segments = emit_info
410 .src_data_segments
411 .iter()
412 .map(|(data_segment_id, data)| {
413 let empty = BTreeSet::new();
414 let entries = data_to_define.get(&data_segment_id).unwrap_or(&empty);
415 let data_segment = data.clone();
416 data_segment.new_with_whitelist(entries)
417 })
418 .collect::<IdVec<_>>();
419 if verbose {
420 SegmentLayout::debug_layout(
421 &module_info.symbols,
422 module_id.to_string(),
423 &data_segments,
424 );
425 }
426
427 let mut data_segment_outputs = IdMap::new();
428
429 let mem_start = if main_module {
430 let first_segment = data_segments
431 .iter()
432 .next()
433 .expect("There should be at least one data segment")
434 .1;
435 first_segment.memory_offset()
436 } else {
437 0
438 };
439
440 let mut segment_mem_offset = 0;
442 log::trace!("Data segments for module: {:#?}", data_segments);
443 for (id, segment) in data_segments.iter() {
444 let lib_base_global_id = lib_base_import.as_ref().map(|id| id.as_raw_index() as u32);
445
446 let (new_segment_offset, out) =
447 segment.to_segment_output(lib_base_global_id, mem_start, segment_mem_offset);
448 segment_mem_offset = new_segment_offset + out.as_raw().len();
450
451 data_segment_outputs.insert(id, out);
452 }
453
454 let is_static_symbol = |symbol: AnySymbolId| {
456 let main_module = static_main
457 .as_ref()
458 .expect("is_static should be called only for submodules");
459 let symbol_id = Id::from_index(symbol);
460 let symbol = module_info.symbols.get(symbol_id).unwrap();
461 match symbol.kind {
462 SymbolKind::Func { input_id } => {
463 main_module.functions.get_output_id(input_id).is_some()
464 }
465 SymbolKind::DataDefined { segment_id, .. } => {
466 let main = static_main.as_ref().unwrap();
467 let Some(segment) = main.data.get(segment_id) else {
468 return false;
469 };
470 segment.symbols().get(&symbol_id).is_some()
471 }
472 _ => false,
473 }
474 };
475
476 let mut data_relocations = IdMap::new();
477
478 for (segment_id, data_segment) in data_segment_outputs.iter() {
480 for (symbol_index, sym) in data_segment.symbols() {
481 let sym_relocs = module_info
482 .symbols
483 .get(*symbol_index)
484 .expect("symbol should be valid")
485 .relocs
486 .iter()
487 .map(|reloc| {
488 let relocation_context = modify::RelocationContext {
489 dyn_relocate: !main_module
490 && !is_static_symbol(reloc.index as AnySymbolId),
491 containing_symbol: Some(modify::DataSymbolWithOffset {
492 storage_segment_id: segment_id,
493 storage_symbol_id: *symbol_index,
494 storage_offset_in_data: reloc.offset, }),
496 };
497
498 let mut reloc = reloc.clone();
500 reloc.offset += sym.data_mem_offset as u32;
501 modify::DataModifyEntry::from_relocation_entry(&reloc, &relocation_context)
502 })
503 .collect::<Result<Vec<_>>>()
504 .unwrap();
505 data_relocations
506 .entry(segment_id)
507 .or_insert_with(Vec::new)
508 .extend(sym_relocs);
509 }
510 }
511
512 let mut defined_functions = vec![];
513
514 for &(func_id, mut export, sym_id) in &funcs_to_define {
515 let func_relocs = &*module_info.symbols.get(sym_id).unwrap().relocs;
517
518 let modification_list = func_relocs
519 .iter()
520 .map(|entry| {
521 let relocation_context = modify::RelocationContext {
522 dyn_relocate: !main_module && !is_static_symbol(entry.index as AnySymbolId),
523 containing_symbol: None,
524 };
525 modify::CodeModifyEntry::from_relocation_entry(&entry, &relocation_context)
526 })
527 .collect::<Result<Vec<_>, _>>()
528 .unwrap();
529
530 if export && is_nonexportable(sym_id) {
534 defined_functions.push(DefinedFunction {
535 export: true,
536 input_func_id: func_id,
537 kind: DefinedFunctionKind::Trampoline {},
538 });
539 export = false
540 }
541
542 defined_functions.push(DefinedFunction {
543 export,
544 input_func_id: func_id,
545 kind: DefinedFunctionKind::Copied { modification_list },
546 });
547 }
548
549 defined_functions.extend(indirect_funcs_stubs.iter().map(
550 |(input_func_id, need_export)| DefinedFunction {
551 export: *need_export,
552 input_func_id: *input_func_id,
553 kind: DefinedFunctionKind::IndirectTrampoline {
554 table_index_offset: emit_info.external_entrypoint_index(input_func_id).unwrap(),
555 },
556 },
557 ));
558
559 defined_functions.extend(
560 import_funcs_stubs
561 .iter()
562 .map(|input_func_id| DefinedFunction {
563 export: true,
564 input_func_id: *input_func_id,
565 kind: DefinedFunctionKind::Trampoline {},
566 }),
567 );
568
569 import_functions.sort();
570 defined_functions.sort();
571
572 log::trace!("import_functions: {:#?}", import_functions);
573 log::trace!("defined_functions: {:#?}", defined_functions);
574
575 let funcs = ImportsOrDefined::new(import_functions, defined_functions).lock();
576
577 let indirect_function_table: Vec<_> = module_info
578 .indirect_function_list
579 .iter()
580 .filter(|indirect_func_id| funcs.get_output_id(**indirect_func_id).is_some())
581 .copied()
582 .collect();
583
584 let indirect_functions = IndirectFunctionEmitInfo::new(
585 main_module.then(|| emit_info.num_entrypoints()),
586 indirect_function_table,
587 );
588
589 let sub_module_extra = lib_base_import.map(|lib_base| {
590 let table_base = globals.push_import(GlobalImport::New {
591 input_global_id: None,
592 global_name: Cow::Borrowed("__table_base"),
593 global_type: wasm_encoder::GlobalType {
594 val_type: wasm_encoder::ValType::I32,
595 mutable: false,
596 shared: false,
597 },
598 });
599
600 let entrypoints = output_module_info
601 .split_points
602 .iter()
603 .map(|sp| sp.export_func())
604 .collect::<Vec<_>>();
605
606 let extern_modules = shared_modules
607 .iter()
608 .map(|module_id| {
609 let got_base = GotBase {
610 lib_base_id: globals.push_import(GlobalImport::New {
611 input_global_id: None,
612 global_name: Cow::Owned(format!("__{}_lib_base", module_id)),
613 global_type: wasm_encoder::GlobalType {
614 val_type: wasm_encoder::ValType::I32,
615 mutable: false,
616 shared: false,
617 },
618 }),
619 table_base_id: globals.push_import(GlobalImport::New {
620 input_global_id: None,
621 global_name: Cow::Owned(format!("__{}_table_base", module_id)),
622 global_type: wasm_encoder::GlobalType {
623 val_type: wasm_encoder::ValType::I32,
624 mutable: false,
625 shared: false,
626 },
627 }),
628 };
629 (module_id.clone(), got_base)
630 })
631 .collect::<Vec<_>>();
632
633 let export_got_with_id = module_id.as_shared().cloned();
634 SubModuleExtra {
635 self_base: GotBase {
636 lib_base_id: lib_base,
637 table_base_id: table_base,
638 },
639 extern_modules,
640 entrypoints,
641 export_got_with_id,
642 }
643 });
644
645 let mut global_tmp_store = BTreeMap::new();
646 if !main_module {
647 for (store_type, val_type) in init_each_store_var() {
648 let global_id = globals.imports.len() + globals.defined.len();
649 global_tmp_store.insert(store_type, OutputGlobalId::from_index(global_id));
650
651 globals
652 .defined
653 .push(DefinedGlobal::WithConstructor(GlobalType {
654 val_type,
655 mutable: true,
656 shared: false,
657 }));
658 }
659 }
660 Self {
663 src: module_info,
664 data: data_segment_outputs,
665 data_relocations,
666 globals: globals.lock(),
667 sub_module_extra,
668 global_tmp_store,
669 indirect_functions,
670 functions: funcs,
671 linkage_type,
672 linked_modules: shared_modules.to_vec(),
673 incremental_version: version,
674 }
675 }
676
677 pub(crate) fn get_submodule_extra(
679 &self,
680 shared: Option<&SharedModuleIdentifier>,
681 ) -> Option<&GotBase> {
682 if let Some(sub_module_extra) = &self.sub_module_extra {
683 if let Some(shared) = shared {
684 for (module_id, got_base) in &sub_module_extra.extern_modules {
685 if module_id == shared {
686 return Some(got_base);
687 }
688 }
689 } else {
690 return Some(&sub_module_extra.self_base);
691 }
692 }
693 None
694 }
695
696 fn is_main(&self) -> bool {
697 self.sub_module_extra.is_none()
698 }
699
700 fn _num_extra_global_imports(&self) -> usize {
701 if !self.is_main() {
702 SubModuleExtra::MAIN_GLOBAL_EXPORTS_COUNT as usize + 2
703 } else {
704 0
705 }
706 }
707
708 fn generate(
709 &'any self,
710 computed_modules: &'any ComputedModules<'any, 'src>,
711 output_module: &mut wasm_encoder::Module,
712 precise_modification: bool,
713 ) -> Result<()> {
714 self.generate_dylink0_section(output_module)?;
715 self.generate_type_section(output_module)?;
717 self.generate_import_section(computed_modules, output_module);
718 self.generate_function_section(output_module);
719 if self.is_main() {
720 self.generate_table_element_sections(output_module)?;
722 self.generate_memory_section(output_module);
723 }
724 self.generate_global_section(output_module)?;
725 self.generate_export_section(output_module);
726 self.generate_start_function_section(output_module)?;
727 self.generate_element_section(output_module)?;
728
729 let code_relocs =
730 self.generate_code_section(computed_modules, output_module, precise_modification)?;
731 let data_relocs = self.generate_data_section(computed_modules, output_module)?;
732
733 self.generate_compiler_tools_sections(output_module, code_relocs, data_relocs)?;
736 self.generate_target_features_section(output_module)?;
737 self.generate_custom_sections(output_module)?;
738 Ok(())
739 }
740
741 fn generate_type_section(&self, output_module: &mut wasm_encoder::Module) -> Result<()> {
743 let mut section = wasm_encoder::TypeSection::new();
745 for (_id, input_func_type) in self.src.wasm.types.iter() {
747 let output_func_type: wasm_encoder::FuncType =
748 input_func_type.clone().try_into().unwrap();
749 section.ty().function(
750 output_func_type.params().iter().cloned(),
751 output_func_type.results().iter().cloned(),
752 );
753 }
754 output_module.section(§ion);
755 Ok(())
756 }
757
758 fn generate_import_section(
759 &self,
760 computed_modules: &'any ComputedModules<'any, 'src>,
761 output_module: &mut wasm_encoder::Module,
762 ) {
763 let mut section = wasm_encoder::ImportSection::new();
768
769 for (index, import_fn) in self.functions.imports() {
770 let ty = wasm_encoder::EntityType::Function(
771 self.get_function_type(index).as_raw_index() as u32,
772 );
773 let fn_name = import_fn.import_name();
774 let module_name = import_fn.module_name();
775 section.import(&module_name, &fn_name, ty);
776 }
777
778 match &self.sub_module_extra {
779 None => {
780 for (_id, import) in self.src.wasm.imports.iter() {
782 if matches!(
783 import.ty,
784 wasmparser::TypeRef::Func(_) | wasmparser::TypeRef::Global(_)
785 ) {
786 continue;
787 }
788 let ty: wasm_encoder::EntityType = import.ty.try_into().unwrap();
789 section.import(import.module, import.name, ty);
790 }
791 }
792
793 Some(_) => {
794 for (_, item) in self.globals.imports() {
796 section.import(
797 item.module_name().as_ref(),
798 item.import_name().as_ref(),
799 *item.global_type(),
800 );
801 }
802
803 section.import(
804 "__wamex",
805 "__indirect_function_table",
806 computed_modules
807 .main_module
808 .indirect_functions
809 .calculate_indirect_function_table_type(),
810 );
811
812 for (memory_index, memory) in self.src.wasm.memories.iter() {
814 let ty: wasm_encoder::MemoryType = (*memory).into();
815 section.import("__wamex", self.get_memory_name(memory_index).as_str(), ty);
816 }
817 }
818 }
819
820 output_module.section(§ion);
821 }
822
823 fn _get_input_func_id(&self, index: OutputFuncId) -> InputFuncId {
824 self.functions
825 .get_input_id(index)
826 .expect("Output function index should be valid")
827 }
828
829 fn _get_output_func_id(&self, input_func_id: InputFuncId) -> Option<OutputFuncId> {
830 self.functions.get_output_id(input_func_id)
831 }
832
833 fn get_function_type(&self, index: OutputFuncId) -> FuncTypeId {
836 let input_func_id = self._get_input_func_id(index);
837
838 self.src.get_function_type_id(input_func_id)
839 }
840 fn get_function_name(&self, index: OutputFuncId, exported: bool) -> Cow<'src, str> {
844 let input_func_id = self._get_input_func_id(index);
845 let mut name = self
846 .src
847 .wasm
848 .names
849 .functions
850 .get(input_func_id)
851 .map(|name| (*name).into())
852 .unwrap_or_else(|| format!("func_{index}").into());
853
854 let namespace = exported
855 || matches!(
856 self.functions
857 .get_defined_for_output_id(index)
858 .map(|def| &def.kind),
859 Some(DefinedFunctionKind::Trampoline { .. })
861 | Some(DefinedFunctionKind::IndirectTrampoline { .. })
862 );
863
864 if namespace {
865 name = format!("__wamex_{}", name).into()
866 }
867 name
868 }
869
870 fn get_global_name(&self, index: InputGlobalId) -> Cow<'src, str> {
871 self.src
872 .wasm
873 .names
874 .globals
875 .get(index)
876 .map(|name| (*name).into())
877 .or_else(|| {
878 self.src
879 .export_map
880 .get(&(
881 wasmparser::ExternalKind::Global as isize,
882 index.as_raw_index(), ))
884 .map(|(_, name)| (*name).into())
885 })
886 .unwrap_or_else(|| format!("__global_{index}").into())
887 }
888
889 fn get_memory_name(&self, index: MemoryId) -> String {
890 self.src
891 .wasm
892 .names
893 .memories
894 .get(index)
895 .map(|name| name.to_string())
896 .or_else(|| {
897 self.src
898 .export_map
899 .get(&(
900 wasmparser::ExternalKind::Memory as isize,
901 index.as_raw_index(), ))
903 .map(|(_, name)| name.to_string())
904 })
905 .unwrap_or_else(|| format!("__memory_{index}"))
906 }
907 fn generate_export_section(&self, output_module: &mut wasm_encoder::Module) {
908 let mut section = wasm_encoder::ExportSection::new();
909 let mut existing_exports = HashSet::<borrow::Cow<'_, str>>::new();
910 if self.is_main() {
912 for (_id, export) in self.src.wasm.exports.iter() {
913 let mut index = export.index;
914 if export.kind == wasmparser::ExternalKind::Func {
915 let Some(func_id) = self._get_output_func_id(InputFuncId::from_index(index))
916 else {
917 continue;
918 };
919 index = func_id.as_raw_index() as u32;
920 }
921 section.export(export.name, export.kind.into(), index);
922 existing_exports.insert(export.name.into());
923 }
924 }
925
926 for (func_id, func) in self.functions.defined() {
927 if !func.export {
928 continue;
929 }
930 let name = self.get_function_name(func_id, true);
931
932 if existing_exports.contains(&name) {
933 continue;
934 }
935 section.export(
936 &name,
937 wasm_encoder::ExportKind::Func,
938 func_id.as_raw_index() as u32,
939 );
940 }
941
942 match &self.sub_module_extra {
943 Some(extra) => {
944 if let Some(export_got_with_id) = &extra.export_got_with_id {
945 let lib_base_name = format!("__{}_lib_base", export_got_with_id);
946 let table_base_name = format!("__{}_table_base", export_got_with_id);
947 if existing_exports.contains(lib_base_name.as_str())
948 || existing_exports.contains(table_base_name.as_str())
949 {
950 panic!(
951 "GOT base globals {lib_base_name} or {table_base_name} already exist in exports"
952 );
953 }
954 section.export(
956 &lib_base_name,
957 wasm_encoder::ExportKind::Global,
958 extra.self_base.lib_base_id.as_raw_index() as u32,
959 );
960 section.export(
961 &table_base_name,
962 wasm_encoder::ExportKind::Global,
963 extra.self_base.table_base_id.as_raw_index() as u32,
964 );
965 existing_exports.insert(lib_base_name.into());
966 existing_exports.insert(table_base_name.into());
967 }
968 }
969 None => {
970 let white_list = SubModuleExtra::MAIN_GLOBAL_EXPORTS;
972 for (global_index, _) in self.src.wasm.globals.iter() {
973 let name = self.get_global_name(global_index);
974 if existing_exports.contains(&name) {
975 continue;
976 }
977 if !white_list.contains(&&*name) {
978 continue;
979 }
980 section.export(
982 &name,
983 wasm_encoder::ExportKind::Global,
984 global_index.as_raw_index() as u32,
985 );
986 existing_exports.insert(name);
987 }
988
989 white_list.iter().for_each(|name| {
990 debug_assert!(
991 existing_exports.contains(*name),
992 "Main module should export {name}"
993 );
994 });
995
996 if !existing_exports.contains("__indirect_function_table") {
997 section.export(
998 "__indirect_function_table",
999 wasm_encoder::ExportKind::Table,
1000 0,
1001 );
1002 }
1003 }
1004 }
1005
1006 output_module.section(§ion);
1007 }
1008
1009 fn find_void_type(&self) -> FuncTypeId {
1010 for (fn_id, fn_type) in self.src.wasm.types.iter() {
1011 if fn_type.params().is_empty() && fn_type.results().is_empty() {
1012 return fn_id;
1013 }
1014 }
1015
1016 panic!("Void type not found in type section");
1017 }
1018 fn generate_function_section(&self, output_module: &mut wasm_encoder::Module) {
1019 let mut section: wasm_encoder::FunctionSection = wasm_encoder::FunctionSection::new();
1020 for (index, _func) in self.functions.defined() {
1021 let func_type = self.get_function_type(index);
1022 section.function(func_type.as_raw_index() as u32);
1023 }
1024 if !self.is_main() {
1026 section.function(self.find_void_type().as_raw_index() as u32);
1027 }
1028
1029 output_module.section(§ion);
1030 }
1031
1032 fn generate_table_element_sections(
1034 &self,
1035 output_module: &mut wasm_encoder::Module,
1036 ) -> Result<()> {
1037 let mut section = wasm_encoder::TableSection::new();
1038 section.table(
1039 self.indirect_functions
1040 .calculate_indirect_function_table_type(),
1041 );
1042 output_module.section(§ion);
1043 Ok(())
1044 }
1045
1046 fn _generate_element_section_segment(
1047 section: &mut wasm_encoder::ElementSection,
1048 offset: &wasm_encoder::ConstExpr,
1049 func_ids: Vec<u32>,
1050 ) {
1051 section.segment(wasm_encoder::ElementSegment {
1052 mode: wasm_encoder::ElementMode::Active {
1053 table: None,
1054 offset,
1055 },
1056 elements: wasm_encoder::Elements::Functions(func_ids.into()),
1057 });
1058 }
1059 fn _function_ids_for_element_section(&self) -> Result<Vec<u32>> {
1060 let func_ids: Vec<u32> = self
1061 .indirect_functions
1062 .table_entries
1063 .iter()
1064 .map(|input_func_id| -> Result<u32> {
1065 let output_func_id = self._get_output_func_id(*input_func_id).ok_or_else(|| {
1066 anyhow!("No output function corresponding to input function {input_func_id:?}")
1067 })?;
1068 Ok(output_func_id.as_raw_index() as u32)
1069 })
1070 .collect::<Result<Vec<_>>>()?;
1071 Ok(func_ids)
1072 }
1073
1074 fn generate_element_section(&self, output_module: &mut wasm_encoder::Module) -> Result<()> {
1091 let mut section = wasm_encoder::ElementSection::new();
1092
1093 let element_start = if let Some(sub_module_extra) = &self.sub_module_extra {
1094 wasm_encoder::ConstExpr::global_get(
1095 sub_module_extra.self_base.table_base_id.as_raw_index() as u32,
1096 )
1097 } else {
1098 wasm_encoder::ConstExpr::i32_const(1_i32) };
1100
1101 let func_ids = self._function_ids_for_element_section()?;
1102 Self::_generate_element_section_segment(&mut section, &element_start, func_ids);
1103
1104 match &self.sub_module_extra {
1106 None => {
1107 let (defined_id, _) = self
1108 .functions
1109 .defined()
1110 .next()
1111 .expect("we need any defined function in main module");
1112 let id = defined_id.as_raw_index() + self.functions.imports().len();
1113
1114 let abort_fn_id = id as u32; let num_lazy_entries = self.indirect_functions.num_extra_stubs;
1116 let start_of_lazy_fns = self.indirect_functions.table_entries.len() as i32 + 1;
1117
1118 let stub_vec = vec![abort_fn_id; num_lazy_entries as usize];
1119 let element_start = wasm_encoder::ConstExpr::i32_const(start_of_lazy_fns);
1120 Self::_generate_element_section_segment(&mut section, &element_start, stub_vec);
1121 }
1122 Some(sub_module) => {
1123 if let LinkageType::DynamicLinking { table_offset, .. } = &self.linkage_type {
1124 let entry_point_offset = *table_offset as i32;
1125
1126 let lazy_entrypoints = sub_module
1127 .entrypoints
1128 .iter()
1129 .map(|input_func_id| {
1130 let output_func_id = self
1131 ._get_output_func_id(*input_func_id)
1132 .expect("Function should be defined");
1133 output_func_id.as_raw_index() as u32
1134 })
1135 .collect::<Vec<_>>();
1136 let element_start = wasm_encoder::ConstExpr::i32_const(entry_point_offset);
1137
1138 Self::_generate_element_section_segment(
1139 &mut section,
1140 &element_start,
1141 lazy_entrypoints,
1142 );
1143 }
1144 }
1145 }
1146 output_module.section(§ion);
1147 Ok(())
1148 }
1149
1150 fn generate_memory_section(&self, output_module: &mut wasm_encoder::Module) {
1151 if self.src.wasm.memories.is_empty() {
1152 return;
1153 }
1154 let mut section = wasm_encoder::MemorySection::new();
1155 for (_idx, memory) in self.src.wasm.memories.iter() {
1156 section.memory((*memory).into());
1157 }
1158 output_module.section(§ion);
1159 }
1160
1161 fn generate_global_section(&self, output_module: &mut wasm_encoder::Module) -> Result<()> {
1162 let mut section = wasm_encoder::GlobalSection::new();
1163 for (_, global) in self.globals.defined() {
1164 match global {
1165 DefinedGlobal::PlainCopy { global, .. } => {
1166 section.global(
1167 global.ty.try_into().unwrap(),
1168 &global.init_expr.clone().try_into().unwrap(),
1169 );
1170 }
1171 DefinedGlobal::WithConstructor(global_type) => {
1172 if self.is_main() {
1173 bail!("Trying to define global for main module");
1174 }
1175 section.global(
1176 *global_type,
1177 &globals::global_init_tmp(global_type.val_type),
1178 );
1179 }
1180 }
1181 }
1182 output_module.section(§ion);
1183 Ok(())
1184 }
1185
1186 fn generate_start_function_section(
1187 &'any self,
1188 output_module: &mut wasm_encoder::Module,
1189 ) -> Result<()> {
1190 if !self.is_main() {
1191 let start = wasm_encoder::StartSection {
1192 function_index: self.functions.len() as u32,
1193 };
1194 output_module.section(&start);
1195 }
1196 Ok(())
1197 }
1198
1199 fn _generate_indirect_stub_function(
1207 &'any self,
1208 section: &mut wasm_encoder::CodeSection,
1209 input_func_id: InputFuncId,
1210 table_index: u32,
1211 ) -> Result<Vec<RelocationEntry>> {
1212 let func_type_id = &self.src.get_function_type_id(input_func_id);
1213 let func_type = &self.src.wasm.types[*func_type_id];
1214
1215 let mut func = wasm_encoder::Function::new([]);
1216 for (param_i, _param_type) in func_type.params().iter().enumerate() {
1217 func.instruction(&wasm_encoder::Instruction::LocalGet(param_i as u32));
1218 }
1219 func.instruction(&wasm_encoder::Instruction::I32Const(table_index as i32));
1220 func.instruction(&wasm_encoder::Instruction::CallIndirect {
1221 type_index: func_type_id.as_raw_index() as u32,
1222 table_index: 0, });
1224 func.instruction(&wasm_encoder::Instruction::End);
1225 section.function(&func);
1226 Ok(vec![])
1228 }
1229
1230 fn _generate_import_call_stub(
1240 &'any self,
1241 section: &mut wasm_encoder::CodeSection,
1242 input_func_id: InputFuncId,
1243 ) -> Result<Vec<RelocationEntry>> {
1244 let func_type_id = &self.src.get_function_type_id(input_func_id);
1245 let func_type = &self.src.wasm.types[*func_type_id];
1246
1247 let import_fn = self
1248 ._get_output_func_id(input_func_id)
1249 .expect("Imported function should have output id");
1250
1251 let mut func = wasm_encoder::Function::new([]);
1252 for (param_i, _param_type) in func_type.params().iter().enumerate() {
1253 func.instruction(&wasm_encoder::Instruction::LocalGet(param_i as u32));
1254 }
1255 func.instruction(&wasm_encoder::Instruction::Call(
1256 import_fn.as_raw_index() as u32
1257 ));
1258 func.instruction(&wasm_encoder::Instruction::End);
1259 section.function(&func);
1260 Ok(vec![])
1262 }
1263
1264 fn _generate_defined_function(
1265 &'any self,
1266 section: &mut wasm_encoder::CodeSection,
1267 computed_modules: &'any ComputedModules<'any, 'src>,
1268 function_start_offset: usize,
1269 input_func_id: InputFuncId,
1270 modification_list: &[modify::CodeModifyEntry],
1271 precise_modification: bool,
1272 ) -> Result<Vec<RelocationEntry>> {
1273 let mut code_relocs = Vec::new();
1274 let defined_id = self
1275 .src
1276 .as_defined_function_id(input_func_id)
1277 .expect("Defined function expected");
1278
1279 let global_id_mapper = |global_id: InputGlobalId| self.globals.get_output_id(global_id);
1280
1281 let modify_fn = if precise_modification {
1282 ModifyContext::emit_code_with_changes
1283 } else {
1284 ModifyContext::emit_code_in_place
1285 };
1286
1287 let (result, modified_relocs) = modify_fn(
1288 self,
1289 computed_modules,
1290 global_id_mapper,
1291 defined_id,
1292 input_func_id,
1293 modification_list,
1294 )?;
1295 for mut reloc in modified_relocs {
1296 reloc.offset += function_start_offset as u32;
1297 code_relocs.push(reloc);
1298 }
1299 section.raw(&result);
1300
1301 Ok(code_relocs)
1302 }
1303
1304 fn generate_code_section(
1305 &'any self,
1306 computed_modules: &'any ComputedModules<'any, 'src>,
1307 output_module: &mut wasm_encoder::Module,
1308 precise_modification: bool,
1309 ) -> Result<Vec<RelocationEntry>> {
1310 let defined_functions_count = self.functions.defined().len() as u32
1311 + if !self.is_main() {
1312 1 } else {
1314 0
1315 };
1316
1317 let mut section = wasm_encoder::CodeSection::new();
1318 let mut code_relocs = Vec::new();
1319 for (_id, output_func) in self.functions.defined() {
1320 let relocs = match &output_func.kind {
1321 DefinedFunctionKind::Trampoline {} => {
1322 self._generate_import_call_stub(&mut section, output_func.input_func_id)
1323 }
1324 DefinedFunctionKind::IndirectTrampoline { table_index_offset } => self
1325 ._generate_indirect_stub_function(
1326 &mut section,
1327 output_func.input_func_id,
1328 computed_modules.indirect_entrypoints_offset() + *table_index_offset,
1329 ),
1330 DefinedFunctionKind::Copied { modification_list } => {
1331 let function_start_offset =
1332 encoding_size(defined_functions_count) + section.byte_len();
1333 self._generate_defined_function(
1334 &mut section,
1335 computed_modules,
1336 function_start_offset,
1337 output_func.input_func_id,
1338 modification_list,
1339 precise_modification,
1340 )
1341 }
1342 };
1343
1344 code_relocs.extend(relocs?);
1345 }
1346
1347 if self.sub_module_extra.is_some() {
1348 let relocate = RelocateState {
1349 input_module: self.src,
1350 computed_modules,
1351 emit_module: self,
1352 global_id_mapper: &|global_id: InputGlobalId| self.globals.get_output_id(global_id),
1353 };
1354
1355 let start_fn = StartFnGen::new(
1356 relocate,
1357 MEMORY_INDEX,
1358 self.data_relocations
1359 .iter()
1360 .flat_map(|(_, entries)| entries.iter()),
1361 )?;
1362
1363 section.function(&start_fn.generate_fn());
1364 }
1365 output_module.section(§ion);
1366
1367 Ok(code_relocs)
1368 }
1369 fn generate_data_section(
1370 &'any self,
1371 computed_modules: &'any ComputedModules<'any, 'src>,
1372 output_module: &mut wasm_encoder::Module,
1373 ) -> Result<Vec<RelocationEntry>> {
1374 let relocs = Vec::new();
1376 let mut section = wasm_encoder::DataSection::new();
1377
1378 for (id, out) in self.data.iter() {
1379 let mut data = out.data_segment(MEMORY_INDEX);
1380 if let Some(relocs) = self.data_relocations.get(id) {
1385 for entry in relocs.iter() {
1386 let state = modify::StartFnModifyContext {
1387 data_segment: &mut data.data,
1388 relocate: RelocateState {
1389 input_module: self.src,
1390 computed_modules,
1391 emit_module: self,
1392 global_id_mapper: &|global_id: InputGlobalId| {
1393 self.globals.get_output_id(global_id)
1394 },
1395 },
1396 };
1397 state.apply_relocation(entry)?;
1398 }
1399 }
1400 section.segment(data);
1401 }
1402
1403 output_module.section(§ion);
1404 Ok(relocs)
1405 }
1406 fn generate_target_features_section(
1407 &self,
1408 output_module: &mut wasm_encoder::Module,
1409 ) -> Result<()> {
1410 let mut features = self.src.wasm.target_features.clone();
1411 features.features.extended_const = true;
1412 output_module.section(&features.encode_custom_section());
1413 Ok(())
1414 }
1415
1416 fn generate_dylink0_section(
1417 &'any self,
1418 output_module: &mut wasm_encoder::Module,
1419 ) -> Result<()> {
1420 if !self.is_main() {
1421 let data = Dylink0Section {
1422 memory_alignment: std::mem::size_of::<u32>() as u32, memory_size: self
1424 .data
1425 .iter()
1426 .last()
1427 .map(|(_, seg)| seg.memory_offset() + seg.as_raw().len())
1428 .unwrap_or_default() as u32,
1429 table_size: self.indirect_functions.table_entries.len() as u32,
1430 table_alignment: 0,
1431 needed_libraries: self
1432 .linked_modules
1433 .iter()
1434 .map(|m| m.to_string().into())
1435 .collect(),
1436
1437 import_info: vec![],
1439 };
1440 let section = wasm_encoder::CustomSection {
1441 name: "dylink.0".into(),
1442 data: data.encode_section().into(),
1443 };
1444 output_module.section(§ion);
1445 }
1446 Ok(())
1447 }
1448
1449 fn generate_compiler_tools_sections(
1451 &self,
1452 output_module: &mut wasm_encoder::Module,
1453 shifted_code_relocs: Vec<RelocationEntry>,
1454 shifted_data_relocs: Vec<RelocationEntry>,
1455 ) -> Result<()> {
1456 let wamex_version = wasm_encoder::CustomSection {
1457 name: "__wamex_version".into(),
1458 data: self.incremental_version.encode().to_vec().into(),
1459 };
1460
1461 output_module.section(&wamex_version);
1462
1463 let mut functions = wasm_encoder::NameMap::new();
1464 for output_id in self.functions.iter_all_ids() {
1465 let name = self.get_function_name(output_id, false);
1466
1467 functions.append(output_id.as_raw_index() as u32, &name);
1468 }
1469
1470 let mut names = wasm_encoder::NameSection::new();
1471 names.functions(&functions);
1472 output_module.section(&names.as_custom());
1473
1474 Ok(())
1481 }
1482 fn generate_custom_sections(&self, output_module: &mut wasm_encoder::Module) -> Result<()> {
1485 for (_, custom) in &self.src.wasm.custom_sections {
1486 match &*custom.name {
1487 "__wasm_bindgen_unstable" => {
1488 if !self.is_main() {
1489 continue; }
1491 }
1492 _ => {
1493 log::warn!(
1494 "Skipping unsuported custom section during emit: {}",
1495 custom.name
1496 );
1497 continue;
1498 }
1499 };
1500 let section = wasm_encoder::CustomSection {
1501 name: (&*custom.name).into(),
1502 data: (&*custom.data).into(),
1503 };
1504 output_module.section(§ion);
1505 }
1506 Ok(())
1507 }
1508}
1509
1510#[derive(Debug, Default)]
1511pub struct IndirectFunctionEmitInfo {
1512 pub table_entries: Vec<InputFuncId>,
1513 pub function_table_index: HashMap<InputFuncId, usize>,
1514 pub num_extra_stubs: u64,
1515}
1516
1517impl IndirectFunctionEmitInfo {
1518 fn new(num_extra_stubs: Option<u64>, table_entries: Vec<InputFuncId>) -> Self {
1519 let num_stub_at_start = if num_extra_stubs.is_some() { 1 } else { 0 };
1521 let function_table_index: HashMap<_, _> = table_entries
1522 .iter()
1523 .enumerate()
1524 .map(|(i, func_id)| (*func_id, i + num_stub_at_start))
1525 .collect();
1526
1527 Self {
1528 table_entries,
1529 function_table_index,
1530 num_extra_stubs: num_extra_stubs.unwrap_or(0),
1531 }
1532 }
1533 fn calculate_indirect_function_table_type(&self) -> wasm_encoder::TableType {
1534 let indirect_table_size = self.table_entries.len() as u64 + 1 + self.num_extra_stubs; wasm_encoder::TableType {
1538 element_type: wasm_encoder::RefType::FUNCREF,
1539 minimum: indirect_table_size,
1540 maximum: None, shared: false,
1542 table64: false,
1543 }
1544 }
1545}
1546
1547#[derive(Debug)]
1548pub struct ModuleDecl {
1549 pub split_points: Vec<SplitPoint>,
1550
1551 split_points_offset: u32,
1553}
1554
1555#[derive(Debug)]
1556pub struct CommonEmitInfo<'src> {
1557 pub src_data_segments: IdVec<SegmentLayout<'src>>,
1558
1559 pub split_point_imports: BTreeSet<InputFuncId>,
1561 pub modules_decl: HashMap<ModuleIdentifier, ModuleDecl>,
1562}
1563
1564impl<'src> CommonEmitInfo<'src> {
1565 fn module_entrypoints_range_shifted(
1569 &self,
1570 stubs_start: u32,
1571 module_id: &ModuleIdentifier,
1572 ) -> Option<Range<u32>> {
1573 self.modules_decl.get(module_id).map(|r| {
1574 let start = stubs_start + r.split_points_offset;
1575 let end = stubs_start + r.split_points_offset + r.split_points.len() as u32;
1576 start..end
1577 })
1578 }
1579 fn external_entrypoint_index(&self, entrypoint_func: &InputFuncId) -> Option<u32> {
1580 self.modules_decl.values().find_map(|module| {
1581 module
1582 .split_points
1583 .iter()
1584 .position(|sp| sp.import_func() == *entrypoint_func)
1585 .map(|pos| module.split_points_offset + pos as u32)
1586 })
1587 }
1588
1589 fn is_external_entrypoint(&self, import_fn: &InputFuncId) -> bool {
1591 self.split_point_imports.contains(import_fn)
1592 }
1593
1594 fn num_entrypoints(&self) -> u64 {
1595 self.split_point_imports.len() as u64
1596 }
1597
1598 pub fn new(
1599 module: &analysis::ModuleInfo<'src>,
1600 verbose: bool,
1601 program_info: &SplitProgramInfo,
1602 ) -> Result<Self> {
1603 let mut split_point_imports = BTreeSet::new();
1604 let mut modules_decl = HashMap::new();
1605 for (module_index, (id, output_module)) in program_info.output_modules.iter().enumerate() {
1606 let SplitModuleIdentifier::Single(id) = &id else {
1607 debug_assert!(
1608 output_module.split_points.is_empty(),
1609 "Expected no split points on shared module"
1610 );
1611 continue;
1612 };
1613 modules_decl.insert(
1614 id.clone(),
1615 ModuleDecl {
1616 split_points: output_module.split_points.clone(),
1617 split_points_offset: module_index as u32,
1618 },
1619 );
1620
1621 for split_point in output_module.split_points.iter() {
1622 split_point_imports.insert(split_point.import_func());
1623 }
1624 }
1625
1626 let data_segments_symbols = Self::chunk_by(
1628 module.symbols.iter_data_symbols(),
1629 |(left_segment, ..), (right_segment, ..)| left_segment == right_segment,
1630 );
1631 let data_segments: IdVec<SegmentLayout<'src>> = module
1632 .wasm
1633 .data
1634 .section_payload
1635 .data_segments
1636 .iter()
1637 .map(|(data_segment, data)| {
1638 let data_symbols = data_segments_symbols
1639 .get(data_segment.as_raw_index())
1640 .cloned()
1641 .expect("Symbols for data segment not found");
1642 let segment_info = &module.wasm.linking.segments_info[data_segment.as_raw_index()];
1643
1644 SegmentLayout::new_inner(
1645 data,
1646 segment_info,
1647 data_symbols.into_iter().map(|(_, id, record)| (id, record)),
1648 )
1649 })
1650 .collect::<Result<IdVec<SegmentLayout<'src>>>>()?;
1651
1652 if verbose {
1653 SegmentLayout::debug_layout(&module.symbols, String::from("input"), &data_segments);
1654 }
1655 Ok(CommonEmitInfo {
1656 split_point_imports,
1657 src_data_segments: data_segments,
1658 modules_decl,
1659 })
1660 }
1661
1662 fn chunk_by<F, U>(items: impl Iterator<Item = U>, comparator: F) -> Vec<Vec<U>>
1663 where
1664 F: Fn(&U, &U) -> bool,
1665 {
1666 let mut result = Vec::new();
1667 let mut current_chunk = Vec::new();
1668
1669 for item in items {
1670 if let Some(prev) = current_chunk.last() {
1671 if !comparator(prev, &item) {
1672 result.push(current_chunk);
1673 current_chunk = Vec::new();
1674 }
1675 }
1676 current_chunk.push(item);
1677 }
1678
1679 if !current_chunk.is_empty() {
1680 result.push(current_chunk);
1681 }
1682
1683 result
1684 }
1685}
1686
1687const MAIN_ID: SplitModuleIdentifier = SplitModuleIdentifier::Single(ModuleIdentifier::Main);
1688
1689struct ComputedModules<'a, 'src> {
1690 main_module: ModuleEmitState<'a, 'src>,
1691 shared_modules: BTreeMap<SharedModuleIdentifier, ModuleEmitState<'a, 'src>>,
1692 sub_modules: BTreeMap<ModuleIdentifier, ModuleEmitState<'a, 'src>>,
1693}
1694
1695impl<'a, 'src> ComputedModules<'a, 'src> {
1696 pub fn produce_state(
1697 common_emit_info: &'a CommonEmitInfo<'src>,
1698 verbose: bool,
1699 module: &'a analysis::ModuleInfo<'src>,
1700 program_info: &SplitProgramInfo,
1701 version: BumpVersion,
1702 is_nonexported_fn: impl Fn(SymbolId) -> bool + Copy,
1703 ) -> Result<Self> {
1704 let modules_ids_iter = program_info
1705 .output_modules
1706 .iter()
1707 .enumerate()
1708 .map(|(output_module_index, (id, _))| (output_module_index, id.clone()));
1709
1710 for (id, output_module) in program_info.output_modules.iter() {
1711 let SplitModuleIdentifier::Shared(_) = id else {
1712 continue;
1713 };
1714 log::debug!("Shared_modules_info {id:?}: {output_module:?}");
1715 }
1716
1717 const NO_DEPS: Vec<SharedModuleIdentifier> = Vec::new();
1718 let all_shared_deps = modules_ids_iter
1719 .clone()
1720 .filter_map(|(_output_module_index, id)| {
1721 if let SplitModuleIdentifier::Shared(shared_with) = id {
1722 Some(shared_with)
1723 } else {
1724 None
1725 }
1726 })
1727 .collect::<Vec<_>>();
1728
1729 let dyn_linkage = true; let main_module = modules_ids_iter
1732 .clone()
1733 .into_iter()
1734 .find_map(|(output_module_index, id)| {
1735 if id == MAIN_ID {
1736 Some((output_module_index, id))
1737 } else {
1738 None
1739 }
1740 })
1741 .map(|(output_module_index, id)| {
1742 log::info!("Calculating module: {id}");
1743 let linkage_type = if dyn_linkage {
1744 LinkageType::DynamicLinking {
1746 table_offset: 0,
1747 table_num_entrypoints: 0,
1748 }
1749 } else {
1750 LinkageType::OriginalLayout
1751 };
1752
1753 (
1754 ModuleEmitState::produce_state(
1755 module,
1756 verbose,
1757 common_emit_info,
1758 &program_info.output_modules[output_module_index],
1759 None,
1760 &NO_DEPS,
1761 linkage_type,
1762 is_nonexported_fn,
1763 version,
1764 ),
1765 id,
1766 )
1767 })
1768 .expect("Main module not found");
1769
1770 let all_sub_modules = modules_ids_iter
1771 .into_iter()
1772 .filter(|(_output_module_index, id)| *id != MAIN_ID)
1773 .map(|(output_module_index, id)| {
1774 log::info!("Calculating module: {id}");
1775 let stubs_start = main_module.0.indirect_functions.table_entries.len() + 1;
1776
1777 let table_range = if let SplitModuleIdentifier::Single(id) = &id {
1778 common_emit_info
1779 .module_entrypoints_range_shifted(stubs_start as u32, id)
1780 .expect("Module split points not found")
1781 } else {
1782 0..0
1783 };
1784
1785 let linkage_type = if dyn_linkage {
1786 LinkageType::DynamicLinking {
1787 table_offset: table_range.start,
1788 table_num_entrypoints: table_range.len() as u32,
1789 }
1790 } else {
1791 LinkageType::OriginalLayout
1792 };
1793
1794 let module_deps = id.collect_deps(&all_shared_deps);
1795 (
1796 ModuleEmitState::produce_state(
1797 module,
1798 verbose,
1799 common_emit_info,
1800 &program_info.output_modules[output_module_index],
1801 Some(&main_module.0),
1802 &module_deps,
1803 linkage_type,
1804 is_nonexported_fn,
1805 version,
1806 ),
1807 id,
1808 )
1809 })
1810 .collect::<Vec<_>>();
1811 let mut sub_modules = BTreeMap::new();
1812 let mut shared_modules = BTreeMap::new();
1813 for (state_res, id) in all_sub_modules {
1814 match id {
1815 SplitModuleIdentifier::Single(id) => {
1816 sub_modules.insert(id, state_res);
1817 }
1818 SplitModuleIdentifier::Shared(shared_with) => {
1819 shared_modules.insert(shared_with, state_res);
1820 }
1821 }
1822 }
1823
1824 Ok(Self {
1825 main_module: main_module.0,
1826 shared_modules,
1827 sub_modules,
1828 })
1829 }
1830 fn indirect_entrypoints_offset(&self) -> u32 {
1831 self.main_module.indirect_functions.table_entries.len() as u32 + 1
1833 }
1834
1835 fn iter_modules(
1836 &self,
1837 ) -> impl Iterator<Item = (SplitModuleIdentifier, &ModuleEmitState<'a, 'src>)> {
1838 let shared_iters = self
1839 .shared_modules
1840 .iter()
1841 .map(|(id, state)| (SplitModuleIdentifier::Shared(id.clone()), state));
1842 let single_iters = self
1843 .sub_modules
1844 .iter()
1845 .map(|(id, state)| (SplitModuleIdentifier::Single(id.clone()), state));
1846 let main_iter = std::iter::once((MAIN_ID, &self.main_module));
1847 main_iter.chain(single_iters).chain(shared_iters)
1848 }
1849
1850 fn emit_modules(
1851 &self,
1852 precise_modification: bool,
1853 whitelist: Option<&BTreeSet<SplitModuleIdentifier>>,
1854 mut emit_fn: impl FnMut(&SplitModuleIdentifier, &[u8]) -> anyhow::Result<()>,
1855 ) -> anyhow::Result<()> {
1856 for (identifier, state) in self.iter_modules() {
1857 if let Some(whitelist) = whitelist {
1858 if !whitelist.contains(&identifier) {
1859 log::info!("Skipping module {identifier} as not in whitelist");
1860 continue;
1861 }
1862 }
1863 log::info!("Generating module {identifier}");
1864
1865 let mut encoder = wasm_encoder::Module::new();
1866 state
1867 .generate(self, &mut encoder, precise_modification)
1868 .with_context(|| format!("Error generating {:?}", identifier))?;
1869
1870 emit_fn(&identifier, encoder.as_slice())
1871 .with_context(|| format!("Error emitting {:?}", identifier))?;
1872 }
1873 Ok(())
1874 }
1875}
1876
1877pub fn merge_main_shared(program_info: &mut SplitProgramInfo) {
1879 let (shared_with_main, mut other): (Vec<_>, Vec<_>) =
1880 std::mem::take(&mut program_info.output_modules)
1881 .into_iter()
1882 .partition(|(id, _)| {
1883 if let SplitModuleIdentifier::Shared(shared_with) = id {
1884 shared_with.contains(&ModuleIdentifier::Main)
1885 } else {
1886 false
1887 }
1888 });
1889
1890 let (left_to_main, main_module, right_to_main) = {
1892 let main_module_index = other
1893 .iter()
1894 .enumerate()
1895 .find(|(_, (id, _))| *id == MAIN_ID)
1896 .expect("Main module not found")
1897 .0;
1898 let (before, main_and_next) = other.split_at_mut(main_module_index);
1899 let (main_module, after) = main_and_next.split_at_mut(1);
1900 let main_module = &mut main_module[0].1;
1901 (before, main_module, after)
1902 };
1903
1904 let is_imported_by_other = |node: &SymbolId| {
1906 left_to_main
1907 .iter()
1908 .chain(right_to_main.iter())
1909 .any(|(_, mod_state)| mod_state.imports.contains(node))
1910 || right_to_main
1911 .iter()
1912 .any(|(_, mod_state)| mod_state.imports.contains(node))
1913 };
1914
1915 #[cfg(debug_assertions)]
1916 let mut check_imports = vec![];
1917
1918 for (id, mut shared_module) in shared_with_main {
1919 debug_assert!(shared_module.split_points.is_empty());
1920
1921 for node in &shared_module.exports {
1922 if !main_module.imports.remove(node) {
1925 log::trace!(
1926 "Shared module symbol not found in main: {node:?}. It probably was removed in other shared entry."
1927 );
1928 }
1929 if is_imported_by_other(node) {
1931 main_module.exports.insert(*node);
1932 }
1933 }
1934
1935 #[cfg(debug_assertions)]
1937 for node in &shared_module.imports {
1938 check_imports.push(*node);
1939 }
1940 log::trace!(
1941 "extending main defined symbols with shared ({id:?}): {:?}",
1942 shared_module.defined_symbols
1943 );
1944
1945 main_module
1946 .defined_symbols
1947 .extend(std::mem::take(&mut shared_module.defined_symbols));
1948 }
1949
1950 debug_assert!(main_module.imports.is_empty());
1951 #[cfg(debug_assertions)]
1952 for node in check_imports {
1953 assert!(
1954 main_module.defined_symbols.contains(&node),
1955 "Shared module import not found in main defined symbols: {node:?}"
1956 );
1957 }
1958
1959 program_info.output_modules = std::mem::take(&mut other);
1960}
1961
1962pub fn emit_modules<'a, 'src>(
1963 module: &'a analysis::ModuleInfo<'src>,
1964 verbose: bool,
1965 program_info: &SplitProgramInfo,
1966 wbg_fns: &MiniSet<SymbolId>,
1967 precise_modification: bool,
1968 whitelist: Option<&BTreeSet<SplitModuleIdentifier>>,
1969 version: BumpVersion,
1970 emit_fn: impl FnMut(&SplitModuleIdentifier, &[u8]) -> anyhow::Result<()>,
1971) -> anyhow::Result<()> {
1972 let emit_info = CommonEmitInfo::new(module, verbose, program_info)?;
1973 let calculated = ComputedModules::produce_state(
1974 &emit_info,
1975 verbose,
1976 module,
1977 program_info,
1978 version,
1979 |func_id| wbg_fns.contains(&func_id),
1980 )
1981 .context("Error calculating modules")?;
1982 calculated.emit_modules(precise_modification, whitelist, emit_fn)?;
1983 Ok(())
1984}