authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-01-19 01:29:01+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-01-19 15:28:17+01:00
log71ac82ecb028605545b67eaa50b34f4e2494de44
tree1c59134020383b2aba81b91bed163c5279182acd
parent02c138fe7011346ebab5e4b24ba0f8575bb52173

SPIR-V: Make emitting binary more efficient


2 files changed, 67 insertions(+), 20 deletions(-)

src/codegen/spirv.zig+6
...@@ -19,4 +19,10 @@ pub const SPIRVModule = struct {...@@ -19,4 +19,10 @@ pub const SPIRVModule = struct {
19 pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {19 pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {
2020
21 }21 }
22
23 pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void {
24 const word_count = @intCast(u32, args.len + 1);
25 try code.append((word_count << 16) | @enumToInt(instr));
26 try code.appendSlice(args);
27 }
22};28};
src/link/SpirV.zig+61-20
...@@ -12,7 +12,23 @@ const trace = @import("../tracy.zig").trace;...@@ -12,7 +12,23 @@ const trace = @import("../tracy.zig").trace;
12const build_options = @import("build_options");12const build_options = @import("build_options");
13const spec = @import("../codegen/spirv/spec.zig");13const spec = @import("../codegen/spirv/spec.zig");
1414
15//! SPIR-V Documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html15//! SPIR-V Spec documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html
16//! According to above documentation, a SPIR-V module has the following logical layout:
17//! Header.
18//! OpCapability instructions.
19//! OpExtension instructions.
20//! OpExtInstImport instructions.
21//! A single OpMemoryModel instruction.
22//! All entry points, declared with OpEntryPoint instructions.
23//! All execution-mode declarators; OpExecutionMode and OpExecutionModeId instructions.
24//! Debug instructions:
25//! - First, OpString, OpSourceExtension, OpSource, OpSourceContinued (no forward references).
26//! - OpName and OpMemberName instructions.
27//! - OpModuleProcessed instructions.
28//! All annotation (decoration) instructions.
29//! All type declaration instructions, constant instructions, global variable declarations, (preferrably) OpUndef instructions.
30//! All function declarations without a body (extern functions presumably).
31//! All regular functions.
1632
17pub const FnData = struct {33pub const FnData = struct {
18 id: ?u32 = null,34 id: ?u32 = null,
...@@ -103,7 +119,6 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {...@@ -103,7 +119,6 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
103 decl.fn_link.spirv.code.deinit(self.base.allocator);119 decl.fn_link.spirv.code.deinit(self.base.allocator);
104 decl.fn_link.spirv = undefined;120 decl.fn_link.spirv = undefined;
105}121}
106
107pub fn flush(self: *SpirV, comp: *Compilation) !void {122pub fn flush(self: *SpirV, comp: *Compilation) !void {
108 if (build_options.have_llvm and self.base.options.use_lld) {123 if (build_options.have_llvm and self.base.options.use_lld) {
109 return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.124 return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
...@@ -118,34 +133,60 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -118,34 +133,60 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
118133
119 const module = self.base.options.module.?;134 const module = self.base.options.module.?;
120135
121 const file = self.base.file.?;136 var binary = std.ArrayList(u32).init(self.base.allocator);
122 var bw = std.io.bufferedWriter(file.writer());137 defer binary.deinit();
123 const writer = bw.writer();
124138
125 // Header139 // Header
126 // SPIR-V files support both little and big endian words. The actual format is disambiguated by140 {
127 // the magic number. This backend uses little endian.141 const header = [_]u32{
128 try writer.writeIntLittle(u32, spec.magic_number);142 spec.magic_number,
129 try writer.writeIntLittle(u32, (spec.version.major << 16) | (spec.version.minor) << 8);143 (spec.version.major << 16) | (spec.version.minor << 8),
130 try writer.writeIntLittle(u32, 0); // TODO: Register Zig compiler magic number.144 0, // TODO: Register Zig compiler magic number.
131 try writer.writeIntLittle(u32, self.spirv_module.idBound());145 self.spirv_module.idBound(),
132 try writer.writeIntLittle(u32, 0); // Schema.146 0, // Schema (currently reserved for future use in the SPIR-V spec).
133147 };
134 // Declarations148 try binary.appendSlice(&header);
149 }
150
151 // Collect list of buffers to write.
152 // SPIR-V files support both little and big endian words. The actual format is
153 // disambiguated by the magic number, and so theoretically we don't need to worry
154 // about endian-ness when writing the final binary.
155 var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator);
156 defer all_buffers.deinit();
157
158 // Pre-allocate enough for the binary info + all functions
159 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
160
161 all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items));
162
163 // Functions
135 for (module.decl_table.items()) |entry| {164 for (module.decl_table.items()) |entry| {
136 const decl = entry.value;165 const decl = entry.value;
137 switch (decl.typed_value) {166 switch (decl.typed_value) {
138 .most_recent => |tvm| {167 .most_recent => |tvm| {
139 const fn_data = &decl.fn_link.spirv;168 const fn_data = &decl.fn_link.spirv;
140169 all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items));
141 // TODO: This could probably be more efficient.
142 for (fn_data.code.items) |word| {
143 try writer.writeIntLittle(u32, word);
144 }
145 },170 },
146 .never_succeeded => continue,171 .never_succeeded => continue,
147 }172 }
148 }173 }
149174
150 try bw.flush();175 var file_size: u64 = 0;
176 for (all_buffers.items) |iov| {
177 file_size += iov.iov_len;
178 }
179
180 const file = self.base.file.?;
181 try file.seekTo(0);
182 try file.setEndPos(file_size);
183 try file.pwritevAll(all_buffers.items, 0);
184}
185
186fn wordsToIovConst(words: []const u32) std.os.iovec_const {
187 const bytes = std.mem.sliceAsBytes(words);
188 return .{
189 .iov_base = bytes.ptr,
190 .iov_len = bytes.len,
191 };
151}192}