aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/spirv/Module.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-09-23 12:37:48 -0700
committerGitHub <noreply@github.com>2023-09-23 12:37:48 -0700
commitb00287175c044682ea025805de05a6ac26a42771 (patch)
tree1c6ef32977a615238c231c79c020c16a8b98da5d /src/codegen/spirv/Module.zig
parent865b2e259bf78dbf1d4c1051b5fff68b90bca65f (diff)
parentcff8ab88f5ffe24771aa9c6e839eef03bc22f3d2 (diff)
downloadzig-b00287175c044682ea025805de05a6ac26a42771.tar.gz
zig-b00287175c044682ea025805de05a6ac26a42771.zip
Merge pull request #17174 from Snektron/spirv-stuffies
spirv gaming
Diffstat (limited to 'src/codegen/spirv/Module.zig')
-rw-r--r--src/codegen/spirv/Module.zig121
1 files changed, 101 insertions, 20 deletions
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,