aboutsummaryrefslogtreecommitdiff
path: root/src/link/SpirV.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/link/SpirV.zig')
-rw-r--r--src/link/SpirV.zig119
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