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 {...@@ -1098,6 +1098,17 @@ pub const ExternOptions = struct {
1098 is_thread_local: bool = false,1098 is_thread_local: bool = false,
1099 is_dll_import: bool = false,1099 is_dll_import: bool = false,
1100 relocation: Relocation = .any,1100 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
1102 pub const Relocation = enum(u1) {1113 pub const Relocation = enum(u1) {
1103 /// Any type of relocation is allowed.1114 /// Any type of relocation is allowed.
src/InternPool.zig+24-1
...@@ -2247,6 +2247,7 @@ pub const Key = union(enum) {...@@ -2247,6 +2247,7 @@ pub const Key = union(enum) {
2247 is_threadlocal: bool,2247 is_threadlocal: bool,
2248 is_dll_import: bool,2248 is_dll_import: bool,
2249 relocation: std.builtin.ExternOptions.Relocation,2249 relocation: std.builtin.ExternOptions.Relocation,
2250 decoration: ?std.builtin.ExternOptions.Decoration,
2250 is_const: bool,2251 is_const: bool,
2251 alignment: Alignment,2252 alignment: Alignment,
2252 @"addrspace": std.builtin.AddressSpace,2253 @"addrspace": std.builtin.AddressSpace,
...@@ -6006,6 +6007,8 @@ pub const Tag = enum(u8) {...@@ -6006,6 +6007,8 @@ pub const Tag = enum(u8) {
6006 flags: Flags,6007 flags: Flags,
6007 owner_nav: Nav.Index,6008 owner_nav: Nav.Index,
6008 zir_index: TrackedInst.Index,6009 zir_index: TrackedInst.Index,
6010 location_or_descriptor_set: u32,
6011 descriptor_binding: u32,
60096012
6010 pub const Flags = packed struct(u32) {6013 pub const Flags = packed struct(u32) {
6011 linkage: std.builtin.GlobalLinkage,6014 linkage: std.builtin.GlobalLinkage,
...@@ -6014,10 +6017,22 @@ pub const Tag = enum(u8) {...@@ -6014,10 +6017,22 @@ pub const Tag = enum(u8) {
6014 is_dll_import: bool,6017 is_dll_import: bool,
6015 relocation: std.builtin.ExternOptions.Relocation,6018 relocation: std.builtin.ExternOptions.Relocation,
6016 source: Source,6019 source: Source,
6017 _: u24 = 0,6020 decoration_type: DecorationType,
6021 _: u22 = 0,
60186022
6019 pub const Source = enum(u1) { builtin, syntax };6023 pub const Source = enum(u1) { builtin, syntax };
6024 pub const DecorationType = enum(u2) { none, location, descriptor };
6020 };6025 };
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 }
6021 };6036 };
60226037
6023 /// Trailing:6038 /// Trailing:
...@@ -7375,6 +7390,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -7375,6 +7390,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
7375 .is_threadlocal = extra.flags.is_threadlocal,7390 .is_threadlocal = extra.flags.is_threadlocal,
7376 .is_dll_import = extra.flags.is_dll_import,7391 .is_dll_import = extra.flags.is_dll_import,
7377 .relocation = extra.flags.relocation,7392 .relocation = extra.flags.relocation,
7393 .decoration = extra.decoration(),
7378 .is_const = nav.status.fully_resolved.is_const,7394 .is_const = nav.status.fully_resolved.is_const,
7379 .alignment = nav.status.fully_resolved.alignment,7395 .alignment = nav.status.fully_resolved.alignment,
7380 .@"addrspace" = nav.status.fully_resolved.@"addrspace",7396 .@"addrspace" = nav.status.fully_resolved.@"addrspace",
...@@ -9264,15 +9280,22 @@ pub fn getExtern(...@@ -9264,15 +9280,22 @@ pub fn getExtern(
9264 .@"linksection" = .none,9280 .@"linksection" = .none,
9265 .@"addrspace" = key.@"addrspace",9281 .@"addrspace" = key.@"addrspace",
9266 }) catch unreachable; // capacity asserted above9282 }) 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 };
9267 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{9287 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
9268 .ty = key.ty,9288 .ty = key.ty,
9269 .lib_name = key.lib_name,9289 .lib_name = key.lib_name,
9290 .location_or_descriptor_set = location_or_descriptor_set,
9291 .descriptor_binding = descriptor_binding,
9270 .flags = .{9292 .flags = .{
9271 .linkage = key.linkage,9293 .linkage = key.linkage,
9272 .visibility = key.visibility,9294 .visibility = key.visibility,
9273 .is_threadlocal = key.is_threadlocal,9295 .is_threadlocal = key.is_threadlocal,
9274 .is_dll_import = key.is_dll_import,9296 .is_dll_import = key.is_dll_import,
9275 .relocation = key.relocation,9297 .relocation = key.relocation,
9298 .decoration_type = decoration_type,
9276 .source = key.source,9299 .source = key.source,
9277 },9300 },
9278 .zir_index = key.zir_index,9301 .zir_index = key.zir_index,
src/Sema.zig+8
...@@ -25714,6 +25714,7 @@ fn resolveExternOptions(...@@ -25714,6 +25714,7 @@ fn resolveExternOptions(
25714 is_thread_local: bool,25714 is_thread_local: bool,
25715 is_dll_import: bool,25715 is_dll_import: bool,
25716 relocation: std.builtin.ExternOptions.Relocation,25716 relocation: std.builtin.ExternOptions.Relocation,
25717 decoration: ?std.builtin.ExternOptions.Decoration,
25717} {25718} {
25718 const pt = sema.pt;25719 const pt = sema.pt;
25719 const zcu = pt.zcu;25720 const zcu = pt.zcu;
...@@ -25730,6 +25731,7 @@ fn resolveExternOptions(...@@ -25730,6 +25731,7 @@ fn resolveExternOptions(
25730 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });25731 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25731 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });25732 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25732 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });25733 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
25734 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);25736 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
25735 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });25737 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
...@@ -25764,6 +25766,10 @@ fn resolveExternOptions(...@@ -25764,6 +25766,10 @@ fn resolveExternOptions(
25764 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });25766 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
25765 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);25767 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
25767 if (name.len == 0) {25773 if (name.len == 0) {
25768 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});25774 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
25769 }25775 }
...@@ -25780,6 +25786,7 @@ fn resolveExternOptions(...@@ -25780,6 +25786,7 @@ fn resolveExternOptions(
25780 .is_thread_local = is_thread_local_val.toBool(),25786 .is_thread_local = is_thread_local_val.toBool(),
25781 .is_dll_import = is_dll_import_val.toBool(),25787 .is_dll_import = is_dll_import_val.toBool(),
25782 .relocation = relocation,25788 .relocation = relocation,
25789 .decoration = decoration,
25783 };25790 };
25784}25791}
2578525792
...@@ -25839,6 +25846,7 @@ fn zirBuiltinExtern(...@@ -25839,6 +25846,7 @@ fn zirBuiltinExtern(
25839 .is_threadlocal = options.is_thread_local,25846 .is_threadlocal = options.is_thread_local,
25840 .is_dll_import = options.is_dll_import,25847 .is_dll_import = options.is_dll_import,
25841 .relocation = options.relocation,25848 .relocation = options.relocation,
25849 .decoration = options.decoration,
25842 .is_const = ptr_info.flags.is_const,25850 .is_const = ptr_info.flags.is_const,
25843 .alignment = ptr_info.flags.alignment,25851 .alignment = ptr_info.flags.alignment,
25844 .@"addrspace" = ptr_info.flags.address_space,25852 .@"addrspace" = ptr_info.flags.address_space,
src/Zcu.zig+3
...@@ -2107,6 +2107,7 @@ pub const SrcLoc = struct {...@@ -2107,6 +2107,7 @@ pub const SrcLoc = struct {
2107 .init_field_thread_local,2107 .init_field_thread_local,
2108 .init_field_dll_import,2108 .init_field_dll_import,
2109 .init_field_relocation,2109 .init_field_relocation,
2110 .init_field_decoration,
2110 => |builtin_call_node| {2111 => |builtin_call_node| {
2111 const wanted = switch (src_loc.lazy) {2112 const wanted = switch (src_loc.lazy) {
2112 .init_field_name => "name",2113 .init_field_name => "name",
...@@ -2120,6 +2121,7 @@ pub const SrcLoc = struct {...@@ -2120,6 +2121,7 @@ pub const SrcLoc = struct {
2120 .init_field_thread_local => "thread_local",2121 .init_field_thread_local => "thread_local",
2121 .init_field_dll_import => "dll_import",2122 .init_field_dll_import => "dll_import",
2122 .init_field_relocation => "relocation",2123 .init_field_relocation => "relocation",
2124 .init_field_decoration => "decoration",
2123 else => unreachable,2125 else => unreachable,
2124 };2126 };
2125 const tree = try src_loc.file_scope.getTree(zcu);2127 const tree = try src_loc.file_scope.getTree(zcu);
...@@ -2600,6 +2602,7 @@ pub const LazySrcLoc = struct {...@@ -2600,6 +2602,7 @@ pub const LazySrcLoc = struct {
2600 init_field_thread_local: Ast.Node.Offset,2602 init_field_thread_local: Ast.Node.Offset,
2601 init_field_dll_import: Ast.Node.Offset,2603 init_field_dll_import: Ast.Node.Offset,
2602 init_field_relocation: Ast.Node.Offset,2604 init_field_relocation: Ast.Node.Offset,
2605 init_field_decoration: Ast.Node.Offset,
2603 /// The source location points to the value of an item in a specific2606 /// The source location points to the value of an item in a specific
2604 /// case of a `switch`.2607 /// case of a `switch`.
2605 switch_case_item: SwitchItem,2608 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...@@ -1259,6 +1259,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1259 .visibility = .default,1259 .visibility = .default,
1260 .is_dll_import = false,1260 .is_dll_import = false,
1261 .relocation = .any,1261 .relocation = .any,
1262 .decoration = null,
1262 .is_const = is_const,1263 .is_const = is_const,
1263 .alignment = modifiers.alignment,1264 .alignment = modifiers.alignment,
1264 .@"addrspace" = modifiers.@"addrspace",1265 .@"addrspace" = modifiers.@"addrspace",
...@@ -3458,6 +3459,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V...@@ -3458,6 +3459,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
3458 .visibility = e.visibility,3459 .visibility = e.visibility,
3459 .is_dll_import = e.is_dll_import,3460 .is_dll_import = e.is_dll_import,
3460 .relocation = e.relocation,3461 .relocation = e.relocation,
3462 .decoration = e.decoration,
3461 .alignment = e.alignment,3463 .alignment = e.alignment,
3462 .@"addrspace" = e.@"addrspace",3464 .@"addrspace" = e.@"addrspace",
3463 .zir_index = e.zir_index,3465 .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 {...@@ -254,7 +254,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
254 try cg.module.debugName(func_result_id, nav.fqn.toSlice(ip));254 try cg.module.debugName(func_result_id, nav.fqn.toSlice(ip));
255 },255 },
256 .global => {256 .global => {
257 assert(ip.indexToKey(val.toIntern()) == .@"extern");257 const key = ip.indexToKey(val.toIntern()).@"extern";
258258
259 const storage_class = cg.module.storageClass(nav.getAddrspace());259 const storage_class = cg.module.storageClass(nav.getAddrspace());
260 assert(storage_class != .generic); // These should be instance globals260 assert(storage_class != .generic); // These should be instance globals
...@@ -277,14 +277,32 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -277,14 +277,32 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
277 }277 }
278 }278 }
279279
280 switch (ip.indexToKey(ty.toIntern())) {280 try cg.module.decorate(ptr_ty_id, .{
281 .func_type, .opaque_type => {},281 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },
282 else => {282 });
283 try cg.module.decorate(ptr_ty_id, .{283
284 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },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 },
285 });291 });
286 },292 },
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 };
288 },306 },
289 else => {},307 else => {},
290 }308 }