Skip to content

Commit f28a20e

Browse files
author
Arnaud Riess
committed
refactor: replace usize indices with typed index wrappers for improved type safety
1 parent b633e49 commit f28a20e

9 files changed

Lines changed: 246 additions & 116 deletions

File tree

crates/herkos/src/codegen/constructor.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ pub fn emit_const_globals<B: Backend>(_backend: &B, info: &ModuleInfo) -> String
3939
pub fn emit_element_segments(info: &ModuleInfo, table_receiver: &str) -> String {
4040
let mut code = String::new();
4141
for seg in &info.element_segments {
42-
for (i, func_idx) in seg.func_indices.iter().enumerate() {
42+
for (i, func_gidx) in seg.func_indices.iter().enumerate() {
4343
let table_idx = seg.offset + i;
44-
let local_func_idx = *func_idx - info.num_imported_functions();
44+
let global_func_idx_usize = func_gidx.as_usize();
45+
let local_func_idx = global_func_idx_usize - info.num_imported_functions();
46+
let local_func_idx_wrapped = LocalFuncIdx::new(local_func_idx);
4547
let type_idx = info
46-
.ir_functions
47-
.get(local_func_idx)
48-
.map(|f| f.type_idx)
48+
.ir_function(local_func_idx_wrapped)
49+
.map(|f| f.type_idx.as_usize())
4950
.unwrap_or(0);
5051
code.push_str(&format!(
5152
" {}.set({}, Some(FuncRef {{ type_index: {}, func_index: {} }})).unwrap();\n",

crates/herkos/src/codegen/export.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn generate_export_impl<B: Backend>(_backend: &B, info: &ModuleInfo) -> Stri
1717
let export_names: std::collections::HashMap<usize, &str> = info
1818
.func_exports
1919
.iter()
20-
.map(|e| (e.func_index, e.name.as_str()))
20+
.map(|e| (e.func_index.as_usize(), e.name.as_str()))
2121
.collect();
2222

2323
// Generate accessor methods for all functions

crates/herkos/src/codegen/function.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,11 @@ pub fn generate_function_with_info<B: Backend>(
7070
func_idx,
7171
..
7272
} => {
73-
// func_idx is in global space (imports + locals).
74-
// For Milestone 1, we error on imported functions during codegen,
75-
// so just use a fallback type here if it's an import.
76-
let ty = if *func_idx >= info.num_imported_functions() {
77-
let local_idx = func_idx - info.num_imported_functions();
78-
info.ir_functions
79-
.get(local_idx)
80-
.and_then(|f| f.return_type)
81-
.unwrap_or(WasmType::I32)
82-
} else {
83-
// Call to imported function — will error during codegen.
84-
// Use fallback type for now.
85-
WasmType::I32
86-
};
73+
// func_idx is in local space (imports already excluded)
74+
let ty = info
75+
.ir_function(func_idx.clone())
76+
.and_then(|f| f.return_type)
77+
.unwrap_or(WasmType::I32);
8778
var_types.insert(*dest, ty);
8879
}
8980
IrInstr::CallImport {
@@ -93,8 +84,7 @@ pub fn generate_function_with_info<B: Backend>(
9384
} => {
9485
// Look up import signature from func_imports
9586
let ty = info
96-
.func_imports
97-
.get(*import_idx)
87+
.func_import(import_idx.clone())
9888
.and_then(|imp| imp.return_type)
9989
.unwrap_or(WasmType::I32);
10090
var_types.insert(*dest, ty);
@@ -107,17 +97,9 @@ pub fn generate_function_with_info<B: Backend>(
10797
}
10898
}
10999
IrInstr::GlobalGet { dest, index } => {
110-
// Distinguish imported globals (lower indices) from local globals
111-
let ty = if *index < info.imported_globals.len() {
112-
// Imported global
113-
info.imported_globals[*index].wasm_type
114-
} else {
115-
// Local global — adjust index by removing imported count
116-
let local_idx = *index - info.imported_globals.len();
117-
info.globals
118-
.get(local_idx)
119-
.map(|g| g.init_value.ty())
120-
.unwrap_or(WasmType::I32)
100+
let ty = match info.resolve_global(*index) {
101+
ResolvedGlobal::Imported(_idx, g) => g.wasm_type,
102+
ResolvedGlobal::Local(_idx, g) => g.init_value.ty(),
121103
};
122104
var_types.insert(*dest, ty);
123105
}
@@ -127,8 +109,7 @@ pub fn generate_function_with_info<B: Backend>(
127109
..
128110
} => {
129111
let ty = info
130-
.type_signatures
131-
.get(*type_idx)
112+
.type_signature(type_idx.clone())
132113
.and_then(|s| s.return_type)
133114
.unwrap_or(WasmType::I32);
134115
var_types.insert(*dest, ty);
@@ -351,7 +332,7 @@ fn has_global_import_access(ir_func: &IrFunction, num_imported_globals: usize) -
351332
}
352333
ir_func.blocks.iter().any(|block| {
353334
block.instructions.iter().any(|instr| {
354-
matches!(instr, IrInstr::GlobalGet { index, .. } | IrInstr::GlobalSet { index, .. } if *index < num_imported_globals)
335+
matches!(instr, IrInstr::GlobalGet { index, .. } | IrInstr::GlobalSet { index, .. } if index.as_usize() < num_imported_globals)
355336
})
356337
})
357338
}

crates/herkos/src/codegen/instruction.rs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ pub fn generate_instruction_with_info<B: Backend>(
4646
let has_globals = info.has_mutable_globals();
4747
let has_memory = info.has_memory;
4848
let has_table = info.has_table();
49-
backend.emit_call(*dest, *func_idx, args, has_globals, has_memory, has_table)
49+
backend.emit_call(
50+
*dest,
51+
func_idx.as_usize(),
52+
args,
53+
has_globals,
54+
has_memory,
55+
has_table,
56+
)
5057
}
5158

5259
IrInstr::CallImport {
@@ -62,38 +69,26 @@ pub fn generate_instruction_with_info<B: Backend>(
6269
type_idx,
6370
table_idx,
6471
args,
65-
} => generate_call_indirect(*dest, *type_idx, *table_idx, args, info),
72+
} => generate_call_indirect(*dest, type_idx.clone(), *table_idx, args, info),
6673

6774
IrInstr::Assign { dest, src } => backend.emit_assign(*dest, *src),
6875

69-
IrInstr::GlobalGet { dest, index } => {
70-
if *index < info.imported_globals.len() {
71-
// Imported global — access via host trait getter
72-
let g = &info.imported_globals[*index];
76+
IrInstr::GlobalGet { dest, index } => match info.resolve_global(*index) {
77+
ResolvedGlobal::Imported(_idx, g) => {
7378
format!(" {} = host.get_{}();", dest, g.name)
74-
} else {
75-
// Local global — use corrected index and backend
76-
let local_idx = index - info.imported_globals.len();
77-
let is_mutable = info
78-
.globals
79-
.get(local_idx)
80-
.map(|g| g.mutable)
81-
.unwrap_or(true);
82-
backend.emit_global_get(*dest, local_idx, is_mutable)
8379
}
84-
}
80+
ResolvedGlobal::Local(idx, g) => {
81+
let is_mutable = g.mutable;
82+
backend.emit_global_get(*dest, idx.as_usize(), is_mutable)
83+
}
84+
},
8585

86-
IrInstr::GlobalSet { index, value } => {
87-
if *index < info.imported_globals.len() {
88-
// Imported global — access via host trait setter
89-
let g = &info.imported_globals[*index];
86+
IrInstr::GlobalSet { index, value } => match info.resolve_global(*index) {
87+
ResolvedGlobal::Imported(_idx, g) => {
9088
format!(" host.set_{}({});", g.name, value)
91-
} else {
92-
// Local global — use corrected index and backend
93-
let local_idx = index - info.imported_globals.len();
94-
backend.emit_global_set(local_idx, *value)
9589
}
96-
}
90+
ResolvedGlobal::Local(idx, _g) => backend.emit_global_set(idx.as_usize(), *value),
91+
},
9792

9893
IrInstr::MemorySize { dest } => backend.emit_memory_size(*dest),
9994

@@ -163,7 +158,7 @@ pub fn generate_terminator_with_mapping<B: Backend>(
163158
/// 3. Dispatches to the matching function via a match on func_index
164159
fn generate_call_indirect(
165160
dest: Option<VarId>,
166-
type_idx: usize,
161+
type_idx: TypeIdx,
167162
table_idx: VarId,
168163
args: &[VarId],
169164
info: &ModuleInfo,
@@ -174,11 +169,12 @@ fn generate_call_indirect(
174169

175170
// Canonicalize the type index for structural equivalence (Wasm spec §4.4.9).
176171
// Two different type indices with identical (params, results) must match.
172+
let type_idx_usize = type_idx.as_usize();
177173
let canon_idx = info
178174
.canonical_type
179-
.get(type_idx)
175+
.get(type_idx_usize)
180176
.copied()
181-
.unwrap_or(type_idx);
177+
.unwrap_or(type_idx_usize);
182178

183179
let mut code = String::new();
184180

@@ -218,7 +214,7 @@ fn generate_call_indirect(
218214
));
219215

220216
for (func_idx, ir_func) in info.ir_functions.iter().enumerate() {
221-
if ir_func.type_idx == canon_idx {
217+
if ir_func.type_idx.as_usize() == canon_idx {
222218
code.push_str(&format!(
223219
" {} => func_{}({})?,\n",
224220
func_idx, func_idx, args_str

crates/herkos/src/codegen/mod.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ mod tests {
195195
}],
196196
entry_block: BlockId(0),
197197
return_type: Some(WasmType::I32),
198-
type_idx: 0,
198+
type_idx: TypeIdx::new(0),
199199
needs_host: false,
200200
};
201201

@@ -245,7 +245,7 @@ mod tests {
245245
}],
246246
entry_block: BlockId(0),
247247
return_type: None,
248-
type_idx: 0,
248+
type_idx: TypeIdx::new(0),
249249
needs_host: false,
250250
};
251251

@@ -350,7 +350,7 @@ mod tests {
350350
}],
351351
entry_block: BlockId(0),
352352
return_type: Some(WasmType::I64),
353-
type_idx: 0,
353+
type_idx: TypeIdx::new(0),
354354
needs_host: false,
355355
};
356356

@@ -413,7 +413,7 @@ mod tests {
413413
}],
414414
entry_block: BlockId(0),
415415
return_type: Some(WasmType::I32),
416-
type_idx: 0,
416+
type_idx: TypeIdx::new(0),
417417
needs_host: false,
418418
};
419419

@@ -457,15 +457,15 @@ mod tests {
457457
id: BlockId(0),
458458
instructions: vec![IrInstr::GlobalGet {
459459
dest: VarId(0),
460-
index: 0,
460+
index: GlobalIdx::new(0),
461461
}],
462462
terminator: IrTerminator::Return {
463463
value: Some(VarId(0)),
464464
},
465465
}],
466466
entry_block: BlockId(0),
467467
return_type: Some(WasmType::I32),
468-
type_idx: 0,
468+
type_idx: TypeIdx::new(0),
469469
needs_host: false,
470470
};
471471

@@ -484,7 +484,7 @@ mod tests {
484484
data_segments: Vec::new(),
485485
func_exports: vec![FuncExport {
486486
name: "get_value".to_string(),
487-
func_index: 0,
487+
func_index: LocalFuncIdx::new(0),
488488
}],
489489
type_signatures: Vec::new(),
490490
canonical_type: Vec::new(),
@@ -530,7 +530,7 @@ mod tests {
530530
}],
531531
entry_block: BlockId(0),
532532
return_type: Some(WasmType::I32),
533-
type_idx: 0,
533+
type_idx: TypeIdx::new(0),
534534
needs_host: false,
535535
};
536536

@@ -549,7 +549,7 @@ mod tests {
549549
}],
550550
func_exports: vec![FuncExport {
551551
name: "load_word".to_string(),
552-
func_index: 0,
552+
func_index: LocalFuncIdx::new(0),
553553
}],
554554
type_signatures: Vec::new(),
555555
canonical_type: Vec::new(),
@@ -587,15 +587,15 @@ mod tests {
587587
id: BlockId(0),
588588
instructions: vec![IrInstr::GlobalGet {
589589
dest: VarId(0),
590-
index: 0,
590+
index: GlobalIdx::new(0),
591591
}],
592592
terminator: IrTerminator::Return {
593593
value: Some(VarId(0)),
594594
},
595595
}],
596596
entry_block: BlockId(0),
597597
return_type: Some(WasmType::I32),
598-
type_idx: 0,
598+
type_idx: TypeIdx::new(0),
599599
needs_host: false,
600600
};
601601

@@ -614,7 +614,7 @@ mod tests {
614614
data_segments: Vec::new(),
615615
func_exports: vec![FuncExport {
616616
name: "get_const".to_string(),
617-
func_index: 0,
617+
func_index: LocalFuncIdx::new(0),
618618
}],
619619
type_signatures: Vec::new(),
620620
canonical_type: Vec::new(),

crates/herkos/src/ir/builder/assembly.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ fn build_element_segments(parsed: &ParsedModule) -> Vec<ElementSegmentDef> {
8888
.iter()
8989
.map(|es| ElementSegmentDef {
9090
offset: es.offset as usize,
91-
func_indices: es.func_indices.iter().map(|idx| *idx as usize).collect(),
91+
func_indices: es
92+
.func_indices
93+
.iter()
94+
.map(|idx| GlobalFuncIdx::new(*idx as usize))
95+
.collect(),
9296
})
9397
.collect()
9498
}
@@ -104,7 +108,7 @@ fn build_function_exports(parsed: &ParsedModule, num_imported_functions: usize)
104108
.filter(|e| e.kind == ExportKind::Func && (e.index as usize) >= num_imported_functions)
105109
.map(|e| FuncExport {
106110
name: e.name.clone(),
107-
func_index: (e.index as usize) - num_imported_functions,
111+
func_index: LocalFuncIdx::new((e.index as usize) - num_imported_functions),
108112
})
109113
.collect()
110114
}
@@ -122,7 +126,7 @@ fn enrich_ir_functions(
122126
let num_imported_globals = imported_globals.len();
123127
for (func_idx, func) in parsed.functions.iter().enumerate() {
124128
if let Some(ir_func) = ir_functions.get_mut(func_idx) {
125-
ir_func.type_idx = canonical_type[func.type_idx as usize];
129+
ir_func.type_idx = TypeIdx::new(canonical_type[func.type_idx as usize]);
126130
ir_func.needs_host = function_calls_imports(ir_func, num_imported_globals);
127131
}
128132
}
@@ -138,7 +142,7 @@ fn function_calls_imports(ir_func: &IrFunction, num_imported_globals: usize) ->
138142
instr,
139143
IrInstr::GlobalGet { index, .. }
140144
| IrInstr::GlobalSet { index, .. }
141-
if *index < num_imported_globals
145+
if index.as_usize() < num_imported_globals
142146
))
143147
})
144148
})
@@ -162,7 +166,7 @@ fn build_call_indirect_signatures(parsed: &ParsedModule) -> Vec<FuncSignature> {
162166
FuncSignature {
163167
params,
164168
return_type,
165-
type_idx: 0,
169+
type_idx: TypeIdx::new(0),
166170
needs_host: false,
167171
}
168172
})

crates/herkos/src/ir/builder/core.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,8 @@ impl IrBuilder {
233233
blocks: self.blocks.clone(),
234234
entry_block: entry,
235235
return_type,
236-
type_idx: 0, // Set by enrich_ir_functions during assembly
237-
needs_host: false, // Set by enrich_ir_functions during assembly
236+
type_idx: TypeIdx::new(0), // Set by enrich_ir_functions during assembly
237+
needs_host: false, // Set by enrich_ir_functions during assembly
238238
})
239239
}
240240

0 commit comments

Comments
 (0)