ported dynamic arrays and strlen to l2
This commit is contained in:
303
stdlib/arr.sl
303
stdlib/arr.sl
@@ -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
248
stdlib/arr_asm.sl
Normal 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
|
||||||
|
}
|
||||||
|
;
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user