From 3f9df4a32f7b4af636cd1d326ce62f563fb6aea3 Mon Sep 17 00:00:00 2001 From: Toni Date: Sat, 14 Mar 2026 11:44:14 +0100 Subject: [PATCH] don't heap allocate in io.printf --- src/analyzer.rs | 22 ++++++++++++---------- src/codegen_x86_64.rs | 19 ++++++++++++++++--- src/std/std.zr | 29 +++++++++++------------------ 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 5623270..a772880 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -14,7 +14,15 @@ pub struct Analyzer { impl Analyzer { pub fn new() -> Analyzer { Analyzer { - functions: HashMap::new(), + functions: HashMap::from([ + ("_builtin_heap_head".into(), -1), + ("_builtin_heap_tail".into(), -1), + ("_builtin_read64".into(), -1), + ("_builtin_set64".into(), -1), + ("_builtin_syscall".into(), -1), + ("io.printf".into(), -1), + ("_builtin_environ".into(), -1), + ]), constants: HashMap::new(), structs: HashMap::new(), } @@ -160,22 +168,16 @@ impl Analyzer { args, } => { if let Expr::Variable(callee_name) = *callee.clone() { - if callee_name.lexeme.starts_with("_builtin_") - || self.functions.contains_key(&callee_name.lexeme) - { + if self.functions.contains_key(&callee_name.lexeme) { // its a function (defined/builtin/extern) if let Some(arity) = self.functions.get(&callee_name.lexeme) { - if *arity >= 0 - && *arity != args.len() as i32 - && callee_name.lexeme != "io.printf" - // TODO: disgusting hack - { + if *arity >= 0 && *arity != args.len() as i32 { return error!( &paren.loc, format!("expected {} arguments, got {}", arity, args.len()) ); } - } else if !callee_name.lexeme.starts_with("_builtin_") { + } else { return error!( &paren.loc, format!("undefined function: {}", callee_name.lexeme) diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 0a17f0d..f80af6e 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -142,6 +142,21 @@ _builtin_syscall: mov r9, [rsp+8] syscall ret + +section .text.io.printf +io.printf: + push rbp + mov rbp, rsp + sub rsp, 48 + mov [rsp], rsi + mov [rsp + 8], rdx + mov [rsp + 16], rcx + mov [rsp + 24], r8 + mov [rsp + 32], r9 + lea rsi, [rsp] + call io._printf_impl + leave + ret " ); @@ -650,9 +665,7 @@ _builtin_environ: } if let Expr::Variable(callee_name) = &**callee { - if callee_name.lexeme.starts_with("_builtin_") - || self.analyzer.functions.contains_key(&callee_name.lexeme) - { + if self.analyzer.functions.contains_key(&callee_name.lexeme) { // its a function (defined/builtin/extern) emit!(&mut self.output, " call {}", callee_name.lexeme); } else { diff --git a/src/std/std.zr b/src/std/std.zr index aaac357..eb1d63e 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -186,35 +186,28 @@ func mem.write32[x: ptr, d: i64] : void func mem.write64[x: ptr, d: i64] : void _builtin_set64(x, d) -// TODO: we don't have variadics so we manually skip arity checking in the analyzer -func io.printf[s: str, a1: any, a2: any, a3: any, a4: any, a5: any] : void - // TODO: we really shouldn't heap allocate to print something - let args: Array = [a1, a2, a3, a4, a5] - let arg_idx: i64 = 0 - +func io._printf_impl[s: str, args: ptr] : void while s[0] if s[0] == '%' s = s + 1 if s[0] == 'd' - io.print_i64(array.nth(args, arg_idx)) - arg_idx = arg_idx + 1 + io.print_i64(mem.read64(args)) + args = args + 8 else if s[0] == 'x' - io.print_i64_hex(array.nth(args, arg_idx)) - arg_idx = arg_idx + 1 + io.print_i64_hex(mem.read64(args)) + args = args + 8 else if s[0] == 's' - io.print(array.nth(args, arg_idx)) - arg_idx = arg_idx + 1 - else if (s[0] == 'c') - io.print_char(array.nth(args, arg_idx)) - arg_idx = arg_idx + 1 - else if (s[0] == '%') + io.print(mem.read64(args)) + args = args + 8 + else if s[0] == 'c' + io.print_char(mem.read64(args)) + args = args + 8 + else if s[0] == '%' io.print_char('%') else io.print_char(s[0]) s = s + 1 - - array.free(args) func io.print_sized[x: str, size: i64] : void _builtin_syscall(SYS_write, 1, x, size)