diff options
| author | Robin Voetter <robin@voetter.nl> | 2024-10-29 19:58:51 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 19:58:51 +0100 |
| commit | 3450809e3db14226b7f8e57bdfa7fde590e803d8 (patch) | |
| tree | a1100c41f7a5be7c9f608e7b0316a42523f2b36c /src/codegen/spirv.zig | |
| parent | 7025c06eb6102117ec472cac4586a3841bdb6fa8 (diff) | |
| parent | ae57f6fd07d1830f9179222207671d30d9f9d707 (diff) | |
| download | zig-3450809e3db14226b7f8e57bdfa7fde590e803d8.tar.gz zig-3450809e3db14226b7f8e57bdfa7fde590e803d8.zip | |
Merge pull request #21826 from Snektron/spirv-vulkan
spirv: vulkan setup
Diffstat (limited to 'src/codegen/spirv.zig')
| -rw-r--r-- | src/codegen/spirv.zig | 176 |
1 files changed, 144 insertions, 32 deletions
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 279b7d5056..46c23d7d53 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -169,6 +169,13 @@ pub const Object = struct { /// via the usual `intern_map` mechanism. ptr_types: PtrTypeMap = .{}, + /// For test declarations for Vulkan, we have to add a push constant with a pointer to a + /// buffer that we can use. We only need to generate this once, this holds the link information + /// related to that. + error_push_constant: ?struct { + push_constant_ptr: SpvModule.Decl.Index, + } = null, + pub fn init(gpa: Allocator) Object { return .{ .gpa = gpa, @@ -1640,13 +1647,18 @@ const NavGen = struct { comptime assert(zig_call_abi_ver == 3); switch (fn_info.cc) { - .auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {}, - else => @panic("TODO"), + .auto, + .spirv_kernel, + .spirv_fragment, + .spirv_vertex, + .spirv_device, + => {}, + else => unreachable, } - // TODO: Put this somewhere in Sema.zig - if (fn_info.is_var_args) - return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); + // Guaranteed by callConvSupportsVarArgs, there are nog SPIR-V CCs which support + // varargs. + assert(!fn_info.is_var_args); // Note: Logic is different from functionType(). const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len); @@ -1838,11 +1850,16 @@ const NavGen = struct { return switch (as) { .generic => switch (target.os.tag) { .vulkan => .Private, - else => .Generic, + .opencl => .Generic, + else => unreachable, }, .shared => .Workgroup, .local => .Private, - .global => .CrossWorkgroup, + .global => switch (target.os.tag) { + .opencl => .CrossWorkgroup, + .vulkan => .PhysicalStorageBuffer, + else => unreachable, + }, .constant => .UniformConstant, .input => .Input, .output => .Output, @@ -2898,30 +2915,118 @@ const NavGen = struct { .flags = .{ .address_space = .global }, }); const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct); - const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty}); - - const test_id = self.spv.declPtr(spv_test_decl_index).result_id; const spv_decl_index = try self.spv.allocDecl(.func); const kernel_id = self.spv.declPtr(spv_decl_index).result_id; + // for some reason we don't need to decorate the push constant here... + try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); + + const section = &self.spv.sections.functions; + + const target = self.getTarget(); - const error_id = self.spv.allocId(); const p_error_id = self.spv.allocId(); + switch (target.os.tag) { + .opencl => { + const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty}); - const section = &self.spv.sections.functions; - try section.emit(self.spv.gpa, .OpFunction, .{ - .id_result_type = try self.resolveType(Type.void, .direct), - .id_result = kernel_id, - .function_control = .{}, - .function_type = kernel_proto_ty_id, - }); - try section.emit(self.spv.gpa, .OpFunctionParameter, .{ - .id_result_type = ptr_anyerror_ty_id, - .id_result = p_error_id, - }); - try section.emit(self.spv.gpa, .OpLabel, .{ - .id_result = self.spv.allocId(), - }); + try section.emit(self.spv.gpa, .OpFunction, .{ + .id_result_type = try self.resolveType(Type.void, .direct), + .id_result = kernel_id, + .function_control = .{}, + .function_type = kernel_proto_ty_id, + }); + + try section.emit(self.spv.gpa, .OpFunctionParameter, .{ + .id_result_type = ptr_anyerror_ty_id, + .id_result = p_error_id, + }); + + try section.emit(self.spv.gpa, .OpLabel, .{ + .id_result = self.spv.allocId(), + }); + }, + .vulkan => { + const ptr_ptr_anyerror_ty_id = self.spv.allocId(); + try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ + .id_result = ptr_ptr_anyerror_ty_id, + .storage_class = .PushConstant, + .type = ptr_anyerror_ty_id, + }); + + if (self.object.error_push_constant == null) { + const spv_err_decl_index = try self.spv.allocDecl(.global); + try self.spv.declareDeclDeps(spv_err_decl_index, &.{}); + + const push_constant_struct_ty_id = try self.spv.structType( + &.{ptr_anyerror_ty_id}, + &.{"error_out_ptr"}, + ); + try self.spv.decorate(push_constant_struct_ty_id, .Block); + try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } }); + + const ptr_push_constant_struct_ty_id = self.spv.allocId(); + try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ + .id_result = ptr_push_constant_struct_ty_id, + .storage_class = .PushConstant, + .type = push_constant_struct_ty_id, + }); + + try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{ + .id_result_type = ptr_push_constant_struct_ty_id, + .id_result = self.spv.declPtr(spv_err_decl_index).result_id, + .storage_class = .PushConstant, + }); + + self.object.error_push_constant = .{ + .push_constant_ptr = spv_err_decl_index, + }; + } + + try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{ + .entry_point = kernel_id, + .mode = .{ .LocalSize = .{ + .x_size = 1, + .y_size = 1, + .z_size = 1, + } }, + }); + + const kernel_proto_ty_id = try self.functionType(Type.void, &.{}); + try section.emit(self.spv.gpa, .OpFunction, .{ + .id_result_type = try self.resolveType(Type.void, .direct), + .id_result = kernel_id, + .function_control = .{}, + .function_type = kernel_proto_ty_id, + }); + try section.emit(self.spv.gpa, .OpLabel, .{ + .id_result = self.spv.allocId(), + }); + + const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr; + const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id; + + const zero_id = try self.constInt(Type.u32, 0, .direct); + // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use + // a load. + const tmp = self.spv.allocId(); + try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ + .id_result_type = ptr_ptr_anyerror_ty_id, + .id_result = tmp, + .base = push_constant_id, + .indexes = &.{zero_id}, + }); + try section.emit(self.spv.gpa, .OpLoad, .{ + .id_result_type = ptr_anyerror_ty_id, + .id_result = p_error_id, + .pointer = tmp, + }); + }, + else => unreachable, + } + + const test_id = self.spv.declPtr(spv_test_decl_index).result_id; + const error_id = self.spv.allocId(); try section.emit(self.spv.gpa, .OpFunctionCall, .{ .id_result_type = anyerror_ty_id, .id_result = error_id, @@ -2931,17 +3036,25 @@ const NavGen = struct { try section.emit(self.spv.gpa, .OpStore, .{ .pointer = p_error_id, .object = error_id, + .memory_access = .{ + .Aligned = .{ .literal_integer = @sizeOf(u16) }, + }, }); try section.emit(self.spv.gpa, .OpReturn, {}); try section.emit(self.spv.gpa, .OpFunctionEnd, {}); - try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); - // Just generate a quick other name because the intel runtime crashes when the entry- // point name is the same as a different OpName. const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); defer self.gpa.free(test_name); - try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel); + + const execution_mode: spec.ExecutionModel = switch (target.os.tag) { + .vulkan => .GLCompute, + .opencl => .Kernel, + else => unreachable, + }; + + try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode); } fn genNav(self: *NavGen, do_codegen: bool) !void { @@ -2969,11 +3082,10 @@ const NavGen = struct { try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ .id_result_type = return_ty_id, .id_result = result_id, - .function_control = switch (fn_info.cc) { - .@"inline" => .{ .Inline = true }, - else => .{}, - }, .function_type = prototype_ty_id, + // Note: the backend will never be asked to generate an inline function + // (this is handled in sema), so we don't need to set function_control here. + .function_control = .{}, }); comptime assert(zig_call_abi_ver == 3); |
