406 lines
12 KiB
Rust
406 lines
12 KiB
Rust
/*!
|
|
* 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
|
|
}
|
|
} |