aboutsummaryrefslogtreecommitdiff
path: root/src/compile.zig
blob: 822153fac0527c3a39a308795ebd7ffd039957ea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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;
    }
};