ported dynamic arrays and strlen to l2

This commit is contained in:
IgorCielniak
2026-02-03 09:41:11 +01:00
parent 2b78ca671c
commit c2fcaf8f8d
3 changed files with 344 additions and 232 deletions

View File

@@ -9,240 +9,113 @@
# Allocation: mmap; free: munmap. # Allocation: mmap; free: munmap.
# Growth: allocate new block, copy elements, munmap old block. # Growth: allocate new block, copy elements, munmap old block.
import mem.sl
# : arr_new ( cap -- arr ) # : arr_new ( cap -- arr )
:asm arr_new { # Create a new array with given initial capacity (minimum 1)
mov r14, [r12] ; requested cap word arr_new
cmp r14, 1 dup 1 < if drop 1 end
jge .cap_ok dup 8 * 24 + alloc
mov r14, 1 dup 0 ! # len = 0
.cap_ok: over over 8 + swap ! # cap = requested cap
; bytes = 24 + cap*8 dup 24 + over 16 + swap ! # data = arr + 24
mov rsi, r14 nip
shl rsi, 3 end
add rsi, 24
; mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
xor rdi, rdi
mov rdx, 3
mov r10, 34
mov r8, -1
xor r9, r9
mov rax, 9
syscall
; header
mov qword [rax], 0
mov [rax + 8], r14
lea rbx, [rax + 24]
mov [rax + 16], rbx
; replace cap with arr pointer
mov [r12], rax
ret
}
;
# : arr_len ( arr -- len ) # : arr_len ( arr -- len )
:asm arr_len { word arr_len @ end
mov rax, [r12]
mov rax, [rax]
mov [r12], rax
ret
}
;
# : arr_cap ( arr -- cap ) # : arr_cap ( arr -- cap )
:asm arr_cap { word arr_cap 8 + @ end
mov rax, [r12]
mov rax, [rax + 8]
mov [r12], rax
ret
}
;
# : arr_data ( arr -- ptr ) # : arr_data ( arr -- ptr )
:asm arr_data { word arr_data 16 + @ end
mov rax, [r12]
mov rax, [rax + 16]
mov [r12], rax
ret
}
;
# : arr_free ( arr -- ) # : arr_free ( arr -- )
:asm arr_free { word arr_free
mov rbx, [r12] ; base dup arr_cap 8 * 24 + free
mov rcx, [rbx + 8] ; cap end
mov rsi, rcx
shl rsi, 3 # Helper: copy n qwords from src to dst (dst src n --)
add rsi, 24 word arr_copy_elements
mov rdi, rbx while dup 0 > do
mov rax, 11 over @ 3 pick swap ! # dst = *src
syscall swap 8 + swap # src += 8
add r12, 8 ; drop arr rot 8 + -rot # dst += 8
ret 1 -
} end
; drop 2drop
end
# : arr_reserve ( cap arr -- arr ) # : arr_reserve ( cap arr -- arr )
# Ensures capacity >= cap; returns (possibly moved) arr pointer. # Ensures capacity >= cap; returns (possibly moved) arr pointer.
:asm arr_reserve { word arr_reserve
mov rbx, [r12] ; arr swap dup 1 < if drop 1 end swap # reqcap arr
mov r14, [r12 + 8] ; requested cap
cmp r14, 1
jge .req_ok
mov r14, 1
.req_ok:
mov rdx, [rbx + 8] ; old cap
cmp rdx, r14
jae .no_change
; alloc new block: bytes = 24 + reqcap*8 # Check: if arr_cap >= reqcap, do nothing
mov rsi, r14 over over arr_cap swap
shl rsi, 3 >= if
add rsi, 24 nip
xor rdi, rdi else
mov rdx, 3 # Allocate new block
mov r10, 34 over 8 * 24 + alloc
mov r8, -1
xor r9, r9
mov rax, 9
syscall
mov r10, rax ; new base # Copy header
lea r9, [r10 + 24] ; new data over arr_len over swap ! # len
2 pick over 8 + swap ! # cap = reqcap
dup 24 + over 16 + swap ! # data = newarr + 24
; header # Copy elements
mov r8, [rbx] ; len dup arr_data
mov [r10], r8 2 pick arr_data
mov [r10 + 8], r14 3 pick arr_len
mov [r10 + 16], r9 arr_copy_elements
; copy elements from old data # Free old and return new
mov r11, [rbx + 16] ; old data swap arr_free
xor rcx, rcx ; i nip
.copy_loop: end
cmp rcx, r8 end
je .copy_done
mov rdx, [r11 + rcx*8]
mov [r9 + rcx*8], rdx
inc rcx
jmp .copy_loop
.copy_done:
; munmap old block
mov rsi, [rbx + 8]
shl rsi, 3
add rsi, 24
mov rdi, rbx
mov rax, 11
syscall
; return new arr only
mov [r12 + 8], r10
add r12, 8
ret
.no_change:
; drop cap, keep arr
mov [r12 + 8], rbx
add r12, 8
ret
}
;
# : arr_push ( x arr -- arr ) # : arr_push ( x arr -- arr )
:asm arr_push { # Push element onto array, growing if needed
mov rbx, [r12] ; arr word arr_push
mov rcx, [rbx] ; len dup arr_len over arr_cap >= if
mov rdx, [rbx + 8] ; cap dup arr_cap dup 1 < if drop 1 end 2 *
cmp rcx, rdx over arr_reserve
jb .have_space nip
end
; grow: newcap = max(1, cap) * 2 # Store x at data[len]
mov r14, rdx dup arr_data over arr_len 8 * +
cmp r14, 1 rot over swap ! drop
jae .cap_ok
mov r14, 1
.cap_ok:
shl r14, 1
; alloc new block # Increment len
mov rsi, r14 dup @ 1 + over swap !
shl rsi, 3 end
add rsi, 24
xor rdi, rdi
mov rdx, 3
mov r10, 34
mov r8, -1
xor r9, r9
mov rax, 9
syscall
mov r10, rax ; new base
lea r9, [r10 + 24] ; new data
; header
mov rcx, [rbx] ; len (reload; syscall clobbers rcx)
mov [r10], rcx
mov [r10 + 8], r14
mov [r10 + 16], r9
; copy old data
mov r11, [rbx + 16] ; old data
xor r8, r8
.push_copy_loop:
cmp r8, rcx
je .push_copy_done
mov rdx, [r11 + r8*8]
mov [r9 + r8*8], rdx
inc r8
jmp .push_copy_loop
.push_copy_done:
; munmap old block
mov rsi, [rbx + 8]
shl rsi, 3
add rsi, 24
mov rdi, rbx
mov rax, 11
syscall
; switch to new base
mov rbx, r10
.have_space:
; store element at data[len]
mov r9, [rbx + 16]
mov rax, [r12 + 8] ; x
mov rcx, [rbx] ; len
mov [r9 + rcx*8], rax
inc rcx
mov [rbx], rcx
; return arr only
mov [r12 + 8], rbx
add r12, 8
ret
}
;
# : arr_pop ( arr -- x arr ) # : arr_pop ( arr -- x arr )
:asm arr_pop { # Pop element from array (returns 0 if empty)
mov rbx, [r12] ; arr word arr_pop
mov rcx, [rbx] ; len dup arr_len 0 == if
test rcx, rcx 0 swap
jz .empty else
dec rcx # Decrement len
mov [rbx], rcx dup @ 1 - over swap !
mov rdx, [rbx + 16] ; data # Get element at new len position
mov rax, [rdx + rcx*8] dup arr_data over arr_len 8 * + @
jmp .push swap
.empty: end
xor rax, rax end
.push:
sub r12, 8 # : arr_get ( i arr -- x )
mov [r12], rax # Get element at index i
ret word arr_get
} arr_data swap 8 * + @
; end
# : arr_set ( x i arr -- )
# Set element at index i to x
word arr_set
arr_data swap 8 * + swap !
end

248
stdlib/arr_asm.sl Normal file
View File

@@ -0,0 +1,248 @@
# Dynamic arrays (qword elements)
#
# Layout at address `arr`:
# [arr + 0] len (qword)
# [arr + 8] cap (qword)
# [arr + 16] data (qword) = arr + 24
# [arr + 24] elements (cap * 8 bytes)
#
# Allocation: mmap; free: munmap.
# Growth: allocate new block, copy elements, munmap old block.
# : arr_new ( cap -- arr )
:asm arr_new {
mov r14, [r12] ; requested cap
cmp r14, 1
jge .cap_ok
mov r14, 1
.cap_ok:
; bytes = 24 + cap*8
mov rsi, r14
shl rsi, 3
add rsi, 24
; mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
xor rdi, rdi
mov rdx, 3
mov r10, 34
mov r8, -1
xor r9, r9
mov rax, 9
syscall
; header
mov qword [rax], 0
mov [rax + 8], r14
lea rbx, [rax + 24]
mov [rax + 16], rbx
; replace cap with arr pointer
mov [r12], rax
ret
}
;
# : arr_len ( arr -- len )
:asm arr_len {
mov rax, [r12]
mov rax, [rax]
mov [r12], rax
ret
}
;
# : arr_cap ( arr -- cap )
:asm arr_cap {
mov rax, [r12]
mov rax, [rax + 8]
mov [r12], rax
ret
}
;
# : arr_data ( arr -- ptr )
:asm arr_data {
mov rax, [r12]
mov rax, [rax + 16]
mov [r12], rax
ret
}
;
# : arr_free ( arr -- )
:asm arr_free {
mov rbx, [r12] ; base
mov rcx, [rbx + 8] ; cap
mov rsi, rcx
shl rsi, 3
add rsi, 24
mov rdi, rbx
mov rax, 11
syscall
add r12, 8 ; drop arr
ret
}
;
# : arr_reserve ( cap arr -- arr )
# Ensures capacity >= cap; returns (possibly moved) arr pointer.
:asm arr_reserve {
mov rbx, [r12] ; arr
mov r14, [r12 + 8] ; requested cap
cmp r14, 1
jge .req_ok
mov r14, 1
.req_ok:
mov rdx, [rbx + 8] ; old cap
cmp rdx, r14
jae .no_change
; alloc new block: bytes = 24 + reqcap*8
mov rsi, r14
shl rsi, 3
add rsi, 24
xor rdi, rdi
mov rdx, 3
mov r10, 34
mov r8, -1
xor r9, r9
mov rax, 9
syscall
mov r10, rax ; new base
lea r9, [r10 + 24] ; new data
; header
mov r8, [rbx] ; len
mov [r10], r8
mov [r10 + 8], r14
mov [r10 + 16], r9
; copy elements from old data
mov r11, [rbx + 16] ; old data
xor rcx, rcx ; i
.copy_loop:
cmp rcx, r8
je .copy_done
mov rdx, [r11 + rcx*8]
mov [r9 + rcx*8], rdx
inc rcx
jmp .copy_loop
.copy_done:
; munmap old block
mov rsi, [rbx + 8]
shl rsi, 3
add rsi, 24
mov rdi, rbx
mov rax, 11
syscall
; return new arr only
mov [r12 + 8], r10
add r12, 8
ret
.no_change:
; drop cap, keep arr
mov [r12 + 8], rbx
add r12, 8
ret
}
;
# : arr_push ( x arr -- arr )
:asm arr_push {
mov rbx, [r12] ; arr
mov rcx, [rbx] ; len
mov rdx, [rbx + 8] ; cap
cmp rcx, rdx
jb .have_space
; grow: newcap = max(1, cap) * 2
mov r14, rdx
cmp r14, 1
jae .cap_ok
mov r14, 1
.cap_ok:
shl r14, 1
; alloc new block
mov rsi, r14
shl rsi, 3
add rsi, 24
xor rdi, rdi
mov rdx, 3
mov r10, 34
mov r8, -1
xor r9, r9
mov rax, 9
syscall
mov r10, rax ; new base
lea r9, [r10 + 24] ; new data
; header
mov rcx, [rbx] ; len (reload; syscall clobbers rcx)
mov [r10], rcx
mov [r10 + 8], r14
mov [r10 + 16], r9
; copy old data
mov r11, [rbx + 16] ; old data
xor r8, r8
.push_copy_loop:
cmp r8, rcx
je .push_copy_done
mov rdx, [r11 + r8*8]
mov [r9 + r8*8], rdx
inc r8
jmp .push_copy_loop
.push_copy_done:
; munmap old block
mov rsi, [rbx + 8]
shl rsi, 3
add rsi, 24
mov rdi, rbx
mov rax, 11
syscall
; switch to new base
mov rbx, r10
.have_space:
; store element at data[len]
mov r9, [rbx + 16]
mov rax, [r12 + 8] ; x
mov rcx, [rbx] ; len
mov [r9 + rcx*8], rax
inc rcx
mov [rbx], rcx
; return arr only
mov [r12 + 8], rbx
add r12, 8
ret
}
;
# : arr_pop ( arr -- x arr )
:asm arr_pop {
mov rbx, [r12] ; arr
mov rcx, [rbx] ; len
test rcx, rcx
jz .empty
dec rcx
mov [rbx], rcx
mov rdx, [rbx + 16] ; data
mov rax, [rdx + rcx*8]
jmp .push
.empty:
xor rax, rax
.push:
sub r12, 8
mov [r12], rax
ret
}
;

View File

@@ -33,23 +33,14 @@ end
# : strlen ( addr -- len ) # : strlen ( addr -- len )
# for null terminated strings # for null terminated strings
word strlen
:asm strlen { 0 swap # len addr
mov rsi, [r12] ; address while dup c@ 0 != do
xor rcx, rcx ; length counter 1 + # addr++
.strlen_loop: swap 1 + swap # len++
mov al, [rsi] end
test al, al drop # drop addr, leave len
jz .strlen_done end
inc rcx
inc rsi
jmp .strlen_loop
.strlen_done:
mov rax, rcx
mov [r12], rax ; store length on stack
ret
}
;
word digitsN>num # ( d_{n-1} ... d0 n -- value ), digits bottom=MSD, top=LSD, length on top (MSD-most significant digit, LSD-least significant digit) word digitsN>num # ( d_{n-1} ... d0 n -- value ), digits bottom=MSD, top=LSD, length on top (MSD-most significant digit, LSD-least significant digit)
0 swap # place accumulator below length 0 swap # place accumulator below length