diff --git a/README.md b/README.md index 903889c..253b5c7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A very cool language * Compiles to x86_64 Assembly * Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std) * Produces tiny static executables (~30KB with musl) -* ~~No libc required~~ (SOON; still used for memory allocation and DNS resolution) +* No libc required! * Sometimes works * Has the pipe operator diff --git a/examples/curl.zr b/examples/curl.zr deleted file mode 100644 index 32828ca..0000000 --- a/examples/curl.zr +++ /dev/null @@ -1,70 +0,0 @@ -func main[argc: i64, argv: ptr] : i64 - if argc < 2 - panic("url missing") - - let url: str = mem.read64(argv + 8) - - if str.len(url) <= 7 - panic("missing url scheme") - - if !str.equal(str.substr(url, 0, 7), "http://") - panic("invalid url scheme") - - let url_len: i64 = str.len(url) - let host_start = 7 - let i: i64 = host_start - while i < url_len - if url[i] == '/' - break - i = i + 1 - - let host: str = str.substr(url, host_start, i - host_start) - let path: str = "/" - if i < url_len - path = str.substr(url, i, url_len - i) - - let s: i64 = net.connect(host, 80) - if s < 0 - panic("failed to connect") - - // very leaky - let req: str = "GET " - req = str.concat(req, path) - req = str.concat(req, " HTTP/1.0\r\nHost: ") - req = str.concat(req, host) - req = str.concat(req, "\r\nConnection: close\r\n\r\n") - net.send(s, req, str.len(req)) - mem.free(req) - - let header_buf: str = mem.alloc(8192) - let header_size = 0 - let found: bool = false - let end_index: i64 = -1 - - while !found && header_size < 8192 - let n: i64 = net.read(s, header_buf + header_size, 8192 - header_size) - if n <= 0 - break - let current_size: i64 = header_size + n - i = 0 - while i <= current_size - 4 - if header_buf[i] == 13 && header_buf[i + 1] == 10 && header_buf[i + 2] == 13 && header_buf[i + 3] == 10 - found = true - end_index = i + 4 - break - i = i + 1 - header_size = current_size - - if end_index < header_size - io.print_sized(header_buf + end_index, header_size - end_index) - mem.free(header_buf) - - let buffer: ptr = mem.alloc(4096) - while true - let n: i64 = net.read(s, buffer, 4096) - if n <= 0 - break - io.print_sized(buffer, n) - mem.free(buffer) - - net.close(s) diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index d138ab7..d12b981 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -106,6 +106,20 @@ impl<'a> CodegenX86_64<'a> { "section .note.GNU-stack db 0 +section .bss + _heap_head: resq 1 + _heap_tail: resq 1 + +section .text._builtin_heap_head +_builtin_heap_head: + lea rax, [rel _heap_head] + ret + +section .text._builtin_heap_tail +_builtin_heap_tail: + lea rax, [rel _heap_tail] + ret + section .text._builtin_read64 _builtin_read64: mov rax, qword [rdi] diff --git a/src/std/std.zr b/src/std/std.zr index 5d6c2d5..ad5461b 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -1,19 +1,128 @@ -extern malloc -extern realloc -extern free -extern gethostbyname - func panic[msg: str] : void io.print("PANIC: ") io.println(msg) (0 / 0) // crashes program which is kinda better since you get a gdb backtrace os.exit(1) -func mem.alloc[x: i64] : ptr - return malloc(x) +const MEM_BLOCK_SIZE = 24 + +struct mem.Block + size: i64 + free: bool + next: mem.Block + +func mem._align[x: i64] : i64 + return (x + 7) & -8 + +func mem._split_block[blk: mem.Block, needed: i64] : void + if blk->size >= needed + MEM_BLOCK_SIZE + 8 + let new_blk: mem.Block = blk + MEM_BLOCK_SIZE + needed + new_blk->size = blk->size - needed - MEM_BLOCK_SIZE + new_blk->free = true + new_blk->next = blk->next + + blk->size = needed + blk->next = new_blk + + if mem.read64(_builtin_heap_tail()) == blk + mem.write64(_builtin_heap_tail(), new_blk) + +func mem._request_space[size: i64] : mem.Block + let blk: mem.Block = _builtin_syscall(SYS_brk, 0) + let new_brk: ptr = blk + MEM_BLOCK_SIZE + size + let result: ptr = _builtin_syscall(SYS_brk, new_brk) + if result != new_brk + return 0 + + blk->size = size + blk->free = false + blk->next = 0 + + let tail: mem.Block = mem.read64(_builtin_heap_tail()) + if tail + tail->next = blk + + mem.write64(_builtin_heap_tail(), blk) + return blk + +func mem._coalesce[] : void + let cur: mem.Block = mem.read64(_builtin_heap_head()) + while cur && cur->next + let next: mem.Block = cur->next + if cur->free && next->free + cur->size = cur->size + MEM_BLOCK_SIZE + next->size + cur->next = next->next + if !cur->next + mem.write64(_builtin_heap_tail(), cur) + else + cur = cur->next + +func mem.alloc[size: i64] : ptr + if size == 0 + return 0 + + size = mem._align(size) + + let cur: mem.Block = mem.read64(_builtin_heap_head()) + while cur + if cur->free && cur->size >= size + mem._split_block(cur, size) + cur->free = false + return cur + MEM_BLOCK_SIZE + cur = cur->next + + let blk: mem.Block = mem._request_space(size) + if !blk + return 0 + + if !mem.read64(_builtin_heap_head()) + mem.write64(_builtin_heap_head(), blk) + + return blk + MEM_BLOCK_SIZE func mem.free[x: ptr] : void - free(x) + if !x + return 0 + + let blk: mem.Block = x - MEM_BLOCK_SIZE + blk->free = true + mem._coalesce() + +func mem.realloc[x: ptr, new_size: i64] : ptr + if !x + return mem.alloc(new_size) + + if new_size == 0 + mem.free(x) + return 0 + + new_size = mem._align(new_size) + + let blk: mem.Block = x - MEM_BLOCK_SIZE + if blk->size >= new_size + mem._split_block(blk, new_size) + return x + + let next: mem.Block = blk->next + if next && next->free + let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size + if combined >= new_size + blk->size = combined + blk->next = next->next + if !blk->next + mem.write64(_builtin_heap_tail(), blk) + mem._split_block(blk, new_size) + return x + + let new_ptr: ptr = mem.alloc(new_size) + if !new_ptr + return 0 + + for i in 0..blk->size + new_ptr[i] = x[i] + + mem.free(x) + return new_ptr func mem.zero_and_free[x: ptr, size: i64] : void mem.zero(x, size) @@ -465,7 +574,7 @@ func array.push[xs: Array, x: i64] : void let new_capacity = 4 if xs->capacity != 0 new_capacity = xs->capacity * 2 - xs->data = realloc(xs->data, new_capacity * 8) + xs->data = mem.realloc(xs->data, new_capacity * 8) xs->capacity = new_capacity mem.write64(xs->data + xs->size * 8, x) @@ -661,13 +770,7 @@ func net.listen[packed_host: i64, port: i64] : i64 return s -func net.connect[host: str, port: i64] : i64 - let he: ptr = gethostbyname(host) - if he == 0 - return -1 - - let ip_ptr: ptr = mem.read64(mem.read64(he + 24)) - +func net.connect[packed_host: i64, port: i64] : i64 let s: i64 = _builtin_syscall(SYS_socket, 2, 1, 0) if s < 0 return -1 @@ -675,12 +778,13 @@ func net.connect[host: str, port: i64] : i64 let sa: ptr = mem.alloc(16) mem.zero(sa, 16) sa[0] = 2 + sa[1] = 0 sa[2] = (port >> 8) & 255 sa[3] = port & 255 - sa[4] = ip_ptr[0] - sa[5] = ip_ptr[1] - sa[6] = ip_ptr[2] - sa[7] = ip_ptr[3] + sa[4] = (packed_host >> 24) & 255 + sa[5] = (packed_host >> 16) & 255 + sa[6] = (packed_host >> 8) & 255 + sa[7] = packed_host & 255 if _builtin_syscall(SYS_connect, s, sa, 16) < 0 mem.free(sa)