commit f4c4daec863cf7394e141aefc5ad52e9c34f5979 (tree)
parent c9ca79fb487f934b001b4d947e8c8808b4062cb2
Author: Ali Cheraghi <alichraghi@proton.me>
Date: Sun, 14 Jun 2026 21:22:51 +0330
spirv: emit debug info for invocation globals
Diffstat:
3 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
@@ -556,6 +556,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
try cg.module.sections.functions.append(gpa, cg.body);
try cg.module.debugNameFmt(initializer_id, "initializer of {f}", .{nav.fqn.fmt(ip)});
+ try cg.module.debugName(result_id, nav.fqn.toSlice(ip));
try cg.module.sections.globals.emit(gpa, .OpExtInst, .{
.id_result_type = ptr_ty_id,
diff --git a/src/link/SpirV/lower_invocation_globals.zig b/src/link/SpirV/lower_invocation_globals.zig
@@ -366,6 +366,8 @@ const ModuleBuilder = struct {
/// The first ID of the new entry points. Entry points are allocated from
/// here according to their index in `info.entry_points`.
entry_point_new_id_base: u32,
+ /// OpName operands saved for invocation globals to re-emit.
+ global_names: std.array_hash_map.Auto(ResultId, []const Word) = .empty,
/// A set of all function types in the new program. SPIR-V mandates that these are unique,
/// and until a general type deduplication pass is programmed, we just handle it here via this.
function_types: std.array_hash_map.Custom(FunctionType, ResultId, FunctionType.Context, true) = .empty,
@@ -403,14 +405,41 @@ const ModuleBuilder = struct {
binary.functions_start = self.new_functions_section orelse binary.instructions.len;
}
+ fn emitGlobalNames(self: *ModuleBuilder, info: ModuleInfo) !void {
+ for (info.functions.keys(), info.functions.values()) |func, fn_info| {
+ if (info.dead_initializers.contains(func)) continue;
+ const new_info = self.function_new_info.get(func) orelse continue;
+ for (fn_info.invocation_globals.keys(), 0..) |global, i| {
+ if (!info.live_invocation_globals.contains(global)) continue;
+ const name_words = self.global_names.get(global) orelse continue;
+ const id = new_info.invocationGlobalId(i);
+ try self.section.emitRaw(self.arena, .OpName, 1 + name_words.len);
+ self.section.writeOperand(ResultId, id);
+ self.section.writeWords(name_words);
+ }
+ }
+ }
+
/// Process everything from `binary` up to the first function and emit it into the builder.
fn processPreamble(self: *ModuleBuilder, binary: BinaryModule, info: ModuleInfo) !void {
+ var emitted_global_names = false;
var it = binary.iterateInstructions();
while (it.next()) |inst| {
+ if (!emitted_global_names) switch (inst.opcode.class()) {
+ .annotation, .type_declaration, .constant_creation => {
+ try self.emitGlobalNames(info);
+ emitted_global_names = true;
+ },
+ else => {},
+ };
+
switch (inst.opcode) {
.OpName => {
const id: ResultId = @enumFromInt(inst.operands[0]);
- if (info.invocation_globals.contains(id)) continue;
+ if (info.invocation_globals.contains(id)) {
+ try self.global_names.put(self.arena, id, inst.operands[1..]);
+ continue;
+ }
if (info.dead_initializers.contains(id)) continue;
},
.OpExtInstImport => {
@@ -446,11 +475,6 @@ const ModuleBuilder = struct {
continue;
},
.OpTypeFunction => {
- // Re-emitted in `emitFunctionTypes()`. We can do this because
- // OpTypeFunction's may not currently be used anywhere that is not
- // directly with an OpFunction. For now we ignore Intels function
- // pointers extension, that is not a problem with a generalized
- // pass anyway.
continue;
},
.OpFunction => break,
@@ -459,6 +483,10 @@ const ModuleBuilder = struct {
try self.section.emitRawInstruction(self.arena, inst.opcode, inst.operands);
}
+
+ if (!emitted_global_names) {
+ try self.emitGlobalNames(info);
+ }
}
/// Derive new information required for further emitting this module,
diff --git a/src/link/SpirV/prune_unused.zig b/src/link/SpirV/prune_unused.zig
@@ -100,20 +100,13 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void {
};
if (!alive.isSet(index)) continue;
} else {
- // annotation-style: emit only if all id operands are alive
- id_offset_buf.items.len = 0;
- parser.parseInstructionResultIds(binary.*, inst, &id_offset_buf) catch continue;
- var all_alive = true;
- for (id_offset_buf.items) |off| {
- const id: ResultId = @enumFromInt(inst.operands[off]);
- if (id_to_index.get(id)) |idx| {
- if (!alive.isSet(idx)) {
- all_alive = false;
- break;
- }
- }
+ // annotation-style: emit only if the target id is alive
+ if (inst.operands.len > 0) {
+ const target: ResultId = @enumFromInt(inst.operands[0]);
+ if (id_to_index.get(target)) |idx| {
+ if (!alive.isSet(idx)) continue;
+ } else continue;
}
- if (!all_alive) continue;
}
}