| ... | ... | @@ -16,11 +16,16 @@ |
| 16 | 16 | //! All function declarations without a body (extern functions presumably). |
| 17 | 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 | 23 | const SpirV = @This(); |
| 20 | 24 | |
| 21 | 25 | const std = @import("std"); |
| 22 | 26 | const Allocator = std.mem.Allocator; |
| 23 | 27 | const assert = std.debug.assert; |
| 28 | const log = std.log.scoped(.link); |
| 24 | 29 | |
| 25 | 30 | const Module = @import("../Module.zig"); |
| 26 | 31 | const Compilation = @import("../Compilation.zig"); |
| ... | ... | @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; |
| 30 | 35 | const build_options = @import("build_options"); |
| 31 | 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 | 39 | pub const FnData = struct { |
| 34 | | id: ?u32 = null, |
| 35 | | code: std.ArrayListUnmanaged(u32) = .{}, |
| 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, |
| 41 | // so just set it to undefined. |
| 42 | id: u32 = undefined |
| 36 | 43 | }; |
| 37 | 44 | |
| 38 | 45 | base: link.File, |
| 39 | 46 | |
| 40 | | // TODO: Does this file need to support multiple independent modules? |
| 41 | | spirv_module: codegen.SPIRVModule, |
| 42 | | |
| 43 | 47 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { |
| 44 | 48 | const spirv = try gpa.create(SpirV); |
| 45 | 49 | spirv.* = .{ |
| ... | ... | @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { |
| 49 | 53 | .file = null, |
| 50 | 54 | .allocator = gpa, |
| 51 | 55 | }, |
| 52 | | .spirv_module = codegen.SPIRVModule.init(gpa), |
| 53 | 56 | }; |
| 54 | 57 | |
| 55 | 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 | 90 | return spirv; |
| 88 | 91 | } |
| 89 | 92 | |
| 90 | | 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(); |
| 93 | pub fn deinit(self: *SpirV) void {} |
| 108 | 94 | |
| 109 | | // Free excess allocated memory for this Decl. |
| 110 | | fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); |
| 111 | | } |
| 95 | pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {} |
| 112 | 96 | |
| 113 | 97 | pub fn updateDeclExports( |
| 114 | 98 | self: *SpirV, |
| ... | ... | @@ -117,12 +101,7 @@ pub fn updateDeclExports( |
| 117 | 101 | exports: []const *Module.Export, |
| 118 | 102 | ) !void {} |
| 119 | 103 | |
| 120 | | 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 | | } |
| 104 | pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {} |
| 126 | 105 | |
| 127 | 106 | pub fn flush(self: *SpirV, comp: *Compilation) !void { |
| 128 | 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 | 118 | const module = self.base.options.module.?; |
| 140 | 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 | 151 | var binary = std.ArrayList(u32).init(self.base.allocator); |
| 143 | 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 | 154 | try binary.appendSlice(&[_]u32{ |
| 149 | 155 | spec.magic_number, |
| 150 | 156 | (spec.version.major << 16) | (spec.version.minor << 8), |
| 151 | 157 | 0, // TODO: Register Zig compiler magic number. |
| 152 | | self.spirv_module.idBound(), |
| 158 | spirv_module.resultIdBound(), // ID bound. |
| 153 | 159 | 0, // Schema (currently reserved for future use in the SPIR-V spec). |
| 154 | 160 | }); |
| 155 | 161 | |
| 156 | 162 | try writeCapabilities(&binary, target); |
| 157 | 163 | try writeMemoryModel(&binary, target); |
| 158 | 164 | |
| 159 | | // Collect list of buffers to write. |
| 160 | | // SPIR-V files support both little and big endian words. The actual format is |
| 161 | | // disambiguated by the magic number, and so theoretically we don't need to worry |
| 162 | | // about endian-ness when writing the final binary. |
| 163 | | var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); |
| 164 | | defer all_buffers.deinit(); |
| 165 | | |
| 166 | | // Pre-allocate enough for the binary info + all functions |
| 167 | | try all_buffers.ensureCapacity(module.decl_table.count() + 1); |
| 168 | | |
| 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 | | } |
| 165 | // Note: The order of adding sections to the final binary |
| 166 | // follows the SPIR-V logical module format! |
| 167 | var all_buffers = [_]std.os.iovec_const{ |
| 168 | wordsToIovConst(binary.items), |
| 169 | wordsToIovConst(spirv_module.types_and_globals.items), |
| 170 | wordsToIovConst(spirv_module.fn_decls.items), |
| 171 | }; |
| 172 | |
| 173 | const file = self.base.file.?; |
| 174 | const bytes = std.mem.sliceAsBytes(binary.items); |
| 181 | 175 | |
| 182 | 176 | var file_size: u64 = 0; |
| 183 | | for (all_buffers.items) |iov| { |
| 177 | for (all_buffers) |iov| { |
| 184 | 178 | file_size += iov.iov_len; |
| 185 | 179 | } |
| 186 | 180 | |
| 187 | | const file = self.base.file.?; |
| 188 | 181 | try file.seekTo(0); |
| 189 | 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 | 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 | 224 | .iov_base = bytes.ptr, |
| 232 | 225 | .iov_len = bytes.len, |
| 233 | 226 | }; |
| 234 | | } |
| 227 | } |
| | \ No newline at end of file |