diff --git a/stages/lisp0/lisp1.asm b/stages/lisp0/lisp1.asm index 820cbe5..3a91038 100644 --- a/stages/lisp0/lisp1.asm +++ b/stages/lisp0/lisp1.asm @@ -5,10 +5,10 @@ section .bss buf resb 0x100 align 8,db 0 atoms times 24 resb 8 -global env - heap resq 0 -section .data +global env + +section .rodata QUOTE_STR db "quote", 0 QUOTE_STR_LEN equ $ - QUOTE_STR TRUE_STR db "true", 0 @@ -67,6 +67,25 @@ section .data NTH_STR_LEN equ $ - NTH_STR SET_NTH_STR db "set-nth" SET_NTH_STR_LEN equ $ - SET_NTH_STR + AS_CHAR_STR db "as-char" + AS_CHAR_STR_LEN equ $ - AS_CHAR_STR + AS_INT_STR db "as-int" + AS_INT_STR_LEN equ $ - AS_INT_STR + MAKE_ARR_STR db "make-arr" + MAKE_ARR_STR_LEN equ $ - MAKE_ARR_STR + ALLOC_STR db "allocate" + ALLOC_STR_LEN equ $ - ALLOC_STR + DEALLOC_STR db "deallocate" + DEALLOC_STR_LEN equ $ - DEALLOC_STR + CONS_STR db "cons" + CONS_STR_LEN equ $ - CONS_STR + PROGN_STR db "progn" + PROGN_STR_LEN equ $ - PROGN_STR + + +section .data + align 8,db 0 + heap dq 0 align 8, db 0 ATOM_QUOTE: @@ -798,12 +817,12 @@ parse_string: dtor_table: dd 0 - dd dtor_table - dtor_num - dd dtor_table - dtor_prim - dd dtor_table - dtor_cons - dd dtor_table - dtor_clos - dd dtor_table - dtor_atom - dd dtor_table - dtor_arr + dd dtor_num - dtor_table + dd dtor_prim - dtor_table + dd dtor_cons - dtor_table + dd dtor_clos - dtor_table + dd 0 ; dd dtor_table - dtor_atom + dd dtor_arr - dtor_table dd 0 dtor_num: @@ -844,22 +863,47 @@ dtor_atom: call heap_dealloc ret dtor_arr: + push rdi call obj_ptr_part - push rax mov rdi, qword [rax + 16] ; data pointer + mov ecx, dword [rax + 8] ; length mov eax, edi and eax, 0x7 - lea rsi, [rel OBJ_SIZES] - movzx eax, byte [rsi + rax] ; size of each element - mul dword [rax + 8] ; capacity + cmp al, OBJ_NUM + ja .dealloc + + push r12 + push r13 + xor r12, r12 + mov r13, rdi +.loop: + cmp r12, rcx + jae .done + mov rdi, qword [r13 + r12*8] + call obj_dec_ref + inc r12 + jmp .loop +.done: + pop r13 + pop r12 +.dealloc: + mov eax, ecx + mov ecx, edi + and ecx, 0x7 + test cl, cl + setnz cl + lea rcx, [rcx + rcx*2] + shl eax, cl mov esi, eax - call obj_into_ptr_part + and rdi, -8 call heap_dealloc pop rdi - mov esi, 16 + mov esi, 24 call heap_dealloc ret + + obj_inc_ref: call obj_is_nil @@ -875,9 +919,7 @@ obj_inc_ref: .done: ret .num: - call obj_ptr_part - shr rax, 56 - test eax, OBJ_NUM_MAGIC + call num_is_inline je .done jmp .inc @@ -895,7 +937,7 @@ obj_dec_ref: jnz .done call obj_tag_part lea rdx, qword [rel dtor_table] - movsx esi, dword [rdx + rax*4] + movsx rsi, dword [rdx + rax*4] test esi, esi jz .done add rdx, rsi @@ -903,9 +945,7 @@ obj_dec_ref: .done: ret .num: - call obj_ptr_part - shr rax, 56 - test eax, OBJ_NUM_MAGIC + call num_is_inline je .done jmp .dec @@ -988,7 +1028,7 @@ make_cons: make_atom: push rdi push rsi - mov edi, 16 + mov edi, 24 mov esi, 8 call heap_alloc pop rsi @@ -1034,7 +1074,8 @@ align 8,db 0 is_nil: obj_is_nil: - cmp rdi, qword [rel nil] + lea rax, qword [rel nil] + cmp rdi, rax sete al ret @@ -1088,7 +1129,6 @@ num_val: call num_is_inline je .inline call obj_ptr_part - call obj_ptr_part mov rax, qword [rax + 8] ret .inline: @@ -1102,11 +1142,283 @@ num_set_val: je .inline call obj_ptr_part mov qword [rax + 8], rsi + mov rax, rdi ret .inline: mov edi, esi jmp make_inline_num +arr_nth: + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_ptr_part + mov rax, qword [rax + 16] ; data + mov ecx, dword [rax + 8] ; len + cmp rsi, rcx + jae panic_abort + mov rdi, rax + call obj_tag_part + cmp al, OBJ_BYTE + je .byte + cmp al, OBJ_NUM + je .num + call obj_into_ptr_part + mov rax, qword [rdi + rsi * 8] + ret +.byte: + mov dil, byte [rax + rsi] + call make_byte + ret +.num: + mov edi, dword [rax + rsi * 8] + call make_num + ret + +arr_set_nth: + sub rsp, 24 + mov qword [rsp], rdi ; arr + mov qword [rsp + 8], rsi ; idx + mov qword [rsp + 16], rdx ; value + call arr_nth + mov rdi, rax + call obj_tag_part + mov cl, byte [rsp + 16] ; value + and cl, 0x7 + cmp al, cl + jne panic_abort ; assert!( old_value.tag() == value.tag()) + call obj_dec_ref ; destroy old value + mov rdi, qword [rsp] ; arr + call obj_ptr_part + mov rdi, qword [rax + 16] ; data + mov rsi, qword [rsp + 8] ; idx + mov eax, edi + and eax, 0x7 + cmp al, OBJ_BYTE + je .byte + cmp al, OBJ_NUM + je .num + call obj_into_ptr_part + mov rax, qword [rsp + 16] ; value + mov qword [rdi + rsi * 8], rax + add rsp, 24 + ret +.byte: + mov al, byte [rsp + 17] ; value + mov byte [rdi + rsi], al + add rsp, 24 + ret +.num: + push rdi + mov rdi, qword [rsp + 16] ; value + call num_val + pop rdi + mov qword [rdi + rsi * 8], rax + mov rdi, qword [rsp + 16] ; value + call obj_dec_ref + add rsp, 24 + ret + +;; duplicates the array $rdi, akin to `arr.clone()` in Rust. +;; Additionally reserves space for $esi new elements. +arr_dup: + push r12 + sub rsp, 32 + mov qword [rsp], rdi ; arr + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_into_ptr_part + mov rax, qword [rdi + 8] + mov rdx, qword [rdi + 16] + mov qword [rsp + 8], rax ; {len, cap} + mov qword [rsp + 16], rdx ; data + + mov rdi, rdx + call obj_tag_part + test al, al + sete al + lea rcx, [rax + rax*2] + mov byte [rsp + 24], cl + mov byte [rsp + 25], al ; tag + + mov edi, esi + add edi, dword [rsp + 8] ; new_cap = len1 + new_elements + mov dword [rsp + 12], edi ; new_cap + + shl edi, cl + mov esi, 8 + call heap_alloc + mov rdi, qword [rsp + 16] ; data1 + mov qword [rsp + 16], rax ; new_data + mov rsi, rax + mov edx, dword [rsp + 8] ; len1 + mov cl, byte [rsp + 24] ; stride factor + shl edx, cl + call memcpy + + cmp byte [rsp + 25], OBJ_NUM + jbe .done + + mov r12d, dword [rsp + 8] ; len1 +.loop: + test r12, r12 + jz .done + dec r12 + + mov rdi, qword [rsp + 16] ; new_data + lea rdi, [rdi + r12*8] + call obj_inc_ref + jmp .loop + +.done: + mov rdx, qword [rsp + 16] ; new_data + mov edi, dword [rsp + 8] ; len1 + mov esi, dword [rsp + 12] ; new_cap + mov ecx, dword [rsp] + and ecx, 0x7 + call make_arr + mov qword [rsp + 8], rax ; new_arr + + mov rdi, qword [rsp] + call obj_dec_ref + + mov rax, qword [rsp + 8] ; new_arr + add rsp, 32 + pop r12 + ret + +arr_concat: + push r12 + sub rsp, 64 + mov qword [rsp], rdi ; arr1 + mov qword [rsp + 24], rsi ; arr2 + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_into_ptr_part + mov rax, qword [rdi + 8] + mov rdx, qword [rdi + 16] + mov qword [rsp + 8], rax ; {len, cap} + mov qword [rsp + 16], rdx ; data1 + mov rdi, qword [rsp + 24] ; arr2 + call obj_into_ptr_part + mov rax, qword [rdi + 8] + mov rdx, qword [rdi + 16] + mov qword [rsp + 32], rax ; {len, cap} + mov qword [rsp + 40], rdx ; data2 + + mov rdi, rdx + call obj_tag_part + test al, al + sete al + lea rcx, [rax + rax*2] + mov byte [rsp + 56], cl + + mov edi, dword [rsp + 8] ; len1 + add edi, dword [rsp + 32] ; len1 + len2 + shl edi, cl + mov esi, 8 + call heap_alloc + mov qword [rsp + 48], rax ; new_data + mov rdi, qword [rsp + 16] ; data1 + mov rsi, qword [rsp + 48] ; new_data + mov edx, dword [rsp + 8] ; len1 + mov cl, byte [rsp + 56] ; stride factor + shl edx, cl + call memcpy + mov rdi, qword [rsp + 40] ; data2 + mov rsi, qword [rsp + 48] ; new_data + mov edx, dword [rsp + 32] ; len2 + mov cl, byte [rsp + 56] ; stride factor + shl edx, cl + call memcpy + + mov rdi, qword [rsp + 16] ; data1 + call obj_tag_part + cmp al, OBJ_NUM + jbe .done + ; inc_ref for each element in new_data + mov r12d, dword [rsp + 8] ; len1 + add r12d, dword [rsp + 32] ; len1 + len2 + +.loop: + test r12, r12 + jz .done + dec r12 + + mov rdi, qword [rsp + 48] ; new_data + lea rdi, [rdi + r12*8] + call obj_inc_ref + jmp .loop + +.done: + mov rdi, qword [rsp] ; arr1 + call obj_dec_ref + mov rdi, qword [rsp + 24] ; arr2 + call obj_dec_ref + mov rdx, qword [rsp + 48] ; new_data + mov esi, dword [rsp + 8] + add esi, dword [rsp + 32] ; len1 + len2 + mov edi, esi + mov ecx, dword [rsp] + and ecx, 0x7 + call make_arr + + add rsp, 64 + pop r12 + ret + +arr_append: + sub rsp, 32 + mov qword [rsp], rdi ; arr + mov qword [rsp + 8], rsi ; value + + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_into_ptr_part + mov rax, qword [rdi + 8] ; {len, cap} + mov rdx, qword [rdi + 16] ; data + mov qword [rsp + 16], rax ; {len, cap} + mov qword [rsp + 24], rdx ; data + + mov rdi, rdx ; data + call obj_tag_part + + mov cl, byte [rsp + 8] ; value + and cl, 0x7 + cmp al, cl + jne panic_abort ; assert!( value.tag() == arr.data.tag() ) + cmp al, OBJ_BYTE + je .byte + cmp al, OBJ_NUM + je .num + jmp .ptr +.byte: + mov cl, byte [rsp + 8] ; value + call obj_into_ptr_part + mov eax, dword [rsp + 16] ; len + add rdi, rax + mov byte [rdi], cl ; data[len] = value + jmp .done +.num: + mov rdi, qword [rsp + 8] ; value + call num_val + mov qword [rsp + 8], rax +.ptr: + mov rcx, qword [rsp + 8] ; value + mov eax, dword [rsp + 16] ; len + mov rdi, qword [rsp + 24] ; data + call obj_into_ptr_part + mov qword [rdi + rax * 8], rcx +.done: + mov rdi, qword [rsp] ; arr + call obj_ptr_part + inc dword [rax + 8] ; len += 1 + add rsp, 32 + ret + car: call obj_tag_part cmp al, OBJ_CONS @@ -1123,6 +1435,58 @@ cdr: mov rax, qword [rax + 16] ret +caar: + call car + mov rdi, rax + call car + ret + +cadr: + call car + mov rdi, rax + call cdr + ret + + ;; take a list $rdi and spill the first $rsi elements to $rdx +spill_list: + push r12 + mov r12, rsi + mov rsi, rdx +.loop: + test r12, r12 + jz .done + dec r12 + call car_cdr + mov rdi, rdx + mov qword [rsi + r12*8], rax + jmp .loop +.done: + pop r12 + ret + +;; (a . (b . _)) => $rax = a, $rdx = b +;; (a . nil) => $rax = a, $rdx = nil +car_cdar_or_nil: + call car_cdr + lea rdi, [rel nil] + cmp rdi, rdx + je .done + mov rdi, rdx + and rdx, 7 + cmp dl, OBJ_CONS + jne panic_abort + call obj_into_ptr_part + mov rdx, qword [rdi + 8] ; cdar +.done: + ret + +car_cdar_or_panic: + call car_cdar_or_nil + lea rdi, [rel nil] + cmp rdi, rdx + je panic_abort + ret + set_cdr: call obj_tag_part cmp al, OBJ_CONS @@ -1136,8 +1500,8 @@ car_cdr: cmp al, OBJ_CONS jne panic_abort call obj_ptr_part - mov rax, qword [rax + 8] ; car mov rdx, qword [rax + 16] ; cdr + mov rax, qword [rax + 8] ; car ret clos_env: @@ -1168,7 +1532,7 @@ clos_body: ;; Atoms & GEnv -section .bss +section .data align 8, db 0 env resq 1 env_tail resq 1 @@ -1207,6 +1571,7 @@ cons_atom_prim: mov rsi, rax jmp genv_append +global init_env init_env: lea rdi, [rel ATOM_T] mov esi, OBJ_ATOM @@ -1224,7 +1589,7 @@ init_env: lea rdi, [rel ATOM_QUOTE] or rdi, OBJ_ATOM mov rsi, rax - jmp genv_append + call genv_append mov rdi, PLUS_STR mov esi, PLUS_STR_LEN @@ -1316,19 +1681,19 @@ init_env: lea rdx, [rel p_let] call cons_atom_prim - mov rdi, LETSTAR_STR - mov esi, LETSTAR_STR_LEN - lea rdx, [rel p_letstar] + mov rdi, LET_STAR_STR + mov esi, LET_STAR_STR_LEN + lea rdx, [rel p_let_star] call cons_atom_prim mov rdi, STR_LEN_STR mov esi, STR_LEN_STR_LEN - lea rdx, [rel p_str_len] + lea rdx, [rel p_arr_len] call cons_atom_prim mov rdi, STR_PARTS_STR mov esi, STR_PARTS_STR_LEN - lea rdx, [rel p_str_parts] + lea rdx, [rel p_arr_decompose] call cons_atom_prim mov rdi, SYSCALL_STR @@ -1356,12 +1721,47 @@ init_env: lea rdx, [rel p_set_nth] call cons_atom_prim + mov rdi, MAKE_ARR_STR + mov esi, MAKE_ARR_STR_LEN + lea rdx, [rel p_make_arr] + call cons_atom_prim + + mov rdi, AS_CHAR_STR + mov esi, AS_CHAR_STR_LEN + lea rdx, [rel p_int_to_byte] + call cons_atom_prim + + mov rdi, AS_INT_STR + mov esi, AS_INT_STR_LEN + lea rdx, [rel p_byte_to_int] + call cons_atom_prim + + mov rdi, ALLOC_STR + mov esi, ALLOC_STR_LEN + lea rdx, [rel p_allocate] + call cons_atom_prim + + mov rdi, DEALLOC_STR + mov esi, DEALLOC_STR_LEN + lea rdx, [rel p_deallocate] + call cons_atom_prim + + mov rdi, CONS_STR + mov esi, CONS_STR_LEN + lea rdx, [rel p_cons] + call cons_atom_prim + + mov rdi, PROGN_STR + mov esi, PROGN_STR_LEN + lea rdx, [rel p_progn] + call cons_atom_prim + mov rax, qword [rel env] ret ;; Evaluation -global eval: +global eval ;; evaluate the expression $rdi in the environment $rsi. eval: call obj_tag_part @@ -1369,24 +1769,30 @@ eval: je .atom cmp al, OBJ_CONS jne .uneval - sub rsp, 16 + sub rsp, 24 mov qword [rsp], rsi ; save env call cdr mov qword [rsp + 8], rax ; cdr call car mov rdi, rax ; car mov rsi, qword [rsp] ; env - call eval + call eval + mov qword [rsp + 16], rax mov rdi, rax ; eval(car, env) mov rsi, qword [rsp + 8] ; cdr mov rdx, qword [rsp] ; env call apply ; apply(eval(car, env), cdr, env) - add rsp, 16 + mov rdi, qword [rsp + 16] + mov qword [rsp + 16], rax + call obj_dec_ref + mov rax, qword [rsp + 16] + add rsp, 24 ret .atom: call assoc - ret + mov rdi, rax .uneval: + call obj_inc_ref mov rax, rdi ret @@ -1397,7 +1803,7 @@ apply: je .prim cmp al, OBJ_CLOS jne panic_abort - call reduce + call apply_clos ret .prim: call obj_ptr_part @@ -1414,7 +1820,7 @@ assoc: .loop: mov rdi, qword [rsp] ; env call is_nil - jnz .not_found + je .not_found call obj_tag_part cmp al, OBJ_CONS jne .not_found @@ -1424,7 +1830,6 @@ assoc: mov rdi, rax ; sym mov rsi, qword [rsp + 8] ; symbol call obj_eq - test al, al je .found mov rdi, qword [rsp] ; env call cdr @@ -1435,7 +1840,6 @@ assoc: call car mov rdi, rax ; (sym . val) call cdr - mov rax, rdi ; return val add rsp, 16 ret .not_found: @@ -1444,7 +1848,7 @@ assoc: ret ;; applies the closure $rdi to the argument list $rsi in the environment $rdx, and returns the result. -reduce: +apply_clos: sub rsp, 32 mov qword [rsp], rdx ; env mov qword [rsp + 8], rdi ; closure @@ -1456,17 +1860,23 @@ reduce: mov rdi, rsi mov rsi, qword [rsp] ; env call eval_list + mov qword [rsp], rax mov rsi, rax ; eval_list(args, env) mov rdi, qword [rsp + 8] ; closure call clos_params mov rdi, rax ; params mov rdx, qword [rsp + 16] ; clos_env + ;; we don't incref these objects because they are temporary call bind_list ; bind_list(params, eval_list(args, env), clos_env) mov rsi, rax ; new_env mov rdi, qword [rsp + 8] ; closure call clos_body mov rdi, rax ; body - call eval + call eval ; (REF) we return this, so don't decref + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] add rsp, 32 ret @@ -1513,7 +1923,7 @@ eval_list: mov qword [rsp + 24], rdx ; cdr mov rdi, rax ; car mov rsi, qword [rsp] ; env - call eval + call eval ; (REF) this is returned, so don't decref mov rdi, rax ; eval(car, env) lea rsi, [rel nil] call cons ; (eval(car, env) . nil) @@ -1533,7 +1943,7 @@ eval_list: jmp .tailcall .not_cons: mov rsi, qword [rsp] ; env - call eval + call eval ; (REF) this is returned, so don't decref mov rsi, rax mov rdi, qword [rsp + 8] ; tail call set_cdr @@ -1621,7 +2031,7 @@ obj_eq: mov rdi, qword [rax + 8] ; rhs data pointer mov rdx, qword [rsp] ; lhs data pointer mov rcx, qword [rsp + 8] ; lhs length - cmp rsi, rdx ; rhs length == lhs length? + cmp rsi, rcx ; rhs length == lhs length? jne .not_equal call strcmp test al, al @@ -1632,6 +2042,16 @@ obj_eq: mov rsi, qword [rsp + 8] ; rhs add rsp, 24 jmp arr_eq +.equal: + add rsp, 24 + xor eax, eax + mov al, 1 + ret +.not_equal: + add rsp, 24 + xor eax, eax + cmp eax, 1 + ret arr_eq: ; compare lengths @@ -1663,11 +2083,15 @@ arr_eq: and dl, 0x7 cmp al, dl ; lhs data type == rhs data type? jne .not_equal - cmp al, OBJ_BYTE - jne .elementwise + cmp al, OBJ_NUM + ja .elementwise ; strcmp mov rdi, qword [rsp + 8] ; lhs data pointer mov esi, dword [rsp] ; length + shl al, 3 ; + mul esi + test al, al + cmovz eax, esi ; OBJ_NUM ? 8 : 1 mov rdx, qword [rsp + 24] ; rhs data pointer mov ecx, esi call strcmp @@ -1675,23 +2099,16 @@ arr_eq: je .equal jmp .not_equal .elementwise: - mov byte [rsp + 4], al ; data type xor r12, r12 and qword [rsp + 8], -8 ; lhs data pointer and qword [rsp + 24], -8 ; rhs data pointer .loop: cmp r12d, dword [rsp] ; index < length? jge .equal - movzx ecx, byte [rsp + 4] ; data type - lea rax, byte [rel OBJ_SIZES] - movzx eax, byte [rax + rcx] ; size of each element - mul r12d mov rdi, qword [rsp + 8] ; lhs data pointer - add rdi, rax ; lhs data pointer + index * size - or rdi, rcx ; lhs data pointer + index * size | data type + lea rdi, [rdi + r12 * 8] ; lhs[i] mov rsi, qword [rsp + 24] ; rhs data pointer - add rsi, rax ; rhs data pointer + index * size - or rsi, rcx ; rhs data pointer + index * size | data type + lea rsi, [rsi + r12 * 8] ; rhs[i] call obj_eq ; lhs[i] == rhs[i]? jne .not_equal inc r12d @@ -1709,9 +2126,963 @@ arr_eq: cmp eax, 1 ret - +;; Evaluation Primitives +;; folds all elements in the list $rdi using the binary function $rdx in the environment $rsi with an accumulator value of $rcx, returning the result in $rax +;; fn fold_list(List, Env, (U, T, Env) -> U, U) -> U +fold_list: + sub rsp, 32 + mov qword [rsp], rdi ; rest + mov qword [rsp + 8], rsi ; env + mov qword [rsp + 16], rcx ; acc + mov qword [rsp + 24], rdx ; func +.loop: + mov rdi, qword [rsp] + call car_cdr + mov qword [rsp], rdx ; rest = cdr + mov rsi, rax + mov rdi, qword [rsp + 16] ; $rdi = acc + mov rdx, qword [rsp + 8] ; $rdx = env + mov rax, qword [rsp + 24] ; $rax = func + call rax + mov qword [rsp + 16], rax ; update acc to result of func(acc, car) + mov rdi, qword [rsp] ; $rdi = rest + lea rsi, [rel nil] + cmp rdi, rsi + jne .loop + mov rax, qword [rsp + 16] ; return acc + add rsp, 32 + ret + +;; like fold_list, but requires that the list is non-empty +;; fn reduce_list(List, Env, (T, T, Env) -> T) -> T +reduce_list: + mov rcx, rdx ; func + call car_cdr + lea rdi, [rel nil] + cmp rdi, rdx + je .done + mov rdi, rdx ; list + mov rdx, rcx ; func + mov rcx, rax ; acc + call fold_list + ret +.done: + mov rax, rdx + ret + +;; like reduce_list, calls a predicate instead of a binary function, and returns "t" if the predicate returns non-zero for any pair of adjacent elements, and nil otherwise +;; predicate: fn (T, T, Env) -> bool +any2_list: + sub rsp, 24 + mov qword [rsp], rdi ; rest + mov qword [rsp + 8], rsi ; env + mov qword [rsp + 16], rdx ; pred +.loop: + call car_cdr + mov rsi, rax + mov qword [rsp], rdx + mov rdi, rdx + lea rdx, [rel nil] + cmp rdi, rdx + je .false + call car ; car(rest) + mov rdi, rax + mov rdx, qword [rsp + 8] ; env + mov rax, qword [rsp + 16] ; pred + call rax + ; func(car(rest), car(cdr(rest)), env) + test al, al + jnz .true + jmp .loop +.true: + mov al, 1 + add rsp, 24 + ret +.false: + xor al, al + add rsp, 24 + ret + + ; TODO: probably decref everything here? + +;; |a, b| {a += b; a} +add_inner: + xchg rsi, rdi + call num_val + add rsi, rax + mov rax, rsi + ret + +sub_inner: + xchg rsi, rdi + call num_val + sub rsi, rax + mov rax, rsi + ret + +bitand_inner: + xchg rsi, rdi + call num_val + and rsi, rax + mov rax, rsi + ret + +bitor_inner: + xchg rsi, rdi + call num_val + or rsi, rax + mov rax, rsi + ret + +bitxor_inner: + xchg rsi, rdi + call num_val + xor rsi, rax + mov rax, rsi + ret + +imul_inner: + xchg rsi, rdi + call num_val + mul rsi + ret + +idiv_inner: + xchg rsi, rdi + call num_val + xor rdx, rdx + xchg rsi, rax + idiv rsi + ret + +irem_inner: + xchg rsi, rdi + call num_val + xor rdx, rdx + xchg rsi, rax + idiv rsi + mov rax, rdx + ret + +lt_inner: + call num_val + mov rdi, rsi + mov rsi, rax + call num_val + lea rdi, [rel ATOM_T] + or rdi, OBJ_ATOM + cmp rsi, rax + cmovge rdi, qword [rel nil] + ret + +neq_inner: + call obj_eq + ret + +;; (Env, (var val), Env) -> Env +;; $rdi = acc-env, $rsi = (var val), $rdx = genv +p_let_inner: + sub rsp, 16 + mov qword [rsp], rdi ; acc-env + mov qword [rsp + 8], rsi ; (var val) + mov rdi, rsi + call cdr + mov rdi, rax ; (val . nil) + call car + mov rdi, rax ; val + mov rsi, rdx ; genv + call eval ; (REF) we return this, so don't decref + mov rsi, rax ; val + mov rdi, qword [rsp + 8] ; (var val) + call car + mov rdi, rax ; var + mov rdx, qword [rsp] ; acc-env + call prepend ; ((var . val) . acc-env) + add rsp, 16 + ret + +;; (Env, (var val), Env) -> Env +;; $rdi = acc-env, $rsi = (var val), $rdx = genv +p_let_star_inner: + sub rsp, 16 + mov qword [rsp], rdi ; acc-env + mov qword [rsp + 8], rsi ; (var val) + mov rdi, rsi + call cdr + mov rdi, rax + call car + mov rdi, rax + mov rsi, qword [rsp] ; acc-env + call eval ; (REF) we return this, so don't decref + mov rsi, rax ; val + mov rdi, qword [rsp + 8] ; (var val) + call car + mov rdi, rax ; var + mov rdx, qword [rsp] ; acc-env + call prepend ; ((var . val) . acc-env) + add rsp, 16 + ret + +p_eq: + sub rsp, 8 + mov qword [rsp], rsi ; env + push rsi + call eval_list + mov rsi, qword [rsp] ; env + mov qword [rsp], rax ; evaled_list + mov rdi, rax + lea rdx, [rel neq_inner] + call any2_list + test al, al + lea rax, [rel nil] + lea rsi, [rel ATOM_T] + or rsi, OBJ_ATOM + cmove rax, rsi + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rax, qword [rsp] ; result + add rsp, 8 + ret + +p_lt: + call eval_list + push rax + mov rdi, rax + call car_cdar_or_panic + mov rdi, rax + mov rsi, rdx + call lt_inner + pop rdi + push rax + call obj_dec_ref + pop rax + ret + +p_add: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov ecx, 0 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel add_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_sub: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov ecx, 0 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel sub_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_bitand: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov rcx, -1 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel bitand_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_bitor: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov ecx, 0 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel bitor_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_bitxor: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov ecx, 0 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel bitxor_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_mul: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov ecx, 1 + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel imul_inner] + call fold_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_div: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel idiv_inner] + call reduce_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_rem: + sub rsp, 16 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov rdi, qword [rsp] ; $rdi = evaled list + mov rsi, qword [rsp + 8] ; $rsi = env + lea rdx, [rel irem_inner] + call reduce_list + mov rdi, qword [rsp] ; evaled_list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rdi, qword [rsp] ; result + call make_num + add rsp, 16 + ret + +p_car: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call caar + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov qword [rsp], rax + add rsp, 8 + ret + +p_cdr: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call cadr + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov qword [rsp], rax + add rsp, 8 + ret + +p_is_nil: +p_not: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car + lea rsi, [rel nil] + cmp rsi, rax + lea rdi, [rel ATOM_T] + or rdi, OBJ_ATOM + cmove rax, rdi + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_quote: + call car + mov rdi, rax + call obj_inc_ref + mov rax, rdi + ret + +p_lambda: + sub rsp, 24 + mov qword [rsp + 16], rsi ; env + call car_cdr ; (params . (body . nil)) + mov qword [rsp], rax ; params + mov rdi, rdx + call car + mov qword [rsp + 8], rax ; body + mov rdi, qword [rsp] ; params + call obj_inc_ref + mov rdi, qword [rsp + 8] ; body + call obj_inc_ref + mov rdi, qword [rsp + 16] ; env + call obj_inc_ref + mov rdi, qword [rsp] ; params + mov rsi, qword [rsp + 8] ; body + mov rdx, qword [rsp + 16] ; env + call clos + add rsp, 24 + ret + +p_define: + sub rsp, 16 + call car_cdar_or_panic ; (symbol . value) + mov qword [rsp], rax ; symbol + mov qword [rsp + 8], rdx ; value + mov rdi, rax + call obj_inc_ref + mov rdi, qword [rsp + 8] ; value + call eval + mov qword [rsp + 8], rax ; evaled value + mov rdi, rax + call obj_inc_ref + mov rsi, rdi + mov rdi, qword [rsp] ; symbol + call genv_append + mov rax, qword [rsp + 8] ; evaled value + add rsp, 16 + ret + +p_eval: + sub rsp, 16 + mov qword [rsp], rsi ; env + call eval_list + mov qword [rsp + 8], rax ; evaled list + mov rdi, rax + call car + mov rdi, rax + mov rsi, qword [rsp] ; env + call eval + mov rdi, qword [rsp + 8] ; evaled list + mov qword [rsp + 8], rax ; result + call obj_dec_ref + mov rax, qword [rsp + 8] ; result + add rsp, 16 + ret +;; (if cond then else) +;; cond = car($rdi) +;; then = car(cdr($rdi)) +;; else = car(cdr(cdr($rdi))) +;; result = eval(cond) ? eval(then) : eval(else) +p_if: + ; eval(car(cdr(not(eval(car($rdi), $rsi)) ? cdr(rdi) : $rdi)) $rsi) + ; + ; env + ; else + ; then + ; cond + sub rsp, 32 + mov qword [rsp + 24], rsi ; env + mov rsi, 3 + lea rdx, [rsp] + call spill_list + mov rdi, qword [rsp] ; cond + mov rsi, qword [rsp + 24] ; env + call eval + mov qword [rsp], rax ; eval(cond) + mov rdi, rax + call is_nil + mov rdi, qword [rsp + 8] ; then + cmove rdi, qword [rsp + 16] ; else + mov rsi, qword [rsp + 24] ; env + call eval + mov qword [rsp + 8], rax ; result + mov rdi, qword [rsp] ; eval(cond) + call obj_dec_ref + mov rax, qword [rsp + 8] ; result + add rsp, 32 + ret +;; (let ((var1 val1) (var2 val2) ...) body) +;; $rdi = (bindings expr) where bindings = ((var1 val1) (var2 val2) ...) +p_let: + sub rsp, 24 + call car_cdar_or_panic + mov qword [rsp], rax ; bindings + mov qword [rsp + 8], rdx ; body + mov qword [rsp + 16], rsi ; env + mov qword [rsp], rax ; bindings + + mov rdi, rsi + call obj_inc_ref + + mov rdi, qword [rsp] ; bindings + mov rsi, qword [rsp + 16] ; env + mov rcx, rsi + lea rdx, [rel p_let_inner] + call fold_list + mov qword [rsp + 16], rax ; body_env + + mov rsi, rax + mov rdi, qword [rsp + 8] ; body + call eval + mov qword [rsp], rax ; result + mov rdi, qword [rsp + 16] ; body_env + call obj_dec_ref + mov rax, qword [rsp] ; result + add rsp, 24 + ret + +p_let_star: + sub rsp, 24 + call car_cdar_or_panic + mov qword [rsp], rax ; bindings + mov qword [rsp + 8], rdx ; body + mov qword [rsp + 16], rsi ; env + mov qword [rsp], rax ; bindings + + mov rdi, rsi + call obj_inc_ref + + mov rdi, qword [rsp] ; bindings + mov rsi, qword [rsp + 16] ; env + mov rcx, rsi + lea rdx, [rel p_let_star_inner] + call fold_list + mov qword [rsp + 16], rax ; body_env + + mov rsi, rax + mov rdi, qword [rsp + 8] ; body + call eval + mov qword [rsp], rax ; result + mov rdi, qword [rsp + 16] ; body_env + call obj_dec_ref + mov rax, qword [rsp] ; result + add rsp, 24 + ret + +p_int_to_byte: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car + mov rdi, rax + call num_val + mov edi, eax + call make_byte + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_byte_to_int: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car + shl eax, 8 + and eax, 0xff + mov edi, eax + call make_num + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_concat: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car_cdar_or_panic ; (a . b) + mov rdi, rax + mov rsi, rdx + call arr_concat + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_append: + sub rsp, 24 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car_cdar_or_panic ; (arr . e) + mov qword [rsp + 8], rax ; arr + mov qword [rsp + 16], rdx ; e + mov rdi, qword [rsp + 8] ; arr + xor esi, esi + call arr_dup + mov qword [rsp + 8], rax ; arr' + mov rsi, qword [rsp + 16] ; e + mov rdi, rax + call arr_append + + mov rdi, qword [rsp] ; evaled list + call obj_dec_ref + mov rax, qword [rsp + 8] ; arr' + add rsp, 24 + ret + +p_nth: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car_cdar_or_panic ; (arr . n) + push rax + mov rdi, rdx + call num_val + mov esi, eax + pop rdi + call arr_nth + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_set_nth: + push r12 + sub rsp, 32 + call eval_list + mov qword [rsp + 24], rax + + mov rdi, rax + mov rsi, 3 + lea rdx, [rsp] + call spill_list + + mov rdi, qword [rsp] ; arr + xor esi, esi + call arr_dup + + mov qword [rsp], rax ; arr' + mov rdi, rax + mov rsi, qword [rsp + 8] ; n + mov rdx, qword [rsp + 16] ; val + call arr_set_nth + + mov rdi, qword [rsp + 24] ; evaled list + call obj_dec_ref + mov rax, qword [rsp] ; arr' + add rsp, 32 + pop r12 + ret + + ;; returns the length of the input string as a number +p_arr_len: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car + mov rdi, rax + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_ptr_part + mov edi, dword [rax + 8] ; length + call make_num + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +;; returns the pointer and length part of the string object as a pair of numbers +p_arr_decompose: + sub rsp, 16 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car + mov rdi, rax + call obj_tag_part + cmp al, OBJ_ARR + jne panic_abort + call obj_ptr_part + mov esi, dword [rax + 8] ; length + mov dword [rsp + 8], esi + mov rdi, qword [rax + 16] ; pointer + call make_num + mov edi, dword [rsp + 8] + mov qword [rsp + 8], rax ; pointer num + call make_num + mov rsi, rax ; length num + mov rdi, qword [rsp + 8] ; pointer num + call cons + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 16 + ret + +;; creates a new array object +;; (make-arr data len cap type) -> arr +p_make_arr: + sub rsp, 40 + call eval_list + mov qword [rsp + 32], rax + + mov rdi, rax + mov rsi, 4 + lea rdx, [rsp] + call spill_list + + mov rdi, qword [rsp] + call num_val + mov qword [rsp], rax ; data + + mov rdi, qword [rsp + 8] + call num_val + mov qword [rsp + 8], rax ; len + + mov rdi, qword [rsp + 16] + call num_val + mov qword [rsp + 16], rax ; cap + + mov rdi, qword [rsp + 24] + call num_val + mov qword [rsp + 24], rax ; type + + mov rdx, qword [rsp] ; data + mov rdi, qword [rsp + 8] ; len + mov rsi, qword [rsp + 16] ; cap + mov rcx, qword [rsp + 24] ; type + call make_arr + mov rdi, qword [rsp + 32] ; eval + mov qword [rsp + 32], rax + call obj_dec_ref + mov rax, qword [rsp + 32] ; result + add rsp, 40 + ret + +p_allocate: + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + call car_cdar_or_panic ; (size . align) + mov rdi, rax + call num_val + mov esi, eax ; size + mov rdi, rdx + call num_val + mov edi, eax ; align + xchg rdi, rsi + call heap_alloc + + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + add rsp, 8 + ret + +p_deallocate: + sub rsp, 32 + call eval_list + mov qword [rsp + 24], rax + + mov rdi, rax + mov esi, 3 + lea rdx, [rsp] + call spill_list + + mov rdi, qword [rsp] ; ptr + call num_val + mov qword [rsp], rax ; ptr + + mov rdi, qword [rsp + 8] ; size + call num_val + mov qword [rsp + 8], rax ; size + + mov rdi, qword [rsp + 16] ; align + call num_val + mov edx, eax ; align + mov esi, dword [rsp + 8] ; size + mov rdi, qword [rsp] ; ptr + call heap_dealloc + + mov rdi, qword [rsp + 24] + call obj_dec_ref + lea rax, [rel nil] + add rsp, 32 + ret + +;; performs a system call, returning the result as a number +;; (syscall num arg1 arg2 arg3) -> syscall(num, arg1, arg2, arg3) +p_syscall: + push r12 + push r13 + sub rsp, 8 + call eval_list + mov qword [rsp], rax + mov rdi, rax + ; r11 = syscall num + ; r12 = arg0 + xor r11, r11 + xor r12, r12 + xor r13, r13 + call ._next_arg ; syacall num + jnz .do_syscall + mov r11, rcx + call ._next_arg ; arg0 + jnz .do_syscall + mov r12, rcx + call ._next_arg ; arg1 + jnz .do_syscall + mov rsi, rcx + call ._next_arg ; arg2 + jnz .do_syscall + mov r13, rcx + call ._next_arg ; arg3 + jnz .do_syscall + mov r10, rcx + call ._next_arg ; arg4 + jnz .do_syscall + mov r8, rcx + call ._next_arg ; arg5 + jnz .do_syscall + mov r9, rcx +.do_syscall: + mov rax, r11 ; syscall num + mov rdi, r12 ; arg0 + mov rdx, r13 ; arg2 + syscall + mov rdi, rax + call make_num + + mov rdi, qword [rsp] + mov qword [rsp], rax + call obj_dec_ref + mov rax, qword [rsp] + + add rsp, 8 + pop r13 + pop r12 + ret + ;; fn _next_arg(list) -> ($rcx=arg, $rdi=rest) +._next_arg: + call is_nil + test al, al + jnz ._next_arg_done + call car_cdr + mov rdi, rax ; arg + call num_val + mov rdi, rdx ; rest + mov rcx, rax ; arg value + mov al, 0 + test al, al +._next_arg_done: + ret + +p_cons: + sub rsp, 8 + call eval_list + mov qword [rsp], rax ; evaled list + mov rdi, rax + call car_cdar_or_panic ; (a . b) + mov rdi, rax ; a + call obj_inc_ref + mov rsi, rdx ; b + call obj_inc_ref + mov rdi, rax ; a' + mov rsi, rdx ; b' + call cons ; (a' . b') + mov rdi, qword [rsp] ; evaled list + mov qword [rsp], rax ; result + call obj_dec_ref + mov rax, qword [rsp] ; result + add rsp, 8 + ret + +p_progn: + sub rsp, 32 + mov qword [rsp + 8], rsi ; env + call eval_list + mov qword [rsp], rax ; evaled list + mov qword [rsp + 16], rax ; rest + lea rdi, [rel nil] + mov qword [rsp + 24], rdi ; init result + mov rdi, rax + +.loop: + call obj_is_nil + je .done + + mov rdi, qword [rsp + 24] ; result + call obj_dec_ref + + mov rdi, qword [rsp + 16] ; rest + call car_cdr + mov qword [rsp + 16], rdx ; rest + mov rdi, rax ; car + mov rsi, qword [rsp + 8] ; env + call eval + mov qword [rsp + 24], rax ; result + jmp .loop +.done: + mov rdi, qword [rsp] ; evaled list + call obj_dec_ref + mov rax, qword [rsp + 24] ; result + add rsp, 32 + ret + diff --git a/stages/lisp0/test.rs b/stages/lisp0/test.rs index 10558da..46cc335 100644 --- a/stages/lisp0/test.rs +++ b/stages/lisp0/test.rs @@ -8,12 +8,17 @@ unsafe extern "C" { fn getc() -> SourceIterResult; fn peekc() -> SourceIterResult; + fn eval(expr: Object, env: Object) -> Object; + fn init_env() -> Object; + fn parse_next_token() -> Object; #[link_name = "ifile"] static mut IFILE: Source; #[link_name = "nil"] static NIL: (); + #[link_name = "env"] + static GENV: (); } use std::ptr::NonNull; @@ -59,8 +64,8 @@ impl std::fmt::Debug for Object { } fn fmt_prim(prim: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let prim_ptr = unsafe { prim.cast::().read() }; - write!(f, "#", prim_ptr) + let prim_ptr = unsafe { prim.cast::<*const ()>().read() }; + write!(f, "#", prim_ptr) } fn fmt_cons(cons: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -110,6 +115,7 @@ impl std::fmt::Debug for Object { atom.byte_add(8).cast::().read(), ) }; + let slice = unsafe { core::slice::from_raw_parts(ptr, len) }; let string = core::str::from_utf8(slice).unwrap_or(""); write!(f, "'{string}") @@ -335,4 +341,28 @@ mod tests { println!("{:?}", obj); } } + + static mut ENV_INIT: std::cell::LazyCell = + std::cell::LazyCell::new(|| unsafe { init_env() }); + + #[test] + fn global_env() { + unsafe { + let env = unsafe { *ENV_INIT }; + println!("{env:?}"); + } + } + + #[test] + fn eval_add() { + let file = ManuallyDrop::new(File::open("tests/add.l").unwrap()); + unsafe { + init_source(&raw mut IFILE, file.as_raw_fd()); + let expr = parse_next_token(); + let env = unsafe { *ENV_INIT }; + let result = eval(expr, env); + eprint!("done: "); + eprintln!("{result:?}"); + } + } }