diff options
Diffstat (limited to 'src/codegen/spirv/Module.zig')
| -rw-r--r-- | src/codegen/spirv/Module.zig | 121 |
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, |
