aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/spirv.zig
blob: a1cbe8a3bee5aefdba88eeb72e77b83b7de938d5 (plain)
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
const std = @import("std");
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.codegen);

const spec = @import("spirv/spec.zig");
const Module = @import("../Module.zig");
const Decl = Module.Decl;
const Type = @import("../type.zig").Type;

pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);

pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void {
    const word_count = @intCast(u32, args.len + 1);
    try code.append((word_count << 16) | @enumToInt(instr));
    try code.appendSlice(args);
}

pub const SPIRVModule = struct {
    next_result_id: u32 = 0,

    target: std.Target,

    types: TypeMap,

    types_and_globals: std.ArrayList(u32),
    fn_decls: std.ArrayList(u32),

    pub fn init(target: std.Target, allocator: *Allocator) SPIRVModule {
        return .{
            .target = target,
            .types = TypeMap.init(allocator),
            .types_and_globals = std.ArrayList(u32).init(allocator),
            .fn_decls = std.ArrayList(u32).init(allocator),
        };
    }

    pub fn deinit(self: *SPIRVModule) void {
        self.fn_decls.deinit();
        self.types_and_globals.deinit();
        self.types.deinit();
        self.* = undefined;
    }

    pub fn allocResultId(self: *SPIRVModule) u32 {
        defer self.next_result_id += 1;
        return self.next_result_id;
    }

    pub fn resultIdBound(self: *SPIRVModule) u32 {
        return self.next_result_id;
    }

    pub fn getOrGenType(self: *SPIRVModule, t: Type) !u32 {
        // We can't use getOrPut here so we can recursively generate types.
        if (self.types.get(t)) |already_generated| {
            return already_generated;
        }

        const result = self.allocResultId();

        switch (t.zigTypeTag()) {
            .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }),
            .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }),
            .Int => {
                const int_info = t.intInfo(self.target);
                try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{
                    result,
                    int_info.bits,
                    switch (int_info.signedness) {
                        .unsigned => 0,
                        .signed => 1,
                    },
                });
            },
            // TODO: Verify that floatBits() will be correct.
            .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }),
            .Null,
            .Undefined,
            .EnumLiteral,
            .ComptimeFloat,
            .ComptimeInt,
            .Type,
            => unreachable, // Must be const or comptime.

            .BoundFn => unreachable, // this type will be deleted from the language.

            else => return error.TODO,
        }

        try self.types.put(t, result);
        return result;
    }

    pub fn gen(self: *SPIRVModule, decl: *Decl) !void {
        switch (decl.ty.zigTypeTag()) {
            .Fn => {
                log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) });

                _ = try self.getOrGenType(decl.ty.fnReturnType());
            },
            else => return error.TODO,
        }
    }
};