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, pub const Type = union(enum) { constant: Constant, bin_op: BinOp, }; pub const Constant = struct { dest: VReg, value: u64, pub fn sources(_: Constant) Sources { return Sources.init(0) catch unreachable; } }; pub const BinOp = struct { dest: VReg, lhs: VReg, rhs: VReg, op: Op, const Op = enum { add, }; pub fn sources(self: BinOp) Sources { return Sources.fromSlice(&.{ self.lhs, self.rhs }) catch unreachable; } }; pub fn sources(self: Instr) Sources { return switch (self.type) { inline else => |instr| instr.sources(), }; } pub fn dest(self: *const Instr) VReg { return switch (self.type) { inline .constant, .bin_op => |s| s.dest, }; } pub const Sources = std.BoundedArray(VReg, 2); }; 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; } };