authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-10-15 01:35:56+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-10-23 09:18:03+03:30
logf51d2cf02ab5593f8e0e7923b34d8a68048bc0d8
tree25f735886a63492662bab9b5a166b22a7b2becf7
parent94ba9ff797fdc59f4f5b7e927245c3fe613c2f73

llvm: pass extensions and enable debug info


8 files changed, 155 insertions(+), 74 deletions(-)

src/Compilation.zig+2-1
......@@ -1300,7 +1300,7 @@ pub const cache_helpers = struct {
13001300 const tag: @typeInfo(Config.DebugFormat).@"union".tag_type.? = x;
13011301 hh.add(tag);
13021302 switch (x) {
1303 .strip, .code_view => {},
1303 .strip, .code_view, .nsdi => {},
13041304 .dwarf => |f| hh.add(f),
13051305 }
13061306 }
......@@ -6303,6 +6303,7 @@ fn addCommonCCArgs(
63036303 try argv.ensureUnusedCapacity(2);
63046304 switch (comp.config.debug_format) {
63056305 .strip => {},
6306 .nsdi => argv.appendAssumeCapacity("-g"),
63066307 .code_view => {
63076308 // -g is required here because -gcodeview doesn't trigger debug info
63086309 // generation, it only changes the type of information generated.
src/Compilation/Config.zig+4-1
......@@ -72,6 +72,8 @@ pub const DebugFormat = union(enum) {
7272 strip,
7373 dwarf: std.dwarf.Format,
7474 code_view,
75 /// Non-semantic Shader Debug Info
76 nsdi,
7577};
7678
7779pub const Options = struct {
......@@ -517,7 +519,8 @@ pub fn resolve(options: Options) ResolveError!Config {
517519 .windows, .uefi => .code_view,
518520 else => .{ .dwarf = .@"32" },
519521 },
520 .spirv, .hex, .raw, .plan9 => .strip,
522 .spirv => .nsdi,
523 .hex, .raw, .plan9 => .strip,
521524 };
522525 };
523526
src/codegen/llvm.zig+1
......@@ -455,6 +455,7 @@ pub const Object = struct {
455455
456456 switch (comp.config.debug_format) {
457457 .strip => unreachable,
458 .nsdi => {},
458459 .dwarf => |f| {
459460 module_flags.appendAssumeCapacity(try o.builder.metadataTuple(&.{
460461 behavior_max,
src/link/Elf/ZigObject.zig+1-1
......@@ -224,7 +224,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
224224 try dwarf.initMetadata();
225225 self.dwarf = dwarf;
226226 },
227 .code_view => unreachable,
227 .code_view, .nsdi => unreachable,
228228 }
229229}
230230
src/link/Elf2.zig+8-8
......@@ -5146,7 +5146,7 @@ fn create(
51465146 .dwarf = .init(&elf.base, switch (comp.config.debug_format) {
51475147 .strip => .@"32", // for .eh_frame
51485148 .dwarf => |v| v,
5149 .code_view => unreachable,
5149 .code_view, .nsdi => unreachable,
51505150 }),
51515151 .dwarf_shared = comptime .initFill(.{
51525152 .first_target_reloc = .none,
......@@ -5261,7 +5261,7 @@ fn initHeaders(
52615261 const have_debug_frame = machine == .X86_64 and switch (comp.config.debug_format) {
52625262 .strip => false,
52635263 .dwarf => !comp.config.any_unwind_tables,
5264 .code_view => unreachable,
5264 .code_view, .nsdi => unreachable,
52655265 };
52665266 const addr_align: Alignment = switch (class) {
52675267 .NONE, _ => unreachable,
......@@ -5322,7 +5322,7 @@ fn initHeaders(
53225322 shnum += 1; // .debug_str
53235323 shnum += 1; // .debug_str_offsets
53245324 },
5325 .code_view => unreachable,
5325 .code_view, .nsdi => unreachable,
53265326 }
53275327 if (@"type" != .REL) {
53285328 shnum += 1; // .got
......@@ -6548,7 +6548,7 @@ fn initHeaders(
65486548 .node_align = elf.mf.flags.block_size,
65496549 });
65506550 },
6551 .code_view => unreachable,
6551 .code_view, .nsdi => unreachable,
65526552 }
65536553
65546554 // If we have an entry point symbol, create the undefined reference now.
......@@ -10400,7 +10400,7 @@ fn updateContainerTypeInner(
1040010400 try elf.dwarf.const_pool.updateContainerType(pt, .{ .elf2 = elf }, ty, success);
1040110401 try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf });
1040210402 },
10403 .code_view => unreachable,
10403 .code_view, .nsdi => unreachable,
1040410404 }
1040510405 if (!success) return;
1040610406 var lazy_it = elf.lazy.iterator();
......@@ -10426,7 +10426,7 @@ pub fn addConst(
1042610426 try elf.dwarf_consts.ensureUnusedCapacity(gpa, 1);
1042710427 try elf.dwarf.addConst(cpi, val, &addConstNode);
1042810428 },
10429 .code_view => unreachable,
10429 .code_view, .nsdi => unreachable,
1043010430 }
1043110431}
1043210432fn addConstNode(
......@@ -10502,7 +10502,7 @@ fn updateConstInner(
1050210502 }
1050310503 try elf.genPending(pt);
1050410504 },
10505 .code_view => unreachable,
10505 .code_view, .nsdi => unreachable,
1050610506 }
1050710507}
1050810508
......@@ -11259,7 +11259,7 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
1125911259 elf.resetNodeRelocs(debug_info_ni);
1126011260 try elf.dwarf.genDecl(pt, &di_nw, pending.instance);
1126111261 },
11262 .code_view => unreachable,
11262 .code_view, .nsdi => unreachable,
1126311263 }
1126411264 try elf.genPendingDebug(gpa);
1126511265 assert(elf.pending_uavs.items.len == 0); // no UAVs added by lazy code/data or by debug info
src/link/MachO/ZigObject.zig+1-1
......@@ -62,7 +62,7 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void {
6262 self.debug_info_header_dirty = true;
6363 self.debug_line_header_dirty = true;
6464 },
65 .code_view => unreachable,
65 .code_view, .nsdi => unreachable,
6666 }
6767}
6868
src/link/Spirv/Flush.zig+109-62
......@@ -2,6 +2,7 @@ const std = @import("std");
22const Allocator = std.mem.Allocator;
33const Path = std.Build.Cache.Path;
44const assert = std.debug.assert;
5const stringToEnum = std.meta.stringToEnum;
56const InternPool = @import("../../InternPool.zig");
67const link = @import("../../link.zig");
78const Type = @import("../../Type.zig");
......@@ -34,6 +35,7 @@ decorations: Section,
3435globals: Section,
3536declarations: Section,
3637functions: Section,
38nonsemantic_info: bool = false,
3739
3840const Symbol = struct {
3941 id: Id,
......@@ -42,7 +44,7 @@ const Symbol = struct {
4244};
4345
4446const Annotation = struct {
45 target: Word,
47 target_id: Word,
4648 class: spec.Class,
4749 start: usize,
4850 end: usize,
......@@ -224,6 +226,7 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
224226 const gpa = comp.gpa;
225227 const arena = flush.arena;
226228 const diags = &comp.link_diags;
229 const target = &comp.root_mod.resolved_target.result;
227230
228231 const ids = try gpa.alloc(Id, mir.id_bound);
229232 defer gpa.free(ids);
......@@ -264,7 +267,7 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
264267 offset += inst.len;
265268
266269 if (header.opcode == .OpDecorate) {
267 const target = inst[1];
270 const target_id = inst[1];
268271 const decoration: spec.Decoration = @fromBackingInt(inst[2]);
269272 if (decoration == .linkage_attributes) {
270273 const parameters = inst[3..];
......@@ -273,7 +276,7 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
273276 const linkage: spec.LinkageType = @fromBackingInt(parameters[parameters.len - 1]);
274277 const gop = try flush.symbols.getOrPut(arena, name);
275278 if (!gop.found_existing) gop.value_ptr.* = .{
276 .id = ids[target],
279 .id = ids[target_id],
277280 .defined = false,
278281 .declaration = &.{},
279282 };
......@@ -285,19 +288,19 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
285288 }
286289 gop.value_ptr.defined = true;
287290 },
288 .import => try imports.put(gpa, target, @intCast(gop.index)),
291 .import => try imports.put(gpa, target_id, @intCast(gop.index)),
289292 .link_once_odr => {
290293 return diags.failParse(path.?, "unsupported linkage '{t}'", .{linkage});
291294 },
292295 }
293296
294 ids[target] = gop.value_ptr.id;
297 ids[target_id] = gop.value_ptr.id;
295298 continue;
296299 }
297300 }
298301
299302 const class = header.opcode.class();
300 const target: Word = switch (header.opcode) {
303 const target_id: Word = switch (header.opcode) {
301304 .OpName, .OpMemberName => inst[1],
302305 .OpDecorationGroup, .OpGroupDecorate, .OpGroupMemberDecorate => {
303306 return diags.failParse(path.?, "decoration groups are not supported", .{});
......@@ -305,32 +308,34 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
305308 else => if (class == .annotation) inst[1] else continue,
306309 };
307310 try annotations.append(gpa, .{
308 .target = target,
311 .target_id = target_id,
309312 .class = class,
310313 .start = start,
311314 .end = offset,
312315 });
313316 }
314317
318 var result_ids: std.ArrayList(Word) = .empty;
319 defer result_ids.deinit(gpa);
320
315321 const starts = try gpa.alloc(u32, mir.id_bound + 1);
316322 defer gpa.free(starts);
317323 @memset(starts, 0);
318324
319 for (annotations.items) |annotation| starts[annotation.target + 1] += 1;
325 for (annotations.items) |annotation| starts[annotation.target_id + 1] += 1;
320326 for (1..starts.len) |i| starts[i] += starts[i - 1];
321327
322 const cursors = try gpa.dupe(u32, starts);
323 defer gpa.free(cursors);
324 const by_target = try gpa.alloc(u32, annotations.items.len);
325 defer gpa.free(by_target);
326 for (annotations.items, 0..) |annotation, i| {
327 by_target[cursors[annotation.target]] = @intCast(i);
328 cursors[annotation.target] += 1;
328 const grouped_indices = try gpa.alloc(u32, annotations.items.len);
329 defer gpa.free(grouped_indices);
330 {
331 const next_slot = try gpa.dupe(u32, starts);
332 defer gpa.free(next_slot);
333 for (annotations.items, 0..) |annotation, i| {
334 grouped_indices[next_slot[annotation.target_id]] = @intCast(i);
335 next_slot[annotation.target_id] += 1;
336 }
329337 }
330338
331 var results: std.ArrayList(Word) = .empty;
332 defer results.deinit(gpa);
333
334339 offset = 0;
335340 while (offset < words.len) {
336341 const start = offset;
......@@ -340,25 +345,67 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
340345 input.mark += 1;
341346 flush.deps.clearRetainingCapacity();
342347
343 const result_index = header.opcode.resultIndex() orelse {
344 switch (header.opcode) {
345 .OpEntryPoint => {
346 try input.copy(flush, &flush.object_entry_points, start, offset, null);
347 },
348 .OpExecutionMode, .OpExecutionModeId => {
349 try input.copy(flush, &flush.object_execution_modes, start, offset, null);
350 },
351 .OpTypeForwardPointer => {
352 forward_pointers.set(inst[1]);
353 try input.copy(flush, &flush.globals, start, offset, null);
354 },
355 else => {},
356 }
357 continue;
348 const result_index = header.opcode.resultIndex() orelse switch (header.opcode) {
349 .OpCapability => {
350 const capability = std.enums.fromInt(spec.Capability, inst[1]) orelse {
351 return diags.failParse(path.?, "unknown capability '{d}'", .{inst[1]});
352 };
353
354 // TODO: this isn't a full validation. we probably have to automate this
355 // based on some generated info from SPIRV-Headers/SPIRV-Registry
356 const allowed = switch (capability) {
357 .linkage => true,
358 .shader => target.os.tag == .vulkan or target.os.tag == .opengl,
359 .kernel, .addresses => target.os.tag == .opencl,
360 .physical_storage_buffer_addresses => target.os.tag == .vulkan and target.cpu.arch == .spirv64,
361 else => if (stringToEnum(std.Target.spirv.Feature, @tagName(capability))) |f| target.cpu.has(.spirv, f) else false,
362 };
363 if (!allowed) return diags.failParse(
364 path.?,
365 "SPIR-V capability '{t}' is not enabled by the target feature set",
366 .{capability},
367 );
368 continue;
369 },
370 .OpExtension => {
371 const name = std.mem.sliceTo(std.mem.sliceAsBytes(inst[1..]), 0);
372 if (std.mem.eql(u8, name, "SPV_KHR_non_semantic_info")) {
373 flush.nonsemantic_info = true;
374 continue;
375 }
376
377 const allowed = allowed: {
378 if (stringToEnum(std.Target.spirv.Feature, name)) |f| {
379 break :allowed target.cpu.has(.spirv, f);
380 }
381 break :allowed true;
382 };
383 if (!allowed) return diags.failParse(
384 path.?,
385 "SPIR-V extension '{s}' is not enabled by the target feature set",
386 .{name},
387 );
388 continue;
389 },
390 .OpEntryPoint => {
391 try input.copy(flush, &flush.object_entry_points, start, offset, null);
392 continue;
393 },
394 .OpExecutionMode, .OpExecutionModeId => {
395 try input.copy(flush, &flush.object_execution_modes, start, offset, null);
396 continue;
397 },
398 .OpTypeForwardPointer => {
399 forward_pointers.set(inst[1]);
400 try input.copy(flush, &flush.globals, start, offset, null);
401 continue;
402 },
403 else => continue,
358404 };
359 const result = inst[result_index];
360 results.clearRetainingCapacity();
361 try results.append(gpa, result);
405
406 const result_id = inst[result_index];
407 result_ids.clearRetainingCapacity();
408 try result_ids.append(gpa, result_id);
362409
363410 var has_body = false;
364411 if (header.opcode == .OpFunction) {
......@@ -366,16 +413,16 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
366413 const body: spec.InstructionHeader = @bitCast(words[offset]);
367414 if (body.opcode == .OpLabel) has_body = true;
368415 if (body.opcode.resultIndex()) |index| {
369 try results.append(gpa, words[offset + index]);
416 try result_ids.append(gpa, words[offset + index]);
370417 }
371418 offset += body.word_count;
372419 if (body.opcode == .OpFunctionEnd) break;
373420 }
374421 }
375422
376 for (results.items) |own| input.marks[own] = input.mark;
423 for (result_ids.items) |own| input.marks[own] = input.mark;
377424
378 if (imports.get(result)) |symbol_index| {
425 if (imports.get(result_id)) |symbol_index| {
379426 flush.scratch.reset();
380427 try input.copy(flush, &flush.scratch, start, offset, null);
381428 const declaration = try arena.dupe(Word, flush.scratch.instructions.items);
......@@ -385,30 +432,30 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
385432
386433 switch (header.opcode) {
387434 .OpFunction => if (!has_body) return diags.failParse(path.?, "function declaration without linkage", .{}),
388 .OpVariable => try flush.variables.put(arena, ids[result], @fromBackingInt(inst[3])),
389 else => if (!forward_pointers.isSet(result)) {
435 .OpVariable => try flush.variables.put(arena, ids[result_id], @fromBackingInt(inst[3])),
436 else => if (!forward_pointers.isSet(result_id)) {
390437 flush.scratch.reset();
391438 try input.copy(flush, &flush.scratch, start, offset, start + result_index);
392 for (by_target[starts[result]..starts[result + 1]]) |index| {
439 for (grouped_indices[starts[result_id]..starts[result_id + 1]]) |index| {
393440 const annotation = annotations.items[index];
394441 if (annotation.class != .annotation) continue;
395 const target = annotation.start + 1;
396 try input.copy(flush, &flush.scratch, annotation.start, annotation.end, target);
442 const target_id = annotation.start + 1;
443 try input.copy(flush, &flush.scratch, annotation.start, annotation.end, target_id);
397444 }
398445
399446 const key = std.mem.sliceAsBytes(flush.scratch.instructions.items);
400447 const gop = try flush.interned.getOrPut(arena, key);
401448 if (gop.found_existing) {
402 ids[result] = gop.value_ptr.*;
449 ids[result_id] = gop.value_ptr.*;
403450 continue;
404451 }
405452 gop.key_ptr.* = try arena.dupe(u8, key);
406 gop.value_ptr.* = ids[result];
453 gop.value_ptr.* = ids[result_id];
407454 },
408455 }
409456
410 for (results.items) |target| {
411 for (by_target[starts[target]..starts[target + 1]]) |index| {
457 for (result_ids.items) |target_id| {
458 for (grouped_indices[starts[target_id]..starts[target_id + 1]]) |index| {
412459 const annotation = annotations.items[index];
413460 const section = switch (annotation.class) {
414461 .debug => &flush.names,
......@@ -428,7 +475,7 @@ fn load(flush: *Flush, mir: *const Mir, path: ?Path) link.Error!void {
428475 try input.copy(flush, section, start, offset, null);
429476 if (header.opcode == .OpFunction) {
430477 const references = try arena.dupe(Id, flush.deps.items);
431 try flush.function_references.put(arena, ids[result], references);
478 try flush.function_references.put(arena, ids[result_id], references);
432479 }
433480 }
434481
......@@ -468,25 +515,25 @@ fn emitCapabilities(flush: *Flush, out: *Section) link.Error!void {
468515 try out.emit(gpa, .OpCapability, .{ .capability = .linkage });
469516 }
470517
471 inline for (@typeInfo(spec.Capability).@"enum".field_names) |name| {
472 if (@hasField(std.Target.spirv.Feature, name)) {
473 const feature = @field(std.Target.spirv.Feature, name);
474 if (target.cpu.has(.spirv, feature)) {
475 try out.emit(gpa, .OpCapability, .{ .capability = @field(spec.Capability, name) });
476 }
477 }
518 for (std.enums.values(spec.Capability)) |capability| {
519 const feature = stringToEnum(std.Target.spirv.Feature, @tagName(capability)) orelse continue;
520 if (!target.cpu.has(.spirv, feature)) continue;
521 try out.emit(gpa, .OpCapability, .{ .capability = capability });
478522 }
479523
480524 if (target.cpu.arch == .spirv64 and target.os.tag == .vulkan) {
481525 try out.emit(gpa, .OpExtension, .{ .name = "SPV_KHR_physical_storage_buffer" });
482526 }
483527
484 inline for (@typeInfo(spec.Extension).@"enum".field_names) |name| {
485 if (comptime std.mem.startsWith(u8, name, "v1_")) continue;
486 if (@hasField(std.Target.spirv.Feature, name)) {
487 const feature = @field(std.Target.spirv.Feature, name);
488 if (target.cpu.has(.spirv, feature)) try out.emit(gpa, .OpExtension, .{ .name = name });
489 }
528 for (std.enums.values(spec.Extension)) |extension| {
529 const name = @tagName(extension);
530 if (std.mem.startsWith(u8, name, "v1_")) continue;
531 const feature = stringToEnum(std.Target.spirv.Feature, name) orelse continue;
532 if (!target.cpu.has(.spirv, feature)) continue;
533 try out.emit(gpa, .OpExtension, .{ .name = name });
534 }
535 if (flush.nonsemantic_info) {
536 try out.emit(gpa, .OpExtension, .{ .name = "SPV_KHR_non_semantic_info" });
490537 }
491538}
492539
src/main.zig+29
......@@ -3582,6 +3582,35 @@ fn buildOutputType(
35823582 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
35833583 }
35843584
3585 if (build_options.have_llvm and create_module.resolved_options.use_llvm and target.cpu.arch.isSpirV()) {
3586 // LLVM has no way to set this non-globally other
3587 // than SPIRVTranslate() but it cannot emits assembly.
3588 var extensions: std.ArrayList(u8) = .empty;
3589 defer extensions.deinit(gpa);
3590 try extensions.appendSlice(gpa, "--spirv-ext=");
3591
3592 var has_extension = false;
3593 if (!root_mod.strip) {
3594 try extensions.appendSlice(gpa, "+SPV_KHR_non_semantic_info");
3595 has_extension = true;
3596 }
3597
3598 for (std.enums.values(std.Target.spirv.Feature)) |feature| {
3599 const name = @tagName(feature);
3600 if (!mem.startsWith(u8, name, "SPV_")) continue;
3601 if (!target.cpu.has(.spirv, feature)) continue;
3602 if (mem.eql(u8, name, "SPV_KHR_variable_pointers") and target.cpu.has(.spirv, .v1_3)) continue;
3603
3604 if (has_extension) try extensions.append(gpa, ',');
3605 try extensions.append(gpa, '+');
3606 try extensions.appendSlice(gpa, name);
3607 has_extension = true;
3608 }
3609
3610 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", try extensions.toOwnedSliceSentinel(gpa, 0) };
3611 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
3612 }
3613
35853614 const clang_passthrough_mode = switch (arg_mode) {
35863615 .cc, .cpp, .translate_c => true,
35873616 else => false,