From 213e3d4b37bc19cdb6941c14efc9708b04d5fe3d Mon Sep 17 00:00:00 2001 From: Toni Date: Wed, 24 Jun 2026 17:20:57 +0200 Subject: [PATCH] floats --- src/codegen_x86_64.rs | 101 +++++++++++++++++++++++++++--------------- src/std/std.zr | 30 ++++++++----- src/symbol_table.rs | 2 + src/typechecker.rs | 17 ++++++- test.zr | 68 ++++++++++++++++++---------- 5 files changed, 146 insertions(+), 72 deletions(-) diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 988fd97..675b41c 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -137,6 +137,17 @@ _builtin_set64: mov [rdi], rsi ret +section .text._builtin_cvtsi2sd +_builtin_cvtsi2sd: + cvtsi2sd xmm0, rdi + movq rax, xmm0 + ret + +section .text._builtin_cvttsd2si +_builtin_cvttsd2si: + cvttsd2si rax, xmm0 + ret + section .text._builtin_syscall _builtin_syscall: mov rax, rdi @@ -682,8 +693,11 @@ _builtin_environ: emit!(&mut self.output, " push rax"); } - let arg_count = args.len(); - self.emit_call_setup(arg_count); + let arg_types: Vec = args + .iter() + .map(|a| self.expr_types[&a.id].clone()) + .collect(); + self.emit_call_setup(&arg_types); if let ExprKind::Variable(callee_name) = &callee.kind { if self @@ -704,7 +718,7 @@ _builtin_environ: emit!(&mut self.output, " call rax"); } - self.emit_call_cleanup(arg_count); + self.emit_call_cleanup(args.len()); } ExprKind::ArrayLiteral(exprs) => { emit!(&mut self.output, " mov rdi, 24"); @@ -805,50 +819,65 @@ _builtin_environ: emit!(&mut self.output, " push rax"); } - let arg_count = 1 + args.len(); - self.emit_call_setup(arg_count); + let mut arg_types = vec![]; + arg_types.push(receiver_type.clone()); + arg_types.extend(args.iter().map(|a| self.expr_types[&a.id].clone())); + + self.emit_call_setup(&arg_types); emit!(&mut self.output, " call {}", func_name); - self.emit_call_cleanup(arg_count); + self.emit_call_cleanup(1 + args.len()); } } Ok(()) } - fn emit_call_setup(&mut self, arg_count: usize) { - if arg_count <= 6 { - for i in (0..arg_count).rev() { - emit!(&mut self.output, " pop {}", REGISTERS[i]); - } - } else { - for (i, reg) in REGISTERS.iter().enumerate() { - let offset = 8 * (arg_count - 1 - i); - emit!( - &mut self.output, - " mov {}, QWORD [rsp + {}]", - reg, - offset - ); - } - // TODO: since all zern values are 64bit large we currently cannot call - // external functions that expect a non-64bit value past the 6th argument - let num_stack = arg_count - 6; - for i in 0..num_stack { - let arg_idx = arg_count - 1 - i; - let offset = 8 * (arg_count - 1 - arg_idx); - emit!( - &mut self.output, - " mov rax, QWORD [rsp + {}]", - offset + 8 * i - ); - emit!(&mut self.output, " push rax"); + fn emit_call_setup(&mut self, arg_types: &[String]) { + let arg_count = arg_types.len(); + + let to_register = arg_count.min(6); + let mut fp_idx = 0; + let mut int_idx = 0; + for (i, arg_type) in arg_types.iter().enumerate().take(to_register) { + let offset = 8 * (arg_count - 1 - i); + emit!(&mut self.output, " mov rax, QWORD [rsp + {}]", offset); + if arg_type == "f64" { + emit!(&mut self.output, " movq xmm{}, rax", fp_idx); + fp_idx += 1; + } else { + emit!(&mut self.output, " mov {}, rax", REGISTERS[int_idx]); + int_idx += 1; } } + + // TODO: since all zern values are 64bit large we currently cannot call + // external functions that expect a non-64bit value past the 6th argument + let num_stack = arg_count.saturating_sub(6); + for i in 0..num_stack { + let arg_idx = arg_count - 1 - i; + let offset = 8 * (arg_count - 1 - arg_idx); + emit!( + &mut self.output, + " mov rax, QWORD [rsp + {}]", + offset + 8 * i + ); + emit!(&mut self.output, " push rax"); + } + + emit!(&mut self.output, " mov al, {}", fp_idx); + + if num_stack == 0 { + emit!(&mut self.output, " add rsp, {}", 8 * to_register); + } } fn emit_call_cleanup(&mut self, arg_count: usize) { - if arg_count > 6 { - emit!(&mut self.output, " add rsp, {}", 8 * (arg_count - 6)); - emit!(&mut self.output, " add rsp, {}", 8 * arg_count); + let num_stack = arg_count.saturating_sub(6); + if num_stack > 0 { + emit!( + &mut self.output, + " add rsp, {}", + 8 * (arg_count + num_stack) + ); } } diff --git a/src/std/std.zr b/src/std/std.zr index a7ca675..8871ad6 100644 --- a/src/std/std.zr +++ b/src/std/std.zr @@ -309,18 +309,6 @@ func io.read_line[] : str b->destroy() return s -func io.file_exists[path: str] : bool - return _builtin_syscall(SYS_faccessat, AT_FDCWD, path, F_OK, 0) == 0 - -func io.is_a_directory[path: str] : bool - st := _stackalloc(256) // it has 21 mixed-size fields so ptr for now - - rc := _builtin_syscall(SYS_newfstatat, AT_FDCWD, path, st, 0) - if rc != 0 - return false - - return (mem.read32(st + 24) & S_IFMT) == S_IFDIR - func io.read_text_file[path: str] : str, bool fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0) if fd < 0 @@ -929,6 +917,12 @@ func Array._partition[arr: Array, low: i64, high: i64] : i64 arr->set(high, temp) return i + 1 +func Array.contains_str[arr: Array, s: str] : bool + for i in 0..arr->size + if (arr->nth(i) as str)->equal(s) + return true + return false + func Array.count[arr: Array, item: any] : i64 count := 0 for i in 0..arr->size @@ -1108,6 +1102,18 @@ func os.run_shell_command[command: str] : i64, bool else return -(st & 0x7f), true +func os.file_exists[path: str] : bool + return _builtin_syscall(SYS_faccessat, AT_FDCWD, path, F_OK, 0) == 0 + +func os.is_a_directory[path: str] : bool + st := _stackalloc(256) // it has 21 mixed-size fields so ptr for now + + rc := _builtin_syscall(SYS_newfstatat, AT_FDCWD, path, st, 0) + if rc != 0 + return false + + return (mem.read32(st + 24) & S_IFMT) == S_IFDIR + func os.list_directory[path: str] : Array, bool fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0) if fd < 0 diff --git a/src/symbol_table.rs b/src/symbol_table.rs index 183442b..3261704 100644 --- a/src/symbol_table.rs +++ b/src/symbol_table.rs @@ -51,6 +51,8 @@ impl SymbolTable { "_builtin_set64".into(), FnType::new("void", vec!["ptr", "i64"]), ), + ("_builtin_cvtsi2sd".into(), FnType::new("f64", vec!["i64"])), + ("_builtin_cvttsd2si".into(), FnType::new("i64", vec!["f64"])), ("_builtin_syscall".into(), FnType::new_variadic("i64")), ("_builtin_environ".into(), FnType::new("ptr", vec![])), ("_var_arg".into(), FnType::new("any", vec!["i64"])), diff --git a/src/typechecker.rs b/src/typechecker.rs index 57ae6e4..9186683 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -33,7 +33,7 @@ macro_rules! expect_types { }; } -static BUILTIN_TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "any"]; +static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "f64", "str", "bool", "ptr", "any"]; pub struct Env { scopes: Vec>, @@ -300,6 +300,9 @@ impl<'a> TypeChecker<'a> { "unrecognized type: ".to_owned() + &return_type ); } + if return_type == "f64" { + return error!(&return_types[0].loc, "returning f64 not implemented yet"); + } self.current_function_return_type = return_type.clone(); @@ -314,6 +317,12 @@ impl<'a> TypeChecker<'a> { "unrecognized type: ".to_owned() + ¶m.var_type.lexeme ); } + if param.var_type.lexeme == "f64" { + return error!( + ¶m.var_name.loc, + "f64 params not implemented yet" + ); + } env.define_var( param.var_name.lexeme.clone(), @@ -549,6 +558,12 @@ impl<'a> TypeChecker<'a> { Ok(field.field_type.clone()) } ExprKind::Cast { expr, type_name } => { + if type_name.lexeme == "f64" { + return error!( + &type_name.loc, + "use _builtin_cvtsi2sd and _builtin_cvttsd2si to cast to f64" + ); + } self.typecheck_expr(env, expr)?; if !self.is_valid_type_name(&type_name.lexeme) { return error!( diff --git a/test.zr b/test.zr index 3747676..90fdbdf 100644 --- a/test.zr +++ b/test.zr @@ -1,32 +1,52 @@ -func run_test[x: str] : void - build_blacklist := ["examples/euler", "examples/raylib.zr", "examples/chip8.zr", "examples/sqlite_todo.zr"] - run_blacklist := ["guess_number.zr", "tcp_server.zr", "udp_server.zr"] +struct TestRunner + build_blacklist: Array + run_blacklist: Array + custom_build_flags: HashMap + custom_run_args: HashMap - for i in 0..build_blacklist->size - if x->equal(build_blacklist->nth(i)) - io.printf("Skipping %s...\n", x) - return +func TestRunner.run_directory[tr: TestRunner, dir: str] : void + ~files, ok := os.list_directory(dir) + if !ok + panic("failed to open test directory") + for i in 0..files->size + joined := dir->concat("/")->concat(files->nth(i)) + if os.is_a_directory(joined) + tr->run_directory(joined) + else + tr->run_test(joined) + +func TestRunner.run_test[tr: TestRunner, x: str] : void + name := os.basename(x) + + if tr->build_blacklist->contains_str(name) + io.printf("Skipping %s...\n", x) + return io.printf("Building %s...\n", x) + build_cmd := "./target/release/zern "->concat(x) + ~custom_build_flags, ok := tr->custom_build_flags->get(name) + if ok + build_cmd = build_cmd->concat(" ")->concat(custom_build_flags) + build_start_time := os.time() - ~status, ok := os.run_shell_command("./target/release/zern "->concat(x)) + ~status, ok := os.run_shell_command(build_cmd) if !ok || status != 0 os.exit(1) build_end_time := os.time() io.printf("%dms\n", build_end_time - build_start_time) - for i in 0..run_blacklist->size - if x->contains(run_blacklist->nth(i)) - io.printf("Skipping %s...\n", x) - return + if tr->run_blacklist->contains_str(name) + io.printf("Skipping %s...\n", x) + return io.printf("Running %s...\n", x) run_cmd := "./out" - if x->equal("examples/curl.zr") - run_cmd = run_cmd->concat(" http://example.com") + ~custom_run_args, ok := tr->custom_run_args->get(name) + if ok + run_cmd = run_cmd->concat(" ")->concat(custom_run_args) run_start_time := os.time() ~status, ok := os.run_shell_command(run_cmd) @@ -37,18 +57,20 @@ func run_test[x: str] : void io.printf("Running %s took %dms\n", x, run_end_time - run_start_time) -func run_directory[dir: str] : void - ~files, ok := os.list_directory(dir) - if !ok - panic("failed to open test directory") - for i in 0..files->size - run_test(dir->concat(files->nth(i))) - func main[] : i64 ~status, ok := os.run_shell_command("cargo build --release") if !ok || status != 0 os.exit(1) - run_directory("examples/") - run_directory("examples/euler/") + tr := new TestRunner + tr->build_blacklist = [] + tr->run_blacklist = ["raylib.zr", "sqlite_todo.zr", "guess_number.zr", "udp_server.zr", "chip8.zr", "tcp_server.zr"] + tr->custom_build_flags = HashMap.new() + tr->custom_build_flags->insert("raylib.zr", "-m -C \"-lraylib\"") + tr->custom_build_flags->insert("chip8.zr", "-m -C \"-lraylib\"") + tr->custom_build_flags->insert("sqlite_todo.zr", "-m -C \"-lsqlite3\"") + tr->custom_run_args = HashMap.new() + tr->custom_run_args->insert("curl.zr", "http://example.com") + + tr->run_directory("examples") io.println("DONE")