From ab607d455e47c35b980c3281ef5c3fb433a770a7 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 17 Jan 2021 16:18:00 +0100 Subject: SPIR-V: Initial architecture definitions and setup --- src/link.zig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/link.zig') diff --git a/src/link.zig b/src/link.zig index 6914131bea..fdbb7efd4b 100644 --- a/src/link.zig +++ b/src/link.zig @@ -133,6 +133,7 @@ pub const File = struct { macho: MachO.TextBlock, c: C.DeclBlock, wasm: void, + spirv: void, }; pub const LinkFn = union { @@ -141,6 +142,7 @@ pub const File = struct { macho: MachO.SrcFn, c: C.FnBlock, wasm: ?Wasm.FnData, + spirv: void, }; pub const Export = union { @@ -149,6 +151,7 @@ pub const File = struct { macho: MachO.Export, c: void, wasm: void, + spirv: void, }; /// For DWARF .debug_info. @@ -177,6 +180,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. + .spirv => return error.SpirVObjectFormatUnimplemented, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -192,6 +196,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. + .spirv => return error.SpirVObjectFormatUnimplemented, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -207,6 +212,7 @@ pub const File = struct { .macho => &(try MachO.openPath(allocator, sub_path, options)).base, .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, + .spirv => return error.SpirVObjectFormatUnimplemented, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -595,6 +601,7 @@ pub const File = struct { macho, c, wasm, + spirv, }; pub const ErrorFlags = struct { @@ -605,6 +612,7 @@ pub const File = struct { pub const Coff = @import("link/Coff.zig"); pub const Elf = @import("link/Elf.zig"); pub const MachO = @import("link/MachO.zig"); + pub const SpirV = @import("link/SpirV.zig"); pub const Wasm = @import("link/Wasm.zig"); }; -- cgit v1.2.3 From b2b87b590011d8df52874e3f9bd1f88d1b0189d1 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 00:34:44 +0100 Subject: SPIR-V: Linking and codegen setup --- src/Module.zig | 5 +- src/codegen/spirv.zig | 22 +++++++++ src/link.zig | 29 +++++++---- src/link/SpirV.zig | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 3 ++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 src/codegen/spirv.zig create mode 100644 src/link/SpirV.zig (limited to 'src/link.zig') diff --git a/src/Module.zig b/src/Module.zig index fa9722814e..464124c7b9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1622,7 +1622,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // in `Decl` to notice that the line number did not change. self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } } else { @@ -1855,6 +1855,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.TextBlock.empty }, .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .fn_link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, @@ -1862,6 +1863,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.SrcFn.empty }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = null }, + .spirv => .{ .spirv = .{} }, }, .generation = 0, .is_pub = false, @@ -1959,6 +1961,7 @@ pub fn analyzeExport( .macho => .{ .macho = link.File.MachO.Export{} }, .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .owner_decl = owner_decl, .exported_decl = exported_decl, diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig new file mode 100644 index 0000000000..f0ebd49e1d --- /dev/null +++ b/src/codegen/spirv.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const spec = @import("spirv/spec.zig"); +const Module = @import("../Module.zig"); +const Decl = Module.Decl; + +pub const SPIRVModule = struct { + // TODO: Also use a free list. + next_id: u32 = 0, + + pub fn allocId(self: *SPIRVModule) u32 { + defer self.next_id += 1; + return self.next_id; + } + + pub fn idBound(self: *SPIRVModule) u32 { + return self.next_id; + } + + pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void { + + } +}; diff --git a/src/link.zig b/src/link.zig index fdbb7efd4b..a19a261f55 100644 --- a/src/link.zig +++ b/src/link.zig @@ -142,7 +142,7 @@ pub const File = struct { macho: MachO.SrcFn, c: C.FnBlock, wasm: ?Wasm.FnData, - spirv: void, + spirv: SpirV.FnData, }; pub const Export = union { @@ -180,7 +180,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -196,7 +196,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -212,7 +212,7 @@ pub const File = struct { .macho => &(try MachO.openPath(allocator, sub_path, options)).base, .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -242,7 +242,7 @@ pub const File = struct { .mode = determineMode(base.options), }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } @@ -287,7 +287,7 @@ pub const File = struct { f.close(); base.file = null; }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } @@ -300,6 +300,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), } } @@ -309,7 +310,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), - .wasm => {}, + .wasm, .spirv => {}, } } @@ -321,7 +322,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), - .wasm => {}, + .wasm, .spirv => {}, } } @@ -368,6 +369,11 @@ pub const File = struct { parent.deinit(); base.allocator.destroy(parent); }, + .spirv => { + const parent = @fieldParentPtr(SpirV, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, } } @@ -401,6 +407,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), .c => return @fieldParentPtr(C, "base", base).flush(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), + .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), } } @@ -413,6 +420,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), .c => return @fieldParentPtr(C, "base", base).flushModule(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), + .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), } } @@ -424,6 +432,7 @@ pub const File = struct { .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), .c => @fieldParentPtr(C, "base", base).freeDecl(decl), .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), + .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), } } @@ -433,7 +442,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).error_flags, .macho => return @fieldParentPtr(MachO, "base", base).error_flags, .c => return .{ .no_entry_point_found = false }, - .wasm => return ErrorFlags{}, + .wasm, .spirv => return ErrorFlags{}, } } @@ -451,6 +460,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), } } @@ -461,6 +471,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), .c => unreachable, .wasm => unreachable, + .spirv => unreachable, } } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig new file mode 100644 index 0000000000..207e460174 --- /dev/null +++ b/src/link/SpirV.zig @@ -0,0 +1,131 @@ +const SpirV = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const link = @import("../link.zig"); +const codegen = @import("../codegen/spirv.zig"); +const trace = @import("../tracy.zig").trace; +const build_options = @import("build_options"); +const spec = @import("../codegen/spirv/spec.zig"); + +pub const FnData = struct { + id: ?u32 = null, + code: std.ArrayListUnmanaged(u32) = .{}, +}; + +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.* = .{ + .base = .{ + .tag = .spirv, + .options = options, + .file = null, + .allocator = gpa, + }, + }; + return spirv; +} + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*SpirV { + assert(options.object_format == .spirv); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all. + if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. + + // TODO: read the file and keep vaild parts instead of truncating + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); + errdefer file.close(); + + const spirv = try createEmpty(allocator, options); + errdefer spirv.base.destroy(); + + spirv.base.file = file; + return spirv; +} + +pub fn deinit(self: *SpirV) void { +} + +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(); + + // Free excess allocated memory for this Decl. + fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); +} + +pub fn updateDeclExports( + self: *SpirV, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, +) !void {} + +pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { + decl.fn_link.spirv.code.deinit(self.base.allocator); + decl.fn_link.spirv = undefined; +} + +pub fn flush(self: *SpirV, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. + } else { + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *SpirV, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const module = self.base.options.module.?; + + const file = self.base.file.?; + var bw = std.io.bufferedWriter(file.writer()); + const writer = bw.writer(); + + // Header + // SPIR-V files support both little and big endian words. The actual format is disambiguated by + // the magic number. This backend uses little endian. + try writer.writeIntLittle(u32, spec.magic_number); + try writer.writeIntLittle(u32, (spec.version.major << 16) | (spec.version.minor) << 8); + try writer.writeIntLittle(u32, 0); // TODO: Register Zig compiler magic number. + try writer.writeIntLittle(u32, self.spirv_module.idBound()); + try writer.writeIntLittle(u32, 0); // Schema. + + // Declarations + for (module.decl_table.items()) |entry| { + const decl = entry.value; + switch (decl.typed_value) { + .most_recent => |tvm| { + const fn_data = &decl.fn_link.spirv; + for (fn_data.code.items) |word| { + try writer.writeIntLittle(u32, word); + } + }, + .never_succeeded => continue, + } + } + + try bw.flush(); +} diff --git a/src/main.zig b/src/main.zig index 13bea13a5e..a86ec4ccf5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -302,6 +302,7 @@ const usage_build_generic = \\ pe Portable Executable (Windows) \\ coff Common Object File Format (Windows) \\ macho macOS relocatables + \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) \\ hex (planned) Intel IHEX \\ raw (planned) Dump machine code directly \\ -dirafter [dir] Add directory to AFTER include search path @@ -1515,6 +1516,8 @@ fn buildOutputType( break :blk .hex; } else if (mem.eql(u8, ofmt, "raw")) { break :blk .raw; + } else if (mem.eql(u8, ofmt, "spirv")) { + break :blk .spirv; } else { fatal("unsupported object format: {s}", .{ofmt}); } -- cgit v1.2.3