Files
hakorune/src/backend/wasm/memory.rs

406 lines
12 KiB
Rust
Raw Normal View History

/*!
* WASM Memory Management - Box layout and heap allocation
*
* Phase 8.3 PoC2: Implements bump allocator and Box memory layout
* Memory Layout: 0x000-0x3FF (reserved), 0x400-0x7FF (stack), 0x800+ (heap)
*/
use super::WasmError;
use std::collections::HashMap;
/// Box memory layout definition
#[derive(Debug, Clone)]
pub struct BoxLayout {
pub type_id: u32,
pub size: u32,
pub field_offsets: HashMap<String, u32>,
}
impl BoxLayout {
pub fn new(type_name: &str) -> Self {
// Assign consistent type IDs for standard Box types
let type_id = match type_name {
"StringBox" => 0x1001,
"IntegerBox" => 0x1002,
"BoolBox" => 0x1003,
"ArrayBox" => 0x1004,
"DataBox" => 0x1005, // For testing
_ => {
// Generate ID from hash for custom types
type_name.chars().map(|c| c as u32).sum::<u32>() % 65536 + 0x2000
}
};
Self {
type_id,
size: 12, // Header: type_id + ref_count + field_count
field_offsets: HashMap::new(),
}
}
pub fn add_field(&mut self, field_name: String) {
let offset = self.size;
self.field_offsets.insert(field_name, offset);
self.size += 4; // Each field is 4 bytes (i32)
}
pub fn get_field_offset(&self, field_name: &str) -> Option<u32> {
self.field_offsets.get(field_name).copied()
}
}
/// WASM memory manager
pub struct MemoryManager {
/// Known Box layouts
box_layouts: HashMap<String, BoxLayout>,
/// Current heap pointer (starts at 0x800)
heap_start: u32,
}
impl MemoryManager {
pub fn new() -> Self {
let mut manager = Self {
box_layouts: HashMap::new(),
heap_start: 0x800, // 2KB reserved for stack/globals
};
// Register standard Box types
manager.register_standard_box_types();
manager
}
/// Register standard built-in Box types
fn register_standard_box_types(&mut self) {
// StringBox: [type_id][ref_count][field_count][ptr_to_chars][length]
self.register_box_type("StringBox".to_string(), vec!["data_ptr".to_string(), "length".to_string()]);
// IntegerBox: [type_id][ref_count][field_count][value]
self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]);
// BoolBox: [type_id][ref_count][field_count][value]
self.register_box_type("BoolBox".to_string(), vec!["value".to_string()]);
// DataBox: [type_id][ref_count][field_count][value] - for testing
self.register_box_type("DataBox".to_string(), vec!["value".to_string()]);
}
/// Register a Box type layout
pub fn register_box_type(&mut self, type_name: String, fields: Vec<String>) {
let mut layout = BoxLayout::new(&type_name);
for field in fields {
layout.add_field(field);
}
self.box_layouts.insert(type_name, layout);
}
/// Get Box layout by type name
pub fn get_box_layout(&self, type_name: &str) -> Option<&BoxLayout> {
self.box_layouts.get(type_name)
}
/// Generate WASM globals for heap management
pub fn get_globals(&self) -> Vec<String> {
vec![
format!("(global $heap_ptr (mut i32) (i32.const {}))", self.heap_start),
]
}
/// Generate heap allocation function with 4-byte alignment
pub fn get_malloc_function(&self) -> String {
format!(
r#"(func $malloc (param $size i32) (result i32)
(local $ptr i32)
(local $aligned_size i32)
;; Align size to 4-byte boundary
local.get $size
i32.const 3
i32.add
i32.const -4
i32.and
local.set $aligned_size
;; Get current heap pointer
global.get $heap_ptr
local.set $ptr
;; Advance heap pointer by aligned size
global.get $heap_ptr
local.get $aligned_size
i32.add
global.set $heap_ptr
;; Return allocated pointer
local.get $ptr
)"#
)
}
/// Generate Box allocation function for specific type
pub fn get_box_alloc_function(&self, type_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
Ok(format!(
r#"(func $alloc_{} (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const {}
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const {}
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const {}
i32.store
;; Return box pointer
local.get $ptr
)"#,
type_name.to_lowercase(),
layout.size,
layout.type_id,
layout.field_offsets.len()
))
}
/// Generate field getter function
pub fn get_field_get_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?;
Ok(format!(
r#"(func $get_{}_{} (param $box_ptr i32) (result i32)
;; Verify type_id (optional safety check)
local.get $box_ptr
i32.load
i32.const {}
i32.ne
if
i32.const 0
return
end
;; Load field value
local.get $box_ptr
i32.const {}
i32.add
i32.load
)"#,
type_name.to_lowercase(),
field_name,
layout.type_id,
offset
))
}
/// Generate field setter function
pub fn get_field_set_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?;
Ok(format!(
r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32)
;; Verify type_id (optional safety check)
local.get $box_ptr
i32.load
i32.const {}
i32.ne
if
return
end
;; Store field value
local.get $box_ptr
i32.const {}
i32.add
local.get $value
i32.store
)"#,
type_name.to_lowercase(),
field_name,
layout.type_id,
offset
))
}
/// Get memory layout constants for documentation
pub fn get_memory_layout_info(&self) -> String {
format!(
r#"
;; Memory Layout:
;; 0x000-0x3FF: Reserved/globals (1KB)
;; 0x400-0x7FF: Stack space (1KB)
;; 0x800+: Heap (bump allocator)
;;
;; Box Layout: [type_id:i32][ref_count:i32][field_count:i32][field0:i32][field1:i32]...
;;
;; Standard Type IDs:
;; StringBox: 0x1001, IntegerBox: 0x1002, BoolBox: 0x1003
;; ArrayBox: 0x1004, DataBox: 0x1005
;; Custom: 0x2000+
;;
;; Heap start: 0x{:x}
"#,
self.heap_start
)
}
/// Get type ID for a Box type
pub fn get_type_id(&self, type_name: &str) -> Option<u32> {
self.box_layouts.get(type_name).map(|layout| layout.type_id)
}
/// Generate generic Box creation helper
pub fn get_generic_box_alloc_function(&self) -> String {
format!(
r#"(func $box_alloc (param $type_id i32) (param $field_count i32) (result i32)
(local $ptr i32)
(local $total_size i32)
;; Calculate total size: header (12) + fields (field_count * 4)
local.get $field_count
i32.const 4
i32.mul
i32.const 12
i32.add
local.set $total_size
;; Allocate memory
local.get $total_size
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
local.get $type_id
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
local.get $field_count
i32.store
;; Return box pointer
local.get $ptr
)"#
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_box_layout_creation() {
let layout = BoxLayout::new("DataBox");
assert_eq!(layout.size, 12); // type_id + ref_count + field_count
assert_eq!(layout.type_id, 0x1005); // DataBox has specific ID
assert!(layout.field_offsets.is_empty());
}
#[test]
fn test_box_layout_field_addition() {
let mut layout = BoxLayout::new("DataBox");
layout.add_field("field1".to_string());
layout.add_field("field2".to_string());
assert_eq!(layout.size, 20); // 12 + 4 + 4
assert_eq!(layout.get_field_offset("field1"), Some(12));
assert_eq!(layout.get_field_offset("field2"), Some(16));
}
#[test]
fn test_memory_manager_standard_types() {
let manager = MemoryManager::new();
// Verify standard types are registered
assert!(manager.get_box_layout("StringBox").is_some());
assert!(manager.get_box_layout("IntegerBox").is_some());
assert!(manager.get_box_layout("BoolBox").is_some());
assert!(manager.get_box_layout("DataBox").is_some());
// Verify type IDs
assert_eq!(manager.get_type_id("StringBox"), Some(0x1001));
assert_eq!(manager.get_type_id("IntegerBox"), Some(0x1002));
assert_eq!(manager.get_type_id("DataBox"), Some(0x1005));
}
#[test]
fn test_memory_manager_registration() {
let mut manager = MemoryManager::new();
manager.register_box_type("CustomBox".to_string(), vec!["x".to_string(), "y".to_string()]);
let layout = manager.get_box_layout("CustomBox").unwrap();
assert_eq!(layout.field_offsets.len(), 2);
assert!(layout.get_field_offset("x").is_some());
assert!(layout.get_field_offset("y").is_some());
assert!(layout.type_id >= 0x2000); // Custom types start at 0x2000
}
#[test]
fn test_malloc_function_generation() {
let manager = MemoryManager::new();
let malloc_func = manager.get_malloc_function();
assert!(malloc_func.contains("$malloc"));
assert!(malloc_func.contains("$heap_ptr"));
assert!(malloc_func.contains("global.get"));
assert!(malloc_func.contains("i32.and")); // Alignment check
}
#[test]
fn test_box_alloc_function_generation() {
let manager = MemoryManager::new();
let alloc_func = manager.get_box_alloc_function("DataBox").unwrap();
assert!(alloc_func.contains("$alloc_databox"));
assert!(alloc_func.contains("call $malloc"));
assert!(alloc_func.contains("4101")); // 0x1005 type ID for DataBox
assert!(alloc_func.contains("i32.const 1")); // ref_count initialization
}
#[test]
fn test_generic_box_alloc_function() {
let manager = MemoryManager::new();
let generic_func = manager.get_generic_box_alloc_function();
assert!(generic_func.contains("$box_alloc"));
assert!(generic_func.contains("$type_id"));
assert!(generic_func.contains("$field_count"));
assert!(generic_func.contains("i32.const 12")); // Header size
}
}