| author | |
| committer | |
| log | b2b87b590011d8df52874e3f9bd1f88d1b0189d1 |
| tree | 3cccc8d6f0424ef50361a9eec66b1ef021e3330d |
| parent | ab607d455e47c35b980c3281ef5c3fb433a770a7 |
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 | 1622 | // in `Decl` to notice that the line number did not change. |
| 1623 | 1623 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| 1624 | 1624 | }, |
| 1625 | .c, .wasm => {}, | |
| 1625 | .c, .wasm, .spirv => {}, | |
| 1626 | 1626 | } |
| 1627 | 1627 | } |
| 1628 | 1628 | } else { |
| ... | ... | @@ -1855,6 +1855,7 @@ fn allocateNewDecl( |
| 1855 | 1855 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 1856 | 1856 | .c => .{ .c = link.File.C.DeclBlock.empty }, |
| 1857 | 1857 | .wasm => .{ .wasm = {} }, |
| 1858 | .spirv => .{ .spirv = {} }, | |
| 1858 | 1859 | }, |
| 1859 | 1860 | .fn_link = switch (mod.comp.bin_file.tag) { |
| 1860 | 1861 | .coff => .{ .coff = {} }, |
| ... | ... | @@ -1862,6 +1863,7 @@ fn allocateNewDecl( |
| 1862 | 1863 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 1863 | 1864 | .c => .{ .c = link.File.C.FnBlock.empty }, |
| 1864 | 1865 | .wasm => .{ .wasm = null }, |
| 1866 | .spirv => .{ .spirv = .{} }, | |
| 1865 | 1867 | }, |
| 1866 | 1868 | .generation = 0, |
| 1867 | 1869 | .is_pub = false, |
| ... | ... | @@ -1959,6 +1961,7 @@ pub fn analyzeExport( |
| 1959 | 1961 | .macho => .{ .macho = link.File.MachO.Export{} }, |
| 1960 | 1962 | .c => .{ .c = {} }, |
| 1961 | 1963 | .wasm => .{ .wasm = {} }, |
| 1964 | .spirv => .{ .spirv = {} }, | |
| 1962 | 1965 | }, |
| 1963 | 1966 | .owner_decl = owner_decl, |
| 1964 | 1967 | .exported_decl = exported_decl, |
src/codegen/spirv.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const std = @import("std"); | |
| 2 | const spec = @import("spirv/spec.zig"); | |
| 3 | const Module = @import("../Module.zig"); | |
| 4 | const Decl = Module.Decl; | |
| 5 | ||
| 6 | pub 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 | 142 | macho: MachO.SrcFn, |
| 143 | 143 | c: C.FnBlock, |
| 144 | 144 | wasm: ?Wasm.FnData, |
| 145 | spirv: void, | |
| 145 | spirv: SpirV.FnData, | |
| 146 | 146 | }; |
| 147 | 147 | |
| 148 | 148 | pub const Export = union { |
| ... | ... | @@ -180,7 +180,7 @@ pub const File = struct { |
| 180 | 180 | .macho => &(try MachO.createEmpty(allocator, options)).base, |
| 181 | 181 | .wasm => &(try Wasm.createEmpty(allocator, options)).base, |
| 182 | 182 | .c => unreachable, // Reported error earlier. |
| 183 | .spirv => return error.SpirVObjectFormatUnimplemented, | |
| 183 | .spirv => &(try SpirV.createEmpty(allocator, options)).base, | |
| 184 | 184 | .hex => return error.HexObjectFormatUnimplemented, |
| 185 | 185 | .raw => return error.RawObjectFormatUnimplemented, |
| 186 | 186 | }; |
| ... | ... | @@ -196,7 +196,7 @@ pub const File = struct { |
| 196 | 196 | .macho => &(try MachO.createEmpty(allocator, options)).base, |
| 197 | 197 | .wasm => &(try Wasm.createEmpty(allocator, options)).base, |
| 198 | 198 | .c => unreachable, // Reported error earlier. |
| 199 | .spirv => return error.SpirVObjectFormatUnimplemented, | |
| 199 | .spirv => &(try SpirV.createEmpty(allocator, options)).base, | |
| 200 | 200 | .hex => return error.HexObjectFormatUnimplemented, |
| 201 | 201 | .raw => return error.RawObjectFormatUnimplemented, |
| 202 | 202 | }; |
| ... | ... | @@ -212,7 +212,7 @@ pub const File = struct { |
| 212 | 212 | .macho => &(try MachO.openPath(allocator, sub_path, options)).base, |
| 213 | 213 | .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, |
| 214 | 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 | 216 | .hex => return error.HexObjectFormatUnimplemented, |
| 217 | 217 | .raw => return error.RawObjectFormatUnimplemented, |
| 218 | 218 | }; |
| ... | ... | @@ -242,7 +242,7 @@ pub const File = struct { |
| 242 | 242 | .mode = determineMode(base.options), |
| 243 | 243 | }); |
| 244 | 244 | }, |
| 245 | .c, .wasm => {}, | |
| 245 | .c, .wasm, .spirv => {}, | |
| 246 | 246 | } |
| 247 | 247 | } |
| 248 | 248 | |
| ... | ... | @@ -287,7 +287,7 @@ pub const File = struct { |
| 287 | 287 | f.close(); |
| 288 | 288 | base.file = null; |
| 289 | 289 | }, |
| 290 | .c, .wasm => {}, | |
| 290 | .c, .wasm, .spirv => {}, | |
| 291 | 291 | } |
| 292 | 292 | } |
| 293 | 293 | |
| ... | ... | @@ -300,6 +300,7 @@ pub const File = struct { |
| 300 | 300 | .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), |
| 301 | 301 | .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), |
| 302 | 302 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), |
| 303 | .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), | |
| 303 | 304 | } |
| 304 | 305 | } |
| 305 | 306 | |
| ... | ... | @@ -309,7 +310,7 @@ pub const File = struct { |
| 309 | 310 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), |
| 310 | 311 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), |
| 311 | 312 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), |
| 312 | .wasm => {}, | |
| 313 | .wasm, .spirv => {}, | |
| 313 | 314 | } |
| 314 | 315 | } |
| 315 | 316 | |
| ... | ... | @@ -321,7 +322,7 @@ pub const File = struct { |
| 321 | 322 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), |
| 322 | 323 | .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), |
| 323 | 324 | .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), |
| 324 | .wasm => {}, | |
| 325 | .wasm, .spirv => {}, | |
| 325 | 326 | } |
| 326 | 327 | } |
| 327 | 328 | |
| ... | ... | @@ -368,6 +369,11 @@ pub const File = struct { |
| 368 | 369 | parent.deinit(); |
| 369 | 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 | } |
| 373 | 379 | |
| ... | ... | @@ -401,6 +407,7 @@ pub const File = struct { |
| 401 | 407 | .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), |
| 402 | 408 | .c => return @fieldParentPtr(C, "base", base).flush(comp), |
| 403 | 409 | .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), |
| 410 | .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), | |
| 404 | 411 | } |
| 405 | 412 | } |
| 406 | 413 | |
| ... | ... | @@ -413,6 +420,7 @@ pub const File = struct { |
| 413 | 420 | .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), |
| 414 | 421 | .c => return @fieldParentPtr(C, "base", base).flushModule(comp), |
| 415 | 422 | .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), |
| 423 | .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), | |
| 416 | 424 | } |
| 417 | 425 | } |
| 418 | 426 | |
| ... | ... | @@ -424,6 +432,7 @@ pub const File = struct { |
| 424 | 432 | .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), |
| 425 | 433 | .c => @fieldParentPtr(C, "base", base).freeDecl(decl), |
| 426 | 434 | .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), |
| 435 | .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), | |
| 427 | 436 | } |
| 428 | 437 | } |
| 429 | 438 | |
| ... | ... | @@ -433,7 +442,7 @@ pub const File = struct { |
| 433 | 442 | .elf => return @fieldParentPtr(Elf, "base", base).error_flags, |
| 434 | 443 | .macho => return @fieldParentPtr(MachO, "base", base).error_flags, |
| 435 | 444 | .c => return .{ .no_entry_point_found = false }, |
| 436 | .wasm => return ErrorFlags{}, | |
| 445 | .wasm, .spirv => return ErrorFlags{}, | |
| 437 | 446 | } |
| 438 | 447 | } |
| 439 | 448 | |
| ... | ... | @@ -451,6 +460,7 @@ pub const File = struct { |
| 451 | 460 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), |
| 452 | 461 | .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), |
| 453 | 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 | } |
| 456 | 466 | |
| ... | ... | @@ -461,6 +471,7 @@ pub const File = struct { |
| 461 | 471 | .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), |
| 462 | 472 | .c => unreachable, |
| 463 | 473 | .wasm => unreachable, |
| 474 | .spirv => unreachable, | |
| 464 | 475 | } |
| 465 | 476 | } |
| 466 | 477 |
src/link/SpirV.zig created+131| ... | ... | @@ -0,0 +1,131 @@ |
| 1 | const SpirV = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const Allocator = std.mem.Allocator; | |
| 5 | const assert = std.debug.assert; | |
| 6 | ||
| 7 | const Module = @import("../Module.zig"); | |
| 8 | const Compilation = @import("../Compilation.zig"); | |
| 9 | const link = @import("../link.zig"); | |
| 10 | const codegen = @import("../codegen/spirv.zig"); | |
| 11 | const trace = @import("../tracy.zig").trace; | |
| 12 | const build_options = @import("build_options"); | |
| 13 | const spec = @import("../codegen/spirv/spec.zig"); | |
| 14 | ||
| 15 | pub const FnData = struct { | |
| 16 | id: ?u32 = null, | |
| 17 | code: std.ArrayListUnmanaged(u32) = .{}, | |
| 18 | }; | |
| 19 | ||
| 20 | base: link.File, | |
| 21 | ||
| 22 | // TODO: Does this file need to support multiple independent modules? | |
| 23 | spirv_module: codegen.SPIRVModule = .{}, | |
| 24 | ||
| 25 | pub 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 | ||
| 38 | pub 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 | ||
| 55 | pub fn deinit(self: *SpirV) void { | |
| 56 | } | |
| 57 | ||
| 58 | pub 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 | ||
| 77 | pub fn updateDeclExports( | |
| 78 | self: *SpirV, | |
| 79 | module: *Module, | |
| 80 | decl: *const Module.Decl, | |
| 81 | exports: []const *Module.Export, | |
| 82 | ) !void {} | |
| 83 | ||
| 84 | pub 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 | ||
| 89 | pub 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 | ||
| 97 | pub 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 | 302 | \\ pe Portable Executable (Windows) |
| 303 | 303 | \\ coff Common Object File Format (Windows) |
| 304 | 304 | \\ macho macOS relocatables |
| 305 | \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) | |
| 305 | 306 | \\ hex (planned) Intel IHEX |
| 306 | 307 | \\ raw (planned) Dump machine code directly |
| 307 | 308 | \\ -dirafter [dir] Add directory to AFTER include search path |
| ... | ... | @@ -1515,6 +1516,8 @@ fn buildOutputType( |
| 1515 | 1516 | break :blk .hex; |
| 1516 | 1517 | } else if (mem.eql(u8, ofmt, "raw")) { |
| 1517 | 1518 | break :blk .raw; |
| 1519 | } else if (mem.eql(u8, ofmt, "spirv")) { | |
| 1520 | break :blk .spirv; | |
| 1518 | 1521 | } else { |
| 1519 | 1522 | fatal("unsupported object format: {s}", .{ofmt}); |
| 1520 | 1523 | } |