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 {...@@ -2014,7 +2014,7 @@ pub const Cpu = struct {
2014 .global, .local, .shared => is_gpu,2014 .global, .local, .shared => is_gpu,
2015 .constant => is_gpu and (context == null or context == .constant),2015 .constant => is_gpu and (context == null or context == .constant),
2016 .param => is_nvptx,2016 .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,
2018 };2018 };
2019 }2019 }
2020};2020};
lib/std/Target/spirv.zig+7-1
...@@ -21,6 +21,7 @@ pub const Feature = enum {...@@ -21,6 +21,7 @@ pub const Feature = enum {
21 generic_pointer,21 generic_pointer,
22 vector16,22 vector16,
23 shader,23 shader,
24 variable_pointers,
24 physical_storage_buffer,25 physical_storage_buffer,
25};26};
2627
...@@ -129,6 +130,11 @@ pub const all_features = blk: {...@@ -129,6 +130,11 @@ pub const all_features = blk: {
129 .description = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",130 .description = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",
130 .dependencies = featureSet(&[_]Feature{.v1_0}),131 .dependencies = featureSet(&[_]Feature{.v1_0}),
131 };132 };
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 };
132 const ti = @typeInfo(Feature);138 const ti = @typeInfo(Feature);
133 for (&result, 0..) |*elem, i| {139 for (&result, 0..) |*elem, i| {
134 elem.index = i;140 elem.index = i;
...@@ -147,7 +153,7 @@ pub const cpu = struct {...@@ -147,7 +153,7 @@ pub const cpu = struct {
147 pub const vulkan_v1_2: CpuModel = .{153 pub const vulkan_v1_2: CpuModel = .{
148 .name = "vulkan_v1_2",154 .name = "vulkan_v1_2",
149 .llvm_name = null,155 .llvm_name = null,
150 .features = featureSet(&[_]Feature{ .v1_5, .shader, .physical_storage_buffer }),156 .features = featureSet(&[_]Feature{ .v1_5, .shader }),
151 };157 };
152158
153 pub const opencl_v2: CpuModel = .{159 pub const opencl_v2: CpuModel = .{
lib/std/builtin.zig+1
...@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {...@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {
531 uniform,531 uniform,
532 push_constant,532 push_constant,
533 storage_buffer,533 storage_buffer,
534 physical_storage_buffer,
534535
535 // AVR address spaces.536 // AVR address spaces.
536 flash,537 flash,
src/codegen/spirv.zig+60-53
...@@ -169,12 +169,10 @@ pub const Object = struct {...@@ -169,12 +169,10 @@ pub const Object = struct {
169 /// via the usual `intern_map` mechanism.169 /// via the usual `intern_map` mechanism.
170 ptr_types: PtrTypeMap = .{},170 ptr_types: PtrTypeMap = .{},
171171
172 /// For test declarations for Vulkan, we have to add a push constant with a pointer to a172 /// For test declarations for Vulkan, we have to add a buffer.
173 /// buffer that we can use. We only need to generate this once, this holds the link information173 /// We only need to generate this once, this holds the link information
174 /// related to that.174 /// related to that.
175 error_push_constant: ?struct {175 error_buffer: ?SpvModule.Decl.Index = null,
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
178176
179 pub fn init(gpa: Allocator, target: std.Target) Object {177 pub fn init(gpa: Allocator, target: std.Target) Object {
180 return .{178 return .{
...@@ -1739,15 +1737,34 @@ const NavGen = struct {...@@ -1739,15 +1737,34 @@ const NavGen = struct {
1739 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {1737 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
1740 return switch (as) {1738 return switch (as) {
1741 .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function,1739 .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,
1742 .shared => .Workgroup,1765 .shared => .Workgroup,
1743 .local => .Function,1766 .local => .Function,
1744 .global => if (self.spv.hasFeature(.shader)) .PhysicalStorageBuffer else .CrossWorkgroup,
1745 .constant => .UniformConstant,
1746 .push_constant => .PushConstant,
1747 .input => .Input,1767 .input => .Input,
1748 .output => .Output,
1749 .uniform => .Uniform,
1750 .storage_buffer => .StorageBuffer,
1751 .gs,1768 .gs,
1752 .fs,1769 .fs,
1753 .ss,1770 .ss,
...@@ -2713,38 +2730,32 @@ const NavGen = struct {...@@ -2713,38 +2730,32 @@ const NavGen = struct {
2713 });2730 });
2714 },2731 },
2715 .vulkan, .opengl => {2732 .vulkan, .opengl => {
2716 const ptr_ptr_anyerror_ty_id = self.spv.allocId();2733 if (self.object.error_buffer == null) {
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) {
2724 const spv_err_decl_index = try self.spv.allocDecl(.global);2734 const spv_err_decl_index = try self.spv.allocDecl(.global);
2725 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});2735 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
27262736
2727 const push_constant_struct_ty_id = self.spv.allocId();2737 const buffer_struct_ty_id = self.spv.allocId();
2728 try self.spv.structType(push_constant_struct_ty_id, &.{ptr_anyerror_ty_id}, &.{"error_out_ptr"});2738 try self.spv.structType(buffer_struct_ty_id, &.{anyerror_ty_id}, &.{"error_out"});
2729 try self.spv.decorate(push_constant_struct_ty_id, .Block);2739 try self.spv.decorate(buffer_struct_ty_id, .Block);
2730 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });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();
2733 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{2743 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2734 .id_result = ptr_push_constant_struct_ty_id,2744 .id_result = ptr_buffer_struct_ty_id,
2735 .storage_class = .PushConstant,2745 .storage_class = self.spvStorageClass(.global),
2736 .type = push_constant_struct_ty_id,2746 .type = buffer_struct_ty_id,
2737 });2747 });
27382748
2749 const buffer_struct_id = self.spv.declPtr(spv_err_decl_index).result_id;
2739 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{2750 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2740 .id_result_type = ptr_push_constant_struct_ty_id,2751 .id_result_type = ptr_buffer_struct_ty_id,
2741 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,2752 .id_result = buffer_struct_id,
2742 .storage_class = .PushConstant,2753 .storage_class = self.spvStorageClass(.global),
2743 });2754 });
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 = .{2758 self.object.error_buffer = spv_err_decl_index;
2746 .push_constant_ptr = spv_err_decl_index,
2747 };
2748 }2759 }
27492760
2750 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{2761 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
...@@ -2767,24 +2778,16 @@ const NavGen = struct {...@@ -2767,24 +2778,16 @@ const NavGen = struct {
2767 .id_result = self.spv.allocId(),2778 .id_result = self.spv.allocId(),
2768 });2779 });
27692780
2770 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;2781 const spv_err_decl_index = self.object.error_buffer.?;
2771 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;2782 const buffer_id = self.spv.declPtr(spv_err_decl_index).result_id;
2772 try decl_deps.append(spv_err_decl_index);2783 try decl_deps.append(spv_err_decl_index);
27732784
2774 const zero_id = try self.constInt(Type.u32, 0);2785 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();
2778 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{2786 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, .{
2785 .id_result_type = ptr_anyerror_ty_id,2787 .id_result_type = ptr_anyerror_ty_id,
2786 .id_result = p_error_id,2788 .id_result = p_error_id,
2787 .pointer = tmp,2789 .base = buffer_id,
2790 .indexes = &.{zero_id},
2788 });2791 });
2789 },2792 },
2790 else => unreachable,2793 else => unreachable,
...@@ -4562,7 +4565,8 @@ const NavGen = struct {...@@ -4562,7 +4565,8 @@ const NavGen = struct {
4562 const field_int_id = blk: {4565 const field_int_id = blk: {
4563 if (field_ty.isPtrAtRuntime(zcu)) {4566 if (field_ty.isPtrAtRuntime(zcu)) {
4564 assert(self.spv.hasFeature(.addresses) or4567 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));
4566 break :blk try self.intFromPtr(field_id);4570 break :blk try self.intFromPtr(field_id);
4567 }4571 }
4568 break :blk try self.bitCast(field_int_ty, field_ty, field_id);4572 break :blk try self.bitCast(field_int_ty, field_ty, field_id);
...@@ -4969,13 +4973,16 @@ const NavGen = struct {...@@ -4969,13 +4973,16 @@ const NavGen = struct {
4969 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4973 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4970 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);4974 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
4971 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});4975 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);4976 const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: {
4973 const active_pl_ptr_id = self.spv.allocId();4977 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
4974 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{4978 const active_pl_ptr_id = self.spv.allocId();
4975 .id_result_type = active_pl_ptr_ty_id,4979 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
4976 .id_result = active_pl_ptr_id,4980 .id_result_type = active_pl_ptr_ty_id,
4977 .operand = pl_ptr_id,4981 .id_result = active_pl_ptr_id,
4978 });4982 .operand = pl_ptr_id,
4983 });
4984 break :blk active_pl_ptr_id;
4985 } else pl_ptr_id;
49794986
4980 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});4987 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});
4981 } else {4988 } else {
src/codegen/spirv/Module.zig+14-12
...@@ -350,6 +350,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {...@@ -350,6 +350,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
350 .vector16 => try self.addCapability(.Vector16),350 .vector16 => try self.addCapability(.Vector16),
351 // Shader351 // Shader
352 .shader => try self.addCapability(.Shader),352 .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 },
353 .physical_storage_buffer => {358 .physical_storage_buffer => {
354 try self.addExtension("SPV_KHR_physical_storage_buffer");359 try self.addExtension("SPV_KHR_physical_storage_buffer");
355 try self.addCapability(.PhysicalStorageBufferAddresses);360 try self.addCapability(.PhysicalStorageBufferAddresses);
...@@ -364,20 +369,17 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {...@@ -364,20 +369,17 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
364 // Emit memory model369 // Emit memory model
365 const addressing_model: spec.AddressingModel = blk: {370 const addressing_model: spec.AddressingModel = blk: {
366 if (self.hasFeature(.shader)) {371 if (self.hasFeature(.shader)) {
367 break :blk switch (self.target.cpu.arch) {372 assert(self.target.cpu.arch == .spirv64);
368 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.373 if (self.hasFeature(.physical_storage_buffer)) break :blk .PhysicalStorageBuffer64;
369 .spirv64 => .PhysicalStorageBuffer64,374 break :blk .Logical;
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 };
378 }375 }
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 };
381 };383 };
382 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{384 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{
383 .addressing_model = addressing_model,385 .addressing_model = addressing_model,
src/target.zig+13-8
...@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(...@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(
501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns
502/// for a particular architecture and address space wether such pointers are logical.502/// for a particular architecture and address space wether such pointers are logical.
503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {
504 if (target.os.tag != .vulkan) {504 if (target.os.tag != .vulkan) return false;
505 return false;
506 }
507505
508 return switch (as) {506 return switch (as) {
509 // TODO: Vulkan doesn't support pointers in the generic address space, we507 // TODO: Vulkan doesn't support pointers in the generic address space, we
510 // should remove this case but this requires a change in defaultAddressSpace().508 // should remove this case but this requires a change in defaultAddressSpace().
511 // For now, at least disable them from being regarded as physical.509 // For now, at least disable them from being regarded as physical.
512 .generic => true,510 .generic => true,
513 // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real511 // For now, all global pointers are represented using StorageBuffer or CrossWorkgroup,
514 // pointers.512 // so these are real pointers.
515 .global => false,513 .global => false,
516 // TODO: Allowed with VK_KHR_variable_pointers.514 .physical_storage_buffer => false,
517 .shared => true,515 .shared => !target.cpu.features.isEnabled(@intFromEnum(std.Target.spirv.Feature.variable_pointers)),
518 .constant, .local, .input, .output, .uniform, .push_constant, .storage_buffer => true,516 .constant,
517 .local,
518 .input,
519 .output,
520 .uniform,
521 .push_constant,
522 .storage_buffer,
523 => true,
519 else => unreachable,524 else => unreachable,
520 };525 };
521}526}