Skip to main content

wamex_cli/emit/
mod.rs

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    /// Keep original layout of data segment and indirect_function_table.
46    /// This will emit "gaps" in "element" and in "data" sections.
47    ///
48    /// This layout is non-portable and doesn't support dynamic module re-building/re-loading.
49    /// It is useful in production, to split size of generated WASM binaries.
50    ///
51    OriginalLayout,
52
53    /// Replace constant offsets with GOT+offset (for memory and table access).
54    /// Allocate memory and table dynamically on module loading.
55    ///
56    /// This allow dynamic reloading of modules.
57    /// Modules entrypoints are still located in fixed position in table.
58    ///
59    DynamicLinking {
60        /// Fixed offset for storing entrypoints.
61        table_offset: u32,
62        /// Number of reserved elements in the table.
63        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        // List of modifications that should be applied to this function.
76        modification_list: Vec<modify::CodeModifyEntry>,
77    },
78    IndirectTrampoline {
79        /// Index of extra table entry after main module entries.
80        table_index_offset: u32,
81    },
82    // Stub function generated for imported functions
83    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    // use existing import function
102    Existing {
103        module_name: &'a str,
104        import_function_name: &'a str,
105    },
106    // Add new import function from another module (e.g. main module).
107    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                // format!("__wamex_link_{}", link_module)
133            }
134        }
135    }
136}
137
138impl ImportedFunction<'_> {
139    pub fn input_func_id(&self) -> InputFuncId {
140        self.input_func_id
141    }
142}
143
144// ignore relocations field in order
145impl 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"]; // "__data_end", "__heap_base" - i
183    #[allow(dead_code)]
184    const MAIN_GLOBAL_EXPORTS_COUNT: u32 = Self::MAIN_GLOBAL_EXPORTS.len() as u32;
185}
186
187// 'any are used because associated types are invariant, and used in default impls for Indexed Vec/Map impls.
188pub struct ModuleEmitState<'any, 'src> {
189    functions: WithOriginalIndex<'src, DefinedFunction>,
190
191    // Global variables:
192    // - lib_base_id for library base address (import)
193    // - existing globals from src module
194    // - "store" globals for `modify::constant_extractions`
195    // - globals for data segments (lib_base_id + offset)
196    globals: WithOriginalIndex<'src, DefinedGlobal<'src>>,
197    pub global_tmp_store: BTreeMap<StoreType, OutputGlobalId>,
198    // extra imports that should be emitted for lib
199    // Not available for main module.
200    sub_module_extra: Option<SubModuleExtra>,
201
202    // Data Section
203    data: IdMap<DataSegmentId, memory_layout::DataSegmentOutput>,
204    //TODO: Remove data_relocations, instead of DataSegmentOutput use SegmentLayout
205    data_relocations: IdMap<DataSegmentId, Vec<modify::DataModifyEntry>>,
206
207    // src module
208    pub src: &'any analysis::ModuleInfo<'src>,
209    // Indirect function table Functions from original table that are used in this module.
210    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; //TODO: Support multiple memories
217impl<'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        // Main module with static layout of memory and table.
227        // None if it is main module.
228        static_main: Option<&Self>,
229        // deps of current module
230        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!("module_id: {module_id:#?}");
237        log::debug!("shared_modules: {shared_modules:#?}");
238        // We need to include definitions for all of the `defined_symbols`.
239        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                // if any module linked to current function
261                let static_export = output_module_info.exports.contains(&sym);
262                // Or it is linked indirectly via split points
263                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            // submodule imports needed function from main module.
293            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                            // TODO: support multiple dep modules
305                            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                            // TODO: only __stack_pointer should be mutable
361                            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        // filter only used entries
409        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        // offset of current segment.
441        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            // TODO: apply relocations to data segment
449            segment_mem_offset = new_segment_offset + out.as_raw().len();
450
451            data_segment_outputs.insert(id, out);
452        }
453
454        // TODO: replace with is_symbol_static (is it located in main module?)
455        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        // TODO: move shift in previous (segment_id, segment) in data_segments.iter()
479        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, // sym.data_mem_offset as u32,
495                            }),
496                        };
497
498                        // relocs has offset relative to symbol - update to be relative to segment
499                        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            // Collect all relocation entries that modify something within this function.
516            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            // TODO: Add trampoline for __wasm_bindgen_ functions that for some reasons exported.
531            // For now there known to be only `wasm_bindgen::__rt::wbg_cast::breaks_if_inlined::`
532            // special functions are exported trough trampolines
533            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        // dbg!(&globals);
661
662        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    // Return got info for a given dep or this module itself.
678    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        // Encode type section
716        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            // for submodules this is imported
721            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_wasm_bindgen_sections(output_module);
734        // Names + Linking + Relocations
735        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    // TODO: Regenerate function types section (remove unused types)
742    fn generate_type_section(&self, output_module: &mut wasm_encoder::Module) -> Result<()> {
743        // Simply copy all types.  Unneeded types may be pruned by `wasm-opt`.
744        let mut section = wasm_encoder::TypeSection::new();
745        // Only use func_types from OutputFunctions
746        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(&section);
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        // TODO: Only main module should have original imports.
764        // Submodule should contain data + function imports from main module.
765        // Additionally import memory + heap + stack globals.
766
767        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                // Copy all non-function imports from input.
781                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                // Import all globals that are exported from main module.
795                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                // Import all memories defined by the input module.
813                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(&section);
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    // Get type of output function by index.
834    // TODO: regenerate type section
835    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    // Get name of output function by index.
841    // Used in generating exports and names section.
842    // TODO: Use for generating import sections as well?
843    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                // modify name for import stubs to avoid conflicts
860                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(), // TODO: convert indexes?
883                    ))
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(), // TODO: convert indexes?
902                    ))
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        // left original exports as is (because this module should be drop-in replacement)
911        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                    // Export GOT base globals.
955                    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                // Export globals.
971                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                    // TODO: fix global id?
981                    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(&section);
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        // add start function
1025        if !self.is_main() {
1026            section.function(self.find_void_type().as_raw_index() as u32);
1027        }
1028
1029        output_module.section(&section);
1030    }
1031
1032    // only for main
1033    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(&section);
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    // The indirect_function table is shared between main module and submodules.
1075    // it's layout is:
1076    // [ 0: empty ]
1077    // [ 1..N: functions used in this module ]
1078    // [ N+1..N+M: reserved space for lazy stubs, main module fill it empty, and submodules fill it with stubs ]
1079    // [ N+M+1.. : dynamic allocated entries - used for tables in submodules ]
1080    //
1081    // Example of final layout:
1082    // 1. After main load:
1083    // [0, f1, f2, f3, ..., s1_entry1_uninit, s1_entry2_uninit, s2_entry1_uninit, ...]
1084    // 2. After submodule load:
1085    // [0, f1, f2, f3, ..., s1_entry1,        s1_entry2,        0,                 s1_f1, s1_f2, ...]
1086    // 3. If submodule reloaded, the following changes are applied:
1087    // [_, _, _, _, ...,    s1_FIX_entry1,    s1_FIX_entry2,    _,                 _,     _,     s1_FIX_f1, s1_FIX_f2, ...]
1088    // Note that original s1_f1 and s1_f2 are not removed, because other submodules may use them.
1089    // And only after calling linker::unload we can reuse these entries.
1090    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) // skip empty entry at index 0 for main module
1099        };
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        // generate empty entries for lazy entrypoints
1105        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; // TODO: Place real abort function
1115                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(&section);
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(&section);
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(&section);
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    // func [param i32 i32 ...] (result i32):
1200    // local.get 0
1201    // local.get 1
1202    // ...
1203    // i32.const table_index
1204    // call_indirect (type type_id) (table 0)
1205    // end
1206    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, // __indirect_function_table // TODO: support multiple tables
1223        });
1224        func.instruction(&wasm_encoder::Instruction::End);
1225        section.function(&func);
1226        // TODO: Add relocations for call_indirect
1227        Ok(vec![])
1228    }
1229
1230    // Import fns can't be exported directy because wasm convert their type to externrefs.
1231    // So instead generate stub function that will call the imported function.
1232
1233    // func [param i32 i32 ...] (result i32):
1234    // local.get 0
1235    // local.get 1
1236    // ...
1237    // call import_func_index
1238    // end
1239    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        // TODO: Add relocations for call/type_ids
1261        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 // start function
1313            } 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(&section);
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        // TODO: Add shifter relocs
1375        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            // Skip empty data segments
1381            // if data.data.is_empty() {
1382            //     continue;
1383            // }
1384            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(&section);
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, // as power of 2
1423                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                //TODO: calculate imports of deps.
1438                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(&section);
1445        }
1446        Ok(())
1447    }
1448
1449    // linking| names
1450    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        // let mut section = wasm_encoder::CustomSection::new("linking");
1475        // section.data(&self.info.source.linking);
1476        // output_module.section(&section);
1477        // dbg!(&self.info.source.names);
1478        // dbg!(&self.info.source.linking);
1479        // dbg!(&self.info.source.relocs);
1480        Ok(())
1481    }
1482    // wasm-bindgen
1483    // other whitelisted
1484    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; // print only on main module
1490                    }
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(&section);
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        // main module has 1 stub at start
1520        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        // + 1 due to empty entry at index 0
1535        let indirect_table_size = self.table_entries.len() as u64 + 1 + self.num_extra_stubs; // reserve space for stubs at start
1536
1537        wasm_encoder::TableType {
1538            element_type: wasm_encoder::RefType::FUNCREF,
1539            minimum: indirect_table_size,
1540            maximum: None, //Some(indirect_table_size as u64), // TODO: limit?
1541            shared: false,
1542            table64: false,
1543        }
1544    }
1545}
1546
1547#[derive(Debug)]
1548pub struct ModuleDecl {
1549    pub split_points: Vec<SplitPoint>,
1550
1551    // offset in indirect_function table where this module's entrypoints start
1552    split_points_offset: u32,
1553}
1554
1555#[derive(Debug)]
1556pub struct CommonEmitInfo<'src> {
1557    pub src_data_segments: IdVec<SegmentLayout<'src>>,
1558
1559    // Imports (corresponding to split points) to exclude from all modules.
1560    pub split_point_imports: BTreeSet<InputFuncId>,
1561    pub modules_decl: HashMap<ModuleIdentifier, ModuleDecl>,
1562}
1563
1564impl<'src> CommonEmitInfo<'src> {
1565    // Return range in indirect_function table corresponding to module.
1566    // Use stubs_start offset to convert to final table indexes.
1567    // Returns None if module is not found.
1568    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    // Checks if given import function is an entrypoint for any module.
1590    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        // re-build data_segments (using only available symbols)
1627        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; // TODO: from args
1730
1731        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                    // Main module has no entrypoints.
1745                    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        // +1 for empty first entry
1832        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
1877// Merge modules that shared with main module into main itself.
1878pub 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    // split iter at 3 parts: before main, main, after main
1891    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    // check import in all remain modules except main
1905    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            // it was exported in shared module, so on main side it had been imported.
1923            // remove from main link symbols.
1924            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            // This was imported not only by main, so export is needed.
1930            if is_imported_by_other(node) {
1931                main_module.exports.insert(*node);
1932            }
1933        }
1934
1935        // imported modules should already be in main
1936        #[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}