From 48c58c0709d19a17fa9bf622c8f93553fc244434 Mon Sep 17 00:00:00 2001 From: Toni Date: Wed, 20 May 2026 13:59:08 +0200 Subject: [PATCH 1/2] stack allocated structs --- examples/chip8.zr | 2 +- src/codegen_x86_64.rs | 25 ++++++++++++++++++++----- src/parser.rs | 11 +++++++++-- src/std/net.zr | 4 ++-- src/std/std.zr | 16 ++++++---------- src/typechecker.rs | 5 ++++- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/examples/chip8.zr b/examples/chip8.zr index 48d1cc0..bddafec 100644 --- a/examples/chip8.zr +++ b/examples/chip8.zr @@ -22,7 +22,7 @@ struct CHIP8 keyboard_map: Array func chip8_create[] : CHIP8 - let c = new CHIP8 + let c = new* CHIP8 c->memory = mem.alloc(4096) mem.zero(c->memory, 4096) c->display = mem.alloc(64*32) diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index c2d5fc9..852b7f8 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -12,6 +12,8 @@ struct Var { pub var_type: String, } +const STACK_SIZE: usize = 256; // TODO: eww + pub struct Env { scopes: Vec>, next_offset: usize, @@ -300,7 +302,8 @@ _builtin_environ: emit!(&mut self.output, "{}:", name.lexeme); emit!(&mut self.output, " push rbp"); emit!(&mut self.output, " mov rbp, rsp"); - emit!(&mut self.output, " sub rsp, 256"); // TODO: eww + let stack_size = (STACK_SIZE + 15) & !15; + emit!(&mut self.output, " sub rsp, {}", stack_size); match params { Params::Normal(params) => { @@ -730,16 +733,28 @@ _builtin_environ: return error!(&op.loc, "can only take address of variables and functions"); } }, - ExprKind::New(struct_name) => { + ExprKind::New { + struct_name, + use_heap, + } => { let struct_fields = &self.symbol_table.structs[&struct_name.lexeme]; - let memory_size = struct_fields.len() * 8; - emit!(&mut self.output, " mov rdi, {}", memory_size); - emit!(&mut self.output, " call mem.alloc"); + + if *use_heap { + emit!(&mut self.output, " mov rdi, {}", memory_size); + emit!(&mut self.output, " call mem.alloc"); + emit!(&mut self.output, " push rax"); + } else { + let aligned_size = (memory_size + 15) & !15; + emit!(&mut self.output, " sub rsp, {}", aligned_size); + emit!(&mut self.output, " mov rax, rsp"); + } emit!(&mut self.output, " push rax"); + emit!(&mut self.output, " sub rsp, 8"); emit!(&mut self.output, " mov rdi, rax"); emit!(&mut self.output, " mov rsi, {}", memory_size); emit!(&mut self.output, " call mem.zero"); + emit!(&mut self.output, " add rsp, 8"); emit!(&mut self.output, " pop rax"); } ExprKind::MemberAccess { left, field } => { diff --git a/src/parser.rs b/src/parser.rs index 8f694d6..2fc3b60 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -121,7 +121,10 @@ pub enum ExprKind { op: Token, expr: Box, }, - New(Token), + New { + struct_name: Token, + use_heap: bool, + }, MemberAccess { left: Box, field: Token, @@ -621,9 +624,13 @@ impl Parser { Ok(Expr::new(ExprKind::ArrayLiteral(xs))) } else if self.match_token(&[TokenType::KeywordNew]) { + let use_heap = self.match_token(&[TokenType::Star]); let struct_name = self.consume(TokenType::Identifier, "expected struct name after 'new'")?; - Ok(Expr::new(ExprKind::New(struct_name))) + Ok(Expr::new(ExprKind::New { + struct_name, + use_heap, + })) } else if self.match_token(&[TokenType::Identifier]) { Ok(Expr::new(ExprKind::Variable(self.previous().clone()))) } else { diff --git a/src/std/net.zr b/src/std/net.zr index 1e0d5d0..e90625c 100644 --- a/src/std/net.zr +++ b/src/std/net.zr @@ -95,7 +95,7 @@ struct net.UDPPacket func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket err.clear() - let s = new net.UDPSocket + let s = new* net.UDPSocket s->fd = _builtin_syscall(SYS_socket, AF_INET, SOCK_DGRAM, 0) if s->fd < 0 @@ -132,7 +132,7 @@ func net.udp_send_to[s: net.UDPSocket, addr: ptr, data: ptr, size: i64] : void _builtin_syscall(SYS_sendto, s->fd, data, size, 0, addr, 16) func net.udp_receive[s: net.UDPSocket, size: i64] : net.UDPPacket - let pkt = new net.UDPPacket + let pkt = new* net.UDPPacket pkt->data = mem.alloc(size) pkt->source_addr = mem.alloc(16) mem.zero(pkt->source_addr, 16) diff --git a/src/std/std.zr b/src/std/std.zr index e9b8349..9907b3a 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -339,7 +339,7 @@ struct io.Buffer func io.Buffer.alloc?[size: i64] : io.Buffer err.clear() - let buffer = new io.Buffer + let buffer = new* io.Buffer buffer->size = size buffer->data = mem.alloc?(size) if err.check() @@ -403,7 +403,7 @@ func io.read_binary_file?[path: str] : io.Buffer err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file") return 0 as io.Buffer - let buf = new io.Buffer + let buf = new* io.Buffer buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2) _builtin_syscall(SYS_lseek, fd, 0, 0) @@ -777,10 +777,10 @@ struct Array capacity: i64 func array.new[] : Array - return new Array + return new* Array func array.new_preallocated_and_zeroed[size: i64] : Array - let xs = new Array + let xs = new* Array if size > 0 xs->data = must(mem.realloc?(xs->data, size * 8)) as ptr mem.zero(xs->data, size * 8) @@ -849,7 +849,7 @@ struct HashMap._Node next: HashMap._Node func HashMap.new[] : HashMap - let map = new HashMap + let map = new* HashMap map->table = array.new_preallocated_and_zeroed(HashMap._TABLE_SIZE) return map @@ -863,7 +863,7 @@ func HashMap.insert[map: HashMap, key: str, value: any] : void return 0 current = current->next - let new_node = new HashMap._Node + let new_node = new* HashMap._Node new_node->key = str.make_copy(key) new_node->value = value new_node->next = array.nth(map->table, index) @@ -982,12 +982,10 @@ struct os.Timespec func os.sleep[ms: i64] : void if ms < 0 panic("negative time passed to os.sleep") - // TODO: we really shouldnt need a heap allocation to sleep let req = new os.Timespec req->tv_sec = ms / 1000 req->tv_nsec = (ms % 1000) * 1000000 _builtin_syscall(SYS_nanosleep, req, 0) - mem.free(req) func os.urandom?[n: i64]: ptr err.clear() @@ -1022,11 +1020,9 @@ struct os.Timeval tv_usec: i64 func os.time[] : i64 - // TODO: we really shouldnt need a heap allocation to check the time let tv = new os.Timeval _builtin_syscall(SYS_gettimeofday, tv, 0) let out = tv->tv_sec * 1000 + tv->tv_usec / 1000 - mem.free(tv) return out // voodoo magic diff --git a/src/typechecker.rs b/src/typechecker.rs index cc90935..055d249 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -483,7 +483,10 @@ impl<'a> TypeChecker<'a> { error!(&op.loc, "can only take address of variables and functions") } }, - ExprKind::New(struct_name) => { + ExprKind::New { + struct_name, + use_heap: _, + } => { if !self.symbol_table.structs.contains_key(&struct_name.lexeme) { return error!( &struct_name.loc, From 174dbfa740f4736722f442af29f5ce097ebaa930 Mon Sep 17 00:00:00 2001 From: Toni Date: Wed, 20 May 2026 14:34:31 +0200 Subject: [PATCH 2/2] make net examples print ip and data --- examples/tcp_server.zr | 8 +++++++- examples/udp_server.zr | 5 +++++ src/std/net.zr | 27 ++++++++++++++------------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/examples/tcp_server.zr b/examples/tcp_server.zr index 94e5bf9..35a55ff 100644 --- a/examples/tcp_server.zr +++ b/examples/tcp_server.zr @@ -3,11 +3,17 @@ func main[] : i64 io.println("Listening on port 8000...") let resp = mem.alloc(60000) + let addr = mem.alloc(16) while true - let conn = net.accept(s) + let conn = net.accept(s, addr) if conn < 0 continue + io.printf("%d.%d.%d.%d:%d\n", addr[4], addr[5], addr[6], addr[7], mem.read16be(addr + 2)) + + io.print_sized(resp, n) + io.println("") + let n = net.read(conn, resp, 60000) net.send(conn, resp, n) net.close(conn) diff --git a/examples/udp_server.zr b/examples/udp_server.zr index ddd8ba0..b8089ed 100644 --- a/examples/udp_server.zr +++ b/examples/udp_server.zr @@ -8,5 +8,10 @@ func main[] : i64 net.UDPPacket.free(pkt) continue + io.printf("%d.%d.%d.%d:%d\n", pkt->source_addr[4], pkt->source_addr[5], pkt->source_addr[6], pkt->source_addr[7], mem.read16be(pkt->source_addr + 2)) + + io.print_sized(pkt->data, pkt->size) + io.println("") + net.udp_send_to(s, pkt->source_addr, pkt->data, pkt->size) net.UDPPacket.free(pkt) diff --git a/src/std/net.zr b/src/std/net.zr index e90625c..2f60493 100644 --- a/src/std/net.zr +++ b/src/std/net.zr @@ -69,8 +69,9 @@ func net.connect?[packed_host: i64, port: i64] : i64 mem.free(sa) return s -func net.accept[s: i64] : i64 - return _builtin_syscall(SYS_accept, s, 0, 0) +func net.accept[s: i64, addr: ptr] : i64 + let addrlen = 16 + return _builtin_syscall(SYS_accept, s, addr, ^addrlen) func net.send[s: i64, data: ptr, size: i64] : void _builtin_syscall(SYS_sendto, s, data, size, 0, 0, 0) @@ -88,11 +89,6 @@ struct net.UDPSocket fd: i64 addr: ptr -struct net.UDPPacket - data: ptr - source_addr: ptr - size: i64 - func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket err.clear() let s = new* net.UDPSocket @@ -131,6 +127,16 @@ func net.create_udp_server?[packed_host: i64, port: i64] : net.UDPSocket func net.udp_send_to[s: net.UDPSocket, addr: ptr, data: ptr, size: i64] : void _builtin_syscall(SYS_sendto, s->fd, data, size, 0, addr, 16) +struct net.UDPPacket + data: ptr + source_addr: ptr + size: i64 + +func net.UDPPacket.free[pkt: net.UDPPacket] : void + mem.free(pkt->data) + mem.free(pkt->source_addr) + mem.free(pkt) + func net.udp_receive[s: net.UDPSocket, size: i64] : net.UDPPacket let pkt = new* net.UDPPacket pkt->data = mem.alloc(size) @@ -146,11 +152,6 @@ func net.UDPSocket.close_and_free[s: net.UDPSocket] : void mem.free(s->addr) mem.free(s) -func net.UDPPacket.free[pkt: net.UDPPacket] : void - mem.free(pkt->data) - mem.free(pkt->source_addr) - mem.free(pkt) - const DNS_TYPE_A = 1 const DNS_CLASS_IN = 1 const DNS_RECURSION_DESIRED = 256 @@ -195,7 +196,7 @@ func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer io.Buffer.free(name) return out -// TODO: dont resolve IPs +// TODO: dont resolve if its already an IP func net.resolve?[domain: str] : i64 err.clear() let query = net.build_dns_query(domain, DNS_TYPE_A)