authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-05-07 15:03:42+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-05-21 12:57:40+03:30
log0901328f12e7ea3d05dc1d5b4a588e595c4bc0bc
tree79320b4a4b280636ec5ff0d85d1bf2414c64a0e3
parentfca5f3602d697bd3de6a36d4504703693133144c

spirv: write error value in an storage buffer


6 files changed, 96 insertions(+), 75 deletions(-)

lib/std/Target.zig+1-1
......@@ -2014,7 +2014,7 @@ pub const Cpu = struct {
20142014 .global, .local, .shared => is_gpu,
20152015 .constant => is_gpu and (context == null or context == .constant),
20162016 .param => is_nvptx,
2017 .input, .output, .uniform, .push_constant, .storage_buffer => is_spirv,
2017 .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,
20182018 };
20192019 }
20202020};
lib/std/Target/spirv.zig+7-1
......@@ -21,6 +21,7 @@ pub const Feature = enum {
2121 generic_pointer,
2222 vector16,
2323 shader,
24 variable_pointers,
2425 physical_storage_buffer,
2526};
2627
......@@ -129,6 +130,11 @@ pub const all_features = blk: {
129130 .description = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",
130131 .dependencies = featureSet(&[_]Feature{.v1_0}),
131132 };
133 result[@intFromEnum(Feature.variable_pointers)] = .{
134 .llvm_name = null,
135 .description = "Enable SPV_KHR_variable_pointers extension and the (VariablePointers, VariablePointersStorageBuffer) capabilities",
136 .dependencies = featureSet(&[_]Feature{.v1_0}),
137 };
132138 const ti = @typeInfo(Feature);
133139 for (&result, 0..) |*elem, i| {
134140 elem.index = i;
......@@ -147,7 +153,7 @@ pub const cpu = struct {
147153 pub const vulkan_v1_2: CpuModel = .{
148154 .name = "vulkan_v1_2",
149155 .llvm_name = null,
150 .features = featureSet(&[_]Feature{ .v1_5, .shader, .physical_storage_buffer }),
156 .features = featureSet(&[_]Feature{ .v1_5, .shader }),
151157 };
152158
153159 pub const opencl_v2: CpuModel = .{
lib/std/builtin.zig+1
......@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {
531531 uniform,
532532 push_constant,
533533 storage_buffer,
534 physical_storage_buffer,
534535
535536 // AVR address spaces.
536537 flash,
src/codegen/spirv.zig+60-53
......@@ -169,12 +169,10 @@ pub const Object = struct {
169169 /// via the usual `intern_map` mechanism.
170170 ptr_types: PtrTypeMap = .{},
171171
172 /// For test declarations for Vulkan, we have to add a push constant with a pointer to a
173 /// buffer that we can use. We only need to generate this once, this holds the link information
172 /// For test declarations for Vulkan, we have to add a buffer.
173 /// We only need to generate this once, this holds the link information
174174 /// related to that.
175 error_push_constant: ?struct {
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
175 error_buffer: ?SpvModule.Decl.Index = null,
178176
179177 pub fn init(gpa: Allocator, target: std.Target) Object {
180178 return .{
......@@ -1739,15 +1737,34 @@ const NavGen = struct {
17391737 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
17401738 return switch (as) {
17411739 .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function,
1740 .global => {
1741 if (self.spv.hasFeature(.kernel)) return .CrossWorkgroup;
1742 return .StorageBuffer;
1743 },
1744 .push_constant => {
1745 assert(self.spv.hasFeature(.shader));
1746 return .PushConstant;
1747 },
1748 .output => {
1749 assert(self.spv.hasFeature(.shader));
1750 return .Output;
1751 },
1752 .uniform => {
1753 assert(self.spv.hasFeature(.shader));
1754 return .Uniform;
1755 },
1756 .storage_buffer => {
1757 assert(self.spv.hasFeature(.shader));
1758 return .StorageBuffer;
1759 },
1760 .physical_storage_buffer => {
1761 assert(self.spv.hasFeature(.physical_storage_buffer));
1762 return .PhysicalStorageBuffer;
1763 },
1764 .constant => .UniformConstant,
17421765 .shared => .Workgroup,
17431766 .local => .Function,
1744 .global => if (self.spv.hasFeature(.shader)) .PhysicalStorageBuffer else .CrossWorkgroup,
1745 .constant => .UniformConstant,
1746 .push_constant => .PushConstant,
17471767 .input => .Input,
1748 .output => .Output,
1749 .uniform => .Uniform,
1750 .storage_buffer => .StorageBuffer,
17511768 .gs,
17521769 .fs,
17531770 .ss,
......@@ -2713,38 +2730,32 @@ const NavGen = struct {
27132730 });
27142731 },
27152732 .vulkan, .opengl => {
2716 const ptr_ptr_anyerror_ty_id = self.spv.allocId();
2717 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2718 .id_result = ptr_ptr_anyerror_ty_id,
2719 .storage_class = .PushConstant,
2720 .type = ptr_anyerror_ty_id,
2721 });
2722
2723 if (self.object.error_push_constant == null) {
2733 if (self.object.error_buffer == null) {
27242734 const spv_err_decl_index = try self.spv.allocDecl(.global);
27252735 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
27262736
2727 const push_constant_struct_ty_id = self.spv.allocId();
2728 try self.spv.structType(push_constant_struct_ty_id, &.{ptr_anyerror_ty_id}, &.{"error_out_ptr"});
2729 try self.spv.decorate(push_constant_struct_ty_id, .Block);
2730 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
2737 const buffer_struct_ty_id = self.spv.allocId();
2738 try self.spv.structType(buffer_struct_ty_id, &.{anyerror_ty_id}, &.{"error_out"});
2739 try self.spv.decorate(buffer_struct_ty_id, .Block);
2740 try self.spv.decorateMember(buffer_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
27312741
2732 const ptr_push_constant_struct_ty_id = self.spv.allocId();
2742 const ptr_buffer_struct_ty_id = self.spv.allocId();
27332743 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2734 .id_result = ptr_push_constant_struct_ty_id,
2735 .storage_class = .PushConstant,
2736 .type = push_constant_struct_ty_id,
2744 .id_result = ptr_buffer_struct_ty_id,
2745 .storage_class = self.spvStorageClass(.global),
2746 .type = buffer_struct_ty_id,
27372747 });
27382748
2749 const buffer_struct_id = self.spv.declPtr(spv_err_decl_index).result_id;
27392750 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2740 .id_result_type = ptr_push_constant_struct_ty_id,
2741 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,
2742 .storage_class = .PushConstant,
2751 .id_result_type = ptr_buffer_struct_ty_id,
2752 .id_result = buffer_struct_id,
2753 .storage_class = self.spvStorageClass(.global),
27432754 });
2755 try self.spv.decorate(buffer_struct_id, .{ .DescriptorSet = .{ .descriptor_set = 0 } });
2756 try self.spv.decorate(buffer_struct_id, .{ .Binding = .{ .binding_point = 0 } });
27442757
2745 self.object.error_push_constant = .{
2746 .push_constant_ptr = spv_err_decl_index,
2747 };
2758 self.object.error_buffer = spv_err_decl_index;
27482759 }
27492760
27502761 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
......@@ -2767,24 +2778,16 @@ const NavGen = struct {
27672778 .id_result = self.spv.allocId(),
27682779 });
27692780
2770 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
2771 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
2781 const spv_err_decl_index = self.object.error_buffer.?;
2782 const buffer_id = self.spv.declPtr(spv_err_decl_index).result_id;
27722783 try decl_deps.append(spv_err_decl_index);
27732784
27742785 const zero_id = try self.constInt(Type.u32, 0);
2775 // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
2776 // a load.
2777 const tmp = self.spv.allocId();
27782786 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
2779 .id_result_type = ptr_ptr_anyerror_ty_id,
2780 .id_result = tmp,
2781 .base = push_constant_id,
2782 .indexes = &.{zero_id},
2783 });
2784 try section.emit(self.spv.gpa, .OpLoad, .{
27852787 .id_result_type = ptr_anyerror_ty_id,
27862788 .id_result = p_error_id,
2787 .pointer = tmp,
2789 .base = buffer_id,
2790 .indexes = &.{zero_id},
27882791 });
27892792 },
27902793 else => unreachable,
......@@ -4562,7 +4565,8 @@ const NavGen = struct {
45624565 const field_int_id = blk: {
45634566 if (field_ty.isPtrAtRuntime(zcu)) {
45644567 assert(self.spv.hasFeature(.addresses) or
4565 (self.spv.hasFeature(.physical_storage_buffer) and field_ty.ptrAddressSpace(zcu) == .storage_buffer));
4568 (self.spv.hasFeature(.physical_storage_buffer) and
4569 field_ty.ptrAddressSpace(zcu) == .storage_buffer));
45664570 break :blk try self.intFromPtr(field_id);
45674571 }
45684572 break :blk try self.bitCast(field_int_ty, field_ty, field_id);
......@@ -4969,13 +4973,16 @@ const NavGen = struct {
49694973 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
49704974 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
49714975 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
4972 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
4973 const active_pl_ptr_id = self.spv.allocId();
4974 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
4975 .id_result_type = active_pl_ptr_ty_id,
4976 .id_result = active_pl_ptr_id,
4977 .operand = pl_ptr_id,
4978 });
4976 const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: {
4977 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
4978 const active_pl_ptr_id = self.spv.allocId();
4979 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
4980 .id_result_type = active_pl_ptr_ty_id,
4981 .id_result = active_pl_ptr_id,
4982 .operand = pl_ptr_id,
4983 });
4984 break :blk active_pl_ptr_id;
4985 } else pl_ptr_id;
49794986
49804987 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});
49814988 } else {
src/codegen/spirv/Module.zig+14-12
......@@ -350,6 +350,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
350350 .vector16 => try self.addCapability(.Vector16),
351351 // Shader
352352 .shader => try self.addCapability(.Shader),
353 .variable_pointers => {
354 try self.addExtension("SPV_KHR_variable_pointers");
355 try self.addCapability(.VariablePointersStorageBuffer);
356 try self.addCapability(.VariablePointers);
357 },
353358 .physical_storage_buffer => {
354359 try self.addExtension("SPV_KHR_physical_storage_buffer");
355360 try self.addCapability(.PhysicalStorageBufferAddresses);
......@@ -364,20 +369,17 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
364369 // Emit memory model
365370 const addressing_model: spec.AddressingModel = blk: {
366371 if (self.hasFeature(.shader)) {
367 break :blk switch (self.target.cpu.arch) {
368 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
369 .spirv64 => .PhysicalStorageBuffer64,
370 else => unreachable,
371 };
372 } else if (self.hasFeature(.kernel)) {
373 break :blk switch (self.target.cpu.arch) {
374 .spirv32 => .Physical32,
375 .spirv64 => .Physical64,
376 else => unreachable,
377 };
372 assert(self.target.cpu.arch == .spirv64);
373 if (self.hasFeature(.physical_storage_buffer)) break :blk .PhysicalStorageBuffer64;
374 break :blk .Logical;
378375 }
379376
380 unreachable;
377 assert(self.hasFeature(.kernel));
378 break :blk switch (self.target.cpu.arch) {
379 .spirv32 => .Physical32,
380 .spirv64 => .Physical64,
381 else => unreachable,
382 };
381383 };
382384 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{
383385 .addressing_model = addressing_model,
src/target.zig+13-8
......@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(
501501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns
502502/// for a particular architecture and address space wether such pointers are logical.
503503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {
504 if (target.os.tag != .vulkan) {
505 return false;
506 }
504 if (target.os.tag != .vulkan) return false;
507505
508506 return switch (as) {
509507 // TODO: Vulkan doesn't support pointers in the generic address space, we
510508 // should remove this case but this requires a change in defaultAddressSpace().
511509 // For now, at least disable them from being regarded as physical.
512510 .generic => true,
513 // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real
514 // pointers.
511 // For now, all global pointers are represented using StorageBuffer or CrossWorkgroup,
512 // so these are real pointers.
515513 .global => false,
516 // TODO: Allowed with VK_KHR_variable_pointers.
517 .shared => true,
518 .constant, .local, .input, .output, .uniform, .push_constant, .storage_buffer => true,
514 .physical_storage_buffer => false,
515 .shared => !target.cpu.features.isEnabled(@intFromEnum(std.Target.spirv.Feature.variable_pointers)),
516 .constant,
517 .local,
518 .input,
519 .output,
520 .uniform,
521 .push_constant,
522 .storage_buffer,
523 => true,
519524 else => unreachable,
520525 };
521526}