|
| 1 | +//! Module-level analysis - extracts metadata from parsed WebAssembly modules. |
| 2 | +//! |
| 3 | +//! This module performs structural analysis on a `ParsedModule` to extract |
| 4 | +//! memory, table, type, import, and function signature information needed |
| 5 | +//! for IR construction and code generation. |
| 6 | +
|
| 7 | +use super::super::types::*; |
| 8 | +use crate::parser::{ImportKind, ParsedModule}; |
| 9 | +use crate::TranspileOptions; |
| 10 | +use anyhow::{Context, Result}; |
| 11 | + |
| 12 | +/// Memory information extracted from the module. |
| 13 | +pub(super) struct MemoryInfo { |
| 14 | + pub(super) has_memory: bool, |
| 15 | + pub(super) has_memory_import: bool, |
| 16 | + pub(super) max_pages: usize, |
| 17 | + pub(super) initial_pages: usize, |
| 18 | +} |
| 19 | + |
| 20 | +/// Table information extracted from the module. |
| 21 | +pub(super) struct TableInfo { |
| 22 | + pub(super) initial: usize, |
| 23 | + pub(super) max: usize, |
| 24 | +} |
| 25 | + |
| 26 | +/// Extracts memory information from a parsed WASM module. |
| 27 | +pub(super) fn extract_memory_info( |
| 28 | + parsed: &ParsedModule, |
| 29 | + options: &TranspileOptions, |
| 30 | +) -> Result<MemoryInfo> { |
| 31 | + let has_memory = parsed.memory.is_some(); |
| 32 | + let has_memory_import = parsed |
| 33 | + .imports |
| 34 | + .iter() |
| 35 | + .any(|imp| matches!(imp.kind, ImportKind::Memory { .. })); |
| 36 | + let max_pages = if let Some(ref mem) = parsed.memory { |
| 37 | + mem.maximum_pages |
| 38 | + .map(|p| p as usize) |
| 39 | + .unwrap_or(options.max_pages) |
| 40 | + } else { |
| 41 | + options.max_pages |
| 42 | + }; |
| 43 | + let initial_pages = parsed |
| 44 | + .memory |
| 45 | + .as_ref() |
| 46 | + .map(|m| m.initial_pages as usize) |
| 47 | + .unwrap_or(0); |
| 48 | + |
| 49 | + Ok(MemoryInfo { |
| 50 | + has_memory, |
| 51 | + has_memory_import, |
| 52 | + max_pages, |
| 53 | + initial_pages, |
| 54 | + }) |
| 55 | +} |
| 56 | + |
| 57 | +/// Extracts table information from a parsed WASM module. |
| 58 | +pub(super) fn extract_table_info(parsed: &ParsedModule) -> TableInfo { |
| 59 | + if let Some(ref tbl) = parsed.table { |
| 60 | + TableInfo { |
| 61 | + initial: tbl.initial_size as usize, |
| 62 | + max: (tbl.max_size.unwrap_or(tbl.initial_size) as usize), |
| 63 | + } |
| 64 | + } else { |
| 65 | + TableInfo { initial: 0, max: 0 } |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +/// Builds canonical type index mapping and type signatures. |
| 70 | +/// |
| 71 | +/// Canonical mapping ensures that call_indirect type checks follow the Wasm spec: |
| 72 | +/// two different type indices with identical (params, results) must match. |
| 73 | +/// We map each type_idx to the smallest index with the same structural signature. |
| 74 | +pub(super) fn build_type_mappings( |
| 75 | + parsed: &ParsedModule, |
| 76 | +) -> (Vec<usize>, Vec<(usize, Option<WasmType>)>) { |
| 77 | + let canonical_type: Vec<usize> = { |
| 78 | + let mut mapping = Vec::with_capacity(parsed.types.len()); |
| 79 | + for (i, ty) in parsed.types.iter().enumerate() { |
| 80 | + let canon = parsed.types[..i] |
| 81 | + .iter() |
| 82 | + .position(|earlier| { |
| 83 | + earlier.params() == ty.params() && earlier.results() == ty.results() |
| 84 | + }) |
| 85 | + .map(|pos| mapping[pos]) |
| 86 | + .unwrap_or(i); |
| 87 | + mapping.push(canon); |
| 88 | + } |
| 89 | + mapping |
| 90 | + }; |
| 91 | + |
| 92 | + let type_sigs: Vec<(usize, Option<WasmType>)> = parsed |
| 93 | + .types |
| 94 | + .iter() |
| 95 | + .map(|ty| { |
| 96 | + let param_count = ty.params().len(); |
| 97 | + let ret = ty |
| 98 | + .results() |
| 99 | + .first() |
| 100 | + .map(|vt| WasmType::from_wasmparser(*vt)); |
| 101 | + (param_count, ret) |
| 102 | + }) |
| 103 | + .collect(); |
| 104 | + |
| 105 | + (canonical_type, type_sigs) |
| 106 | +} |
| 107 | + |
| 108 | +/// Extracts imported globals from a parsed WASM module. |
| 109 | +pub(super) fn build_imported_globals(parsed: &ParsedModule) -> Vec<ImportedGlobalDef> { |
| 110 | + parsed |
| 111 | + .imports |
| 112 | + .iter() |
| 113 | + .filter_map(|imp| { |
| 114 | + if let ImportKind::Global { val_type, mutable } = &imp.kind { |
| 115 | + Some(ImportedGlobalDef { |
| 116 | + module_name: imp.module_name.clone(), |
| 117 | + name: imp.name.clone(), |
| 118 | + wasm_type: WasmType::from_wasmparser(*val_type), |
| 119 | + mutable: *mutable, |
| 120 | + }) |
| 121 | + } else { |
| 122 | + None |
| 123 | + } |
| 124 | + }) |
| 125 | + .collect() |
| 126 | +} |
| 127 | + |
| 128 | +/// Builds the function signature list (imported functions followed by local functions). |
| 129 | +pub(super) fn build_function_signatures(parsed: &ParsedModule) -> Vec<(usize, Option<WasmType>)> { |
| 130 | + let mut func_sigs: Vec<(usize, Option<WasmType>)> = Vec::new(); |
| 131 | + |
| 132 | + // Imported function signatures |
| 133 | + for import in &parsed.imports { |
| 134 | + if let ImportKind::Function(type_idx) = &import.kind { |
| 135 | + let func_type = &parsed.types[*type_idx as usize]; |
| 136 | + let param_count = func_type.params().len(); |
| 137 | + let ret = func_type |
| 138 | + .results() |
| 139 | + .first() |
| 140 | + .map(|vt| WasmType::from_wasmparser(*vt)); |
| 141 | + func_sigs.push((param_count, ret)); |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + // Local function signatures |
| 146 | + for func in &parsed.functions { |
| 147 | + let func_type = &parsed.types[func.type_idx as usize]; |
| 148 | + let param_count = func_type.params().len(); |
| 149 | + let ret = func_type |
| 150 | + .results() |
| 151 | + .first() |
| 152 | + .map(|vt| WasmType::from_wasmparser(*vt)); |
| 153 | + func_sigs.push((param_count, ret)); |
| 154 | + } |
| 155 | + |
| 156 | + func_sigs |
| 157 | +} |
| 158 | + |
| 159 | +/// Parses Wasm operators from a function body. |
| 160 | +pub(super) fn parse_function_operators(body: &[u8]) -> Result<Vec<wasmparser::Operator<'_>>> { |
| 161 | + let mut operators = Vec::new(); |
| 162 | + let mut binary_reader = wasmparser::BinaryReader::new(body, 0); |
| 163 | + |
| 164 | + while !binary_reader.eof() { |
| 165 | + let op = binary_reader |
| 166 | + .read_operator() |
| 167 | + .context("failed to read operator")?; |
| 168 | + operators.push(op); |
| 169 | + } |
| 170 | + |
| 171 | + Ok(operators) |
| 172 | +} |
| 173 | + |
| 174 | +/// Translates all functions in the module to intermediate representation. |
| 175 | +pub(super) fn build_ir_functions( |
| 176 | + parsed: &ParsedModule, |
| 177 | + type_sigs: &[(usize, Option<WasmType>)], |
| 178 | + num_imported_functions: u32, |
| 179 | +) -> Result<Vec<IrFunction>> { |
| 180 | + use super::core::{IrBuilder, ModuleContext}; |
| 181 | + use crate::parser::ImportKind; |
| 182 | + |
| 183 | + let mut ir_builder = IrBuilder::new(); |
| 184 | + let mut ir_functions = Vec::new(); |
| 185 | + |
| 186 | + // Build function signature list (imported + local) |
| 187 | + let func_sigs = build_function_signatures(parsed); |
| 188 | + |
| 189 | + // Build function import list for IR builder |
| 190 | + let func_imports: Vec<(String, String)> = parsed |
| 191 | + .imports |
| 192 | + .iter() |
| 193 | + .filter_map(|imp| match &imp.kind { |
| 194 | + ImportKind::Function(_) => Some((imp.module_name.clone(), imp.name.clone())), |
| 195 | + _ => None, |
| 196 | + }) |
| 197 | + .collect(); |
| 198 | + |
| 199 | + let module_ctx = ModuleContext { |
| 200 | + func_signatures: func_sigs, |
| 201 | + type_signatures: type_sigs.to_vec(), |
| 202 | + num_imported_functions: num_imported_functions as usize, |
| 203 | + func_imports, |
| 204 | + }; |
| 205 | + |
| 206 | + for (func_idx, func) in parsed.functions.iter().enumerate() { |
| 207 | + let func_type = &parsed.types[func.type_idx as usize]; |
| 208 | + |
| 209 | + let params: Vec<_> = func_type |
| 210 | + .params() |
| 211 | + .iter() |
| 212 | + .map(|vt| (*vt, WasmType::from_wasmparser(*vt))) |
| 213 | + .collect(); |
| 214 | + |
| 215 | + let return_type = func_type |
| 216 | + .results() |
| 217 | + .first() |
| 218 | + .map(|vt| WasmType::from_wasmparser(*vt)); |
| 219 | + |
| 220 | + let operators = parse_function_operators(&func.body)?; |
| 221 | + |
| 222 | + let ir_func = ir_builder |
| 223 | + .translate_function(¶ms, &func.locals, return_type, &operators, &module_ctx) |
| 224 | + .with_context(|| format!("failed to build IR for function {}", func_idx))?; |
| 225 | + |
| 226 | + ir_functions.push(ir_func); |
| 227 | + } |
| 228 | + |
| 229 | + Ok(ir_functions) |
| 230 | +} |
0 commit comments