From 601916b828b8d94a89df6950da73ea68a20daa69 Mon Sep 17 00:00:00 2001 From: Mathias Magnusson Date: Mon, 2 Jun 2025 16:39:53 +0200 Subject: make print return integer length --- src/codegen.zig | 24 ++++++++++++++++-------- src/compile.zig | 49 +++++++++++++++++++++++++++++++------------------ src/main.zig | 24 +++++++++++------------- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 1e8ed41..fe58f74 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -560,10 +560,11 @@ const Context = struct { try self.instructions.append(inst); } - fn maybeFreeSources(self: *Context, vregs: compile.Instr.Sources) !void { - for (vregs.slice()) |src| { - if (self.block.?.vreg_last_use.get(src) == self.current_instruction_index.?) { - self.register_allocator.free(src); + fn maybeFreeSources(self: *Context) !void { + const index = self.current_instruction_index.?; + if (self.block.?.vreg_last_use.get(index)) |last_used_here| { + for (last_used_here.items) |vreg| { + self.register_allocator.free(vreg); } } } @@ -601,7 +602,7 @@ const Context = struct { fn genBinOp(self: *Context, bin_op: compile.Instr.BinOp) !void { const lhs = self.register_allocator.get(bin_op.lhs); const rhs = self.register_allocator.get(bin_op.rhs); - try self.maybeFreeSources(bin_op.sources()); + try self.maybeFreeSources(); const reg = try self.register_allocator.allocate(bin_op.dest); switch (bin_op.op) { .add => try self.emit(.add(reg, lhs, rhs)), @@ -616,10 +617,9 @@ const Context = struct { defer self.register_allocator.freeAux(quot); const digit = try self.register_allocator.allocateAux(); defer self.register_allocator.freeAux(digit); - const count = try self.register_allocator.allocateAux(); - defer self.register_allocator.freeAux(count); + const count = try self.register_allocator.allocate(print.dest); - try self.maybeFreeSources(print.sources()); + try self.maybeFreeSources(); try self.emit(.addi(digit, .zero, '\n')); try self.emit(.addi(.sp, .sp, -1)); @@ -642,6 +642,12 @@ const Context = struct { try self.emit(.addi(.a2, count, 0)); // count = count try self.emit(.ecall()); // syscall(no, fd, buf, count) try self.emit(.add(.sp, .sp, count)); + + try self.emit(.addi(count, count, -1)); + } + + fn genDiscard(self: *Context, _: compile.Instr.Discard) !void { + try self.maybeFreeSources(); } fn codegenInstr(self: *Context, instr: compile.Instr) !void { @@ -687,6 +693,8 @@ pub fn create_elf(allocator: Allocator, block: compile.Block) ![]u8 { .ecall(), }); + std.debug.print("allocated regs: {}\n", .{root.fmtHashMap(ctx.register_allocator.allocated)}); + var output_buffer: std.ArrayList(u8) = .init(allocator); errdefer output_buffer.deinit(); try output_buffer.appendNTimes(undefined, @sizeOf(elf.Elf64_Ehdr) + @sizeOf(elf.Elf64_Phdr)); diff --git a/src/compile.zig b/src/compile.zig index 6a79efd..9513a93 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -15,6 +15,7 @@ pub const Instr = struct { constant: Constant, bin_op: BinOp, print: Print, + discard: Discard, }; pub const Constant = struct { @@ -43,6 +44,7 @@ pub const Instr = struct { }; pub const Print = struct { + dest: VReg, arg: VReg, pub fn sources(self: Print) Sources { @@ -50,6 +52,14 @@ pub const Instr = struct { } }; + pub const Discard = struct { + vreg: VReg, + + pub fn sources(self: Discard) Sources { + return Sources.fromSlice(&.{self.vreg}) catch unreachable; + } + }; + pub fn sources(self: Instr) Sources { return switch (self.type) { inline else => |instr| instr.sources(), @@ -58,8 +68,8 @@ pub const Instr = struct { pub fn dest(self: *const Instr) ?VReg { return switch (self.type) { - inline .constant, .bin_op => |s| s.dest, - inline .print => null, + inline .constant, .bin_op, .print => |s| s.dest, + // inline .x => null, }; } @@ -69,13 +79,16 @@ pub const Instr = struct { pub const Block = struct { // arguments: []Reg, instrs: []Instr, - vreg_last_use: std.AutoHashMap(VReg, usize), + vreg_last_use: std.AutoHashMap(usize, std.ArrayList(VReg)), fn init(allocator: Allocator, instrs: []Instr) !Block { - var vreg_last_use: std.AutoHashMap(VReg, usize) = .init(allocator); + var vreg_last_use: std.AutoHashMap(usize, std.ArrayList(VReg)) = .init(allocator); for (0.., instrs) |i, instr| { - for (instr.sources().slice()) |src| - try vreg_last_use.put(src, i); + for (instr.sources().slice()) |src| { + const kv = try vreg_last_use.getOrPut(i); + if (!kv.found_existing) kv.value_ptr.* = .init(allocator); + try kv.value_ptr.append(src); + } } return .{ .instrs = instrs, @@ -112,7 +125,10 @@ const CompileContext = struct { fn compileStmt(self: *Self, stmt: parse.Stmt) !void { switch (stmt.type) { - .expr => |expr| _ = try self.compileExpr(expr), + .expr => |expr| try self.addInstr(.{ + .loc = stmt.loc, + .type = .{ .discard = .{ .vreg = try self.compileExpr(expr) } }, + }), } } @@ -142,18 +158,15 @@ const CompileContext = struct { }); }, .call => |call| { - if (call.proc.type == .identifier and - std.mem.eql(u8, call.proc.loc.getIdent(self.source), "print")) - { - const arg = try self.compileExpr(call.arg); - try self.addInstr(.{ - .loc = expr.loc, - .type = .{ .print = .{ .arg = arg } }, - }); - // BUG: we're returning a bogus virtual register that we didn't write to - } else { + if (call.proc.type != .identifier or + !std.mem.eql(u8, call.proc.loc.getIdent(self.source), "print")) return error.CantCallAnythingButPrint; - } + + const arg = try self.compileExpr(call.arg); + try self.addInstr(.{ + .loc = expr.loc, + .type = .{ .print = .{ .dest = dest, .arg = arg } }, + }); }, .identifier => return error.CantCompileIdentifierExpr, } diff --git a/src/main.zig b/src/main.zig index dd34c53..6692799 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,16 +13,14 @@ pub fn main() !void { var args = std.process.args(); _ = args.next(); - const arg = args.next() orelse ""; - const run_path, const out_file = if (std.mem.eql(u8, arg, "run")) blk: { - const path = try std.fmt.allocPrint(allocator, "/tmp/{}.elf", .{std.crypto.random.int(u32)}); - break :blk .{ path, try std.fs.createFileAbsolute(path, .{ .mode = 0o777 }) }; - } else if (arg.len > 0) - .{ null, try std.fs.cwd().createFile(arg, .{ .mode = 0o777 }) } + const out_path = args.next(); + const out_file = if (out_path) |path| + try std.fs.cwd().createFile(path, .{ .mode = 0o777 }) else - .{ null, std.io.getStdOut() }; - defer if (run_path) |path| allocator.free(path); + std.io.getStdOut(); + const run = if (args.next()) |arg| std.mem.eql(u8, arg, "run") else false; + const output = out_file.writer(); // var br = std.io.bufferedReader(std.io.getStdIn().reader()); @@ -39,9 +37,9 @@ pub fn main() !void { // } const source = + \\1; 1; 1; 1; 1; \\print 18446744073709551615; - \\print (0 - 1); - \\print 69; + \\print (print (0 - 1)); ; var lexer: Lexer = .{ .source = source }; std.debug.print("Tokens:\n", .{}); @@ -67,15 +65,15 @@ pub fn main() !void { const elf = try codegen.create_elf(allocator, block); try output.writeAll(elf); std.debug.print("Run output:\n", .{}); - if (run_path) |path| { + if (run) { out_file.close(); std.debug.print("{}\n", .{std.process.execv( allocator, if (target.cpu.arch == .riscv64 and target.os.tag == .linux) - &.{path} + &.{out_path} else - &.{ "qemu-riscv64", path }, + &.{ "qemu-riscv64", out_path.? }, )}); } } -- cgit v1.2.3