diff options
-rw-r--r-- | src/codegen.zig | 26 | ||||
-rw-r--r-- | src/compile.zig | 29 | ||||
-rw-r--r-- | src/main.zig | 1 |
3 files changed, 22 insertions, 34 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index d2b5d2a..f186304 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -560,11 +560,16 @@ const Context = struct { try self.instructions.append(inst); } - 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); + /// Frees all virtual registers who's last use is the current `compile.Instr` or earlier. This + /// must be called after the current compile.Instr's sources have been retrieved, since this + /// will deallocate them, and after allocating auxiliary registers since otherwise they may + /// collide with the sources. Should be called before allocating results to allow for more + /// register re-use. + fn freeUnusedVRegs(self: *Context) !void { + var it = self.register_allocator.allocated.keyIterator(); + while (it.next()) |vreg| { + if (self.block.?.vreg_last_use.get(vreg.*).? <= self.current_instruction_index.?) { + self.register_allocator.free(vreg.*); } } } @@ -595,6 +600,7 @@ const Context = struct { } fn genConstant(self: *Context, constant: compile.Instr.Constant) !void { + try self.freeUnusedVRegs(); const reg = try self.register_allocator.allocate(constant.dest); try self.genConstantInner(reg, constant.value); } @@ -602,7 +608,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(); + try self.freeUnusedVRegs(); const reg = try self.register_allocator.allocate(bin_op.dest); switch (bin_op.op) { .add => try self.emit(.add(reg, lhs, rhs)), @@ -621,7 +627,7 @@ const Context = struct { defer self.register_allocator.freeAux(digit); const count = try self.register_allocator.allocate(call.dest); - try self.maybeFreeSources(); + try self.freeUnusedVRegs(); try self.emit(.addi(digit, .zero, '\n')); try self.emit(.addi(.sp, .sp, -1)); @@ -648,7 +654,7 @@ const Context = struct { try self.emit(.addi(count, count, -1)); }, .read_int => { - try self.maybeFreeSources(); + try self.freeUnusedVRegs(); const result = try self.register_allocator.allocate(call.dest); const ptr = try self.register_allocator.allocateAux(); @@ -689,10 +695,6 @@ const Context = struct { } } - fn genDiscard(self: *Context, _: compile.Instr.Discard) !void { - try self.maybeFreeSources(); - } - fn codegenInstr(self: *Context, instr: compile.Instr) !void { switch (instr.type) { inline else => |ty| { diff --git a/src/compile.zig b/src/compile.zig index c837495..ac7768e 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -15,7 +15,6 @@ pub const Instr = struct { constant: Constant, bin_op: BinOp, proc_call: ProcCall, - discard: Discard, }; pub const Constant = struct { @@ -58,14 +57,6 @@ 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(), @@ -75,7 +66,6 @@ pub const Instr = struct { pub fn dest(self: *const Instr) ?VReg { return switch (self.type) { inline .constant, .bin_op, .proc_call => |s| s.dest, - // inline .x => null, }; } @@ -85,17 +75,15 @@ pub const Instr = struct { pub const Block = struct { // arguments: []Reg, instrs: []Instr, - vreg_last_use: std.AutoHashMap(usize, std.ArrayList(VReg)), + vreg_last_use: std.AutoHashMap(VReg, usize), fn init(allocator: Allocator, instrs: []Instr) !Block { - var vreg_last_use: std.AutoHashMap(usize, std.ArrayList(VReg)) = .init(allocator); + var vreg_last_use: std.AutoHashMap(VReg, usize) = .init(allocator); for (0.., instrs) |i, instr| { - const kv = try vreg_last_use.getOrPut(i); - if (!kv.found_existing) kv.value_ptr.* = .init(allocator); - for (instr.sources().slice()) |src| { - if (std.mem.indexOfScalar(VReg, kv.value_ptr.items, src) == null) - try kv.value_ptr.append(src); - } + for (instr.sources().slice()) |src| + try vreg_last_use.put(src, i); + if (instr.dest()) |dest| + try vreg_last_use.put(dest, i); } return .{ .instrs = instrs, @@ -134,10 +122,7 @@ const CompileContext = struct { fn compileStmt(self: *Self, stmt: parse.Stmt) !void { switch (stmt.type) { - .expr => |expr| try self.addInstr(.{ - .loc = stmt.loc, - .type = .{ .discard = .{ .vreg = try self.compileExpr(expr) } }, - }), + .expr => |expr| _ = try self.compileExpr(expr), .declare_var => |declare_var| { const val = try self.compileExpr(declare_var.value); const name = declare_var.ident.getIdent(self.source); diff --git a/src/main.zig b/src/main.zig index 15118d8..a9c77ac 100644 --- a/src/main.zig +++ b/src/main.zig @@ -62,6 +62,7 @@ pub fn main() !void { for (block.instrs) |instr| { std.debug.print(" {}\n", .{instr}); } + std.debug.print("Last use of each virtual register: {}\n", .{fmtHashMap(block.vreg_last_use)}); const elf = try codegen.create_elf(allocator, block); try output.writeAll(elf); std.debug.print("Run output:\n", .{}); |