| ... | @@ -16,11 +16,16 @@ | ... | @@ -16,11 +16,16 @@ |
| 16 | //! All function declarations without a body (extern functions presumably). | 16 | //! All function declarations without a body (extern functions presumably). |
| 17 | //! All regular functions. | 17 | //! All regular functions. |
| 18 | | 18 | |
| | 19 | // Because SPIR-V requires re-compilation anyway, and so hot swapping will not work |
| | 20 | // anyway, we simply generate all the code in flushModule. This keeps |
| | 21 | // things considerably simpler. |
| | 22 | |
| 19 | const SpirV = @This(); | 23 | const SpirV = @This(); |
| 20 | | 24 | |
| 21 | const std = @import("std"); | 25 | const std = @import("std"); |
| 22 | const Allocator = std.mem.Allocator; | 26 | const Allocator = std.mem.Allocator; |
| 23 | const assert = std.debug.assert; | 27 | const assert = std.debug.assert; |
| | 28 | const log = std.log.scoped(.link); |
| 24 | | 29 | |
| 25 | const Module = @import("../Module.zig"); | 30 | const Module = @import("../Module.zig"); |
| 26 | const Compilation = @import("../Compilation.zig"); | 31 | const Compilation = @import("../Compilation.zig"); |
| ... | @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; | ... | @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; |
| 30 | const build_options = @import("build_options"); | 35 | const build_options = @import("build_options"); |
| 31 | const spec = @import("../codegen/spirv/spec.zig"); | 36 | const spec = @import("../codegen/spirv/spec.zig"); |
| 32 | | 37 | |
| | 38 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? |
| 33 | pub const FnData = struct { | 39 | pub const FnData = struct { |
| 34 | id: ?u32 = null, | 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, |
| 35 | code: std.ArrayListUnmanaged(u32) = .{}, | 41 | // so just set it to undefined. |
| | 42 | id: u32 = undefined |
| 36 | }; | 43 | }; |
| 37 | | 44 | |
| 38 | base: link.File, | 45 | base: link.File, |
| 39 | | 46 | |
| 40 | // TODO: Does this file need to support multiple independent modules? | | |
| 41 | spirv_module: codegen.SPIRVModule, | | |
| 42 | | | |
| 43 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { | 47 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { |
| 44 | const spirv = try gpa.create(SpirV); | 48 | const spirv = try gpa.create(SpirV); |
| 45 | spirv.* = .{ | 49 | spirv.* = .{ |
| ... | @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { | ... | @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { |
| 49 | .file = null, | 53 | .file = null, |
| 50 | .allocator = gpa, | 54 | .allocator = gpa, |
| 51 | }, | 55 | }, |
| 52 | .spirv_module = codegen.SPIRVModule.init(gpa), | | |
| 53 | }; | 56 | }; |
| 54 | | 57 | |
| 55 | // TODO: Figure out where to put all of these | 58 | // TODO: Figure out where to put all of these |
| ... | @@ -87,28 +90,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -87,28 +90,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 87 | return spirv; | 90 | return spirv; |
| 88 | } | 91 | } |
| 89 | | 92 | |
| 90 | pub fn deinit(self: *SpirV) void { | 93 | pub fn deinit(self: *SpirV) void {} |
| 91 | self.spirv_module.deinit(); | | |
| 92 | } | | |
| 93 | | | |
| 94 | pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { | | |
| 95 | const tracy = trace(@src()); | | |
| 96 | defer tracy.end(); | | |
| 97 | | | |
| 98 | const fn_data = &decl.fn_link.spirv; | | |
| 99 | if (fn_data.id == null) { | | |
| 100 | fn_data.id = self.spirv_module.allocId(); | | |
| 101 | } | | |
| 102 | | | |
| 103 | var managed_code = fn_data.code.toManaged(self.base.allocator); | | |
| 104 | managed_code.items.len = 0; | | |
| 105 | | | |
| 106 | try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl); | | |
| 107 | fn_data.code = managed_code.toUnmanaged(); | | |
| 108 | | 94 | |
| 109 | // Free excess allocated memory for this Decl. | 95 | pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {} |
| 110 | fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); | | |
| 111 | } | | |
| 112 | | 96 | |
| 113 | pub fn updateDeclExports( | 97 | pub fn updateDeclExports( |
| 114 | self: *SpirV, | 98 | self: *SpirV, |
| ... | @@ -117,12 +101,7 @@ pub fn updateDeclExports( | ... | @@ -117,12 +101,7 @@ pub fn updateDeclExports( |
| 117 | exports: []const *Module.Export, | 101 | exports: []const *Module.Export, |
| 118 | ) !void {} | 102 | ) !void {} |
| 119 | | 103 | |
| 120 | pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { | 104 | pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {} |
| 121 | var fn_data = decl.fn_link.spirv; | | |
| 122 | fn_data.code.deinit(self.base.allocator); | | |
| 123 | if (fn_data.id) |id| self.spirv_module.freeId(id); | | |
| 124 | decl.fn_link.spirv = undefined; | | |
| 125 | } | | |
| 126 | | 105 | |
| 127 | pub fn flush(self: *SpirV, comp: *Compilation) !void { | 106 | pub fn flush(self: *SpirV, comp: *Compilation) !void { |
| 128 | if (build_options.have_llvm and self.base.options.use_lld) { | 107 | if (build_options.have_llvm and self.base.options.use_lld) { |
| ... | @@ -139,55 +118,69 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { | ... | @@ -139,55 +118,69 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 139 | const module = self.base.options.module.?; | 118 | const module = self.base.options.module.?; |
| 140 | const target = comp.getTarget(); | 119 | const target = comp.getTarget(); |
| 141 | | 120 | |
| | 121 | var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); |
| | 122 | defer spirv_module.deinit(); |
| | 123 | |
| | 124 | // Allocate an ID for every declaration before generating code, |
| | 125 | // so that we can access them before processing them. |
| | 126 | // TODO: We're allocating an ID unconditionally now, are there |
| | 127 | // declarations which don't generate a result? |
| | 128 | // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. |
| | 129 | { |
| | 130 | for (module.decl_table.items()) |entry| { |
| | 131 | const decl = entry.value; |
| | 132 | if (decl.typed_value != .most_recent) |
| | 133 | continue; |
| | 134 | |
| | 135 | decl.fn_link.spirv.id = spirv_module.allocResultId(); |
| | 136 | log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); |
| | 137 | } |
| | 138 | } |
| | 139 | |
| | 140 | // Now, actually generate the code for all declarations. |
| | 141 | { |
| | 142 | for (module.decl_table.items()) |entry| { |
| | 143 | const decl = entry.value; |
| | 144 | if (decl.typed_value != .most_recent) |
| | 145 | continue; |
| | 146 | |
| | 147 | try spirv_module.gen(decl); |
| | 148 | } |
| | 149 | } |
| | 150 | |
| 142 | var binary = std.ArrayList(u32).init(self.base.allocator); | 151 | var binary = std.ArrayList(u32).init(self.base.allocator); |
| 143 | defer binary.deinit(); | 152 | defer binary.deinit(); |
| 144 | | 153 | |
| 145 | // Note: The order of adding sections to the final binary | | |
| 146 | // follows the SPIR-V logical module format! | | |
| 147 | | | |
| 148 | try binary.appendSlice(&[_]u32{ | 154 | try binary.appendSlice(&[_]u32{ |
| 149 | spec.magic_number, | 155 | spec.magic_number, |
| 150 | (spec.version.major << 16) | (spec.version.minor << 8), | 156 | (spec.version.major << 16) | (spec.version.minor << 8), |
| 151 | 0, // TODO: Register Zig compiler magic number. | 157 | 0, // TODO: Register Zig compiler magic number. |
| 152 | self.spirv_module.idBound(), | 158 | spirv_module.resultIdBound(), // ID bound. |
| 153 | 0, // Schema (currently reserved for future use in the SPIR-V spec). | 159 | 0, // Schema (currently reserved for future use in the SPIR-V spec). |
| 154 | }); | 160 | }); |
| 155 | | 161 | |
| 156 | try writeCapabilities(&binary, target); | 162 | try writeCapabilities(&binary, target); |
| 157 | try writeMemoryModel(&binary, target); | 163 | try writeMemoryModel(&binary, target); |
| 158 | | 164 | |
| 159 | // Collect list of buffers to write. | 165 | // Note: The order of adding sections to the final binary |
| 160 | // SPIR-V files support both little and big endian words. The actual format is | 166 | // follows the SPIR-V logical module format! |
| 161 | // disambiguated by the magic number, and so theoretically we don't need to worry | 167 | var all_buffers = [_]std.os.iovec_const{ |
| 162 | // about endian-ness when writing the final binary. | 168 | wordsToIovConst(binary.items), |
| 163 | var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); | 169 | wordsToIovConst(spirv_module.types_and_globals.items), |
| 164 | defer all_buffers.deinit(); | 170 | wordsToIovConst(spirv_module.fn_decls.items), |
| 165 | | 171 | }; |
| 166 | // Pre-allocate enough for the binary info + all functions | 172 | |
| 167 | try all_buffers.ensureCapacity(module.decl_table.count() + 1); | 173 | const file = self.base.file.?; |
| 168 | | 174 | const bytes = std.mem.sliceAsBytes(binary.items); |
| 169 | all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); | | |
| 170 | | | |
| 171 | for (module.decl_table.items()) |entry| { | | |
| 172 | const decl = entry.value; | | |
| 173 | switch (decl.typed_value) { | | |
| 174 | .most_recent => |tvm| { | | |
| 175 | const fn_data = &decl.fn_link.spirv; | | |
| 176 | all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items)); | | |
| 177 | }, | | |
| 178 | .never_succeeded => continue, | | |
| 179 | } | | |
| 180 | } | | |
| 181 | | 175 | |
| 182 | var file_size: u64 = 0; | 176 | var file_size: u64 = 0; |
| 183 | for (all_buffers.items) |iov| { | 177 | for (all_buffers) |iov| { |
| 184 | file_size += iov.iov_len; | 178 | file_size += iov.iov_len; |
| 185 | } | 179 | } |
| 186 | | 180 | |
| 187 | const file = self.base.file.?; | | |
| 188 | try file.seekTo(0); | 181 | try file.seekTo(0); |
| 189 | try file.setEndPos(file_size); | 182 | try file.setEndPos(file_size); |
| 190 | try file.pwritevAll(all_buffers.items, 0); | 183 | try file.pwritevAll(&all_buffers, 0); |
| 191 | } | 184 | } |
| 192 | | 185 | |
| 193 | fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { | 186 | fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { |
| ... | @@ -231,4 +224,4 @@ fn wordsToIovConst(words: []const u32) std.os.iovec_const { | ... | @@ -231,4 +224,4 @@ fn wordsToIovConst(words: []const u32) std.os.iovec_const { |
| 231 | .iov_base = bytes.ptr, | 224 | .iov_base = bytes.ptr, |
| 232 | .iov_len = bytes.len, | 225 | .iov_len = bytes.len, |
| 233 | }; | 226 | }; |
| 234 | } | 227 | } |
| \ No newline at end of file |