| ... | @@ -6,6 +6,7 @@ const spec = @import("spirv/spec.zig"); | ... | @@ -6,6 +6,7 @@ const spec = @import("spirv/spec.zig"); |
| 6 | const Module = @import("../Module.zig"); | 6 | const Module = @import("../Module.zig"); |
| 7 | const Decl = Module.Decl; | 7 | const Decl = Module.Decl; |
| 8 | const Type = @import("../type.zig").Type; | 8 | const Type = @import("../type.zig").Type; |
| | 9 | const LazySrcLoc = Module.LazySrcLoc; |
| 9 | | 10 | |
| 10 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); | 11 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); |
| 11 | | 12 | |
| ... | @@ -15,30 +16,24 @@ pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []c | ... | @@ -15,30 +16,24 @@ pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []c |
| 15 | try code.appendSlice(args); | 16 | try code.appendSlice(args); |
| 16 | } | 17 | } |
| 17 | | 18 | |
| | 19 | /// This structure represents a SPIR-V binary module being compiled, and keeps track of relevant information |
| | 20 | /// such as code for the different logical sections, and the next result-id. |
| 18 | pub const SPIRVModule = struct { | 21 | pub const SPIRVModule = struct { |
| 19 | next_result_id: u32 = 0, | 22 | next_result_id: u32, |
| 20 | | | |
| 21 | target: std.Target, | | |
| 22 | | | |
| 23 | types: TypeMap, | | |
| 24 | | | |
| 25 | types_and_globals: std.ArrayList(u32), | 23 | types_and_globals: std.ArrayList(u32), |
| 26 | fn_decls: std.ArrayList(u32), | 24 | fn_decls: std.ArrayList(u32), |
| 27 | | 25 | |
| 28 | pub fn init(target: std.Target, allocator: *Allocator) SPIRVModule { | 26 | pub fn init(allocator: *Allocator) SPIRVModule { |
| 29 | return .{ | 27 | return .{ |
| 30 | .target = target, | 28 | .next_result_id = 0, |
| 31 | .types = TypeMap.init(allocator), | | |
| 32 | .types_and_globals = std.ArrayList(u32).init(allocator), | 29 | .types_and_globals = std.ArrayList(u32).init(allocator), |
| 33 | .fn_decls = std.ArrayList(u32).init(allocator), | 30 | .fn_decls = std.ArrayList(u32).init(allocator), |
| 34 | }; | 31 | }; |
| 35 | } | 32 | } |
| 36 | | 33 | |
| 37 | pub fn deinit(self: *SPIRVModule) void { | 34 | pub fn deinit(self: *SPIRVModule) void { |
| 38 | self.fn_decls.deinit(); | | |
| 39 | self.types_and_globals.deinit(); | 35 | self.types_and_globals.deinit(); |
| 40 | self.types.deinit(); | 36 | self.fn_decls.deinit(); |
| 41 | self.* = undefined; | | |
| 42 | } | 37 | } |
| 43 | | 38 | |
| 44 | pub fn allocResultId(self: *SPIRVModule) u32 { | 39 | pub fn allocResultId(self: *SPIRVModule) u32 { |
| ... | @@ -49,21 +44,40 @@ pub const SPIRVModule = struct { | ... | @@ -49,21 +44,40 @@ pub const SPIRVModule = struct { |
| 49 | pub fn resultIdBound(self: *SPIRVModule) u32 { | 44 | pub fn resultIdBound(self: *SPIRVModule) u32 { |
| 50 | return self.next_result_id; | 45 | return self.next_result_id; |
| 51 | } | 46 | } |
| | 47 | }; |
| | 48 | |
| | 49 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. |
| | 50 | pub const DeclGen = struct { |
| | 51 | module: *Module, |
| | 52 | spv: *SPIRVModule, |
| | 53 | |
| | 54 | types: TypeMap, |
| | 55 | |
| | 56 | decl: *Decl, |
| | 57 | error_msg: ?*Module.ErrorMsg, |
| | 58 | |
| | 59 | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| | 60 | @setCold(true); |
| | 61 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| | 62 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); |
| | 63 | return error.AnalysisFail; |
| | 64 | } |
| 52 | | 65 | |
| 53 | pub fn getOrGenType(self: *SPIRVModule, t: Type) !u32 { | 66 | pub fn getOrGenType(self: *DeclGen, t: Type) !u32 { |
| 54 | // We can't use getOrPut here so we can recursively generate types. | 67 | // We can't use getOrPut here so we can recursively generate types. |
| 55 | if (self.types.get(t)) |already_generated| { | 68 | if (self.types.get(t)) |already_generated| { |
| 56 | return already_generated; | 69 | return already_generated; |
| 57 | } | 70 | } |
| 58 | | 71 | |
| 59 | const result = self.allocResultId(); | 72 | const result = self.spv.allocResultId(); |
| 60 | | 73 | |
| 61 | switch (t.zigTypeTag()) { | 74 | switch (t.zigTypeTag()) { |
| 62 | .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }), | 75 | .Void => try writeInstruction(&self.spv.types_and_globals, .OpTypeVoid, &[_]u32{ result }), |
| 63 | .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }), | 76 | .Bool => try writeInstruction(&self.spv.types_and_globals, .OpTypeBool, &[_]u32{ result }), |
| 64 | .Int => { | 77 | .Int => { |
| 65 | const int_info = t.intInfo(self.target); | 78 | const int_info = t.intInfo(self.module.getTarget()); |
| 66 | try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{ | 79 | // TODO: Capabilities. |
| | 80 | try writeInstruction(&self.spv.types_and_globals, .OpTypeInt, &[_]u32{ |
| 67 | result, | 81 | result, |
| 68 | int_info.bits, | 82 | int_info.bits, |
| 69 | switch (int_info.signedness) { | 83 | switch (int_info.signedness) { |
| ... | @@ -72,8 +86,8 @@ pub const SPIRVModule = struct { | ... | @@ -72,8 +86,8 @@ pub const SPIRVModule = struct { |
| 72 | }, | 86 | }, |
| 73 | }); | 87 | }); |
| 74 | }, | 88 | }, |
| 75 | // TODO: Verify that floatBits() will be correct. | 89 | // TODO: Capabilities. |
| 76 | .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }), | 90 | .Float => try writeInstruction(&self.spv.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.module.getTarget()) }), |
| 77 | .Null, | 91 | .Null, |
| 78 | .Undefined, | 92 | .Undefined, |
| 79 | .EnumLiteral, | 93 | .EnumLiteral, |
| ... | @@ -84,23 +98,23 @@ pub const SPIRVModule = struct { | ... | @@ -84,23 +98,23 @@ pub const SPIRVModule = struct { |
| 84 | | 98 | |
| 85 | .BoundFn => unreachable, // this type will be deleted from the language. | 99 | .BoundFn => unreachable, // this type will be deleted from the language. |
| 86 | | 100 | |
| 87 | else => return error.TODO, | 101 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type with tag {}", .{ tag }), |
| 88 | } | 102 | } |
| 89 | | 103 | |
| 90 | try self.types.put(t, result); | 104 | try self.types.put(t, result); |
| 91 | return result; | 105 | return result; |
| 92 | } | 106 | } |
| 93 | | 107 | |
| 94 | pub fn gen(self: *SPIRVModule, decl: *Decl) !void { | 108 | pub fn gen(self: *DeclGen) !void { |
| 95 | const typed_value = decl.typed_value.most_recent.typed_value; | 109 | const typed_value = self.decl.typed_value.most_recent.typed_value; |
| 96 | | 110 | |
| 97 | switch (typed_value.ty.zigTypeTag()) { | 111 | switch (typed_value.ty.zigTypeTag()) { |
| 98 | .Fn => { | 112 | .Fn => { |
| 99 | log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) }); | 113 | log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(self.decl.name) }); |
| 100 | | 114 | |
| 101 | _ = try self.getOrGenType(typed_value.ty.fnReturnType()); | 115 | _ = try self.getOrGenType(typed_value.ty.fnReturnType()); |
| 102 | }, | 116 | }, |
| 103 | else => return error.TODO, | 117 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl with tag {}", .{ tag }), |
| 104 | } | 118 | } |
| 105 | } | 119 | } |
| 106 | }; | 120 | }; |