const std = @import("std"); const Allocator = std.mem.Allocator; const root = @import("root"); const Token = root.Lexer.Token; const Expr = root.Expr; const Location = root.Lexer.Location; pub const VReg = enum(u32) { _ }; pub const Instr = struct { loc: Location, type: Type, const Type = union(enum) { constant: Constant, bin_op: BinOp, }; const Constant = struct { dest: VReg, value: u64, }; const BinOp = struct { dest: VReg, lhs: VReg, rhs: VReg, op: Op, const Op = enum { add, }; }; pub fn sources(self: Instr) std.BoundedArray(VReg, 2) { return switch (self.type) { .bin_op => |bin_op| std.BoundedArray(VReg, 2).fromSlice(&.{ bin_op.lhs, bin_op.rhs }) catch unreachable, .constant => std.BoundedArray(VReg, 2).init(0) catch unreachable, }; } pub fn dest(self: *const Instr) VReg { return switch (self.type) { inline .constant, .bin_op => |s| s.dest, }; } }; pub const Block = struct { // arguments: []Reg, instrs: []Instr, vreg_last_use: std.AutoHashMap(VReg, usize), fn init(allocator: Allocator, instrs: []Instr) !Block { var vreg_last_use: std.AutoHashMap(VReg, usize) = .init(allocator); for (0.., instrs) |i, instr| { for (instr.sources().slice()) |src| try vreg_last_use.put(src, i); } return .{ .instrs = instrs, .vreg_last_use = vreg_last_use, }; } }; pub fn compile(allocator: Allocator, source: []const u8, expr: *const Expr) !Block { const instrs: std.ArrayListUnmanaged(Instr) = try .initCapacity(allocator, 0); var ctx: CompileContext = .{ .allocator = allocator, .source = source, .register_counter = 0, .instrs = instrs, }; _ = try ctx.compileExpr(expr); return .init(allocator, ctx.instrs.items); } const CompileContext = struct { allocator: Allocator, source: []const u8, register_counter: u32, instrs: std.ArrayListUnmanaged(Instr), const Self = @This(); fn addInstr(self: *Self, instr: Instr) !void { try self.instrs.append(self.allocator, instr); } fn compileExpr(self: *Self, expr: *const Expr) !VReg { const dest = self.register(); switch (expr.type) { .integer_literal => try addInstr(self, .{ .loc = expr.loc, .type = .{ .constant = .{ .dest = dest, .value = expr.getInt(self.source) } }, }), .bin_op => |binop| { const lhs = try self.compileExpr(binop.lhs); const rhs = try self.compileExpr(binop.rhs); try addInstr(self, .{ .loc = expr.loc, .type = .{ .bin_op = .{ .dest = dest, .lhs = lhs, .rhs = rhs, .op = switch (binop.op) { .plus => .add, }, }, }, }); }, .invalid => return error.CantCompileInvalidExpr, } return dest; } fn register(self: *Self) VReg { const reg: VReg = @enumFromInt(self.register_counter); self.register_counter += 1; return reg; } };