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...@@ -1622,7 +1622,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
1622 // in `Decl` to notice that the line number did not change.1622 // in `Decl` to notice that the line number did not change.
1623 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });1623 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1624 },1624 },
1625 .c, .wasm => {},1625 .c, .wasm, .spirv => {},
1626 }1626 }
1627 }1627 }
1628 } else {1628 } else {
...@@ -1855,6 +1855,7 @@ fn allocateNewDecl(...@@ -1855,6 +1855,7 @@ fn allocateNewDecl(
1855 .macho => .{ .macho = link.File.MachO.TextBlock.empty },1855 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1856 .c => .{ .c = link.File.C.DeclBlock.empty },1856 .c => .{ .c = link.File.C.DeclBlock.empty },
1857 .wasm => .{ .wasm = {} },1857 .wasm => .{ .wasm = {} },
1858 .spirv => .{ .spirv = {} },
1858 },1859 },
1859 .fn_link = switch (mod.comp.bin_file.tag) {1860 .fn_link = switch (mod.comp.bin_file.tag) {
1860 .coff => .{ .coff = {} },1861 .coff => .{ .coff = {} },
...@@ -1862,6 +1863,7 @@ fn allocateNewDecl(...@@ -1862,6 +1863,7 @@ fn allocateNewDecl(
1862 .macho => .{ .macho = link.File.MachO.SrcFn.empty },1863 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1863 .c => .{ .c = link.File.C.FnBlock.empty },1864 .c => .{ .c = link.File.C.FnBlock.empty },
1864 .wasm => .{ .wasm = null },1865 .wasm => .{ .wasm = null },
1866 .spirv => .{ .spirv = .{} },
1865 },1867 },
1866 .generation = 0,1868 .generation = 0,
1867 .is_pub = false,1869 .is_pub = false,
...@@ -1959,6 +1961,7 @@ pub fn analyzeExport(...@@ -1959,6 +1961,7 @@ pub fn analyzeExport(
1959 .macho => .{ .macho = link.File.MachO.Export{} },1961 .macho => .{ .macho = link.File.MachO.Export{} },
1960 .c => .{ .c = {} },1962 .c => .{ .c = {} },
1961 .wasm => .{ .wasm = {} },1963 .wasm => .{ .wasm = {} },
1964 .spirv => .{ .spirv = {} },
1962 },1965 },
1963 .owner_decl = owner_decl,1966 .owner_decl = owner_decl,
1964 .exported_decl = exported_decl,1967 .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 {...@@ -142,7 +142,7 @@ pub const File = struct {
142 macho: MachO.SrcFn,142 macho: MachO.SrcFn,
143 c: C.FnBlock,143 c: C.FnBlock,
144 wasm: ?Wasm.FnData,144 wasm: ?Wasm.FnData,
145 spirv: void,145 spirv: SpirV.FnData,
146 };146 };
147147
148 pub const Export = union {148 pub const Export = union {
...@@ -180,7 +180,7 @@ pub const File = struct {...@@ -180,7 +180,7 @@ pub const File = struct {
180 .macho => &(try MachO.createEmpty(allocator, options)).base,180 .macho => &(try MachO.createEmpty(allocator, options)).base,
181 .wasm => &(try Wasm.createEmpty(allocator, options)).base,181 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
182 .c => unreachable, // Reported error earlier.182 .c => unreachable, // Reported error earlier.
183 .spirv => return error.SpirVObjectFormatUnimplemented,183 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
184 .hex => return error.HexObjectFormatUnimplemented,184 .hex => return error.HexObjectFormatUnimplemented,
185 .raw => return error.RawObjectFormatUnimplemented,185 .raw => return error.RawObjectFormatUnimplemented,
186 };186 };
...@@ -196,7 +196,7 @@ pub const File = struct {...@@ -196,7 +196,7 @@ pub const File = struct {
196 .macho => &(try MachO.createEmpty(allocator, options)).base,196 .macho => &(try MachO.createEmpty(allocator, options)).base,
197 .wasm => &(try Wasm.createEmpty(allocator, options)).base,197 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
198 .c => unreachable, // Reported error earlier.198 .c => unreachable, // Reported error earlier.
199 .spirv => return error.SpirVObjectFormatUnimplemented,199 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
200 .hex => return error.HexObjectFormatUnimplemented,200 .hex => return error.HexObjectFormatUnimplemented,
201 .raw => return error.RawObjectFormatUnimplemented,201 .raw => return error.RawObjectFormatUnimplemented,
202 };202 };
...@@ -212,7 +212,7 @@ pub const File = struct {...@@ -212,7 +212,7 @@ pub const File = struct {
212 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,212 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
213 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,213 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
214 .c => &(try C.openPath(allocator, sub_path, options)).base,214 .c => &(try C.openPath(allocator, sub_path, options)).base,
215 .spirv => return error.SpirVObjectFormatUnimplemented,215 .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
216 .hex => return error.HexObjectFormatUnimplemented,216 .hex => return error.HexObjectFormatUnimplemented,
217 .raw => return error.RawObjectFormatUnimplemented,217 .raw => return error.RawObjectFormatUnimplemented,
218 };218 };
...@@ -242,7 +242,7 @@ pub const File = struct {...@@ -242,7 +242,7 @@ pub const File = struct {
242 .mode = determineMode(base.options),242 .mode = determineMode(base.options),
243 });243 });
244 },244 },
245 .c, .wasm => {},245 .c, .wasm, .spirv => {},
246 }246 }
247 }247 }
248248
...@@ -287,7 +287,7 @@ pub const File = struct {...@@ -287,7 +287,7 @@ pub const File = struct {
287 f.close();287 f.close();
288 base.file = null;288 base.file = null;
289 },289 },
290 .c, .wasm => {},290 .c, .wasm, .spirv => {},
291 }291 }
292 }292 }
293293
...@@ -300,6 +300,7 @@ pub const File = struct {...@@ -300,6 +300,7 @@ pub const File = struct {
300 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),300 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
301 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),301 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
302 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),302 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
303 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
303 }304 }
304 }305 }
305306
...@@ -309,7 +310,7 @@ pub const File = struct {...@@ -309,7 +310,7 @@ pub const File = struct {
309 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),310 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
310 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),311 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
311 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),312 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
312 .wasm => {},313 .wasm, .spirv => {},
313 }314 }
314 }315 }
315316
...@@ -321,7 +322,7 @@ pub const File = struct {...@@ -321,7 +322,7 @@ pub const File = struct {
321 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),322 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
322 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),323 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
323 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),324 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
324 .wasm => {},325 .wasm, .spirv => {},
325 }326 }
326 }327 }
327328
...@@ -368,6 +369,11 @@ pub const File = struct {...@@ -368,6 +369,11 @@ pub const File = struct {
368 parent.deinit();369 parent.deinit();
369 base.allocator.destroy(parent);370 base.allocator.destroy(parent);
370 },371 },
372 .spirv => {
373 const parent = @fieldParentPtr(SpirV, "base", base);
374 parent.deinit();
375 base.allocator.destroy(parent);
376 },
371 }377 }
372 }378 }
373379
...@@ -401,6 +407,7 @@ pub const File = struct {...@@ -401,6 +407,7 @@ pub const File = struct {
401 .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),407 .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
402 .c => return @fieldParentPtr(C, "base", base).flush(comp),408 .c => return @fieldParentPtr(C, "base", base).flush(comp),
403 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),409 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
410 .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
404 }411 }
405 }412 }
406413
...@@ -413,6 +420,7 @@ pub const File = struct {...@@ -413,6 +420,7 @@ pub const File = struct {
413 .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),420 .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
414 .c => return @fieldParentPtr(C, "base", base).flushModule(comp),421 .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
415 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),422 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
423 .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
416 }424 }
417 }425 }
418426
...@@ -424,6 +432,7 @@ pub const File = struct {...@@ -424,6 +432,7 @@ pub const File = struct {
424 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),432 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
425 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),433 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
426 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),434 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
435 .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl),
427 }436 }
428 }437 }
429438
...@@ -433,7 +442,7 @@ pub const File = struct {...@@ -433,7 +442,7 @@ pub const File = struct {
433 .elf => return @fieldParentPtr(Elf, "base", base).error_flags,442 .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
434 .macho => return @fieldParentPtr(MachO, "base", base).error_flags,443 .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
435 .c => return .{ .no_entry_point_found = false },444 .c => return .{ .no_entry_point_found = false },
436 .wasm => return ErrorFlags{},445 .wasm, .spirv => return ErrorFlags{},
437 }446 }
438 }447 }
439448
...@@ -451,6 +460,7 @@ pub const File = struct {...@@ -451,6 +460,7 @@ pub const File = struct {
451 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),460 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
452 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),461 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
453 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),462 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
463 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports),
454 }464 }
455 }465 }
456466
...@@ -461,6 +471,7 @@ pub const File = struct {...@@ -461,6 +471,7 @@ pub const File = struct {
461 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),471 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
462 .c => unreachable,472 .c => unreachable,
463 .wasm => unreachable,473 .wasm => unreachable,
474 .spirv => unreachable,
464 }475 }
465 }476 }
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 =...@@ -302,6 +302,7 @@ const usage_build_generic =
302 \\ pe Portable Executable (Windows)302 \\ pe Portable Executable (Windows)
303 \\ coff Common Object File Format (Windows)303 \\ coff Common Object File Format (Windows)
304 \\ macho macOS relocatables304 \\ macho macOS relocatables
305 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
305 \\ hex (planned) Intel IHEX306 \\ hex (planned) Intel IHEX
306 \\ raw (planned) Dump machine code directly307 \\ raw (planned) Dump machine code directly
307 \\ -dirafter [dir] Add directory to AFTER include search path308 \\ -dirafter [dir] Add directory to AFTER include search path
...@@ -1515,6 +1516,8 @@ fn buildOutputType(...@@ -1515,6 +1516,8 @@ fn buildOutputType(
1515 break :blk .hex;1516 break :blk .hex;
1516 } else if (mem.eql(u8, ofmt, "raw")) {1517 } else if (mem.eql(u8, ofmt, "raw")) {
1517 break :blk .raw;1518 break :blk .raw;
1519 } else if (mem.eql(u8, ofmt, "spirv")) {
1520 break :blk .spirv;
1518 } else {1521 } else {
1519 fatal("unsupported object format: {s}", .{ofmt});1522 fatal("unsupported object format: {s}", .{ofmt});
1520 }1523 }