Commit 6de456c179

Robin Voetter <robin@voetter.nl>
2024-10-20 16:53:53
spirv: fix up calling conventions for vulkan
* Fragment and Vertex CCs are only valid for SPIR-V when running under Vulkan. * Emit GLCompute instead of Kernel for SPIR-V kernels.
1 parent 9b42bc1
Changed files (3)
src
src/codegen/spirv.zig
@@ -1640,13 +1640,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);
@@ -2969,11 +2974,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);
src/link/SpirV.zig
@@ -161,28 +161,35 @@ pub fn updateExports(
         },
     };
     const nav_ty = ip.getNav(nav_index).typeOf(ip);
+    const target = zcu.getTarget();
     if (ip.isFunctionType(nav_ty)) {
-        const target = zcu.getTarget();
         const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
-        const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) {
-            .spirv_vertex => spec.ExecutionModel.Vertex,
-            .spirv_fragment => spec.ExecutionModel.Fragment,
-            .spirv_kernel => spec.ExecutionModel.Kernel,
+        const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
+        const execution_model: spec.ExecutionModel = switch (target.os.tag) {
+            .vulkan => switch (cc) {
+                .spirv_vertex => .Vertex,
+                .spirv_fragment => .Fragment,
+                .spirv_kernel => .GLCompute,
+                // TODO: We should integrate with the Linkage capability and export this function
+                .spirv_device => return,
+                else => unreachable,
+            },
+            .opencl => switch (cc) {
+                .spirv_kernel => .Kernel,
+                // TODO: We should integrate with the Linkage capability and export this function
+                .spirv_device => return,
+                else => unreachable,
+            },
             else => unreachable,
         };
-        const is_vulkan = target.os.tag == .vulkan;
-
-        if ((!is_vulkan and execution_model == .Kernel) or
-            (is_vulkan and (execution_model == .Fragment or execution_model == .Vertex)))
-        {
-            for (export_indices) |export_idx| {
-                const exp = zcu.all_exports.items[export_idx];
-                try self.object.spv.declareEntryPoint(
-                    spv_decl_index,
-                    exp.opts.name.toSlice(ip),
-                    execution_model,
-                );
-            }
+
+        for (export_indices) |export_idx| {
+            const exp = zcu.all_exports.items[export_idx];
+            try self.object.spv.declareEntryPoint(
+                spv_decl_index,
+                exp.opts.name.toSlice(ip),
+                execution_model,
+            );
         }
     }
 
@@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
     const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
         error.OutOfMemory => return error.OutOfMemory,
         else => |other| {
-            log.err("error while linking: {s}\n", .{@errorName(other)});
+            log.err("error while linking: {s}", .{@errorName(other)});
             return error.FlushFailure;
         },
     };
src/Zcu.zig
@@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
             else => false,
         },
         .stage2_spirv64 => switch (cc) {
-            .spirv_device,
-            .spirv_kernel,
-            .spirv_fragment,
-            .spirv_vertex,
-            => true,
+            .spirv_device, .spirv_kernel => true,
+            .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan,
             else => false,
         },
     };