authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-10-20 17:10:55+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-10-27 15:19:57+01:00
log39013619b943956f0c26422a01f026d845dc96a9
tree6b13949c46bd5ee219ddc9d8f3366b5df38e9792
parent7c6923136718aab50fc20a327e47c0c23517dda2
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: generate test entry points for vulkan


2 files changed, 133 insertions(+), 21 deletions(-)

src/codegen/spirv.zig+124-21
...@@ -169,6 +169,13 @@ pub const Object = struct {...@@ -169,6 +169,13 @@ 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 a
173 /// buffer that we can use. We only need to generate this once, this holds the link information
174 /// related to that.
175 error_push_constant: ?struct {
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
178
172 pub fn init(gpa: Allocator) Object {179 pub fn init(gpa: Allocator) Object {
173 return .{180 return .{
174 .gpa = gpa,181 .gpa = gpa,
...@@ -2908,30 +2915,118 @@ const NavGen = struct {...@@ -2908,30 +2915,118 @@ const NavGen = struct {
2908 .flags = .{ .address_space = .global },2915 .flags = .{ .address_space = .global },
2909 });2916 });
2910 const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct);2917 const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct);
2911 const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
2912
2913 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
29142918
2915 const spv_decl_index = try self.spv.allocDecl(.func);2919 const spv_decl_index = try self.spv.allocDecl(.func);
2916 const kernel_id = self.spv.declPtr(spv_decl_index).result_id;2920 const kernel_id = self.spv.declPtr(spv_decl_index).result_id;
2921 // for some reason we don't need to decorate the push constant here...
2922 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
2923
2924 const section = &self.spv.sections.functions;
2925
2926 const target = self.getTarget();
29172927
2918 const error_id = self.spv.allocId();
2919 const p_error_id = self.spv.allocId();2928 const p_error_id = self.spv.allocId();
2929 switch (target.os.tag) {
2930 .opencl => {
2931 const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
29202932
2921 const section = &self.spv.sections.functions;2933 try section.emit(self.spv.gpa, .OpFunction, .{
2922 try section.emit(self.spv.gpa, .OpFunction, .{2934 .id_result_type = try self.resolveType(Type.void, .direct),
2923 .id_result_type = try self.resolveType(Type.void, .direct),2935 .id_result = kernel_id,
2924 .id_result = kernel_id,2936 .function_control = .{},
2925 .function_control = .{},2937 .function_type = kernel_proto_ty_id,
2926 .function_type = kernel_proto_ty_id,2938 });
2927 });2939
2928 try section.emit(self.spv.gpa, .OpFunctionParameter, .{2940 try section.emit(self.spv.gpa, .OpFunctionParameter, .{
2929 .id_result_type = ptr_anyerror_ty_id,2941 .id_result_type = ptr_anyerror_ty_id,
2930 .id_result = p_error_id,2942 .id_result = p_error_id,
2931 });2943 });
2932 try section.emit(self.spv.gpa, .OpLabel, .{2944
2933 .id_result = self.spv.allocId(),2945 try section.emit(self.spv.gpa, .OpLabel, .{
2934 });2946 .id_result = self.spv.allocId(),
2947 });
2948 },
2949 .vulkan => {
2950 const ptr_ptr_anyerror_ty_id = self.spv.allocId();
2951 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2952 .id_result = ptr_ptr_anyerror_ty_id,
2953 .storage_class = .PushConstant,
2954 .type = ptr_anyerror_ty_id,
2955 });
2956
2957 if (self.object.error_push_constant == null) {
2958 const spv_err_decl_index = try self.spv.allocDecl(.global);
2959 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
2960
2961 const push_constant_struct_ty_id = try self.spv.structType(
2962 &.{ptr_anyerror_ty_id},
2963 &.{"error_out_ptr"},
2964 );
2965 try self.spv.decorate(push_constant_struct_ty_id, .Block);
2966 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
2967
2968 const ptr_push_constant_struct_ty_id = self.spv.allocId();
2969 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2970 .id_result = ptr_push_constant_struct_ty_id,
2971 .storage_class = .PushConstant,
2972 .type = push_constant_struct_ty_id,
2973 });
2974
2975 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2976 .id_result_type = ptr_push_constant_struct_ty_id,
2977 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,
2978 .storage_class = .PushConstant,
2979 });
2980
2981 self.object.error_push_constant = .{
2982 .push_constant_ptr = spv_err_decl_index,
2983 };
2984 }
2985
2986 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
2987 .entry_point = kernel_id,
2988 .mode = .{ .LocalSize = .{
2989 .x_size = 1,
2990 .y_size = 1,
2991 .z_size = 1,
2992 } },
2993 });
2994
2995 const kernel_proto_ty_id = try self.functionType(Type.void, &.{});
2996 try section.emit(self.spv.gpa, .OpFunction, .{
2997 .id_result_type = try self.resolveType(Type.void, .direct),
2998 .id_result = kernel_id,
2999 .function_control = .{},
3000 .function_type = kernel_proto_ty_id,
3001 });
3002 try section.emit(self.spv.gpa, .OpLabel, .{
3003 .id_result = self.spv.allocId(),
3004 });
3005
3006 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
3007 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
3008
3009 const zero_id = try self.constInt(Type.u32, 0, .direct);
3010 // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
3011 // a load.
3012 const tmp = self.spv.allocId();
3013 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
3014 .id_result_type = ptr_ptr_anyerror_ty_id,
3015 .id_result = tmp,
3016 .base = push_constant_id,
3017 .indexes = &.{zero_id},
3018 });
3019 try section.emit(self.spv.gpa, .OpLoad, .{
3020 .id_result_type = ptr_anyerror_ty_id,
3021 .id_result = p_error_id,
3022 .pointer = tmp,
3023 });
3024 },
3025 else => unreachable,
3026 }
3027
3028 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
3029 const error_id = self.spv.allocId();
2935 try section.emit(self.spv.gpa, .OpFunctionCall, .{3030 try section.emit(self.spv.gpa, .OpFunctionCall, .{
2936 .id_result_type = anyerror_ty_id,3031 .id_result_type = anyerror_ty_id,
2937 .id_result = error_id,3032 .id_result = error_id,
...@@ -2941,17 +3036,25 @@ const NavGen = struct {...@@ -2941,17 +3036,25 @@ const NavGen = struct {
2941 try section.emit(self.spv.gpa, .OpStore, .{3036 try section.emit(self.spv.gpa, .OpStore, .{
2942 .pointer = p_error_id,3037 .pointer = p_error_id,
2943 .object = error_id,3038 .object = error_id,
3039 .memory_access = .{
3040 .Aligned = .{ .literal_integer = @sizeOf(u16) },
3041 },
2944 });3042 });
2945 try section.emit(self.spv.gpa, .OpReturn, {});3043 try section.emit(self.spv.gpa, .OpReturn, {});
2946 try section.emit(self.spv.gpa, .OpFunctionEnd, {});3044 try section.emit(self.spv.gpa, .OpFunctionEnd, {});
29473045
2948 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
2949
2950 // Just generate a quick other name because the intel runtime crashes when the entry-3046 // Just generate a quick other name because the intel runtime crashes when the entry-
2951 // point name is the same as a different OpName.3047 // point name is the same as a different OpName.
2952 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});3048 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});
2953 defer self.gpa.free(test_name);3049 defer self.gpa.free(test_name);
2954 try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel);3050
3051 const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
3052 .vulkan => .GLCompute,
3053 .opencl => .Kernel,
3054 else => unreachable,
3055 };
3056
3057 try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode);
2955 }3058 }
29563059
2957 fn genNav(self: *NavGen, do_codegen: bool) !void {3060 fn genNav(self: *NavGen, do_codegen: bool) !void {
src/link/SpirV/lower_invocation_globals.zig+9
...@@ -400,6 +400,15 @@ const ModuleBuilder = struct {...@@ -400,6 +400,15 @@ const ModuleBuilder = struct {
400 self.section.writeWords(inst.operands[2..]);400 self.section.writeWords(inst.operands[2..]);
401 continue;401 continue;
402 },402 },
403 .OpExecutionMode, .OpExecutionModeId => {
404 const original_id: ResultId = @enumFromInt(inst.operands[0]);
405 const new_id_index = info.entry_points.getIndex(original_id).?;
406 const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index);
407 try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len);
408 self.section.writeOperand(ResultId, new_id);
409 self.section.writeWords(inst.operands[1..]);
410 continue;
411 },
403 .OpTypeFunction => {412 .OpTypeFunction => {
404 // Re-emitted in `emitFunctionTypes()`. We can do this because413 // Re-emitted in `emitFunctionTypes()`. We can do this because
405 // OpTypeFunction's may not currently be used anywhere that is not414 // OpTypeFunction's may not currently be used anywhere that is not