authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-01-19 00:34:44+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-01-19 15:28:17+01:00
logb2b87b590011d8df52874e3f9bd1f88d1b0189d1
tree3cccc8d6f0424ef50361a9eec66b1ef021e3330d
parentab607d455e47c35b980c3281ef5c3fb433a770a7

SPIR-V: Linking and codegen setup


5 files changed, 180 insertions(+), 10 deletions(-)

src/Module.zig+4-1
......@@ -1622,7 +1622,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
16221622 // in `Decl` to notice that the line number did not change.
16231623 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
16241624 },
1625 .c, .wasm => {},
1625 .c, .wasm, .spirv => {},
16261626 }
16271627 }
16281628 } else {
......@@ -1855,6 +1855,7 @@ fn allocateNewDecl(
18551855 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
18561856 .c => .{ .c = link.File.C.DeclBlock.empty },
18571857 .wasm => .{ .wasm = {} },
1858 .spirv => .{ .spirv = {} },
18581859 },
18591860 .fn_link = switch (mod.comp.bin_file.tag) {
18601861 .coff => .{ .coff = {} },
......@@ -1862,6 +1863,7 @@ fn allocateNewDecl(
18621863 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
18631864 .c => .{ .c = link.File.C.FnBlock.empty },
18641865 .wasm => .{ .wasm = null },
1866 .spirv => .{ .spirv = .{} },
18651867 },
18661868 .generation = 0,
18671869 .is_pub = false,
......@@ -1959,6 +1961,7 @@ pub fn analyzeExport(
19591961 .macho => .{ .macho = link.File.MachO.Export{} },
19601962 .c => .{ .c = {} },
19611963 .wasm => .{ .wasm = {} },
1964 .spirv => .{ .spirv = {} },
19621965 },
19631966 .owner_decl = owner_decl,
19641967 .exported_decl = exported_decl,
src/codegen/spirv.zig created+22
......@@ -0,0 +1,22 @@
1const std = @import("std");
2const spec = @import("spirv/spec.zig");
3const Module = @import("../Module.zig");
4const Decl = Module.Decl;
5
6pub const SPIRVModule = struct {
7 // TODO: Also use a free list.
8 next_id: u32 = 0,
9
10 pub fn allocId(self: *SPIRVModule) u32 {
11 defer self.next_id += 1;
12 return self.next_id;
13 }
14
15 pub fn idBound(self: *SPIRVModule) u32 {
16 return self.next_id;
17 }
18
19 pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {
20
21 }
22};
src/link.zig+20-9
......@@ -142,7 +142,7 @@ pub const File = struct {
142142 macho: MachO.SrcFn,
143143 c: C.FnBlock,
144144 wasm: ?Wasm.FnData,
145 spirv: void,
145 spirv: SpirV.FnData,
146146 };
147147
148148 pub const Export = union {
......@@ -180,7 +180,7 @@ pub const File = struct {
180180 .macho => &(try MachO.createEmpty(allocator, options)).base,
181181 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
182182 .c => unreachable, // Reported error earlier.
183 .spirv => return error.SpirVObjectFormatUnimplemented,
183 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
184184 .hex => return error.HexObjectFormatUnimplemented,
185185 .raw => return error.RawObjectFormatUnimplemented,
186186 };
......@@ -196,7 +196,7 @@ pub const File = struct {
196196 .macho => &(try MachO.createEmpty(allocator, options)).base,
197197 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
198198 .c => unreachable, // Reported error earlier.
199 .spirv => return error.SpirVObjectFormatUnimplemented,
199 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
200200 .hex => return error.HexObjectFormatUnimplemented,
201201 .raw => return error.RawObjectFormatUnimplemented,
202202 };
......@@ -212,7 +212,7 @@ pub const File = struct {
212212 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
213213 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
214214 .c => &(try C.openPath(allocator, sub_path, options)).base,
215 .spirv => return error.SpirVObjectFormatUnimplemented,
215 .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
216216 .hex => return error.HexObjectFormatUnimplemented,
217217 .raw => return error.RawObjectFormatUnimplemented,
218218 };
......@@ -242,7 +242,7 @@ pub const File = struct {
242242 .mode = determineMode(base.options),
243243 });
244244 },
245 .c, .wasm => {},
245 .c, .wasm, .spirv => {},
246246 }
247247 }
248248
......@@ -287,7 +287,7 @@ pub const File = struct {
287287 f.close();
288288 base.file = null;
289289 },
290 .c, .wasm => {},
290 .c, .wasm, .spirv => {},
291291 }
292292 }
293293
......@@ -300,6 +300,7 @@ pub const File = struct {
300300 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
301301 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
302302 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
303 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
303304 }
304305 }
305306
......@@ -309,7 +310,7 @@ pub const File = struct {
309310 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
310311 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
311312 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
312 .wasm => {},
313 .wasm, .spirv => {},
313314 }
314315 }
315316
......@@ -321,7 +322,7 @@ pub const File = struct {
321322 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
322323 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
323324 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
324 .wasm => {},
325 .wasm, .spirv => {},
325326 }
326327 }
327328
......@@ -368,6 +369,11 @@ pub const File = struct {
368369 parent.deinit();
369370 base.allocator.destroy(parent);
370371 },
372 .spirv => {
373 const parent = @fieldParentPtr(SpirV, "base", base);
374 parent.deinit();
375 base.allocator.destroy(parent);
376 },
371377 }
372378 }
373379
......@@ -401,6 +407,7 @@ pub const File = struct {
401407 .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
402408 .c => return @fieldParentPtr(C, "base", base).flush(comp),
403409 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
410 .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
404411 }
405412 }
406413
......@@ -413,6 +420,7 @@ pub const File = struct {
413420 .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
414421 .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
415422 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
423 .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
416424 }
417425 }
418426
......@@ -424,6 +432,7 @@ pub const File = struct {
424432 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
425433 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
426434 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
435 .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl),
427436 }
428437 }
429438
......@@ -433,7 +442,7 @@ pub const File = struct {
433442 .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
434443 .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
435444 .c => return .{ .no_entry_point_found = false },
436 .wasm => return ErrorFlags{},
445 .wasm, .spirv => return ErrorFlags{},
437446 }
438447 }
439448
......@@ -451,6 +460,7 @@ pub const File = struct {
451460 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
452461 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
453462 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
463 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports),
454464 }
455465 }
456466
......@@ -461,6 +471,7 @@ pub const File = struct {
461471 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
462472 .c => unreachable,
463473 .wasm => unreachable,
474 .spirv => unreachable,
464475 }
465476 }
466477
src/link/SpirV.zig created+131
......@@ -0,0 +1,131 @@
1const SpirV = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;
6
7const Module = @import("../Module.zig");
8const Compilation = @import("../Compilation.zig");
9const link = @import("../link.zig");
10const codegen = @import("../codegen/spirv.zig");
11const trace = @import("../tracy.zig").trace;
12const build_options = @import("build_options");
13const spec = @import("../codegen/spirv/spec.zig");
14
15pub const FnData = struct {
16 id: ?u32 = null,
17 code: std.ArrayListUnmanaged(u32) = .{},
18};
19
20base: link.File,
21
22// TODO: Does this file need to support multiple independent modules?
23spirv_module: codegen.SPIRVModule = .{},
24
25pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {
26 const spirv = try gpa.create(SpirV);
27 spirv.* = .{
28 .base = .{
29 .tag = .spirv,
30 .options = options,
31 .file = null,
32 .allocator = gpa,
33 },
34 };
35 return spirv;
36}
37
38pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*SpirV {
39 assert(options.object_format == .spirv);
40
41 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all.
42 if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
43
44 // TODO: read the file and keep vaild parts instead of truncating
45 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
46 errdefer file.close();
47
48 const spirv = try createEmpty(allocator, options);
49 errdefer spirv.base.destroy();
50
51 spirv.base.file = file;
52 return spirv;
53}
54
55pub fn deinit(self: *SpirV) void {
56}
57
58pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
59 const tracy = trace(@src());
60 defer tracy.end();
61
62 const fn_data = &decl.fn_link.spirv;
63 if (fn_data.id == null) {
64 fn_data.id = self.spirv_module.allocId();
65 }
66
67 var managed_code = fn_data.code.toManaged(self.base.allocator);
68 managed_code.items.len = 0;
69
70 try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl);
71 fn_data.code = managed_code.toUnmanaged();
72
73 // Free excess allocated memory for this Decl.
74 fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len);
75}
76
77pub fn updateDeclExports(
78 self: *SpirV,
79 module: *Module,
80 decl: *const Module.Decl,
81 exports: []const *Module.Export,
82) !void {}
83
84pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
85 decl.fn_link.spirv.code.deinit(self.base.allocator);
86 decl.fn_link.spirv = undefined;
87}
88
89pub fn flush(self: *SpirV, comp: *Compilation) !void {
90 if (build_options.have_llvm and self.base.options.use_lld) {
91 return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
92 } else {
93 return self.flushModule(comp);
94 }
95}
96
97pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
98 const tracy = trace(@src());
99 defer tracy.end();
100
101 const module = self.base.options.module.?;
102
103 const file = self.base.file.?;
104 var bw = std.io.bufferedWriter(file.writer());
105 const writer = bw.writer();
106
107 // Header
108 // SPIR-V files support both little and big endian words. The actual format is disambiguated by
109 // the magic number. This backend uses little endian.
110 try writer.writeIntLittle(u32, spec.magic_number);
111 try writer.writeIntLittle(u32, (spec.version.major << 16) | (spec.version.minor) << 8);
112 try writer.writeIntLittle(u32, 0); // TODO: Register Zig compiler magic number.
113 try writer.writeIntLittle(u32, self.spirv_module.idBound());
114 try writer.writeIntLittle(u32, 0); // Schema.
115
116 // Declarations
117 for (module.decl_table.items()) |entry| {
118 const decl = entry.value;
119 switch (decl.typed_value) {
120 .most_recent => |tvm| {
121 const fn_data = &decl.fn_link.spirv;
122 for (fn_data.code.items) |word| {
123 try writer.writeIntLittle(u32, word);
124 }
125 },
126 .never_succeeded => continue,
127 }
128 }
129
130 try bw.flush();
131}
src/main.zig+3
......@@ -302,6 +302,7 @@ const usage_build_generic =
302302 \\ pe Portable Executable (Windows)
303303 \\ coff Common Object File Format (Windows)
304304 \\ macho macOS relocatables
305 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
305306 \\ hex (planned) Intel IHEX
306307 \\ raw (planned) Dump machine code directly
307308 \\ -dirafter [dir] Add directory to AFTER include search path
......@@ -1515,6 +1516,8 @@ fn buildOutputType(
15151516 break :blk .hex;
15161517 } else if (mem.eql(u8, ofmt, "raw")) {
15171518 break :blk .raw;
1519 } else if (mem.eql(u8, ofmt, "spirv")) {
1520 break :blk .spirv;
15181521 } else {
15191522 fatal("unsupported object format: {s}", .{ofmt});
15201523 }