Skip to content

Commit 6b9d89e

Browse files
Copilotenghitalo
andcommitted
Expand array implementation details: heap allocation, bounds checking, struct fields
Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com>
1 parent 886a1fb commit 6b9d89e

1 file changed

Lines changed: 134 additions & 30 deletions

File tree

WASM_BACKEND_IMPROVEMENTS.md

Lines changed: 134 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ pub mut:
5050

5151
1. **Array Runtime Representation**
5252
- Location: `vlib/builtin/array.v`
53-
- The `array` struct already exists with fields:
54-
- `data: voidptr` - pointer to heap data
55-
- `len: int` - current length
56-
- `cap: int` - capacity
57-
- `element_size: int` - size of each element
58-
- `offset: int` - for slicing support
53+
- The `array` struct already exists with 6 fields:
54+
- `data: voidptr` - pointer to heap-allocated data block
55+
- `offset: int` - offset in bytes for slicing support (avoids copying)
56+
- `len: int` - current length in elements
57+
- `cap: int` - capacity in elements
58+
- `flags: ArrayFlags` - flags controlling growth/shrink behavior
59+
- `element_size: int` - size in bytes of one element
60+
- **Total struct size**: ~24-28 bytes (6 fields: 5 ints + 1 pointer on 32-bit WASM)
61+
- **Memory layout**: Array header is stack/heap allocated, points to separate heap block for data
5962

6063
2. **Type Size Calculation**
6164
- Location: `vlib/v/gen/wasm/serialise/types.v`
@@ -92,58 +95,125 @@ ast.Array {
9295
ast.ArrayInit {
9396
// Current: Creates local var and calls set_with_expr
9497
// Need to:
95-
// 1. Allocate array struct on stack or heap
98+
// 1. Allocate array struct on stack or heap (24-28 bytes)
9699
// 2. Calculate total size: cap * element_size
97100
// 3. Call __new_array() or __new_array_with_default()
98-
// 4. Initialize elements if provided
101+
// 4. Initialize elements if provided (loop and store each)
99102
// 5. Return pointer to array struct
100103
}
101104
```
102105

106+
2. **Array Indexing with Bounds Checking** (line ~731-800):
107+
```v
108+
ast.IndexExpr {
109+
// Current: Only handles ArrayFixed and strings
110+
// Need to add:
111+
ast.Array {
112+
// 1. Load array struct pointer
113+
// 2. Access .data field to get element pointer (offset 0)
114+
// 3. **BOUNDS CHECKING** - Critical for safety:
115+
// a. Load index value to temporary local
116+
// b. Load array.len from struct (i32.load offset=8)
117+
// c. Check upper bound: index >= len (unsigned comparison)
118+
// d. Check lower bound (implicit in unsigned: negative becomes large)
119+
// e. If out of bounds:
120+
// - Call eprintln() with error message including file:line
121+
// - Call panic("index out of range")
122+
// f. Use `if (index >= len) { panic }` pattern (similar to line 790-800)
123+
// 4. Calculate element offset: (index + array.offset) * element_size
124+
// 5. Load actual data pointer: array.data (i32.load offset=0)
125+
// 6. Load/store element at: data_ptr + calculated_offset
126+
//
127+
// **Optimization**: Skip bounds check if:
128+
// - Inside `[direct_array_access]` function
129+
// - Index is compile-time constant within bounds
130+
// - Checker has already verified safety
131+
}
132+
}
133+
```
134+
103135
2. **Array Indexing** (line ~731-800):
104136
```v
105137
ast.IndexExpr {
106138
// Current: Only handles ArrayFixed and strings
107139
// Need to add:
108140
ast.Array {
109141
// 1. Load array struct pointer
110-
// 2. Access .data field to get element pointer
111-
// 3. Bounds check using .len field
112-
// 4. Calculate offset: index * element_size
113-
// 5. Load/store element
142+
// 2. Access .data field to get element pointer (offset 0)
143+
// 3. Bounds check using .len field (offset 8):
144+
// - Load index value to local
145+
// - Load array.len
146+
// - Compare: if index >= len || index < 0, call panic()
147+
// 4. Calculate element offset: (index + array.offset) * element_size
148+
// 5. Load/store element at: array.data + calculated_offset
114149
}
115150
}
116151
```
117152

118-
3. **Array Methods**:
119-
- `push()` - append element, may need reallocation
120-
- `pop()` - remove last element
121-
- `<<` operator - append operator
122-
- `delete()` - remove element at index
123-
- `insert()` - insert at index
124-
- `clone()` - deep copy
125-
- `filter()`, `map()`, `any()`, `all()` - higher order functions
153+
3. **Array Methods** - Essential operations to implement:
154+
- **`push(element)`**:
155+
- Check if len == cap, grow if needed
156+
- Store element at data[len * element_size]
157+
- Increment len
158+
- **`pop()`**:
159+
- Check len > 0, panic if empty
160+
- Decrement len
161+
- Return element at data[len * element_size]
162+
- **`<< operator`** (append): Same as push
163+
- **`delete(index)`**:
164+
- Bounds check
165+
- Shift elements: memcpy(data+index, data+index+1, (len-index-1) * elem_size)
166+
- Decrement len
167+
- **`insert(index, element)`**:
168+
- Ensure capacity
169+
- Shift elements right
170+
- Store element
171+
- Increment len
172+
- **`clone()`**:
173+
- Allocate new array with same cap
174+
- Deep copy data block: vmemcpy(new.data, old.data, len * elem_size)
175+
- **`filter()`, `map()`, `any()`, `all()`**: Higher order functions (Phase 5)
126176

127177
#### 1.2.3 Memory Management (`vlib/v/gen/wasm/mem.v`)
128178

129179
**Required Additions**:
130180

131181
1. **Heap Allocation Functions**:
182+
- **malloc/free equivalents**: The WASM backend uses `vcalloc()` and `malloc()` from `vlib/builtin/wasm/builtin.v`
183+
- `vcalloc(n)` - allocate zeroed memory, already implemented using WASM `memory.fill`
184+
- `malloc(n)` - allocate uninitialized memory (needs implementation or import)
185+
- `free(ptr)` - deallocate memory (needs implementation or stub for now)
186+
- Integration with WASM linear memory model and heap management
187+
188+
2. **Array Allocation Helpers**:
132189
- The WASM backend needs to call builtin functions:
133190
- `__new_array(len, cap, element_size)` - allocate new array
134191
- `__new_array_with_default(len, cap, element_size, default_val)` - with default
192+
- `__new_array_with_multi_default(len, cap, element_size, default_val)` - for complex types
135193
- These are defined in `vlib/builtin/array.v`
194+
- Need to ensure these functions are available in WASM context
136195

137-
2. **Field Access**:
196+
3. **Field Access Helpers**:
138197
- Add helper functions to access array struct fields:
139-
- `load_array_len()` - get length field
140-
- `load_array_cap()` - get capacity field
141-
- `load_array_data()` - get data pointer
142-
- `store_array_len()` - set length field
143-
144-
3. **Array Reallocation**:
145-
- Implement or call `array_ensure_cap()` for growth operations
146-
- Handle memory copying during reallocation
198+
- `load_array_len(arr_ptr)` - get length field (offset +8 bytes)
199+
- `load_array_cap(arr_ptr)` - get capacity field (offset +12 bytes)
200+
- `load_array_data(arr_ptr)` - get data pointer (offset +0 bytes)
201+
- `load_array_offset(arr_ptr)` - get offset field (offset +4 bytes)
202+
- `store_array_len(arr_ptr, len)` - set length field
203+
- Field offsets based on struct layout: data(0), offset(4), len(8), cap(12), flags(16), element_size(20)
204+
205+
4. **Array Reallocation and Growth**:
206+
- Implement or call `array_ensure_cap(arr, required_cap)` for growth operations
207+
- Growth strategy (from `vlib/builtin/array.v`):
208+
```
209+
new_cap = if cap < required { required } else { cap * 2 }
210+
```
211+
- Handle memory copying during reallocation:
212+
- Allocate new data block with `vcalloc(new_cap * element_size)`
213+
- Copy existing data using `vmemcpy(new_data, old_data, len * element_size)`
214+
- Update array struct fields (data, cap)
215+
- Free old data block (when memory management is available)
216+
- Respect `ArrayFlags.nogrow` flag - error if growth attempted when set
147217
148218
#### 1.2.4 Runtime Support (`vlib/builtin/wasm/`)
149219
@@ -157,7 +227,41 @@ ast.Array {
157227
- WASM-specific array helper functions
158228
- Optimized versions of common operations
159229
160-
### 1.3 Module Layer (`vlib/wasm/`)
230+
### 1.3 Integration Points in `gen.v`
231+
232+
**Multiple locations in `gen.v` need updates to handle dynamic arrays**:
233+
234+
1. **Line ~755**: Remove error, add Array handling in IndexExpr
235+
```v
236+
ast.Array {
237+
// Currently: g.w_error('wasm backend does not support dynamic arrays')
238+
// Change to: Handle array indexing with bounds checking (see section 1.2.2)
239+
}
240+
```
241+
242+
2. **Line ~723**: ArrayInit expression handling
243+
- Already partially implemented for fixed arrays
244+
- Extend to call `__new_array()` for dynamic arrays
245+
246+
3. **Function calls involving arrays**:
247+
- Passing arrays as parameters (pass pointer to array struct)
248+
- Returning arrays from functions (return pointer)
249+
- Array assignments (copy pointer, not deep copy unless clone())
250+
251+
4. **Array field access in structs**:
252+
- When struct contains array field, store as array struct
253+
- Load/store entire array struct (24-28 bytes)
254+
255+
5. **Array comparisons**:
256+
- `arr1 == arr2` should compare elements, not pointers
257+
- May need to call array comparison helper
258+
259+
6. **Array in expressions**:
260+
- Binary operations involving arrays
261+
- Array concatenation
262+
- Array slicing `arr[start..end]`
263+
264+
### 1.4 Module Layer (`vlib/wasm/`)
161265

162266
**No changes required** - The `wasm` module is for generating WASM bytecode and already supports all necessary instructions (memory operations, function calls, etc.)
163267

0 commit comments

Comments
 (0)