idk>>??
This commit is contained in:
parent
ca9fd58bed
commit
f4e833ac46
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
lang/target
|
||||
lang/libcompiler/target
|
||||
/target/
|
||||
/stages/build/
|
||||
|
|
|
|||
65
lang/src/typeck.asm
Normal file
65
lang/src/typeck.asm
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
default rel
|
||||
%include "src/ast.inc"
|
||||
|
||||
section .text
|
||||
extern vec_init_with
|
||||
extern vec_push
|
||||
extern vec_get
|
||||
extern vec_insert_sorted
|
||||
extern vec_get_or
|
||||
extern panic
|
||||
extern memcpy
|
||||
extern strcmp
|
||||
extern vec_binary_search_by
|
||||
extern vec_insert
|
||||
|
||||
extern bump_alloc
|
||||
|
||||
;; rdi: *Ctx
|
||||
;; rsi: *Ast
|
||||
;; rdx: node
|
||||
;; rcx: scope
|
||||
;; define-fn fn typeck_for_each(ctx: *mut TypeCheckCtx, ast: *mut Ast, node_index: u64, scope: u64)
|
||||
typeck_for_each:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; start-structs
|
||||
; struct TypeCheckCtx {
|
||||
; type_graph: TCGraph,
|
||||
; ast_to_type_map: Vec<(u64, u64)>,
|
||||
; }
|
||||
; struct TCEdge<E> {
|
||||
; nodes: [u64; 2],
|
||||
; next_edge: [u64; 2],
|
||||
; weight: E,
|
||||
; }
|
||||
; struct TCNode<N> {
|
||||
; next_edge: [u64; 2],
|
||||
; weight: N,
|
||||
; }
|
||||
; struct TCGraph {
|
||||
; types: Vec<TCNode<Type>>,
|
||||
; type_vars: Vec<TCNode<()>>,
|
||||
; edges: Vec<TCEdge<bool>>,
|
||||
; }
|
||||
; end-structs
|
||||
|
||||
; In the pre-pass, the typechecker graph is built according to the typing rules of each ast node.
|
||||
; E.g., for a binary expression, both operands must be of the same type, and the result type must be a supertype of the operand type:
|
||||
; E1(Add, E2(..), E3(..))
|
||||
; E1: T1, E2: T2, E3: T3
|
||||
; T2 == T3, T1 >= T2, T1 >= T3
|
||||
|
||||
; In a subsequent pass, the type-variable graph is simplified and equal type nodes are merged to make traversal easier.
|
||||
; The resulting type graph should be a DAG.
|
||||
|
||||
sub rsp, 32
|
||||
mov [rsp], rsi ; Ast
|
||||
mov [rsp + 8], rdi ; TypeCheckCtx
|
||||
mov [rsp + 16], rdx ; node_index
|
||||
mov [rsp + 24], rcx ; scope
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
;; } typeck_for_each
|
||||
385
stages/as0/as1.asm
Normal file
385
stages/as0/as1.asm
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
section .bss
|
||||
token_buf resb 0x100
|
||||
buf resb 0x100
|
||||
labels resb 0x1000
|
||||
|
||||
section .text
|
||||
extern panic_abort
|
||||
;; returns the next character from the source without advancing.
|
||||
extern peekc
|
||||
;; returns the next character from the source and advances.
|
||||
extern getc
|
||||
;; advances past the next character and peeks the next.
|
||||
extern consuming_peekc
|
||||
|
||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
||||
fasthash:
|
||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
||||
.loop:
|
||||
test rsi, rsi
|
||||
jz .done
|
||||
movzx ecx, byte [rdi] ; get next byte
|
||||
xor eax, ecx
|
||||
imul eax, 0x1000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; @param lhs: (rdi, rsi)
|
||||
;; @param rhs: (rdx, rcx)
|
||||
;; @return al
|
||||
strcmp:
|
||||
cmp rcx, rsi
|
||||
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
||||
xor eax, eax
|
||||
.loop:
|
||||
cmp rsi, rax
|
||||
je .equal
|
||||
movzx ecx, byte [rdx + rax]
|
||||
cmp byte [rdi + rax], cl
|
||||
lea rax, [rax + 1]
|
||||
je .loop
|
||||
seta al ; al = lhs > rhs
|
||||
sbb al, 0 ; al = al - CF
|
||||
ret
|
||||
.equal:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
memcpy:
|
||||
xor rax, rax
|
||||
.loop:
|
||||
cmp rax, rdx
|
||||
je .done
|
||||
mov al, byte [rdi + rax]
|
||||
mov byte [rsi + rax], al
|
||||
lea rax, [rax + 1]
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; returns 1 if the result of `peekc()` is $sil
|
||||
;; treats all characters less than ' ' as spaces.
|
||||
is_ch:
|
||||
push rsi
|
||||
call peekc
|
||||
pop rsi
|
||||
cmp al, ' '
|
||||
setbe cl ; cl = al <= ' '
|
||||
movzx ecx, cl
|
||||
mov edx, ' '
|
||||
mul ecx, edx ; cl = (al < ' ') ? ' ' : 0
|
||||
cmp sil, ' '
|
||||
cmove ax, cx ; al = (sil < ' ') ? cl : al
|
||||
cmp al, sil
|
||||
setz al ; al = al == sil
|
||||
ret
|
||||
|
||||
;; struct packed LabelEntry {
|
||||
;; hash: u64,
|
||||
;; file_offset: Option<NonMaxU32>,
|
||||
;; }
|
||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
||||
push_lbl:
|
||||
push r12
|
||||
lea r12, [rel labels]
|
||||
mov rax, dword [r12 + 0x1000 - 4] ; get current label count
|
||||
cmp rax, 341
|
||||
jge panic_abort
|
||||
lea rax, [rax + rax*2]
|
||||
shl rax, 2 ; $rax = num_labels + size_of::<label_entry>()
|
||||
add r12, rax
|
||||
call fasthash
|
||||
mov qword [r12], rax ; store hash in labels array
|
||||
mov dword [r12 + 8], -1 ; initialize file offset to -1 (invalid)
|
||||
inc dword [r12 + 0x1000 - 4] ; increment label count
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, returns 0 in rax.
|
||||
find_lbl_ptr:
|
||||
call fasthash
|
||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
||||
mov ebx, dword [r15 + 0x6000]
|
||||
.loop:
|
||||
test rbx, rbx
|
||||
jz .not_found
|
||||
dec rbx
|
||||
cmp qword [rdx], rax
|
||||
je .found
|
||||
add rdx, 12 ; move to next label entry
|
||||
jmp .loop
|
||||
.found:
|
||||
mov rax, rdx
|
||||
ret
|
||||
.not_found:
|
||||
xor rax, rax
|
||||
ret
|
||||
|
||||
push_or_find_lbl:
|
||||
call fasthash
|
||||
push rax
|
||||
mov rdi, rax
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .push
|
||||
add rsp, 8
|
||||
ret
|
||||
.push:
|
||||
pop rdi
|
||||
call push_lbl
|
||||
ret
|
||||
|
||||
find_lbl_offset:
|
||||
call find_lbl_ptr
|
||||
test rax, rax
|
||||
jz .not_found
|
||||
mov eax, dword [rax + 8] ; get file offset of label
|
||||
ret
|
||||
.not_found:
|
||||
xor rdi, rdi
|
||||
call panic_abort
|
||||
|
||||
set_lbl_offset:
|
||||
push rsi
|
||||
call find_lbl_ptr
|
||||
pop rsi
|
||||
mov dword [rax + 8], esi ; set file offset of label
|
||||
ret
|
||||
|
||||
is_alpha:
|
||||
movzx edi, dil
|
||||
and edi, 0x1fffdf ; ignore bit 5 (case)
|
||||
add edi, -65 ; subtract 'A'
|
||||
cmp edi, 26 ; check if in range 0-25
|
||||
setb al
|
||||
ret
|
||||
|
||||
is_digit:
|
||||
movzx edi, dil
|
||||
lea eax, [rdi - 48] ; subtract '0'
|
||||
cmp eax, 10 ; check if in range 0-9
|
||||
setb al
|
||||
ret
|
||||
|
||||
;; returns 1 if $dil is a valid identifier character (alphanumeric, '-' or '_'), and 0 otherwise.
|
||||
;; clobbers rdi, rax, rcx
|
||||
is_id_cont:
|
||||
movzx rdi, dil
|
||||
; is_digit {
|
||||
lea eax, [rdi - 48]
|
||||
cmp eax, 10
|
||||
setb al
|
||||
; } || is_alpha {
|
||||
mov ecx, edi
|
||||
and ecx, 0x1fffdf
|
||||
add ecx, -65
|
||||
cmp ecx, 26
|
||||
setb cl
|
||||
; } is_alpha
|
||||
or al, cl
|
||||
cmp edi, 45 ; || '-'
|
||||
sete cl
|
||||
or al, cl
|
||||
cmp edi, 95 ; || '_'
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx.
|
||||
;; $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
to_digit:
|
||||
lea eax, [rsi - 2]
|
||||
cmp eax, 35
|
||||
jae .invalid
|
||||
movzx rdi, dil
|
||||
lea edx, [rdi - 65] ; 'A' = 65
|
||||
and edx, -33 ; convert to uppercase
|
||||
add edx, 10 ; 'A' should map to 10
|
||||
lea eax, [rdi - 48] ; '0' = 48
|
||||
cmp esi, 11
|
||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
||||
cmp edi, 58
|
||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
||||
xor eax, eax
|
||||
cmp edx, esi
|
||||
setb al ; al = edx < radix
|
||||
ret
|
||||
.invalid:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
;; reads from $rdi and parses digits with radix $rsi until a non-digit is encountered.
|
||||
parse_digits:
|
||||
sub rsp, 24
|
||||
mov qword [rsp + 16], rsi ; save the radix
|
||||
mov qword [rsp + 8], rdi ; save the source iterator
|
||||
mov qword [rsp], 0
|
||||
.loop:
|
||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
||||
call peekc
|
||||
mov dil, al
|
||||
mov rsi, qword [rsp + 16] ; restore the radix
|
||||
call to_digit
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, qword [rsp]
|
||||
mov rsi, qword [rsp + 16] ; restore the radix
|
||||
imul rax, rsi
|
||||
add rax, rdx
|
||||
mov qword [rsp], rax
|
||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
||||
call getc
|
||||
jmp .loop
|
||||
|
||||
.done:
|
||||
mov rax, [rsp]
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
parse_num:
|
||||
sub rsp, 24
|
||||
mov qword [rsp], 0 ; acc
|
||||
mov qword [rsp + 8], rdi ; source iterator
|
||||
mov dword [rsp + 16], 10 ; radix
|
||||
call peekc
|
||||
cmp al, `-`
|
||||
jne .skip_sign
|
||||
mov qword [rsp], -1 ; acc = -1
|
||||
call consuming_peekc
|
||||
.skip_sign:
|
||||
cmp al, `0`
|
||||
jne .skip_radix
|
||||
call consuming_peekc
|
||||
cmp al, `x`
|
||||
jne .skip_radix
|
||||
mov dword [rsp + 16], 16 ; radix = 16
|
||||
call consuming_peekc
|
||||
.skip_radix:
|
||||
mov dil, al
|
||||
call to_digit
|
||||
test al, al
|
||||
jz .done
|
||||
mov rax, qword [rsp] ; acc
|
||||
mov rsi, qword [rsp + 16] ; radix
|
||||
mov rcx, rdx
|
||||
imul rax, rsi
|
||||
add rax, rcx
|
||||
mov qword [rsp], rax ; acc = acc * radix + digit
|
||||
mov rdi, qword [rsp + 8] ; source iterator
|
||||
call consuming_peekc
|
||||
jmp .skip_radix
|
||||
.done:
|
||||
mov rax, qword [rsp] ; move the result into rax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
parse_label:
|
||||
push r12
|
||||
lea r12, [rel buf]
|
||||
sub rsp, 8
|
||||
mov qword [rsp], rdi ; source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_alpha
|
||||
test al, al
|
||||
jz .done
|
||||
mov byte [r12], al
|
||||
inc r12
|
||||
mov rdi, qword [rsp] ; restore source iterator
|
||||
call consuming_peekc
|
||||
mov dil, al
|
||||
call is_id_cont
|
||||
test al, al
|
||||
jz .done
|
||||
jmp .loop
|
||||
.done:
|
||||
lea rdi, [rel buf]
|
||||
mov rsi, r12
|
||||
sub rsi, rdi
|
||||
call push_or_find_lbl
|
||||
add rsp, 8
|
||||
pop r12
|
||||
ret
|
||||
|
||||
;; reads the next token into `token_buf`
|
||||
next_token:
|
||||
sub rsp, 8
|
||||
mov qword [rsp], 0
|
||||
.skip_spaces:
|
||||
mov rsi, ' '
|
||||
call is_ch
|
||||
test al, al
|
||||
jz .skipped_spaces:
|
||||
call getc
|
||||
jmp .skip_spaces
|
||||
.skipped_spaces:
|
||||
; leading [ is a memory access
|
||||
mov rsi, `[`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .mem_access
|
||||
; leading @ is label-def
|
||||
mov rsi, '@'
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .label_def
|
||||
; leading ' is a label-ref
|
||||
mov rsi, `'`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .label_ref
|
||||
; leading digit is a number
|
||||
call peekc
|
||||
mov rdi, rax
|
||||
call is_digit
|
||||
test al, al
|
||||
jnz .number
|
||||
; leading ; or # is a comment
|
||||
mov rsi, `;`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .comment
|
||||
mov rsi, `#`
|
||||
call is_ch
|
||||
test al, al
|
||||
jnz .comment
|
||||
; leading , is a separator
|
||||
mov rsi, `,`
|
||||
call is_ch
|
||||
test al, al
|
||||
jz .ident
|
||||
; just skip the comma since its not important for parsing
|
||||
call getc
|
||||
jmp .skip_spaces
|
||||
.ident:
|
||||
; now we have some identifier
|
||||
|
||||
; (mov rax (byte rdi))
|
||||
|
||||
|
||||
|
||||
;; registers:
|
||||
;; r0/rax
|
||||
;; r1/rcx
|
||||
;; r2/rdx
|
||||
;; r3/rbx
|
||||
;; r4/rsp
|
||||
;; r5/rbp
|
||||
;; r6/rsi
|
||||
;; r7/rdi
|
||||
;; r8
|
||||
;; r9
|
||||
;; r10
|
||||
;; r11
|
||||
;; r12
|
||||
;; r13
|
||||
;; r14
|
||||
;; r15
|
||||
;;
|
||||
;; registers of the form `rNUM` may have an optional suffix b, w, d, or q
|
||||
;; legacy registers may also be referenced by their 8-bit, 16-bit, or 32-bit names (e.g. al, ax, eax, rax or spl, sp, esp, rsp)
|
||||
;; high-byte registers (ah, bh, ch, dh) are not supported
|
||||
|
||||
45
stages/as0/types.zig
Normal file
45
stages/as0/types.zig
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const PackedOperandKind = enum(u3) {
|
||||
reg = 0,
|
||||
imm = 1,
|
||||
mem = 2,
|
||||
label = 3,
|
||||
mem_label = 4,
|
||||
|
||||
};
|
||||
|
||||
const Register = enum(u4) {
|
||||
rax = 0,
|
||||
rbx = 1,
|
||||
rcx = 2,
|
||||
rdx = 3,
|
||||
rsi = 4,
|
||||
rdi = 5,
|
||||
rsp = 6,
|
||||
rbp = 7,
|
||||
r8 = 8,
|
||||
r9 = 9,
|
||||
r10 = 10,
|
||||
r11 = 11,
|
||||
r12 = 12,
|
||||
r13 = 13,
|
||||
r14 = 14,
|
||||
r15 = 15,
|
||||
};
|
||||
|
||||
const PackedRegister = packed struct {
|
||||
reg: Register,
|
||||
size: u4,
|
||||
};
|
||||
|
||||
const PackedOperand = union(PackedOperandKind) {
|
||||
reg: PackedRegister,
|
||||
imm: i64,
|
||||
label: u32,
|
||||
mem_label: u32,
|
||||
mem: struct {
|
||||
base: PackedRegister,
|
||||
index: PackedRegister,
|
||||
scale: u2,
|
||||
disp: i32,
|
||||
},
|
||||
};
|
||||
56
stages/lisp0/lisp.in
Normal file
56
stages/lisp0/lisp.in
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
;; Atom String Table
|
||||
; t
|
||||
116
|
||||
; quote
|
||||
113 117 111 116 101
|
||||
; +
|
||||
43
|
||||
; -
|
||||
45
|
||||
; *
|
||||
42
|
||||
; /
|
||||
47
|
||||
; %
|
||||
37
|
||||
; <
|
||||
60
|
||||
; =
|
||||
61
|
||||
; car
|
||||
99 97 114
|
||||
; cdr
|
||||
99 100 114
|
||||
; not
|
||||
100 111 116
|
||||
; lambda
|
||||
108 97 109 98 100 97
|
||||
;define
|
||||
100 101 102 105 100 101
|
||||
; eval
|
||||
101 118 97 108
|
||||
; if
|
||||
105 102
|
||||
; let
|
||||
108 101 116
|
||||
; let*
|
||||
108 101 116 42
|
||||
; str-len
|
||||
115 116 114 45 108 101 100
|
||||
; list-len
|
||||
108 105 115 116 45 108 101 100
|
||||
; str-parts
|
||||
115 116 114 45 112 97 114 116 115
|
||||
; syscall
|
||||
115 121 115 99 97 108 108
|
||||
; print-env
|
||||
112 114 105 100 116 45 101 100 118
|
||||
; \Space
|
||||
92 83 112 97 99 101
|
||||
; \NL
|
||||
92 78 76
|
||||
; \Tab
|
||||
92 84 97 98
|
||||
;; Atom Table
|
||||
; ref-count| length | pointer to string |
|
||||
01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
|
||||
51
test.asm
Normal file
51
test.asm
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
section .text
|
||||
|
||||
global _start
|
||||
_start:
|
||||
mov rdi, 42
|
||||
call main
|
||||
mov rdi, rax
|
||||
mov rax, 60 ; syscall: exit
|
||||
syscall
|
||||
|
||||
global main
|
||||
main:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
jmp .prologue
|
||||
.args:
|
||||
mov qword [rbp -8], rdi
|
||||
.body:
|
||||
mov rbx, 42
|
||||
mov rcx, qword [rbp -8]
|
||||
mov rax, rbx
|
||||
cmp rcx, rax
|
||||
sete bl
|
||||
cmp bl, 0
|
||||
je .Be
|
||||
mov rbx, 7
|
||||
mov qword [rbp -16], rbx
|
||||
mov rdi, qword [rbp -16]
|
||||
mov rax, rsp
|
||||
sub rsp, 8
|
||||
and rsp, -16
|
||||
mov [rsp], rax
|
||||
call main
|
||||
pop rsp
|
||||
mov rbx, rax
|
||||
mov rax, rbx
|
||||
jmp .epilogue
|
||||
jmp .PHI4
|
||||
.Be:
|
||||
lea rax, main
|
||||
jmp .epilogue
|
||||
.PHI4:
|
||||
.epilogue:
|
||||
pop rbx
|
||||
add rsp, 16
|
||||
pop rbp
|
||||
ret
|
||||
.prologue:
|
||||
sub rsp, 16
|
||||
push rbx
|
||||
jmp .args
|
||||
Loading…
Reference in a new issue