diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-09-23 12:37:48 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-23 12:37:48 -0700 |
| commit | b00287175c044682ea025805de05a6ac26a42771 (patch) | |
| tree | 1c6ef32977a615238c231c79c020c16a8b98da5d /src/codegen/spirv | |
| parent | 865b2e259bf78dbf1d4c1051b5fff68b90bca65f (diff) | |
| parent | cff8ab88f5ffe24771aa9c6e839eef03bc22f3d2 (diff) | |
| download | zig-b00287175c044682ea025805de05a6ac26a42771.tar.gz zig-b00287175c044682ea025805de05a6ac26a42771.zip | |
Merge pull request #17174 from Snektron/spirv-stuffies
spirv gaming
Diffstat (limited to 'src/codegen/spirv')
| -rw-r--r-- | src/codegen/spirv/Cache.zig | 12 | ||||
| -rw-r--r-- | src/codegen/spirv/Module.zig | 121 |
2 files changed, 107 insertions, 26 deletions
diff --git a/src/codegen/spirv/Cache.zig b/src/codegen/spirv/Cache.zig index 7a3b6f61f5..68fea5c47a 100644 --- a/src/codegen/spirv/Cache.zig +++ b/src/codegen/spirv/Cache.zig @@ -462,11 +462,11 @@ fn emit( switch (key) { .void_type => { try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); - try spv.debugName(result_id, "void", .{}); + try spv.debugName(result_id, "void"); }, .bool_type => { try section.emit(spv.gpa, .OpTypeBool, .{ .id_result = result_id }); - try spv.debugName(result_id, "bool", .{}); + try spv.debugName(result_id, "bool"); }, .int_type => |int| { try section.emit(spv.gpa, .OpTypeInt, .{ @@ -481,14 +481,14 @@ fn emit( .unsigned => "u", .signed => "i", }; - try spv.debugName(result_id, "{s}{}", .{ ui, int.bits }); + try spv.debugNameFmt(result_id, "{s}{}", .{ ui, int.bits }); }, .float_type => |float| { try section.emit(spv.gpa, .OpTypeFloat, .{ .id_result = result_id, .width = float.bits, }); - try spv.debugName(result_id, "f{}", .{float.bits}); + try spv.debugNameFmt(result_id, "f{}", .{float.bits}); }, .vector_type => |vector| { try section.emit(spv.gpa, .OpTypeVector, .{ @@ -530,11 +530,11 @@ fn emit( section.writeOperand(IdResult, self.resultId(member_type)); } if (self.getString(struct_type.name)) |name| { - try spv.debugName(result_id, "{s}", .{name}); + try spv.debugName(result_id, name); } for (struct_type.memberNames(), 0..) |member_name, i| { if (self.getString(member_name)) |name| { - try spv.memberDebugName(result_id, @as(u32, @intCast(i)), "{s}", .{name}); + try spv.memberDebugName(result_id, @as(u32, @intCast(i)), name); } } // TODO: Decorations? diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index e61ac754ee..81b97ebae5 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -94,6 +94,8 @@ pub const Global = struct { begin_inst: u32, /// The past-end offset into `self.flobals.section`. end_inst: u32, + /// The result-id of the function that initializes this value. + initializer_id: IdRef, }; /// This models a kernel entry point. @@ -284,6 +286,10 @@ fn addEntryPointDeps( const decl = self.declPtr(decl_index); const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep]; + if (seen.isSet(@intFromEnum(decl_index))) { + return; + } + seen.set(@intFromEnum(decl_index)); if (self.globalPtr(decl_index)) |global| { @@ -291,9 +297,7 @@ fn addEntryPointDeps( } for (deps) |dep| { - if (!seen.isSet(@intFromEnum(dep))) { - try self.addEntryPointDeps(dep, seen, interface); - } + try self.addEntryPointDeps(dep, seen, interface); } } @@ -325,20 +329,76 @@ fn entryPoints(self: *Module) !Section { return entry_points; } +/// Generate a function that calls all initialization functions, +/// in unspecified order (an order should not be required here). +/// It generated as follows: +/// %init = OpFunction %void None +/// foreach %initializer: +/// OpFunctionCall %initializer +/// OpReturn +/// OpFunctionEnd +fn initializer(self: *Module, entry_points: *Section) !Section { + var section = Section{}; + errdefer section.deinit(self.gpa); + + // const void_ty_ref = try self.resolveType(Type.void, .direct); + const void_ty_ref = try self.resolve(.void_type); + const void_ty_id = self.resultId(void_ty_ref); + const init_proto_ty_ref = try self.resolve(.{ .function_type = .{ + .return_type = void_ty_ref, + .parameters = &.{}, + } }); + + const init_id = self.allocId(); + try section.emit(self.gpa, .OpFunction, .{ + .id_result_type = void_ty_id, + .id_result = init_id, + .function_control = .{}, + .function_type = self.resultId(init_proto_ty_ref), + }); + try section.emit(self.gpa, .OpLabel, .{ + .id_result = self.allocId(), + }); + + var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len); + defer seen.deinit(self.gpa); + + var interface = std.ArrayList(IdRef).init(self.gpa); + defer interface.deinit(); + + for (self.globals.globals.keys(), self.globals.globals.values()) |decl_index, global| { + try self.addEntryPointDeps(decl_index, &seen, &interface); + try section.emit(self.gpa, .OpFunctionCall, .{ + .id_result_type = void_ty_id, + .id_result = self.allocId(), + .function = global.initializer_id, + }); + } + + try section.emit(self.gpa, .OpReturn, {}); + try section.emit(self.gpa, .OpFunctionEnd, {}); + + try entry_points.emit(self.gpa, .OpEntryPoint, .{ + // TODO: Rusticl does not support this because its poorly defined. + // Do we need to generate a workaround here? + .execution_model = .Kernel, + .entry_point = init_id, + .name = "zig global initializer", + .interface = interface.items, + }); + + try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{ + .entry_point = init_id, + .mode = .Initializer, + }); + + return section; +} + /// Emit this module as a spir-v binary. pub fn flush(self: *Module, file: std.fs.File) !void { // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" - const header = [_]Word{ - spec.magic_number, - // TODO: From cpu features - // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. - (1 << 16) | (4 << 8), - 0, // TODO: Register Zig compiler magic number. - self.idBound(), - 0, // Schema (currently reserved for future use) - }; - // TODO: Perform topological sort on the globals. var globals = try self.orderGlobals(); defer globals.deinit(self.gpa); @@ -349,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void { var types_constants = try self.cache.materialize(self); defer types_constants.deinit(self.gpa); + var init_func = try self.initializer(&entry_points); + defer init_func.deinit(self.gpa); + + const header = [_]Word{ + spec.magic_number, + // TODO: From cpu features + // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. + (1 << 16) | (4 << 8), + 0, // TODO: Register Zig compiler magic number. + self.idBound(), + 0, // Schema (currently reserved for future use) + }; + // Note: needs to be kept in order according to section 2.3! const buffers = &[_][]const Word{ &header, @@ -363,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void { self.sections.types_globals_constants.toWords(), globals.toWords(), self.sections.functions.toWords(), + init_func.toWords(), }; var iovc_buffers: [buffers.len]std.os.iovec_const = undefined; @@ -524,6 +598,7 @@ pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index { .result_id = undefined, .begin_inst = undefined, .end_inst = undefined, + .initializer_id = undefined, }), } @@ -553,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 { return @as(u32, @intCast(self.globals.section.instructions.items.len)); } -pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32) void { +pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, result_id: IdRef, initializer_id: IdRef) void { const global = self.globalPtr(global_index).?; - global.begin_inst = begin_inst; - global.end_inst = @as(u32, @intCast(self.globals.section.instructions.items.len)); + global.* = .{ + .result_id = result_id, + .begin_inst = begin_inst, + .end_inst = @intCast(self.globals.section.instructions.items.len), + .initializer_id = initializer_id, + }; } pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8) !void { @@ -566,18 +645,20 @@ pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8 }); } -pub fn debugName(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { - const name = try std.fmt.allocPrint(self.gpa, fmt, args); - defer self.gpa.free(name); +pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void { try self.sections.debug_names.emit(self.gpa, .OpName, .{ .target = target, .name = name, }); } -pub fn memberDebugName(self: *Module, target: IdResult, member: u32, comptime fmt: []const u8, args: anytype) !void { +pub fn debugNameFmt(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { const name = try std.fmt.allocPrint(self.gpa, fmt, args); defer self.gpa.free(name); + try self.debugName(target, name); +} + +pub fn memberDebugName(self: *Module, target: IdResult, member: u32, name: []const u8) !void { try self.sections.debug_names.emit(self.gpa, .OpMemberName, .{ .type = target, .member = member, |
