authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-06 23:44:10+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-06 23:44:10+01:00
log5e002910df398278f743a04fe40cf34320482cd1
treef7770516b3bf0e2605df1aeb329c83bdd976cb87
parentc475f1fcd547a93d9e75770900b2fa0c45b43de3
parent0ea79c85b2b22266a0b25edf04c392add8387d5b

Merge pull request '`@extern`: add support for SPIR-V locations and descriptors' (#30570) from ashpil/zig:extern-bindings-locations into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30570 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

7 files changed, 73 insertions(+), 32 deletions(-)

lib/std/builtin.zig+11
...@@ -1101,6 +1101,17 @@ pub const ExternOptions = struct {...@@ -1101,6 +1101,17 @@ pub const ExternOptions = struct {
1101 is_thread_local: bool = false,1101 is_thread_local: bool = false,
1102 is_dll_import: bool = false,1102 is_dll_import: bool = false,
1103 relocation: Relocation = .any,1103 relocation: Relocation = .any,
1104 decoration: ?Decoration = null,
1105
1106 pub const Decoration = union(enum) {
1107 location: u32,
1108 descriptor: Descriptor,
1109
1110 pub const Descriptor = struct {
1111 binding: u32,
1112 set: u32,
1113 };
1114 };
11041115
1105 pub const Relocation = enum(u1) {1116 pub const Relocation = enum(u1) {
1106 /// Any type of relocation is allowed.1117 /// Any type of relocation is allowed.
lib/std/gpu.zig-24
...@@ -20,30 +20,6 @@ pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input);...@@ -20,30 +20,6 @@ pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input);
20pub extern const vertex_index: u32 addrspace(.input);20pub extern const vertex_index: u32 addrspace(.input);
21pub extern const instance_index: u32 addrspace(.input);21pub extern const instance_index: u32 addrspace(.input);
2222
23/// Forms the main linkage for `input` and `output` address spaces.
24/// `ptr` must be a reference to variable or struct field.
25pub fn location(comptime ptr: anytype, comptime loc: u32) void {
26 asm volatile (
27 \\OpDecorate %ptr Location $loc
28 :
29 : [ptr] "" (ptr),
30 [loc] "c" (loc),
31 );
32}
33
34/// Forms the main linkage for `input` and `output` address spaces.
35/// `ptr` must be a reference to variable or struct field.
36pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) void {
37 asm volatile (
38 \\OpDecorate %ptr DescriptorSet $set
39 \\OpDecorate %ptr Binding $bind
40 :
41 : [ptr] "" (ptr),
42 [set] "c" (set),
43 [bind] "c" (bind),
44 );
45}
46
47pub const ExecutionMode = union(Tag) {23pub const ExecutionMode = union(Tag) {
48 /// Sets origin of the framebuffer to the upper-left corner24 /// Sets origin of the framebuffer to the upper-left corner
49 origin_upper_left,25 origin_upper_left,
src/InternPool.zig+24-1
...@@ -2316,6 +2316,7 @@ pub const Key = union(enum) {...@@ -2316,6 +2316,7 @@ pub const Key = union(enum) {
2316 is_threadlocal: bool,2316 is_threadlocal: bool,
2317 is_dll_import: bool,2317 is_dll_import: bool,
2318 relocation: std.builtin.ExternOptions.Relocation,2318 relocation: std.builtin.ExternOptions.Relocation,
2319 decoration: ?std.builtin.ExternOptions.Decoration,
2319 is_const: bool,2320 is_const: bool,
2320 alignment: Alignment,2321 alignment: Alignment,
2321 @"addrspace": std.builtin.AddressSpace,2322 @"addrspace": std.builtin.AddressSpace,
...@@ -6075,6 +6076,8 @@ pub const Tag = enum(u8) {...@@ -6075,6 +6076,8 @@ pub const Tag = enum(u8) {
6075 flags: Flags,6076 flags: Flags,
6076 owner_nav: Nav.Index,6077 owner_nav: Nav.Index,
6077 zir_index: TrackedInst.Index,6078 zir_index: TrackedInst.Index,
6079 location_or_descriptor_set: u32,
6080 descriptor_binding: u32,
60786081
6079 pub const Flags = packed struct(u32) {6082 pub const Flags = packed struct(u32) {
6080 linkage: std.builtin.GlobalLinkage,6083 linkage: std.builtin.GlobalLinkage,
...@@ -6083,10 +6086,22 @@ pub const Tag = enum(u8) {...@@ -6083,10 +6086,22 @@ pub const Tag = enum(u8) {
6083 is_dll_import: bool,6086 is_dll_import: bool,
6084 relocation: std.builtin.ExternOptions.Relocation,6087 relocation: std.builtin.ExternOptions.Relocation,
6085 source: Source,6088 source: Source,
6086 _: u24 = 0,6089 decoration_type: DecorationType,
6090 _: u22 = 0,
60876091
6088 pub const Source = enum(u1) { builtin, syntax };6092 pub const Source = enum(u1) { builtin, syntax };
6093 pub const DecorationType = enum(u2) { none, location, descriptor };
6089 };6094 };
6095
6096 pub fn decoration(self: Extern) ?std.builtin.ExternOptions.Decoration {
6097 return switch (self.flags.decoration_type) {
6098 .none => null,
6099 .location => std.builtin.ExternOptions.Decoration{
6100 .location = self.location_or_descriptor_set,
6101 },
6102 .descriptor => std.builtin.ExternOptions.Decoration{ .descriptor = .{ .set = self.location_or_descriptor_set, .binding = self.descriptor_binding } },
6103 };
6104 }
6090 };6105 };
60916106
6092 /// Trailing:6107 /// Trailing:
...@@ -7444,6 +7459,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -7444,6 +7459,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
7444 .is_threadlocal = extra.flags.is_threadlocal,7459 .is_threadlocal = extra.flags.is_threadlocal,
7445 .is_dll_import = extra.flags.is_dll_import,7460 .is_dll_import = extra.flags.is_dll_import,
7446 .relocation = extra.flags.relocation,7461 .relocation = extra.flags.relocation,
7462 .decoration = extra.decoration(),
7447 .is_const = nav.status.fully_resolved.is_const,7463 .is_const = nav.status.fully_resolved.is_const,
7448 .alignment = nav.status.fully_resolved.alignment,7464 .alignment = nav.status.fully_resolved.alignment,
7449 .@"addrspace" = nav.status.fully_resolved.@"addrspace",7465 .@"addrspace" = nav.status.fully_resolved.@"addrspace",
...@@ -9346,15 +9362,22 @@ pub fn getExtern(...@@ -9346,15 +9362,22 @@ pub fn getExtern(
9346 .@"linksection" = .none,9362 .@"linksection" = .none,
9347 .@"addrspace" = key.@"addrspace",9363 .@"addrspace" = key.@"addrspace",
9348 }) catch unreachable; // capacity asserted above9364 }) catch unreachable; // capacity asserted above
9365 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {
9366 .location => |location| .{ Tag.Extern.Flags.DecorationType.location, location, undefined },
9367 .descriptor => |descriptor| .{ Tag.Extern.Flags.DecorationType.descriptor, descriptor.binding, descriptor.set },
9368 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };
9349 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{9369 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
9350 .ty = key.ty,9370 .ty = key.ty,
9351 .lib_name = key.lib_name,9371 .lib_name = key.lib_name,
9372 .location_or_descriptor_set = location_or_descriptor_set,
9373 .descriptor_binding = descriptor_binding,
9352 .flags = .{9374 .flags = .{
9353 .linkage = key.linkage,9375 .linkage = key.linkage,
9354 .visibility = key.visibility,9376 .visibility = key.visibility,
9355 .is_threadlocal = key.is_threadlocal,9377 .is_threadlocal = key.is_threadlocal,
9356 .is_dll_import = key.is_dll_import,9378 .is_dll_import = key.is_dll_import,
9357 .relocation = key.relocation,9379 .relocation = key.relocation,
9380 .decoration_type = decoration_type,
9358 .source = key.source,9381 .source = key.source,
9359 },9382 },
9360 .zir_index = key.zir_index,9383 .zir_index = key.zir_index,
src/Sema.zig+8
...@@ -25916,6 +25916,7 @@ fn resolveExternOptions(...@@ -25916,6 +25916,7 @@ fn resolveExternOptions(
25916 is_thread_local: bool,25916 is_thread_local: bool,
25917 is_dll_import: bool,25917 is_dll_import: bool,
25918 relocation: std.builtin.ExternOptions.Relocation,25918 relocation: std.builtin.ExternOptions.Relocation,
25919 decoration: ?std.builtin.ExternOptions.Decoration,
25919} {25920} {
25920 const pt = sema.pt;25921 const pt = sema.pt;
25921 const zcu = pt.zcu;25922 const zcu = pt.zcu;
...@@ -25935,6 +25936,7 @@ fn resolveExternOptions(...@@ -25935,6 +25936,7 @@ fn resolveExternOptions(
25935 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });25936 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25936 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });25937 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25937 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });25938 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25939 const decoration_src = block.src(.{ .init_field_decoration = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2593825940
25939 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);25941 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
25940 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });25942 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
...@@ -25969,6 +25971,10 @@ fn resolveExternOptions(...@@ -25969,6 +25971,10 @@ fn resolveExternOptions(
25969 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });25971 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
25970 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);25972 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
2597125973
25974 const decoration_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "decoration", .no_embedded_nulls), decoration_src);
25975 const decoration_val = try sema.resolveConstDefinedValue(block, decoration_src, decoration_ref, .{ .simple = .extern_options });
25976 const decoration = try sema.interpretBuiltinType(block, decoration_src, decoration_val, ?std.builtin.ExternOptions.Decoration);
25977
25972 if (name.len == 0) {25978 if (name.len == 0) {
25973 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});25979 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
25974 }25980 }
...@@ -25985,6 +25991,7 @@ fn resolveExternOptions(...@@ -25985,6 +25991,7 @@ fn resolveExternOptions(
25985 .is_thread_local = is_thread_local_val.toBool(),25991 .is_thread_local = is_thread_local_val.toBool(),
25986 .is_dll_import = is_dll_import_val.toBool(),25992 .is_dll_import = is_dll_import_val.toBool(),
25987 .relocation = relocation,25993 .relocation = relocation,
25994 .decoration = decoration,
25988 };25995 };
25989}25996}
2599025997
...@@ -26044,6 +26051,7 @@ fn zirBuiltinExtern(...@@ -26044,6 +26051,7 @@ fn zirBuiltinExtern(
26044 .is_threadlocal = options.is_thread_local,26051 .is_threadlocal = options.is_thread_local,
26045 .is_dll_import = options.is_dll_import,26052 .is_dll_import = options.is_dll_import,
26046 .relocation = options.relocation,26053 .relocation = options.relocation,
26054 .decoration = options.decoration,
26047 .is_const = ptr_info.flags.is_const,26055 .is_const = ptr_info.flags.is_const,
26048 .alignment = ptr_info.flags.alignment,26056 .alignment = ptr_info.flags.alignment,
26049 .@"addrspace" = ptr_info.flags.address_space,26057 .@"addrspace" = ptr_info.flags.address_space,
src/Zcu.zig+3
...@@ -2113,6 +2113,7 @@ pub const SrcLoc = struct {...@@ -2113,6 +2113,7 @@ pub const SrcLoc = struct {
2113 .init_field_thread_local,2113 .init_field_thread_local,
2114 .init_field_dll_import,2114 .init_field_dll_import,
2115 .init_field_relocation,2115 .init_field_relocation,
2116 .init_field_decoration,
2116 => |builtin_call_node| {2117 => |builtin_call_node| {
2117 const wanted = switch (src_loc.lazy) {2118 const wanted = switch (src_loc.lazy) {
2118 .init_field_name => "name",2119 .init_field_name => "name",
...@@ -2126,6 +2127,7 @@ pub const SrcLoc = struct {...@@ -2126,6 +2127,7 @@ pub const SrcLoc = struct {
2126 .init_field_thread_local => "thread_local",2127 .init_field_thread_local => "thread_local",
2127 .init_field_dll_import => "dll_import",2128 .init_field_dll_import => "dll_import",
2128 .init_field_relocation => "relocation",2129 .init_field_relocation => "relocation",
2130 .init_field_decoration => "decoration",
2129 else => unreachable,2131 else => unreachable,
2130 };2132 };
2131 const tree = try src_loc.file_scope.getTree(zcu);2133 const tree = try src_loc.file_scope.getTree(zcu);
...@@ -2606,6 +2608,7 @@ pub const LazySrcLoc = struct {...@@ -2606,6 +2608,7 @@ pub const LazySrcLoc = struct {
2606 init_field_thread_local: Ast.Node.Offset,2608 init_field_thread_local: Ast.Node.Offset,
2607 init_field_dll_import: Ast.Node.Offset,2609 init_field_dll_import: Ast.Node.Offset,
2608 init_field_relocation: Ast.Node.Offset,2610 init_field_relocation: Ast.Node.Offset,
2611 init_field_decoration: Ast.Node.Offset,
2609 /// The source location points to the value of an item in a specific2612 /// The source location points to the value of an item in a specific
2610 /// case of a `switch`.2613 /// case of a `switch`.
2611 switch_case_item: SwitchItem,2614 switch_case_item: SwitchItem,
src/Zcu/PerThread.zig+2
...@@ -1269,6 +1269,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr...@@ -1269,6 +1269,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1269 .visibility = .default,1269 .visibility = .default,
1270 .is_dll_import = false,1270 .is_dll_import = false,
1271 .relocation = .any,1271 .relocation = .any,
1272 .decoration = null,
1272 .is_const = is_const,1273 .is_const = is_const,
1273 .alignment = modifiers.alignment,1274 .alignment = modifiers.alignment,
1274 .@"addrspace" = modifiers.@"addrspace",1275 .@"addrspace" = modifiers.@"addrspace",
...@@ -3490,6 +3491,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V...@@ -3490,6 +3491,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
3490 .visibility = e.visibility,3491 .visibility = e.visibility,
3491 .is_dll_import = e.is_dll_import,3492 .is_dll_import = e.is_dll_import,
3492 .relocation = e.relocation,3493 .relocation = e.relocation,
3494 .decoration = e.decoration,
3493 .alignment = e.alignment,3495 .alignment = e.alignment,
3494 .@"addrspace" = e.@"addrspace",3496 .@"addrspace" = e.@"addrspace",
3495 .zir_index = e.zir_index,3497 .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 }