diff options
Diffstat (limited to 'src/link/SpirV.zig')
| -rw-r--r-- | src/link/SpirV.zig | 119 |
1 files changed, 56 insertions, 63 deletions
diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 7a35752f62..95c747c170 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -16,11 +16,16 @@ //! All function declarations without a body (extern functions presumably). //! All regular functions. +// Because SPIR-V requires re-compilation anyway, and so hot swapping will not work +// anyway, we simply generate all the code in flushModule. This keeps +// things considerably simpler. + const SpirV = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const log = std.log.scoped(.link); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); +// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? pub const FnData = struct { - id: ?u32 = null, - code: std.ArrayListUnmanaged(u32) = .{}, + // We're going to fill these in flushModule, and we're going to fill them unconditionally, + // so just set it to undefined. + id: u32 = undefined }; base: link.File, -// TODO: Does this file need to support multiple independent modules? -spirv_module: codegen.SPIRVModule, - pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { const spirv = try gpa.create(SpirV); spirv.* = .{ @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .file = null, .allocator = gpa, }, - .spirv_module = codegen.SPIRVModule.init(gpa), }; // 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 return spirv; } -pub fn deinit(self: *SpirV) void { - self.spirv_module.deinit(); -} - -pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const fn_data = &decl.fn_link.spirv; - if (fn_data.id == null) { - fn_data.id = self.spirv_module.allocId(); - } - - var managed_code = fn_data.code.toManaged(self.base.allocator); - managed_code.items.len = 0; - - try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl); - fn_data.code = managed_code.toUnmanaged(); +pub fn deinit(self: *SpirV) void {} - // Free excess allocated memory for this Decl. - fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); -} +pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {} pub fn updateDeclExports( self: *SpirV, @@ -117,12 +101,7 @@ pub fn updateDeclExports( exports: []const *Module.Export, ) !void {} -pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { - var fn_data = decl.fn_link.spirv; - fn_data.code.deinit(self.base.allocator); - if (fn_data.id) |id| self.spirv_module.freeId(id); - decl.fn_link.spirv = undefined; -} +pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {} pub fn flush(self: *SpirV, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { @@ -139,55 +118,69 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { const module = self.base.options.module.?; const target = comp.getTarget(); + var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); + defer spirv_module.deinit(); + + // Allocate an ID for every declaration before generating code, + // so that we can access them before processing them. + // TODO: We're allocating an ID unconditionally now, are there + // declarations which don't generate a result? + // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + decl.fn_link.spirv.id = spirv_module.allocResultId(); + log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); + } + } + + // Now, actually generate the code for all declarations. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + try spirv_module.gen(decl); + } + } + var binary = std.ArrayList(u32).init(self.base.allocator); defer binary.deinit(); - // Note: The order of adding sections to the final binary - // follows the SPIR-V logical module format! - try binary.appendSlice(&[_]u32{ spec.magic_number, (spec.version.major << 16) | (spec.version.minor << 8), 0, // TODO: Register Zig compiler magic number. - self.spirv_module.idBound(), + spirv_module.resultIdBound(), // ID bound. 0, // Schema (currently reserved for future use in the SPIR-V spec). }); try writeCapabilities(&binary, target); try writeMemoryModel(&binary, target); - // Collect list of buffers to write. - // SPIR-V files support both little and big endian words. The actual format is - // disambiguated by the magic number, and so theoretically we don't need to worry - // about endian-ness when writing the final binary. - var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); - defer all_buffers.deinit(); - - // Pre-allocate enough for the binary info + all functions - try all_buffers.ensureCapacity(module.decl_table.count() + 1); - - all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); - - for (module.decl_table.items()) |entry| { - const decl = entry.value; - switch (decl.typed_value) { - .most_recent => |tvm| { - const fn_data = &decl.fn_link.spirv; - all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items)); - }, - .never_succeeded => continue, - } - } + // Note: The order of adding sections to the final binary + // follows the SPIR-V logical module format! + var all_buffers = [_]std.os.iovec_const{ + wordsToIovConst(binary.items), + wordsToIovConst(spirv_module.types_and_globals.items), + wordsToIovConst(spirv_module.fn_decls.items), + }; + + const file = self.base.file.?; + const bytes = std.mem.sliceAsBytes(binary.items); var file_size: u64 = 0; - for (all_buffers.items) |iov| { + for (all_buffers) |iov| { file_size += iov.iov_len; } - const file = self.base.file.?; try file.seekTo(0); try file.setEndPos(file_size); - try file.pwritevAll(all_buffers.items, 0); + try file.pwritevAll(&all_buffers, 0); } fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { @@ -231,4 +224,4 @@ fn wordsToIovConst(words: []const u32) std.os.iovec_const { .iov_base = bytes.ptr, .iov_len = bytes.len, }; -} +}
\ No newline at end of file |
