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 {
11011101 is_thread_local: bool = false,
11021102 is_dll_import: bool = false,
11031103 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
11051116 pub const Relocation = enum(u1) {
11061117 /// 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);
2020pub extern const vertex_index: u32 addrspace(.input);
2121pub 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
4723pub const ExecutionMode = union(Tag) {
4824 /// Sets origin of the framebuffer to the upper-left corner
4925 origin_upper_left,
src/InternPool.zig+24-1
......@@ -2316,6 +2316,7 @@ pub const Key = union(enum) {
23162316 is_threadlocal: bool,
23172317 is_dll_import: bool,
23182318 relocation: std.builtin.ExternOptions.Relocation,
2319 decoration: ?std.builtin.ExternOptions.Decoration,
23192320 is_const: bool,
23202321 alignment: Alignment,
23212322 @"addrspace": std.builtin.AddressSpace,
......@@ -6075,6 +6076,8 @@ pub const Tag = enum(u8) {
60756076 flags: Flags,
60766077 owner_nav: Nav.Index,
60776078 zir_index: TrackedInst.Index,
6079 location_or_descriptor_set: u32,
6080 descriptor_binding: u32,
60786081
60796082 pub const Flags = packed struct(u32) {
60806083 linkage: std.builtin.GlobalLinkage,
......@@ -6083,10 +6086,22 @@ pub const Tag = enum(u8) {
60836086 is_dll_import: bool,
60846087 relocation: std.builtin.ExternOptions.Relocation,
60856088 source: Source,
6086 _: u24 = 0,
6089 decoration_type: DecorationType,
6090 _: u22 = 0,
60876091
60886092 pub const Source = enum(u1) { builtin, syntax };
6093 pub const DecorationType = enum(u2) { none, location, descriptor };
60896094 };
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 }
60906105 };
60916106
60926107 /// Trailing:
......@@ -7444,6 +7459,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
74447459 .is_threadlocal = extra.flags.is_threadlocal,
74457460 .is_dll_import = extra.flags.is_dll_import,
74467461 .relocation = extra.flags.relocation,
7462 .decoration = extra.decoration(),
74477463 .is_const = nav.status.fully_resolved.is_const,
74487464 .alignment = nav.status.fully_resolved.alignment,
74497465 .@"addrspace" = nav.status.fully_resolved.@"addrspace",
......@@ -9346,15 +9362,22 @@ pub fn getExtern(
93469362 .@"linksection" = .none,
93479363 .@"addrspace" = key.@"addrspace",
93489364 }) 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 };
93499369 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
93509370 .ty = key.ty,
93519371 .lib_name = key.lib_name,
9372 .location_or_descriptor_set = location_or_descriptor_set,
9373 .descriptor_binding = descriptor_binding,
93529374 .flags = .{
93539375 .linkage = key.linkage,
93549376 .visibility = key.visibility,
93559377 .is_threadlocal = key.is_threadlocal,
93569378 .is_dll_import = key.is_dll_import,
93579379 .relocation = key.relocation,
9380 .decoration_type = decoration_type,
93589381 .source = key.source,
93599382 },
93609383 .zir_index = key.zir_index,
src/Sema.zig+8
......@@ -25916,6 +25916,7 @@ fn resolveExternOptions(
2591625916 is_thread_local: bool,
2591725917 is_dll_import: bool,
2591825918 relocation: std.builtin.ExternOptions.Relocation,
25919 decoration: ?std.builtin.ExternOptions.Decoration,
2591925920} {
2592025921 const pt = sema.pt;
2592125922 const zcu = pt.zcu;
......@@ -25935,6 +25936,7 @@ fn resolveExternOptions(
2593525936 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2593625937 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2593725938 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
2593925941 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
2594025942 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
......@@ -25969,6 +25971,10 @@ fn resolveExternOptions(
2596925971 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
2597025972 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
2597225978 if (name.len == 0) {
2597325979 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
2597425980 }
......@@ -25985,6 +25991,7 @@ fn resolveExternOptions(
2598525991 .is_thread_local = is_thread_local_val.toBool(),
2598625992 .is_dll_import = is_dll_import_val.toBool(),
2598725993 .relocation = relocation,
25994 .decoration = decoration,
2598825995 };
2598925996}
2599025997
......@@ -26044,6 +26051,7 @@ fn zirBuiltinExtern(
2604426051 .is_threadlocal = options.is_thread_local,
2604526052 .is_dll_import = options.is_dll_import,
2604626053 .relocation = options.relocation,
26054 .decoration = options.decoration,
2604726055 .is_const = ptr_info.flags.is_const,
2604826056 .alignment = ptr_info.flags.alignment,
2604926057 .@"addrspace" = ptr_info.flags.address_space,
src/Zcu.zig+3
......@@ -2113,6 +2113,7 @@ pub const SrcLoc = struct {
21132113 .init_field_thread_local,
21142114 .init_field_dll_import,
21152115 .init_field_relocation,
2116 .init_field_decoration,
21162117 => |builtin_call_node| {
21172118 const wanted = switch (src_loc.lazy) {
21182119 .init_field_name => "name",
......@@ -2126,6 +2127,7 @@ pub const SrcLoc = struct {
21262127 .init_field_thread_local => "thread_local",
21272128 .init_field_dll_import => "dll_import",
21282129 .init_field_relocation => "relocation",
2130 .init_field_decoration => "decoration",
21292131 else => unreachable,
21302132 };
21312133 const tree = try src_loc.file_scope.getTree(zcu);
......@@ -2606,6 +2608,7 @@ pub const LazySrcLoc = struct {
26062608 init_field_thread_local: Ast.Node.Offset,
26072609 init_field_dll_import: Ast.Node.Offset,
26082610 init_field_relocation: Ast.Node.Offset,
2611 init_field_decoration: Ast.Node.Offset,
26092612 /// The source location points to the value of an item in a specific
26102613 /// case of a `switch`.
26112614 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
12691269 .visibility = .default,
12701270 .is_dll_import = false,
12711271 .relocation = .any,
1272 .decoration = null,
12721273 .is_const = is_const,
12731274 .alignment = modifiers.alignment,
12741275 .@"addrspace" = modifiers.@"addrspace",
......@@ -3490,6 +3491,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
34903491 .visibility = e.visibility,
34913492 .is_dll_import = e.is_dll_import,
34923493 .relocation = e.relocation,
3494 .decoration = e.decoration,
34933495 .alignment = e.alignment,
34943496 .@"addrspace" = e.@"addrspace",
34953497 .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 }