| author | |
| committer | |
| log | e2d11ff76bad62b094f50a5d36332c70a5c87ea1 |
| tree | 005eaea6adf63132e42e92149e71ad26bbdd83af |
| parent | 3deb86bafdb2f622b9099c405e0e861f70c36a45 |
Execution modes (e.g. `LocalSize`, `OriginUpperLeft`) were previously set
via `gpu.executionMode()`, which used inline assembly to emit `OpExecutionMode`.
The SPIR-V assembler now rejects this instruction and retrieves execution mode information
from function cc, deleting `gpu.executionMode()` entirely.
Two new `spirv_task` and `spirv_mesh` calling conventions are also added
and `PackedCallingConvention.unpack()` now takes a trailing data slice.14 files changed, 300 insertions(+), 148 deletions(-)
lib/std/Target.zig+2| ... | @@ -1984,6 +1984,8 @@ pub const Cpu = struct { | ... | @@ -1984,6 +1984,8 @@ pub const Cpu = struct { |
| 1984 | .spirv_kernel, | 1984 | .spirv_kernel, |
| 1985 | .spirv_fragment, | 1985 | .spirv_fragment, |
| 1986 | .spirv_vertex, | 1986 | .spirv_vertex, |
| 1987 | .spirv_task, | ||
| 1988 | .spirv_mesh, | ||
| 1987 | => &.{ .spirv32, .spirv64 }, | 1989 | => &.{ .spirv32, .spirv64 }, |
| 1988 | 1990 | ||
| 1989 | .ez80_cet, | 1991 | .ez80_cet, |
lib/std/gpu.zig-83| ... | @@ -19,86 +19,3 @@ pub extern const local_invocation_id: @Vector(3, u32) addrspace(.input); | ... | @@ -19,86 +19,3 @@ pub extern const local_invocation_id: @Vector(3, u32) addrspace(.input); |
| 19 | pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input); | 19 | pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input); |
| 20 | pub extern const vertex_index: u32 addrspace(.input); | 20 | pub extern const vertex_index: u32 addrspace(.input); |
| 21 | pub extern const instance_index: u32 addrspace(.input); | 21 | pub extern const instance_index: u32 addrspace(.input); |
| 22 | |||
| 23 | pub const ExecutionMode = union(Tag) { | ||
| 24 | /// Sets origin of the framebuffer to the upper-left corner | ||
| 25 | origin_upper_left, | ||
| 26 | /// Sets origin of the framebuffer to the lower-left corner | ||
| 27 | origin_lower_left, | ||
| 28 | /// Indicates that the fragment shader writes to `frag_depth`, | ||
| 29 | /// replacing the fixed-function depth value. | ||
| 30 | depth_replacing, | ||
| 31 | /// Indicates that per-fragment tests may assume that | ||
| 32 | /// any `frag_depth` built in-decorated value written by the shader is | ||
| 33 | /// greater-than-or-equal to the fragment’s interpolated depth value | ||
| 34 | depth_greater, | ||
| 35 | /// Indicates that per-fragment tests may assume that | ||
| 36 | /// any `frag_depth` built in-decorated value written by the shader is | ||
| 37 | /// less-than-or-equal to the fragment’s interpolated depth value | ||
| 38 | depth_less, | ||
| 39 | /// Indicates that per-fragment tests may assume that | ||
| 40 | /// any `frag_depth` built in-decorated value written by the shader is | ||
| 41 | /// the same as the fragment’s interpolated depth value | ||
| 42 | depth_unchanged, | ||
| 43 | /// Indicates the workgroup size in the x, y, and z dimensions. | ||
| 44 | local_size: LocalSize, | ||
| 45 | |||
| 46 | pub const Tag = enum(u32) { | ||
| 47 | origin_upper_left = 7, | ||
| 48 | origin_lower_left = 8, | ||
| 49 | depth_replacing = 12, | ||
| 50 | depth_greater = 14, | ||
| 51 | depth_less = 15, | ||
| 52 | depth_unchanged = 16, | ||
| 53 | local_size = 17, | ||
| 54 | }; | ||
| 55 | |||
| 56 | pub const LocalSize = struct { x: u32, y: u32, z: u32 }; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /// Declare the mode entry point executes in. | ||
| 60 | pub fn executionMode(comptime entry_point: anytype, comptime mode: ExecutionMode) void { | ||
| 61 | const cc = @typeInfo(@TypeOf(entry_point)).@"fn".attrs.@"callconv"; | ||
| 62 | switch (mode) { | ||
| 63 | .origin_upper_left, | ||
| 64 | .origin_lower_left, | ||
| 65 | .depth_replacing, | ||
| 66 | .depth_greater, | ||
| 67 | .depth_less, | ||
| 68 | .depth_unchanged, | ||
| 69 | => { | ||
| 70 | if (cc != .spirv_fragment) { | ||
| 71 | @compileError( | ||
| 72 | \\invalid execution mode ' | ||
| 73 | ++ @tagName(mode) ++ | ||
| 74 | \\' for function with ' | ||
| 75 | ++ @tagName(cc) ++ | ||
| 76 | \\' calling convention | ||
| 77 | ); | ||
| 78 | } | ||
| 79 | asm volatile ( | ||
| 80 | \\OpExecutionMode %entry_point $mode | ||
| 81 | : | ||
| 82 | : [entry_point] "" (entry_point), | ||
| 83 | [mode] "c" (@intFromEnum(mode)), | ||
| 84 | ); | ||
| 85 | }, | ||
| 86 | .local_size => |size| { | ||
| 87 | if (cc != .spirv_kernel) { | ||
| 88 | @compileError( | ||
| 89 | \\invalid execution mode 'local_size' for function with ' | ||
| 90 | ++ @tagName(cc) ++ | ||
| 91 | \\' calling convention | ||
| 92 | ); | ||
| 93 | } | ||
| 94 | asm volatile ( | ||
| 95 | \\OpExecutionMode %entry_point LocalSize $x $y $z | ||
| 96 | : | ||
| 97 | : [entry_point] "" (entry_point), | ||
| 98 | [x] "c" (size.x), | ||
| 99 | [y] "c" (size.y), | ||
| 100 | [z] "c" (size.z), | ||
| 101 | ); | ||
| 102 | }, | ||
| 103 | } | ||
| 104 | } |
lib/std/lang.zig+36-4| ... | @@ -140,7 +140,7 @@ pub const CallingConvention = union(enum(u8)) { | ... | @@ -140,7 +140,7 @@ pub const CallingConvention = union(enum(u8)) { |
| 140 | pub const kernel: CallingConvention = switch (builtin.target.cpu.arch) { | 140 | pub const kernel: CallingConvention = switch (builtin.target.cpu.arch) { |
| 141 | .amdgcn => .amdgcn_kernel, | 141 | .amdgcn => .amdgcn_kernel, |
| 142 | .nvptx, .nvptx64 => .nvptx_kernel, | 142 | .nvptx, .nvptx64 => .nvptx_kernel, |
| 143 | .spirv32, .spirv64 => .spirv_kernel, | 143 | .spirv32, .spirv64 => .{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }, |
| 144 | else => unreachable, | 144 | else => unreachable, |
| 145 | }; | 145 | }; |
| 146 | 146 | ||
| ... | @@ -337,11 +337,13 @@ pub const CallingConvention = union(enum(u8)) { | ... | @@ -337,11 +337,13 @@ pub const CallingConvention = union(enum(u8)) { |
| 337 | nvptx_device, | 337 | nvptx_device, |
| 338 | nvptx_kernel, | 338 | nvptx_kernel, |
| 339 | 339 | ||
| 340 | // Calling conventions for kernels and shaders on the `spirv`, `spirv32`, and `spirv64` architectures. | 340 | // Calling conventions for kernels and shaders on the `spirv32` and `spirv64` architectures. |
| 341 | spirv_device, | 341 | spirv_device, |
| 342 | spirv_kernel, | ||
| 343 | spirv_fragment, | ||
| 344 | spirv_vertex, | 342 | spirv_vertex, |
| 343 | spirv_kernel: SpirvKernelOptions, | ||
| 344 | spirv_fragment: SpirvFragmentOptions, | ||
| 345 | spirv_task: SpirvKernelOptions, | ||
| 346 | spirv_mesh: SpirvMeshOptions, | ||
| 345 | 347 | ||
| 346 | // Calling conventions for the `ez80` architecture. | 348 | // Calling conventions for the `ez80` architecture. |
| 347 | ez80_cet, | 349 | ez80_cet, |
| ... | @@ -473,6 +475,36 @@ pub const CallingConvention = union(enum(u8)) { | ... | @@ -473,6 +475,36 @@ pub const CallingConvention = union(enum(u8)) { |
| 473 | }; | 475 | }; |
| 474 | }; | 476 | }; |
| 475 | 477 | ||
| 478 | pub const SpirvKernelOptions = struct { | ||
| 479 | x: u32, | ||
| 480 | y: u32, | ||
| 481 | z: u32, | ||
| 482 | }; | ||
| 483 | |||
| 484 | pub const SpirvFragmentOptions = struct { | ||
| 485 | pub const DepthAssumption = enum(u2) { | ||
| 486 | none = 0, | ||
| 487 | greater = 1, | ||
| 488 | less = 2, | ||
| 489 | unchanged = 3, | ||
| 490 | }; | ||
| 491 | |||
| 492 | pixel_centered_integer: bool = false, | ||
| 493 | depth_assumption: DepthAssumption = .none, | ||
| 494 | }; | ||
| 495 | |||
| 496 | pub const SpirvMeshOptions = struct { | ||
| 497 | pub const StageOutput = enum(u2) { | ||
| 498 | output_points = 0, | ||
| 499 | output_lines = 1, | ||
| 500 | output_triangles = 2, | ||
| 501 | }; | ||
| 502 | |||
| 503 | stage_output: StageOutput = .output_triangles, | ||
| 504 | max_primitives: u32 = 1, | ||
| 505 | max_vertices: u32 = 3, | ||
| 506 | }; | ||
| 507 | |||
| 476 | /// Returns the array of `std.Target.Cpu.Arch` to which this `CallingConvention` applies. | 508 | /// Returns the array of `std.Target.Cpu.Arch` to which this `CallingConvention` applies. |
| 477 | /// Asserts that `cc` is not `.auto`, `.@"async"`, `.naked`, or `.@"inline"`. | 509 | /// Asserts that `cc` is not `.auto`, `.@"async"`, `.naked`, or `.@"inline"`. |
| 478 | pub fn archs(cc: CallingConvention) []const std.Target.Cpu.Arch { | 510 | pub fn archs(cc: CallingConvention) []const std.Target.Cpu.Arch { |
src/InternPool.zig+65-4| ... | @@ -4202,12 +4202,14 @@ pub const Index = enum(u32) { | ... | @@ -4202,12 +4202,14 @@ pub const Index = enum(u32) { |
| 4202 | type_function: struct { | 4202 | type_function: struct { |
| 4203 | const @"data.flags.has_comptime_bits" = opaque {}; | 4203 | const @"data.flags.has_comptime_bits" = opaque {}; |
| 4204 | const @"data.flags.has_noalias_bits" = opaque {}; | 4204 | const @"data.flags.has_noalias_bits" = opaque {}; |
| 4205 | const @"data.flags.cc.extraLen()" = opaque {}; | ||
| 4205 | const @"data.params_len" = opaque {}; | 4206 | const @"data.params_len" = opaque {}; |
| 4206 | data: *Tag.TypeFunction, | 4207 | data: *Tag.TypeFunction, |
| 4207 | @"trailing.comptime_bits.len": *@"data.flags.has_comptime_bits", | 4208 | @"trailing.comptime_bits.len": *@"data.flags.has_comptime_bits", |
| 4208 | @"trailing.noalias_bits.len": *@"data.flags.has_noalias_bits", | 4209 | @"trailing.noalias_bits.len": *@"data.flags.has_noalias_bits", |
| 4210 | @"trailing.cc_bits.len": *@"data.flags.cc.extraLen()", | ||
| 4209 | @"trailing.param_types.len": *@"data.params_len", | 4211 | @"trailing.param_types.len": *@"data.params_len", |
| 4210 | trailing: struct { comptime_bits: []u32, noalias_bits: []u32, param_types: []Index }, | 4212 | trailing: struct { comptime_bits: []u32, noalias_bits: []u32, cc_bits: []u32, param_types: []Index }, |
| 4211 | }, | 4213 | }, |
| 4212 | type_tuple: struct { | 4214 | type_tuple: struct { |
| 4213 | const @"data.fields_len" = opaque {}; | 4215 | const @"data.fields_len" = opaque {}; |
| ... | @@ -5165,6 +5167,7 @@ pub const Tag = enum(u8) { | ... | @@ -5165,6 +5167,7 @@ pub const Tag = enum(u8) { |
| 5165 | .trailing = struct { | 5167 | .trailing = struct { |
| 5166 | param_comptime_bits: ?[]u32, | 5168 | param_comptime_bits: ?[]u32, |
| 5167 | param_noalias_bits: ?[]u32, | 5169 | param_noalias_bits: ?[]u32, |
| 5170 | param_cc_bits: ?[]u32, | ||
| 5168 | param_type: []Index, | 5171 | param_type: []Index, |
| 5169 | }, | 5172 | }, |
| 5170 | .config = .{ | 5173 | .config = .{ |
| ... | @@ -5172,6 +5175,8 @@ pub const Tag = enum(u8) { | ... | @@ -5172,6 +5175,8 @@ pub const Tag = enum(u8) { |
| 5172 | .@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32", | 5175 | .@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32", |
| 5173 | .@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits", | 5176 | .@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits", |
| 5174 | .@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32", | 5177 | .@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32", |
| 5178 | .@"trailing.param_cc_bits.?" = .@"payload.flags.cc.extraLen() != 0", | ||
| 5179 | .@"trailing.param_cc_bits.?.len" = .@"payload.flags.cc.extraLen()", | ||
| 5175 | .@"trailing.param_type.len" = .@"payload.params_len", | 5180 | .@"trailing.param_type.len" = .@"payload.params_len", |
| 5176 | }, | 5181 | }, |
| 5177 | }, | 5182 | }, |
| ... | @@ -6983,6 +6988,9 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke | ... | @@ -6983,6 +6988,9 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke |
| 6983 | trail_index += 1; | 6988 | trail_index += 1; |
| 6984 | break :b x; | 6989 | break :b x; |
| 6985 | }; | 6990 | }; |
| 6991 | const cc_extra_len = type_function.data.flags.cc.extraLen(); | ||
| 6992 | const cc = type_function.data.flags.cc.unpack(extra.view().items(.@"0")[trail_index..][0..cc_extra_len]); | ||
| 6993 | trail_index += cc_extra_len; | ||
| 6986 | return .{ | 6994 | return .{ |
| 6987 | .param_types = .{ | 6995 | .param_types = .{ |
| 6988 | .tid = tid, | 6996 | .tid = tid, |
| ... | @@ -6992,7 +7000,7 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke | ... | @@ -6992,7 +7000,7 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke |
| 6992 | .return_type = type_function.data.return_type, | 7000 | .return_type = type_function.data.return_type, |
| 6993 | .comptime_bits = comptime_bits, | 7001 | .comptime_bits = comptime_bits, |
| 6994 | .noalias_bits = noalias_bits, | 7002 | .noalias_bits = noalias_bits, |
| 6995 | .cc = type_function.data.flags.cc.unpack(), | 7003 | .cc = cc, |
| 6996 | .is_var_args = type_function.data.flags.is_var_args, | 7004 | .is_var_args = type_function.data.flags.is_var_args, |
| 6997 | .is_noinline = type_function.data.flags.is_noinline, | 7005 | .is_noinline = type_function.data.flags.is_noinline, |
| 6998 | }; | 7006 | }; |
| ... | @@ -9091,18 +9099,21 @@ pub fn getFuncType( | ... | @@ -9091,18 +9099,21 @@ pub fn getFuncType( |
| 9091 | // ask if it already exists, and if so, revert the lengths of the mutated | 9099 | // ask if it already exists, and if so, revert the lengths of the mutated |
| 9092 | // arrays. This is similar to what `getOrPutTrailingString` does. | 9100 | // arrays. This is similar to what `getOrPutTrailingString` does. |
| 9093 | const prev_extra_len = extra.mutate.len; | 9101 | const prev_extra_len = extra.mutate.len; |
| 9102 | const packed_cc: PackedCallingConvention = .pack(key.cc orelse .auto); | ||
| 9103 | const cc_extra_len = packed_cc.extraLen(); | ||
| 9094 | const params_len: u32 = @intCast(key.param_types.len); | 9104 | const params_len: u32 = @intCast(key.param_types.len); |
| 9095 | 9105 | ||
| 9096 | try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeFunction).@"struct".field_names.len + | 9106 | try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeFunction).@"struct".field_names.len + |
| 9097 | @intFromBool(key.comptime_bits != 0) + | 9107 | @intFromBool(key.comptime_bits != 0) + |
| 9098 | @intFromBool(key.noalias_bits != 0) + | 9108 | @intFromBool(key.noalias_bits != 0) + |
| 9109 | cc_extra_len + | ||
| 9099 | params_len); | 9110 | params_len); |
| 9100 | 9111 | ||
| 9101 | const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{ | 9112 | const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{ |
| 9102 | .params_len = params_len, | 9113 | .params_len = params_len, |
| 9103 | .return_type = key.return_type, | 9114 | .return_type = key.return_type, |
| 9104 | .flags = .{ | 9115 | .flags = .{ |
| 9105 | .cc = .pack(key.cc orelse .auto), | 9116 | .cc = packed_cc, |
| 9106 | .is_var_args = key.is_var_args, | 9117 | .is_var_args = key.is_var_args, |
| 9107 | .has_comptime_bits = key.comptime_bits != 0, | 9118 | .has_comptime_bits = key.comptime_bits != 0, |
| 9108 | .has_noalias_bits = key.noalias_bits != 0, | 9119 | .has_noalias_bits = key.noalias_bits != 0, |
| ... | @@ -9112,6 +9123,18 @@ pub fn getFuncType( | ... | @@ -9112,6 +9123,18 @@ pub fn getFuncType( |
| 9112 | 9123 | ||
| 9113 | if (key.comptime_bits != 0) extra.appendAssumeCapacity(.{key.comptime_bits}); | 9124 | if (key.comptime_bits != 0) extra.appendAssumeCapacity(.{key.comptime_bits}); |
| 9114 | if (key.noalias_bits != 0) extra.appendAssumeCapacity(.{key.noalias_bits}); | 9125 | if (key.noalias_bits != 0) extra.appendAssumeCapacity(.{key.noalias_bits}); |
| 9126 | if (key.cc) |cc| switch (cc) { | ||
| 9127 | .spirv_kernel, .spirv_task => |kernel| extra.appendSliceAssumeCapacity(.{&.{ | ||
| 9128 | kernel.x, | ||
| 9129 | kernel.y, | ||
| 9130 | kernel.z, | ||
| 9131 | }}), | ||
| 9132 | .spirv_mesh => |mesh| extra.appendSliceAssumeCapacity(.{&.{ | ||
| 9133 | mesh.max_primitives, | ||
| 9134 | mesh.max_vertices, | ||
| 9135 | }}), | ||
| 9136 | else => {}, | ||
| 9137 | }; | ||
| 9115 | extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)}); | 9138 | extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)}); |
| 9116 | errdefer extra.mutate.len = prev_extra_len; | 9139 | errdefer extra.mutate.len = prev_extra_len; |
| 9117 | 9140 | ||
| ... | @@ -10704,6 +10727,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo | ... | @@ -10704,6 +10727,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo |
| 10704 | const info = extraData(extra_list, Tag.TypeFunction, data); | 10727 | const info = extraData(extra_list, Tag.TypeFunction, data); |
| 10705 | break :b @sizeOf(Tag.TypeFunction) + | 10728 | break :b @sizeOf(Tag.TypeFunction) + |
| 10706 | (@sizeOf(Index) * info.params_len) + | 10729 | (@sizeOf(Index) * info.params_len) + |
| 10730 | (@as(u32, 4) * info.flags.cc.extraLen()) + | ||
| 10707 | (@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) + | 10731 | (@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) + |
| 10708 | (@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits)); | 10732 | (@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits)); |
| 10709 | }, | 10733 | }, |
| ... | @@ -12573,12 +12597,35 @@ const PackedCallingConvention = packed struct(u18) { | ... | @@ -12573,12 +12597,35 @@ const PackedCallingConvention = packed struct(u18) { |
| 12573 | .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0), | 12597 | .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0), |
| 12574 | .extra = @intFromEnum(pl.save), | 12598 | .extra = @intFromEnum(pl.save), |
| 12575 | }, | 12599 | }, |
| 12600 | std.lang.CallingConvention.SpirvKernelOptions => .{ | ||
| 12601 | .tag = tag, | ||
| 12602 | .incoming_stack_alignment = .none, | ||
| 12603 | .extra = 0, | ||
| 12604 | }, | ||
| 12605 | std.lang.CallingConvention.SpirvFragmentOptions => .{ | ||
| 12606 | .tag = tag, | ||
| 12607 | .incoming_stack_alignment = .none, | ||
| 12608 | .extra = @as(u4, @intFromEnum(pl.depth_assumption)) << 1 | @intFromBool(pl.pixel_centered_integer), | ||
| 12609 | }, | ||
| 12610 | std.lang.CallingConvention.SpirvMeshOptions => .{ | ||
| 12611 | .tag = tag, | ||
| 12612 | .incoming_stack_alignment = .none, | ||
| 12613 | .extra = @intFromEnum(pl.stage_output), | ||
| 12614 | }, | ||
| 12576 | else => comptime unreachable, | 12615 | else => comptime unreachable, |
| 12577 | }, | 12616 | }, |
| 12578 | }; | 12617 | }; |
| 12579 | } | 12618 | } |
| 12580 | 12619 | ||
| 12581 | fn unpack(cc: PackedCallingConvention) std.lang.CallingConvention { | 12620 | fn extraLen(cc: PackedCallingConvention) u2 { |
| 12621 | return switch (cc.tag) { | ||
| 12622 | .spirv_kernel, .spirv_task => 3, | ||
| 12623 | .spirv_mesh => 2, | ||
| 12624 | else => 0, | ||
| 12625 | }; | ||
| 12626 | } | ||
| 12627 | |||
| 12628 | fn unpack(cc: PackedCallingConvention, trailing: []const u32) std.lang.CallingConvention { | ||
| 12582 | return switch (cc.tag) { | 12629 | return switch (cc.tag) { |
| 12583 | inline else => |tag| @unionInit( | 12630 | inline else => |tag| @unionInit( |
| 12584 | std.lang.CallingConvention, | 12631 | std.lang.CallingConvention, |
| ... | @@ -12616,6 +12663,20 @@ const PackedCallingConvention = packed struct(u18) { | ... | @@ -12616,6 +12663,20 @@ const PackedCallingConvention = packed struct(u18) { |
| 12616 | .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(), | 12663 | .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(), |
| 12617 | .save = @enumFromInt(cc.extra), | 12664 | .save = @enumFromInt(cc.extra), |
| 12618 | }, | 12665 | }, |
| 12666 | std.lang.CallingConvention.SpirvKernelOptions => .{ | ||
| 12667 | .x = trailing[0], | ||
| 12668 | .y = trailing[1], | ||
| 12669 | .z = trailing[2], | ||
| 12670 | }, | ||
| 12671 | std.lang.CallingConvention.SpirvFragmentOptions => .{ | ||
| 12672 | .pixel_centered_integer = @bitCast(@as(u1, @truncate(cc.extra))), | ||
| 12673 | .depth_assumption = @enumFromInt(@as(u2, @truncate(cc.extra >> 1))), | ||
| 12674 | }, | ||
| 12675 | std.lang.CallingConvention.SpirvMeshOptions => .{ | ||
| 12676 | .stage_output = @enumFromInt(cc.extra), | ||
| 12677 | .max_primitives = trailing[0], | ||
| 12678 | .max_vertices = trailing[1], | ||
| 12679 | }, | ||
| 12619 | else => comptime unreachable, | 12680 | else => comptime unreachable, |
| 12620 | }, | 12681 | }, |
| 12621 | ), | 12682 | ), |
src/Sema.zig+23-1| ... | @@ -8599,6 +8599,7 @@ fn checkReturnTypeAndCallConv( | ... | @@ -8599,6 +8599,7 @@ fn checkReturnTypeAndCallConv( |
| 8599 | ) CompileError!void { | 8599 | ) CompileError!void { |
| 8600 | const pt = sema.pt; | 8600 | const pt = sema.pt; |
| 8601 | const zcu = pt.zcu; | 8601 | const zcu = pt.zcu; |
| 8602 | const target = zcu.getTarget(); | ||
| 8602 | if (opt_varargs_src) |varargs_src| { | 8603 | if (opt_varargs_src) |varargs_src| { |
| 8603 | try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv"); | 8604 | try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv"); |
| 8604 | } | 8605 | } |
| ... | @@ -8660,6 +8661,21 @@ fn checkReturnTypeAndCallConv( | ... | @@ -8660,6 +8661,21 @@ fn checkReturnTypeAndCallConv( |
| 8660 | .@"inline" => if (is_noinline) { | 8661 | .@"inline" => if (is_noinline) { |
| 8661 | return sema.fail(block, callconv_src, "'noinline' function cannot have calling convention 'inline'", .{}); | 8662 | return sema.fail(block, callconv_src, "'noinline' function cannot have calling convention 'inline'", .{}); |
| 8662 | }, | 8663 | }, |
| 8664 | .spirv_fragment => |fragment| { | ||
| 8665 | if (fragment.pixel_centered_integer and target.os.tag != .opengl) { | ||
| 8666 | return sema.fail(block, callconv_src, "'pixel_centered_integer' is not supported on this target", .{}); | ||
| 8667 | } | ||
| 8668 | }, | ||
| 8669 | .spirv_kernel, .spirv_task => |kernel| { | ||
| 8670 | if (kernel.x == 0 or kernel.y == 0 or kernel.z == 0) { | ||
| 8671 | return sema.fail(block, callconv_src, "kernel workgroup dimensions must be at least 1", .{}); | ||
| 8672 | } | ||
| 8673 | }, | ||
| 8674 | .spirv_mesh => |mesh| { | ||
| 8675 | if (mesh.max_vertices == 0 or mesh.max_primitives == 0) { | ||
| 8676 | return sema.fail(block, callconv_src, "mesh shader 'max_vertices' and 'max_primitives' must be at least 1", .{}); | ||
| 8677 | } | ||
| 8678 | }, | ||
| 8663 | else => {}, | 8679 | else => {}, |
| 8664 | } | 8680 | } |
| 8665 | switch (zcu.callconvSupported(@"callconv")) { | 8681 | switch (zcu.callconvSupported(@"callconv")) { |
| ... | @@ -8770,6 +8786,8 @@ fn callConvIsCallable(cc: std.lang.CallingConvention.Tag) bool { | ... | @@ -8770,6 +8786,8 @@ fn callConvIsCallable(cc: std.lang.CallingConvention.Tag) bool { |
| 8770 | .spirv_kernel, | 8786 | .spirv_kernel, |
| 8771 | .spirv_fragment, | 8787 | .spirv_fragment, |
| 8772 | .spirv_vertex, | 8788 | .spirv_vertex, |
| 8789 | .spirv_task, | ||
| 8790 | .spirv_mesh, | ||
| 8773 | => false, | 8791 | => false, |
| 8774 | 8792 | ||
| 8775 | else => true, | 8793 | else => true, |
| ... | @@ -29127,7 +29145,7 @@ fn callconvCoerceAllowed( | ... | @@ -29127,7 +29145,7 @@ fn callconvCoerceAllowed( |
| 29127 | switch (src_cc) { | 29145 | switch (src_cc) { |
| 29128 | inline else => |src_data, tag| { | 29146 | inline else => |src_data, tag| { |
| 29129 | const dest_data = @field(dest_cc, @tagName(tag)); | 29147 | const dest_data = @field(dest_cc, @tagName(tag)); |
| 29130 | if (@TypeOf(src_data) != void) { | 29148 | if (@TypeOf(src_data) != void and @hasField(@TypeOf(src_data), "incoming_stack_alignment")) { |
| 29131 | const default_stack_align = target.stackAlignment(); | 29149 | const default_stack_align = target.stackAlignment(); |
| 29132 | const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; | 29150 | const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; |
| 29133 | const dest_stack_align = dest_data.incoming_stack_alignment orelse default_stack_align; | 29151 | const dest_stack_align = dest_data.incoming_stack_alignment orelse default_stack_align; |
| ... | @@ -29156,6 +29174,10 @@ fn callconvCoerceAllowed( | ... | @@ -29156,6 +29174,10 @@ fn callconvCoerceAllowed( |
| 29156 | std.lang.CallingConvention.ShInterruptOptions => { | 29174 | std.lang.CallingConvention.ShInterruptOptions => { |
| 29157 | if (src_data.save != dest_data.save) return false; | 29175 | if (src_data.save != dest_data.save) return false; |
| 29158 | }, | 29176 | }, |
| 29177 | std.lang.CallingConvention.SpirvKernelOptions, | ||
| 29178 | std.lang.CallingConvention.SpirvFragmentOptions, | ||
| 29179 | std.lang.CallingConvention.SpirvMeshOptions, | ||
| 29180 | => {}, | ||
| 29159 | else => comptime unreachable, | 29181 | else => comptime unreachable, |
| 29160 | } | 29182 | } |
| 29161 | }, | 29183 | }, |
src/Zcu.zig+1| ... | @@ -4699,6 +4699,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) | ... | @@ -4699,6 +4699,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) |
| 4699 | .stage2_spirv => switch (cc) { | 4699 | .stage2_spirv => switch (cc) { |
| 4700 | .spirv_device, .spirv_kernel => true, | 4700 | .spirv_device, .spirv_kernel => true, |
| 4701 | .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, | 4701 | .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, |
| 4702 | .spirv_task, .spirv_mesh => target.os.tag == .vulkan, | ||
| 4702 | else => false, | 4703 | else => false, |
| 4703 | }, | 4704 | }, |
| 4704 | }; | 4705 | }; |
src/codegen/llvm.zig+6| ... | @@ -4445,6 +4445,10 @@ pub fn toLlvmCallConv(cc: std.lang.CallingConvention, target: *const std.Target) | ... | @@ -4445,6 +4445,10 @@ pub fn toLlvmCallConv(cc: std.lang.CallingConvention, target: *const std.Target) |
| 4445 | std.lang.CallingConvention.CommonOptions, | 4445 | std.lang.CallingConvention.CommonOptions, |
| 4446 | => .{ pl.incoming_stack_alignment, 0, 0 }, | 4446 | => .{ pl.incoming_stack_alignment, 0, 0 }, |
| 4447 | std.lang.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params, 0 }, | 4447 | std.lang.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params, 0 }, |
| 4448 | std.lang.CallingConvention.SpirvKernelOptions, | ||
| 4449 | std.lang.CallingConvention.SpirvFragmentOptions, | ||
| 4450 | std.lang.CallingConvention.SpirvMeshOptions, | ||
| 4451 | => .{ null, 0, 0 }, | ||
| 4448 | else => @compileError("TODO: toLlvmCallConv" ++ @tagName(pl)), | 4452 | else => @compileError("TODO: toLlvmCallConv" ++ @tagName(pl)), |
| 4449 | }, | 4453 | }, |
| 4450 | }; | 4454 | }; |
| ... | @@ -4588,6 +4592,8 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const | ... | @@ -4588,6 +4592,8 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const |
| 4588 | .spirv_kernel, | 4592 | .spirv_kernel, |
| 4589 | .spirv_fragment, | 4593 | .spirv_fragment, |
| 4590 | .spirv_vertex, | 4594 | .spirv_vertex, |
| 4595 | .spirv_task, | ||
| 4596 | .spirv_mesh, | ||
| 4591 | => null, | 4597 | => null, |
| 4592 | }; | 4598 | }; |
| 4593 | } | 4599 | } |
src/codegen/spirv/CodeGen.zig+3-16| ... | @@ -1442,6 +1442,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -1442,6 +1442,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1442 | .spirv_fragment, | 1442 | .spirv_fragment, |
| 1443 | .spirv_vertex, | 1443 | .spirv_vertex, |
| 1444 | .spirv_device, | 1444 | .spirv_device, |
| 1445 | .spirv_task, | ||
| 1446 | .spirv_mesh, | ||
| 1445 | => {}, | 1447 | => {}, |
| 1446 | else => unreachable, | 1448 | else => unreachable, |
| 1447 | } | 1449 | } |
| ... | @@ -2542,15 +2544,6 @@ fn generateTestEntryPoint( | ... | @@ -2542,15 +2544,6 @@ fn generateTestEntryPoint( |
| 2542 | cg.module.error_buffer = spv_err_decl_index; | 2544 | cg.module.error_buffer = spv_err_decl_index; |
| 2543 | } | 2545 | } |
| 2544 | 2546 | ||
| 2545 | try cg.module.sections.execution_modes.emit(gpa, .OpExecutionMode, .{ | ||
| 2546 | .entry_point = kernel_id, | ||
| 2547 | .mode = .{ .local_size = .{ | ||
| 2548 | .x_size = 1, | ||
| 2549 | .y_size = 1, | ||
| 2550 | .z_size = 1, | ||
| 2551 | } }, | ||
| 2552 | }); | ||
| 2553 | |||
| 2554 | const void_ty_id = try cg.resolveType(.void, .direct); | 2547 | const void_ty_id = try cg.resolveType(.void, .direct); |
| 2555 | const kernel_proto_ty_id = try cg.module.functionType(void_ty_id, &.{}); | 2548 | const kernel_proto_ty_id = try cg.module.functionType(void_ty_id, &.{}); |
| 2556 | try section.emit(gpa, .OpFunction, .{ | 2549 | try section.emit(gpa, .OpFunction, .{ |
| ... | @@ -2599,13 +2592,7 @@ fn generateTestEntryPoint( | ... | @@ -2599,13 +2592,7 @@ fn generateTestEntryPoint( |
| 2599 | // point name is the same as a different OpName. | 2592 | // point name is the same as a different OpName. |
| 2600 | const test_name = try std.fmt.allocPrint(cg.module.arena, "test {s}", .{name}); | 2593 | const test_name = try std.fmt.allocPrint(cg.module.arena, "test {s}", .{name}); |
| 2601 | 2594 | ||
| 2602 | const execution_mode: spec.ExecutionModel = switch (target.os.tag) { | 2595 | try cg.module.declareEntryPoint(spv_decl_index, test_name, .{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }); |
| 2603 | .vulkan, .opengl => .gl_compute, | ||
| 2604 | .opencl, .amdhsa => .kernel, | ||
| 2605 | else => unreachable, | ||
| 2606 | }; | ||
| 2607 | |||
| 2608 | try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null); | ||
| 2609 | } | 2596 | } |
| 2610 | 2597 | ||
| 2611 | fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary { | 2598 | fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary { |
src/codegen/spirv/Module.zig+76-20| ... | @@ -132,15 +132,10 @@ pub const Decl = struct { | ... | @@ -132,15 +132,10 @@ pub const Decl = struct { |
| 132 | end_dep: usize = 0, | 132 | end_dep: usize = 0, |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | /// This models a kernel entry point. | ||
| 136 | pub const EntryPoint = struct { | 135 | pub const EntryPoint = struct { |
| 137 | /// The declaration that should be exported. | ||
| 138 | decl_index: Decl.Index, | 136 | decl_index: Decl.Index, |
| 139 | /// The name of the kernel to be exported. | ||
| 140 | name: []const u8, | 137 | name: []const u8, |
| 141 | /// Calling Convention | 138 | cc: std.builtin.CallingConvention, |
| 142 | exec_model: spec.ExecutionModel, | ||
| 143 | exec_mode: ?spec.ExecutionMode = null, | ||
| 144 | }; | 139 | }; |
| 145 | 140 | ||
| 146 | const StructType = struct { | 141 | const StructType = struct { |
| ... | @@ -320,25 +315,89 @@ fn entryPoints(module: *Module) !Section { | ... | @@ -320,25 +315,89 @@ fn entryPoints(module: *Module) !Section { |
| 320 | interface.items.len = 0; | 315 | interface.items.len = 0; |
| 321 | seen.setRangeValue(.{ .start = 0, .end = module.decls.items.len }, false); | 316 | seen.setRangeValue(.{ .start = 0, .end = module.decls.items.len }, false); |
| 322 | 317 | ||
| 318 | const exec_model: spec.ExecutionModel = switch (target.os.tag) { | ||
| 319 | .vulkan, .opengl => switch (entry_point.cc) { | ||
| 320 | .spirv_vertex => .vertex, | ||
| 321 | .spirv_fragment => .fragment, | ||
| 322 | .spirv_kernel => .gl_compute, | ||
| 323 | .spirv_task => .task_ext, | ||
| 324 | .spirv_mesh => .mesh_ext, | ||
| 325 | // TODO: We should integrate with the Linkage capability and export this function | ||
| 326 | .spirv_device => continue, | ||
| 327 | else => unreachable, | ||
| 328 | }, | ||
| 329 | .opencl => switch (entry_point.cc) { | ||
| 330 | .spirv_kernel => .kernel, | ||
| 331 | // TODO: We should integrate with the Linkage capability and export this function | ||
| 332 | .spirv_device => continue, | ||
| 333 | else => unreachable, | ||
| 334 | }, | ||
| 335 | else => unreachable, | ||
| 336 | }; | ||
| 323 | try module.addEntryPointDeps(entry_point.decl_index, &seen, &interface); | 337 | try module.addEntryPointDeps(entry_point.decl_index, &seen, &interface); |
| 324 | try entry_points.emit(module.gpa, .OpEntryPoint, .{ | 338 | try entry_points.emit(module.gpa, .OpEntryPoint, .{ |
| 325 | .execution_model = entry_point.exec_model, | 339 | .execution_model = exec_model, |
| 326 | .entry_point = entry_point_id, | 340 | .entry_point = entry_point_id, |
| 327 | .name = entry_point.name, | 341 | .name = entry_point.name, |
| 328 | .interface = interface.items, | 342 | .interface = interface.items, |
| 329 | }); | 343 | }); |
| 330 | 344 | ||
| 331 | if (entry_point.exec_mode == null and entry_point.exec_model == .fragment) { | 345 | switch (entry_point.cc) { |
| 332 | switch (target.os.tag) { | 346 | .spirv_kernel, .spirv_task => |kernel| { |
| 333 | .vulkan, .opengl => |tag| { | 347 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ |
| 348 | .entry_point = entry_point_id, | ||
| 349 | .mode = .{ .local_size = .{ | ||
| 350 | .x_size = kernel.x, | ||
| 351 | .y_size = kernel.y, | ||
| 352 | .z_size = kernel.z, | ||
| 353 | } }, | ||
| 354 | }); | ||
| 355 | }, | ||
| 356 | .spirv_fragment => |fragment| { | ||
| 357 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | ||
| 358 | .entry_point = entry_point_id, | ||
| 359 | .mode = if (target.os.tag == .vulkan) .origin_upper_left else .origin_lower_left, | ||
| 360 | }); | ||
| 361 | if (fragment.pixel_centered_integer) { | ||
| 334 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | 362 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ |
| 335 | .entry_point = entry_point_id, | 363 | .entry_point = entry_point_id, |
| 336 | .mode = if (tag == .vulkan) .origin_upper_left else .origin_lower_left, | 364 | .mode = .pixel_center_integer, |
| 337 | }); | 365 | }); |
| 338 | }, | 366 | } |
| 339 | .opencl => {}, | 367 | |
| 340 | else => unreachable, | 368 | const exec_mode: ?spec.ExecutionMode.Extended = switch (fragment.depth_assumption) { |
| 341 | } | 369 | .none => null, |
| 370 | .greater => .depth_greater, | ||
| 371 | .less => .depth_less, | ||
| 372 | .unchanged => .depth_unchanged, | ||
| 373 | }; | ||
| 374 | if (exec_mode) |mode| { | ||
| 375 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | ||
| 376 | .entry_point = entry_point_id, | ||
| 377 | .mode = mode, | ||
| 378 | }); | ||
| 379 | } | ||
| 380 | }, | ||
| 381 | .spirv_mesh => |mesh| { | ||
| 382 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | ||
| 383 | .entry_point = entry_point_id, | ||
| 384 | .mode = .{ .output_vertices = .{ .vertex_count = mesh.max_vertices } }, | ||
| 385 | }); | ||
| 386 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | ||
| 387 | .entry_point = entry_point_id, | ||
| 388 | .mode = .{ .output_primitives_ext = .{ .primitive_count = mesh.max_primitives } }, | ||
| 389 | }); | ||
| 390 | |||
| 391 | try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{ | ||
| 392 | .entry_point = entry_point_id, | ||
| 393 | .mode = switch (mesh.stage_output) { | ||
| 394 | .output_points => .output_points, | ||
| 395 | .output_lines => .output_lines_ext, | ||
| 396 | .output_triangles => .output_triangles_ext, | ||
| 397 | }, | ||
| 398 | }); | ||
| 399 | }, | ||
| 400 | else => {}, // TODO: should this be unreachable? | ||
| 342 | } | 401 | } |
| 343 | } | 402 | } |
| 344 | 403 | ||
| ... | @@ -925,15 +984,12 @@ pub fn declareEntryPoint( | ... | @@ -925,15 +984,12 @@ pub fn declareEntryPoint( |
| 925 | module: *Module, | 984 | module: *Module, |
| 926 | decl_index: Decl.Index, | 985 | decl_index: Decl.Index, |
| 927 | name: []const u8, | 986 | name: []const u8, |
| 928 | exec_model: spec.ExecutionModel, | 987 | cc: std.builtin.CallingConvention, |
| 929 | exec_mode: ?spec.ExecutionMode, | ||
| 930 | ) !void { | 988 | ) !void { |
| 931 | const gop = try module.entry_points.getOrPut(module.gpa, module.declPtr(decl_index).result_id); | 989 | const gop = try module.entry_points.getOrPut(module.gpa, module.declPtr(decl_index).result_id); |
| 932 | gop.value_ptr.decl_index = decl_index; | 990 | gop.value_ptr.decl_index = decl_index; |
| 933 | gop.value_ptr.name = name; | 991 | gop.value_ptr.name = name; |
| 934 | gop.value_ptr.exec_model = exec_model; | 992 | gop.value_ptr.cc = cc; |
| 935 | // Might've been set by assembler | ||
| 936 | if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode; | ||
| 937 | } | 993 | } |
| 938 | 994 | ||
| 939 | pub fn debugName(module: *Module, target: Id, name: []const u8) !void { | 995 | pub fn debugName(module: *Module, target: Id, name: []const u8) !void { |
src/link/SpirV.zig+2-20| ... | @@ -179,35 +179,17 @@ pub fn updateExports( | ... | @@ -179,35 +179,17 @@ pub fn updateExports( |
| 179 | }, | 179 | }, |
| 180 | }; | 180 | }; |
| 181 | const nav_ty = ip.getNav(nav_index).resolved.?.type; | 181 | const nav_ty = ip.getNav(nav_index).resolved.?.type; |
| 182 | const target = zcu.getTarget(); | ||
| 183 | if (ip.isFunctionType(nav_ty)) { | 182 | if (ip.isFunctionType(nav_ty)) { |
| 184 | const spv_decl_index = try linker.module.resolveNav(ip, nav_index); | 183 | const spv_decl_index = try linker.module.resolveNav(ip, nav_index); |
| 185 | const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); | 184 | const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); |
| 186 | const exec_model: spec.ExecutionModel = switch (target.os.tag) { | 185 | if (cc == .spirv_device) return; |
| 187 | .vulkan, .opengl => switch (cc) { | ||
| 188 | .spirv_vertex => .vertex, | ||
| 189 | .spirv_fragment => .fragment, | ||
| 190 | .spirv_kernel => .gl_compute, | ||
| 191 | // TODO: We should integrate with the Linkage capability and export this function | ||
| 192 | .spirv_device => return, | ||
| 193 | else => unreachable, | ||
| 194 | }, | ||
| 195 | .opencl => switch (cc) { | ||
| 196 | .spirv_kernel => .kernel, | ||
| 197 | // TODO: We should integrate with the Linkage capability and export this function | ||
| 198 | .spirv_device => return, | ||
| 199 | else => unreachable, | ||
| 200 | }, | ||
| 201 | else => unreachable, | ||
| 202 | }; | ||
| 203 | 186 | ||
| 204 | for (export_indices) |export_idx| { | 187 | for (export_indices) |export_idx| { |
| 205 | const exp = export_idx.ptr(zcu); | 188 | const exp = export_idx.ptr(zcu); |
| 206 | try linker.module.declareEntryPoint( | 189 | try linker.module.declareEntryPoint( |
| 207 | spv_decl_index, | 190 | spv_decl_index, |
| 208 | exp.opts.name.toSlice(ip), | 191 | exp.opts.name.toSlice(ip), |
| 209 | exec_model, | 192 | cc, |
| 210 | null, | ||
| 211 | ); | 193 | ); |
| 212 | } | 194 | } |
| 213 | } | 195 | } |
test/cases/callconv_spirv.zig created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | export fn vert() callconv(.spirv_vertex) void {} | ||
| 2 | export fn frag() callconv(.{ .spirv_fragment = .{ .depth_assumption = .greater } }) void {} | ||
| 3 | export fn comp() callconv(.{ .spirv_kernel = .{ .x = 8, .y = 8, .z = 1 } }) void {} | ||
| 4 | export fn task() callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) void {} | ||
| 5 | export fn mesh() callconv(.{ .spirv_mesh = .{ .stage_output = .output_lines, .max_primitives = 1, .max_vertices = 2 } }) void {} | ||
| 6 | |||
| 7 | // compile | ||
| 8 | // output_mode=Obj | ||
| 9 | // backend=selfhosted | ||
| 10 | // target=spirv64-vulkan | ||
| 11 | // emit_bin=false | ||
test/cases/compile_errors/callconv_spirv_invalid_options.zig created+29| ... | @@ -0,0 +1,29 @@ | ||
| 1 | const F1 = fn () callconv(.{ .spirv_kernel = .{ .x = 0, .y = 1, .z = 1 } }) void; | ||
| 2 | const F2 = fn () callconv(.{ .spirv_task = .{ .x = 1, .y = 0, .z = 1 } }) void; | ||
| 3 | const F3 = fn () callconv(.{ .spirv_mesh = .{ .max_vertices = 0 } }) void; | ||
| 4 | const F4 = fn () callconv(.{ .spirv_fragment = .{ .pixel_centered_integer = true } }) void; | ||
| 5 | export fn entry1() void { | ||
| 6 | const a: F1 = undefined; | ||
| 7 | _ = a; | ||
| 8 | } | ||
| 9 | export fn entry2() void { | ||
| 10 | const a: F2 = undefined; | ||
| 11 | _ = a; | ||
| 12 | } | ||
| 13 | export fn entry3() void { | ||
| 14 | const a: F3 = undefined; | ||
| 15 | _ = a; | ||
| 16 | } | ||
| 17 | export fn entry4() void { | ||
| 18 | const a: F4 = undefined; | ||
| 19 | _ = a; | ||
| 20 | } | ||
| 21 | |||
| 22 | // error | ||
| 23 | // backend=selfhosted | ||
| 24 | // target=spirv64-vulkan | ||
| 25 | // | ||
| 26 | // :1:28: error: kernel workgroup dimensions must be at least 1 | ||
| 27 | // :2:28: error: kernel workgroup dimensions must be at least 1 | ||
| 28 | // :3:28: error: mesh shader 'max_vertices' and 'max_primitives' must be at least 1 | ||
| 29 | // :4:28: error: 'pixel_centered_integer' is not supported on this target | ||
test/cases/compile_errors/callconv_spirv_mesh_task_require_vulkan.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | const F1 = fn () callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) void; | ||
| 2 | const F2 = fn () callconv(.{ .spirv_mesh = .{} }) void; | ||
| 3 | export fn entry1() void { | ||
| 4 | const a: F1 = undefined; | ||
| 5 | _ = a; | ||
| 6 | } | ||
| 7 | export fn entry2() void { | ||
| 8 | const a: F2 = undefined; | ||
| 9 | _ = a; | ||
| 10 | } | ||
| 11 | |||
| 12 | // error | ||
| 13 | // backend=selfhosted | ||
| 14 | // target=spirv64-opengl | ||
| 15 | // | ||
| 16 | // :1:28: error: calling convention 'spirv_task' not supported by compiler backend 'stage2_spirv' | ||
| 17 | // :2:28: error: calling convention 'spirv_mesh' not supported by compiler backend 'stage2_spirv' | ||
test/cases/compile_errors/callconv_spirv_on_unsupported_platform.zig created+29| ... | @@ -0,0 +1,29 @@ | ||
| 1 | const F1 = fn () callconv(.{ .spirv_fragment = .{} }) void; | ||
| 2 | const F2 = fn () callconv(.spirv_vertex) void; | ||
| 3 | const F3 = fn () callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) void; | ||
| 4 | const F4 = fn () callconv(.{ .spirv_mesh = .{} }) void; | ||
| 5 | export fn entry1() void { | ||
| 6 | const a: F1 = undefined; | ||
| 7 | _ = a; | ||
| 8 | } | ||
| 9 | export fn entry2() void { | ||
| 10 | const a: F2 = undefined; | ||
| 11 | _ = a; | ||
| 12 | } | ||
| 13 | export fn entry3() void { | ||
| 14 | const a: F3 = undefined; | ||
| 15 | _ = a; | ||
| 16 | } | ||
| 17 | export fn entry4() void { | ||
| 18 | const a: F4 = undefined; | ||
| 19 | _ = a; | ||
| 20 | } | ||
| 21 | |||
| 22 | // error | ||
| 23 | // backend=selfhosted | ||
| 24 | // target=spirv64-opencl | ||
| 25 | // | ||
| 26 | // :1:28: error: calling convention 'spirv_fragment' not supported by compiler backend 'stage2_spirv' | ||
| 27 | // :2:28: error: calling convention 'spirv_vertex' not supported by compiler backend 'stage2_spirv' | ||
| 28 | // :3:28: error: calling convention 'spirv_task' not supported by compiler backend 'stage2_spirv' | ||
| 29 | // :4:28: error: calling convention 'spirv_mesh' not supported by compiler backend 'stage2_spirv' | ||