swamp_vm/
sparse.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::{TrapCode, Vm, get_reg, i16_from_u8s, set_reg, u16_from_u8s, u32_from_u8s};
6use std::ptr;
7use swamp_vm_isa::SparseIterator;
8
9impl Vm {
10    pub fn execute_sparse_init(
11        &mut self,
12        dest_reg: u8,
13        element_size_0: u8,
14        element_size_1: u8,
15        element_size_2: u8,
16        element_size_3: u8,
17        capacity_0: u8,
18        capacity_1: u8,
19    ) {
20        let element_size = u32_from_u8s!(
21            element_size_0,
22            element_size_1,
23            element_size_2,
24            element_size_3
25        );
26        let capacity = u16_from_u8s!(capacity_0, capacity_1);
27
28        unsafe {
29            let dest_addr = get_reg!(self, dest_reg);
30            let sparse_ptr = self.memory.get_heap_ptr(dest_addr as usize);
31            sparse_mem::init(sparse_ptr, capacity, element_size);
32        }
33    }
34
35    pub fn execute_sparse_add_get_entry_addr(
36        &mut self,
37        dest_entry_addr_reg: u8,
38        dest_handle_reg: u8,
39        sparse_ptr_reg: u8,
40        memory_size_0: u8,
41        memory_size_1: u8,
42        memory_size_2: u8,
43        memory_size_3: u8,
44    ) {
45        let element_size =
46            u32_from_u8s!(memory_size_0, memory_size_1, memory_size_2, memory_size_3);
47
48        unsafe {
49            let sparse_addr = get_reg!(self, sparse_ptr_reg);
50            let sparse_ptr = self.memory.get_heap_ptr(sparse_addr as usize);
51            if let Some((index, generation)) = sparse_mem::allocate(sparse_ptr) {
52                let relative_sparse_addr_offset = sparse_mem::values_offset(sparse_ptr);
53                let addr_for_values_start = sparse_addr as usize + relative_sparse_addr_offset;
54                let element_addr = addr_for_values_start + index as usize * element_size as usize;
55
56                let handle = (index as u32) << 16 | (generation as u32);
57                if self.debug_operations_enabled {
58                    eprintln!("sparse_add: handle: {handle} 0x{handle:X} ({index}:{generation})");
59                }
60                set_reg!(self, dest_handle_reg, handle);
61                set_reg!(self, dest_entry_addr_reg, element_addr as u32);
62            } else {
63                self.internal_trap(TrapCode::SparseOutOfSpace);
64            }
65        }
66    }
67
68    pub fn execute_sparse_get_entry_addr(
69        &mut self,
70        dest_entry_addr_reg: u8,
71        sparse_ptr_reg: u8,
72        int_handle_reg: u8,
73        memory_size_lower: u8,
74        memory_size_upper: u8,
75    ) {
76        let element_size = u16_from_u8s!(memory_size_lower, memory_size_upper);
77        let handle = get_reg!(self, int_handle_reg);
78
79        let index = handle >> 16;
80        let generation = handle & 0xffff;
81
82        unsafe {
83            let sparse_addr = get_reg!(self, sparse_ptr_reg);
84            let sparse_ptr = self.memory.get_heap_ptr(sparse_addr as usize);
85            if sparse_mem::is_alive(sparse_ptr, index as u16, generation as u16) {
86                let relative_sparse_addr_offset = sparse_mem::values_offset(sparse_ptr);
87                let addr_for_values_start = sparse_addr as usize + relative_sparse_addr_offset;
88                let element_addr = addr_for_values_start + index as usize * element_size as usize;
89                set_reg!(self, dest_entry_addr_reg, element_addr);
90
91                if self.debug_operations_enabled {
92                    eprintln!("sparse_get: handle: {handle} 0x{handle:X} ({index}:{generation})");
93                }
94            } else {
95                self.internal_trap(TrapCode::SparseGetFailed)
96            }
97        }
98    }
99
100    pub fn execute_sparse_remove(&mut self, sparse_ptr_reg: u8, int_handle_reg: u8) {
101        unsafe {
102            let sparse_addr = get_reg!(self, sparse_ptr_reg);
103            let sparse_ptr = self.memory.get_heap_ptr(sparse_addr as usize);
104            let handle = get_reg!(self, int_handle_reg);
105            let index = handle >> 16;
106            let generation = handle & 0xffff;
107            let could_be_removed = sparse_mem::remove(sparse_ptr, index as u16, generation as u16);
108            if !could_be_removed {
109                self.internal_trap(TrapCode::SparseRemoveFailed);
110            }
111            if self.debug_operations_enabled {
112                eprintln!("sparse_remove: handle: {handle} 0x{handle:X} ({index}:{generation})");
113            }
114        }
115    }
116
117    pub fn execute_sparse_is_alive(
118        &mut self,
119        dest_reg_bool: u8,
120        sparse_ptr_reg: u8,
121        int_handle_reg: u8,
122    ) {
123        unsafe {
124            let sparse_addr = get_reg!(self, sparse_ptr_reg);
125            let sparse_ptr = self.memory.get_heap_ptr(sparse_addr as usize);
126            let handle = get_reg!(self, int_handle_reg);
127            let index = handle >> 16;
128            let generation = handle & 0xffff;
129            let is_alive = sparse_mem::is_alive(sparse_ptr, index as u16, generation as u16);
130            set_reg!(self, dest_reg_bool, is_alive);
131            if self.debug_operations_enabled {
132                eprintln!("sparse_is_alive: handle: {handle} 0x{handle:X} ({index}:{generation})");
133            }
134        }
135    }
136
137    pub(crate) fn execute_sparse_iter_init(
138        &mut self,
139        target_map_iterator_header_reg: u8,
140        map_header_reg: u8,
141    ) {
142        let sparse_header_addr = get_reg!(self, map_header_reg);
143
144        #[cfg(feature = "debug_vm")]
145        self.debug_operations_enabled;
146        let map_iterator = SparseIterator {
147            sparse_header_heap_ptr: sparse_header_addr,
148            index: 0,
149        };
150
151        let map_iterator_mut_ptr =
152            self.get_ptr_from_reg(target_map_iterator_header_reg) as *mut SparseIterator;
153
154        unsafe {
155            ptr::write(map_iterator_mut_ptr, map_iterator);
156        }
157    }
158
159    pub fn get_sparse_iterator_header_ptr_from_reg(
160        &self,
161        map_iterator_reg: u8,
162    ) -> *mut SparseIterator {
163        self.get_ptr_from_reg(map_iterator_reg) as *mut SparseIterator
164    }
165
166    pub fn execute_sparse_iter_next_pair(
167        &mut self,
168        sparse_iterator_header_reg: u8,
169        target_key_reg: u8,
170        target_value_reg: u8,
171        branch_offset_lower: u8,
172        branch_offset_upper: u8,
173    ) {
174        let sparse_iterator =
175            self.get_sparse_iterator_header_ptr_from_reg(sparse_iterator_header_reg);
176
177        unsafe {
178            let sparse_header_addr = (*sparse_iterator).sparse_header_heap_ptr;
179            let sparse_header_ptr = self.memory.get_heap_const_ptr(sparse_header_addr as usize);
180
181            let element_count = sparse_mem::element_count(sparse_header_ptr);
182            let current_index = (*sparse_iterator).index as usize;
183            if (current_index as u32) < element_count as u32 {
184                let values_index = (*sparse_mem::slot_to_id_ptr_const(sparse_header_ptr)
185                    .add(current_index)) as usize;
186
187                #[cfg(feature = "debug_vm")]
188                if self.debug_operations_enabled {
189                    eprintln!("index {current_index}, slot: {values_index}");
190                }
191
192                let offset_to_values = sparse_mem::values_offset(sparse_header_ptr);
193                let values_start = sparse_header_addr as usize + offset_to_values;
194
195                let element_size = sparse_mem::element_size(sparse_header_ptr) as usize;
196                let entry_addr = values_start + values_index * element_size;
197
198                let generation =
199                    *sparse_mem::generation_ptr_const(sparse_header_ptr).add(values_index);
200                let handle = (values_index as u32) << 16 | (generation as u32);
201                set_reg!(self, target_key_reg, handle);
202                set_reg!(self, target_value_reg, entry_addr);
203                (*sparse_iterator).index += 1;
204            } else {
205                // Jump to the provided address if we're done
206                let branch_offset = i16_from_u8s!(branch_offset_lower, branch_offset_upper);
207
208                #[cfg(feature = "debug_vm")]
209                if self.debug_operations_enabled {
210                    eprintln!("iteration done, jumping {branch_offset}");
211                }
212                self.pc = (self.pc as i32 + branch_offset as i32) as usize;
213            }
214        }
215    }
216
217    pub fn execute_sparse_iter_next(
218        &mut self,
219        sparse_iterator_header_reg: u8,
220        target_entry_address_reg: u8,
221        branch_offset_lower: u8,
222        branch_offset_upper: u8,
223    ) {
224        let sparse_iterator =
225            self.get_sparse_iterator_header_ptr_from_reg(sparse_iterator_header_reg);
226
227        unsafe {
228            let sparse_header_addr = (*sparse_iterator).sparse_header_heap_ptr;
229            let sparse_header_ptr = self.memory.get_heap_const_ptr(sparse_header_addr as usize);
230
231            let element_count = sparse_mem::element_count(sparse_header_ptr);
232            let current_index = (*sparse_iterator).index as usize;
233            if (current_index as u32) < element_count as u32 {
234                let values_index = (*sparse_mem::slot_to_id_ptr_const(sparse_header_ptr)
235                    .add(current_index)) as usize;
236
237                #[cfg(feature = "debug_vm")]
238                if self.debug_operations_enabled {
239                    eprintln!("index {current_index}, slot: {values_index}");
240                }
241
242                let offset_to_values = sparse_mem::values_offset(sparse_header_ptr);
243                let values_start = sparse_header_addr as usize + offset_to_values;
244
245                let element_size = sparse_mem::element_size(sparse_header_ptr) as usize;
246                let entry_addr = values_start + values_index * element_size;
247
248                set_reg!(self, target_entry_address_reg, entry_addr);
249                (*sparse_iterator).index += 1;
250            } else {
251                // Jump to the provided address if we're done
252                let branch_offset = i16_from_u8s!(branch_offset_lower, branch_offset_upper);
253
254                #[cfg(feature = "debug_vm")]
255                if self.debug_operations_enabled {
256                    eprintln!("iteration done, jumping {branch_offset}");
257                }
258                self.pc = (self.pc as i32 + branch_offset as i32) as usize;
259            }
260        }
261    }
262}