authorgravatar for ashpil@pm.meashpil <ashpil@pm.me> 2025-10-05 15:20:50-07:00
committergravatar for ashpil@pm.meashpil <ashpil@pm.me> 2025-12-22 10:00:35-08:00
log24e7500c33fe5945039670fe6ced312dad381d37
treed55b5a6e6c31ac68b2ded4d10c361b774567ee7d
parent3af842f0e89125e65a87e5752234bf7e0051aa12
signaturebadge-check Signed by SSH key SHA256:TBbRxKT+n8Ud4FlUC5p0egketgOWptxhC233zHNyGxA

`@extern`: add support for spir-v locations and descriptors


6 files changed, 73 insertions(+), 8 deletions(-)

lib/std/builtin.zig+11
......@@ -1098,6 +1098,17 @@ pub const ExternOptions = struct {
10981098 is_thread_local: bool = false,
10991099 is_dll_import: bool = false,
11001100 relocation: Relocation = .any,
1101 decoration: ?Decoration = null,
1102
1103 pub const Decoration = union(enum) {
1104 location: u32,
1105 descriptor: Descriptor,
1106
1107 pub const Descriptor = struct {
1108 binding: u32,
1109 set: u32,
1110 };
1111 };
11011112
11021113 pub const Relocation = enum(u1) {
11031114 /// Any type of relocation is allowed.
src/InternPool.zig+24-1
......@@ -2247,6 +2247,7 @@ pub const Key = union(enum) {
22472247 is_threadlocal: bool,
22482248 is_dll_import: bool,
22492249 relocation: std.builtin.ExternOptions.Relocation,
2250 decoration: ?std.builtin.ExternOptions.Decoration,
22502251 is_const: bool,
22512252 alignment: Alignment,
22522253 @"addrspace": std.builtin.AddressSpace,
......@@ -6006,6 +6007,8 @@ pub const Tag = enum(u8) {
60066007 flags: Flags,
60076008 owner_nav: Nav.Index,
60086009 zir_index: TrackedInst.Index,
6010 location_or_descriptor_set: u32,
6011 descriptor_binding: u32,
60096012
60106013 pub const Flags = packed struct(u32) {
60116014 linkage: std.builtin.GlobalLinkage,
......@@ -6014,10 +6017,22 @@ pub const Tag = enum(u8) {
60146017 is_dll_import: bool,
60156018 relocation: std.builtin.ExternOptions.Relocation,
60166019 source: Source,
6017 _: u24 = 0,
6020 decoration_type: DecorationType,
6021 _: u22 = 0,
60186022
60196023 pub const Source = enum(u1) { builtin, syntax };
6024 pub const DecorationType = enum(u2) { none, location, descriptor };
60206025 };
6026
6027 pub fn decoration(self: Extern) ?std.builtin.ExternOptions.Decoration {
6028 return switch (self.flags.decoration_type) {
6029 .none => null,
6030 .location => std.builtin.ExternOptions.Decoration{
6031 .location = self.location_or_descriptor_set,
6032 },
6033 .descriptor => std.builtin.ExternOptions.Decoration{ .descriptor = .{ .set = self.location_or_descriptor_set, .binding = self.descriptor_binding } },
6034 };
6035 }
60216036 };
60226037
60236038 /// Trailing:
......@@ -7375,6 +7390,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
73757390 .is_threadlocal = extra.flags.is_threadlocal,
73767391 .is_dll_import = extra.flags.is_dll_import,
73777392 .relocation = extra.flags.relocation,
7393 .decoration = extra.decoration(),
73787394 .is_const = nav.status.fully_resolved.is_const,
73797395 .alignment = nav.status.fully_resolved.alignment,
73807396 .@"addrspace" = nav.status.fully_resolved.@"addrspace",
......@@ -9264,15 +9280,22 @@ pub fn getExtern(
92649280 .@"linksection" = .none,
92659281 .@"addrspace" = key.@"addrspace",
92669282 }) catch unreachable; // capacity asserted above
9283 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {
9284 .location => |location| .{ Tag.Extern.Flags.DecorationType.location, location, undefined },
9285 .descriptor => |descriptor| .{ Tag.Extern.Flags.DecorationType.descriptor, descriptor.binding, descriptor.set },
9286 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };
92679287 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
92689288 .ty = key.ty,
92699289 .lib_name = key.lib_name,
9290 .location_or_descriptor_set = location_or_descriptor_set,
9291 .descriptor_binding = descriptor_binding,
92709292 .flags = .{
92719293 .linkage = key.linkage,
92729294 .visibility = key.visibility,
92739295 .is_threadlocal = key.is_threadlocal,
92749296 .is_dll_import = key.is_dll_import,
92759297 .relocation = key.relocation,
9298 .decoration_type = decoration_type,
92769299 .source = key.source,
92779300 },
92789301 .zir_index = key.zir_index,
src/Sema.zig+8
......@@ -25714,6 +25714,7 @@ fn resolveExternOptions(
2571425714 is_thread_local: bool,
2571525715 is_dll_import: bool,
2571625716 relocation: std.builtin.ExternOptions.Relocation,
25717 decoration: ?std.builtin.ExternOptions.Decoration,
2571725718} {
2571825719 const pt = sema.pt;
2571925720 const zcu = pt.zcu;
......@@ -25730,6 +25731,7 @@ fn resolveExternOptions(
2573025731 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2573125732 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2573225733 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25734 const decoration_src = block.src(.{ .init_field_decoration = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2573325735
2573425736 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
2573525737 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
......@@ -25764,6 +25766,10 @@ fn resolveExternOptions(
2576425766 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
2576525767 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
2576625768
25769 const decoration_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "decoration", .no_embedded_nulls), decoration_src);
25770 const decoration_val = try sema.resolveConstDefinedValue(block, decoration_src, decoration_ref, .{ .simple = .extern_options });
25771 const decoration = try sema.interpretBuiltinType(block, decoration_src, decoration_val, ?std.builtin.ExternOptions.Decoration);
25772
2576725773 if (name.len == 0) {
2576825774 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
2576925775 }
......@@ -25780,6 +25786,7 @@ fn resolveExternOptions(
2578025786 .is_thread_local = is_thread_local_val.toBool(),
2578125787 .is_dll_import = is_dll_import_val.toBool(),
2578225788 .relocation = relocation,
25789 .decoration = decoration,
2578325790 };
2578425791}
2578525792
......@@ -25839,6 +25846,7 @@ fn zirBuiltinExtern(
2583925846 .is_threadlocal = options.is_thread_local,
2584025847 .is_dll_import = options.is_dll_import,
2584125848 .relocation = options.relocation,
25849 .decoration = options.decoration,
2584225850 .is_const = ptr_info.flags.is_const,
2584325851 .alignment = ptr_info.flags.alignment,
2584425852 .@"addrspace" = ptr_info.flags.address_space,
src/Zcu.zig+3
......@@ -2107,6 +2107,7 @@ pub const SrcLoc = struct {
21072107 .init_field_thread_local,
21082108 .init_field_dll_import,
21092109 .init_field_relocation,
2110 .init_field_decoration,
21102111 => |builtin_call_node| {
21112112 const wanted = switch (src_loc.lazy) {
21122113 .init_field_name => "name",
......@@ -2120,6 +2121,7 @@ pub const SrcLoc = struct {
21202121 .init_field_thread_local => "thread_local",
21212122 .init_field_dll_import => "dll_import",
21222123 .init_field_relocation => "relocation",
2124 .init_field_decoration => "decoration",
21232125 else => unreachable,
21242126 };
21252127 const tree = try src_loc.file_scope.getTree(zcu);
......@@ -2600,6 +2602,7 @@ pub const LazySrcLoc = struct {
26002602 init_field_thread_local: Ast.Node.Offset,
26012603 init_field_dll_import: Ast.Node.Offset,
26022604 init_field_relocation: Ast.Node.Offset,
2605 init_field_decoration: Ast.Node.Offset,
26032606 /// The source location points to the value of an item in a specific
26042607 /// case of a `switch`.
26052608 switch_case_item: SwitchItem,
src/Zcu/PerThread.zig+2
......@@ -1259,6 +1259,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
12591259 .visibility = .default,
12601260 .is_dll_import = false,
12611261 .relocation = .any,
1262 .decoration = null,
12621263 .is_const = is_const,
12631264 .alignment = modifiers.alignment,
12641265 .@"addrspace" = modifiers.@"addrspace",
......@@ -3458,6 +3459,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
34583459 .visibility = e.visibility,
34593460 .is_dll_import = e.is_dll_import,
34603461 .relocation = e.relocation,
3462 .decoration = e.decoration,
34613463 .alignment = e.alignment,
34623464 .@"addrspace" = e.@"addrspace",
34633465 .zir_index = e.zir_index,
src/codegen/spirv/CodeGen.zig+25-7
......@@ -254,7 +254,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
254254 try cg.module.debugName(func_result_id, nav.fqn.toSlice(ip));
255255 },
256256 .global => {
257 assert(ip.indexToKey(val.toIntern()) == .@"extern");
257 const key = ip.indexToKey(val.toIntern()).@"extern";
258258
259259 const storage_class = cg.module.storageClass(nav.getAddrspace());
260260 assert(storage_class != .generic); // These should be instance globals
......@@ -277,14 +277,32 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
277277 }
278278 }
279279
280 switch (ip.indexToKey(ty.toIntern())) {
281 .func_type, .opaque_type => {},
282 else => {
283 try cg.module.decorate(ptr_ty_id, .{
284 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },
280 try cg.module.decorate(ptr_ty_id, .{
281 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },
282 });
283
284 if (key.decoration) |decoration| switch (decoration) {
285 .location => |location| {
286 if (storage_class != .output and storage_class != .input and storage_class != .uniform_constant) {
287 return cg.fail("storage class must be one of (output, input, uniform_constant) but is {s}", .{@tagName(storage_class)});
288 }
289 try cg.module.decorate(result_id, .{
290 .location = .{ .location = location },
285291 });
286292 },
287 }
293 .descriptor => |descriptor| {
294 if (storage_class != .storage_buffer and storage_class != .uniform and storage_class != .uniform_constant) {
295 return cg.fail("storage class must be one of (storage_buffer, uniform, uniform_constant) but is {s}", .{@tagName(storage_class)});
296 }
297 try cg.module.decorate(result_id, .{
298 .binding = .{ .binding_point = descriptor.binding },
299 });
300
301 try cg.module.decorate(result_id, .{
302 .descriptor_set = .{ .descriptor_set = descriptor.set },
303 });
304 },
305 };
288306 },
289307 else => {},
290308 }