authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-06-14 21:22:51+03:30
committergravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-06-18 13:38:58+02:00
logf4c4daec863cf7394e141aefc5ad52e9c34f5979
tree181f0451634aade4e0720d35b6678e26fc868ad9
parentc9ca79fb487f934b001b4d947e8c8808b4062cb2

spirv: emit debug info for invocation globals


3 files changed, 41 insertions(+), 19 deletions(-)

src/codegen/spirv/CodeGen.zig+1
...@@ -556,6 +556,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -556,6 +556,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
556 try cg.module.sections.functions.append(gpa, cg.body);556 try cg.module.sections.functions.append(gpa, cg.body);
557557
558 try cg.module.debugNameFmt(initializer_id, "initializer of {f}", .{nav.fqn.fmt(ip)});558 try cg.module.debugNameFmt(initializer_id, "initializer of {f}", .{nav.fqn.fmt(ip)});
559 try cg.module.debugName(result_id, nav.fqn.toSlice(ip));
559560
560 try cg.module.sections.globals.emit(gpa, .OpExtInst, .{561 try cg.module.sections.globals.emit(gpa, .OpExtInst, .{
561 .id_result_type = ptr_ty_id,562 .id_result_type = ptr_ty_id,
src/link/SpirV/lower_invocation_globals.zig+34-6
...@@ -366,6 +366,8 @@ const ModuleBuilder = struct {...@@ -366,6 +366,8 @@ const ModuleBuilder = struct {
366 /// The first ID of the new entry points. Entry points are allocated from366 /// The first ID of the new entry points. Entry points are allocated from
367 /// here according to their index in `info.entry_points`.367 /// here according to their index in `info.entry_points`.
368 entry_point_new_id_base: u32,368 entry_point_new_id_base: u32,
369 /// OpName operands saved for invocation globals to re-emit.
370 global_names: std.array_hash_map.Auto(ResultId, []const Word) = .empty,
369 /// A set of all function types in the new program. SPIR-V mandates that these are unique,371 /// A set of all function types in the new program. SPIR-V mandates that these are unique,
370 /// and until a general type deduplication pass is programmed, we just handle it here via this.372 /// and until a general type deduplication pass is programmed, we just handle it here via this.
371 function_types: std.array_hash_map.Custom(FunctionType, ResultId, FunctionType.Context, true) = .empty,373 function_types: std.array_hash_map.Custom(FunctionType, ResultId, FunctionType.Context, true) = .empty,
...@@ -403,14 +405,41 @@ const ModuleBuilder = struct {...@@ -403,14 +405,41 @@ const ModuleBuilder = struct {
403 binary.functions_start = self.new_functions_section orelse binary.instructions.len;405 binary.functions_start = self.new_functions_section orelse binary.instructions.len;
404 }406 }
405407
408 fn emitGlobalNames(self: *ModuleBuilder, info: ModuleInfo) !void {
409 for (info.functions.keys(), info.functions.values()) |func, fn_info| {
410 if (info.dead_initializers.contains(func)) continue;
411 const new_info = self.function_new_info.get(func) orelse continue;
412 for (fn_info.invocation_globals.keys(), 0..) |global, i| {
413 if (!info.live_invocation_globals.contains(global)) continue;
414 const name_words = self.global_names.get(global) orelse continue;
415 const id = new_info.invocationGlobalId(i);
416 try self.section.emitRaw(self.arena, .OpName, 1 + name_words.len);
417 self.section.writeOperand(ResultId, id);
418 self.section.writeWords(name_words);
419 }
420 }
421 }
422
406 /// Process everything from `binary` up to the first function and emit it into the builder.423 /// Process everything from `binary` up to the first function and emit it into the builder.
407 fn processPreamble(self: *ModuleBuilder, binary: BinaryModule, info: ModuleInfo) !void {424 fn processPreamble(self: *ModuleBuilder, binary: BinaryModule, info: ModuleInfo) !void {
425 var emitted_global_names = false;
408 var it = binary.iterateInstructions();426 var it = binary.iterateInstructions();
409 while (it.next()) |inst| {427 while (it.next()) |inst| {
428 if (!emitted_global_names) switch (inst.opcode.class()) {
429 .annotation, .type_declaration, .constant_creation => {
430 try self.emitGlobalNames(info);
431 emitted_global_names = true;
432 },
433 else => {},
434 };
435
410 switch (inst.opcode) {436 switch (inst.opcode) {
411 .OpName => {437 .OpName => {
412 const id: ResultId = @enumFromInt(inst.operands[0]);438 const id: ResultId = @enumFromInt(inst.operands[0]);
413 if (info.invocation_globals.contains(id)) continue;439 if (info.invocation_globals.contains(id)) {
440 try self.global_names.put(self.arena, id, inst.operands[1..]);
441 continue;
442 }
414 if (info.dead_initializers.contains(id)) continue;443 if (info.dead_initializers.contains(id)) continue;
415 },444 },
416 .OpExtInstImport => {445 .OpExtInstImport => {
...@@ -446,11 +475,6 @@ const ModuleBuilder = struct {...@@ -446,11 +475,6 @@ const ModuleBuilder = struct {
446 continue;475 continue;
447 },476 },
448 .OpTypeFunction => {477 .OpTypeFunction => {
449 // Re-emitted in `emitFunctionTypes()`. We can do this because
450 // OpTypeFunction's may not currently be used anywhere that is not
451 // directly with an OpFunction. For now we ignore Intels function
452 // pointers extension, that is not a problem with a generalized
453 // pass anyway.
454 continue;478 continue;
455 },479 },
456 .OpFunction => break,480 .OpFunction => break,
...@@ -459,6 +483,10 @@ const ModuleBuilder = struct {...@@ -459,6 +483,10 @@ const ModuleBuilder = struct {
459483
460 try self.section.emitRawInstruction(self.arena, inst.opcode, inst.operands);484 try self.section.emitRawInstruction(self.arena, inst.opcode, inst.operands);
461 }485 }
486
487 if (!emitted_global_names) {
488 try self.emitGlobalNames(info);
489 }
462 }490 }
463491
464 /// Derive new information required for further emitting this module,492 /// Derive new information required for further emitting this module,
src/link/SpirV/prune_unused.zig+6-13
...@@ -100,20 +100,13 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void {...@@ -100,20 +100,13 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void {
100 };100 };
101 if (!alive.isSet(index)) continue;101 if (!alive.isSet(index)) continue;
102 } else {102 } else {
103 // annotation-style: emit only if all id operands are alive103 // annotation-style: emit only if the target id is alive
104 id_offset_buf.items.len = 0;104 if (inst.operands.len > 0) {
105 parser.parseInstructionResultIds(binary.*, inst, &id_offset_buf) catch continue;105 const target: ResultId = @enumFromInt(inst.operands[0]);
106 var all_alive = true;106 if (id_to_index.get(target)) |idx| {
107 for (id_offset_buf.items) |off| {107 if (!alive.isSet(idx)) continue;
108 const id: ResultId = @enumFromInt(inst.operands[off]);108 } else continue;
109 if (id_to_index.get(id)) |idx| {
110 if (!alive.isSet(idx)) {
111 all_alive = false;
112 break;
113 }
114 }
115 }109 }
116 if (!all_alive) continue;
117 }110 }
118 }111 }
119112