Skip to content

Commit dcd0f5d

Browse files
author
Arnaud Riess
committed
refactored IR builder
1 parent fd02baa commit dcd0f5d

6 files changed

Lines changed: 1130 additions & 1016 deletions

File tree

crates/herkos/src/codegen/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ where
2727
for imp in imports {
2828
grouped
2929
.entry(get_module(imp).to_string())
30-
.or_insert_with(Vec::new)
30+
.or_default()
3131
.push(imp);
3232
}
3333
grouped
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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(&params, &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

Comments
 (0)