authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2025-05-25 12:36:53+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-05-25 12:36:53+02:00
logdc6ffc28b57a96fd03f62bc665b6ed28b8e9e67b
treebeb8163c07c11faa90131a1df1d589016d93b231
parent35ba8d95a1afd0bebba3c32cf68990f5129fabfe
parent4bf1e4d198abd2018bf23f9067617800a2bc0554
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23815 from alichraghi/master

spirv: unroll all vector operations

17 files changed, 717 insertions(+), 705 deletions(-)

lib/std/Target.zig+1-1
......@@ -2014,7 +2014,7 @@ pub const Cpu = struct {
20142014 .global, .local, .shared => is_gpu,
20152015 .constant => is_gpu and (context == null or context == .constant),
20162016 .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,
20182018 };
20192019 }
20202020};
lib/std/Target/spirv.zig+127-76
......@@ -1,8 +1,21 @@
1//! This file is auto-generated by tools/update_cpu_features.zig.
2
13const std = @import("../std.zig");
24const CpuFeature = std.Target.Cpu.Feature;
35const CpuModel = std.Target.Cpu.Model;
46
57pub const Feature = enum {
8 addresses,
9 arbitrary_precision_integers,
10 float16,
11 float64,
12 generic_pointer,
13 int64,
14 kernel,
15 matrix,
16 physical_storage_buffer,
17 shader,
18 storage_push_constant16,
619 v1_0,
720 v1_1,
821 v1_2,
......@@ -10,18 +23,8 @@ pub const Feature = enum {
1023 v1_4,
1124 v1_5,
1225 v1_6,
13 int64,
14 float16,
15 float64,
16 matrix,
17 storage_push_constant16,
18 arbitrary_precision_integers,
19 kernel,
20 addresses,
21 generic_pointer,
26 variable_pointers,
2227 vector16,
23 shader,
24 physical_storage_buffer,
2528};
2629
2730pub const featureSet = CpuFeature.FeatureSetFns(Feature).featureSet;
......@@ -34,100 +37,143 @@ pub const all_features = blk: {
3437 const len = @typeInfo(Feature).@"enum".fields.len;
3538 std.debug.assert(len <= CpuFeature.Set.needed_bit_count);
3639 var result: [len]CpuFeature = undefined;
37 result[@intFromEnum(Feature.v1_0)] = .{
38 .llvm_name = null,
39 .description = "Enable version 1.0",
40 .dependencies = featureSet(&[_]Feature{}),
41 };
42 result[@intFromEnum(Feature.v1_1)] = .{
43 .llvm_name = null,
44 .description = "Enable version 1.1",
45 .dependencies = featureSet(&[_]Feature{.v1_0}),
46 };
47 result[@intFromEnum(Feature.v1_2)] = .{
40 result[@intFromEnum(Feature.addresses)] = .{
4841 .llvm_name = null,
49 .description = "Enable version 1.2",
50 .dependencies = featureSet(&[_]Feature{.v1_1}),
42 .description = "Enable Addresses capability",
43 .dependencies = featureSet(&[_]Feature{
44 .v1_0,
45 }),
5146 };
52 result[@intFromEnum(Feature.v1_3)] = .{
47 result[@intFromEnum(Feature.arbitrary_precision_integers)] = .{
5348 .llvm_name = null,
54 .description = "Enable version 1.3",
55 .dependencies = featureSet(&[_]Feature{.v1_2}),
49 .description = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",
50 .dependencies = featureSet(&[_]Feature{
51 .v1_5,
52 }),
5653 };
57 result[@intFromEnum(Feature.v1_4)] = .{
54 result[@intFromEnum(Feature.float16)] = .{
5855 .llvm_name = null,
59 .description = "Enable version 1.4",
60 .dependencies = featureSet(&[_]Feature{.v1_3}),
56 .description = "Enable Float16 capability",
57 .dependencies = featureSet(&[_]Feature{
58 .v1_0,
59 }),
6160 };
62 result[@intFromEnum(Feature.v1_5)] = .{
61 result[@intFromEnum(Feature.float64)] = .{
6362 .llvm_name = null,
64 .description = "Enable version 1.5",
65 .dependencies = featureSet(&[_]Feature{.v1_4}),
63 .description = "Enable Float64 capability",
64 .dependencies = featureSet(&[_]Feature{
65 .v1_0,
66 }),
6667 };
67 result[@intFromEnum(Feature.v1_6)] = .{
68 result[@intFromEnum(Feature.generic_pointer)] = .{
6869 .llvm_name = null,
69 .description = "Enable version 1.6",
70 .dependencies = featureSet(&[_]Feature{.v1_5}),
70 .description = "Enable GenericPointer capability",
71 .dependencies = featureSet(&[_]Feature{
72 .addresses,
73 }),
7174 };
7275 result[@intFromEnum(Feature.int64)] = .{
7376 .llvm_name = null,
7477 .description = "Enable Int64 capability",
75 .dependencies = featureSet(&[_]Feature{.v1_0}),
76 };
77 result[@intFromEnum(Feature.float16)] = .{
78 .llvm_name = null,
79 .description = "Enable Float16 capability",
80 .dependencies = featureSet(&[_]Feature{.v1_0}),
78 .dependencies = featureSet(&[_]Feature{
79 .v1_0,
80 }),
8181 };
82 result[@intFromEnum(Feature.float64)] = .{
82 result[@intFromEnum(Feature.kernel)] = .{
8383 .llvm_name = null,
84 .description = "Enable Float64 capability",
85 .dependencies = featureSet(&[_]Feature{.v1_0}),
84 .description = "Enable Kernel capability",
85 .dependencies = featureSet(&[_]Feature{
86 .v1_0,
87 }),
8688 };
8789 result[@intFromEnum(Feature.matrix)] = .{
8890 .llvm_name = null,
8991 .description = "Enable Matrix capability",
90 .dependencies = featureSet(&[_]Feature{.v1_0}),
92 .dependencies = featureSet(&[_]Feature{
93 .v1_0,
94 }),
95 };
96 result[@intFromEnum(Feature.physical_storage_buffer)] = .{
97 .llvm_name = null,
98 .description = "Enable SPV_KHR_variable_pointers extension and the (VariablePointers, VariablePointersStorageBuffer) capabilities",
99 .dependencies = featureSet(&[_]Feature{
100 .v1_0,
101 }),
102 };
103 result[@intFromEnum(Feature.shader)] = .{
104 .llvm_name = null,
105 .description = "Enable Shader capability",
106 .dependencies = featureSet(&[_]Feature{
107 .matrix,
108 }),
91109 };
92110 result[@intFromEnum(Feature.storage_push_constant16)] = .{
93111 .llvm_name = null,
94112 .description = "Enable SPV_KHR_16bit_storage extension and the StoragePushConstant16 capability",
95 .dependencies = featureSet(&[_]Feature{.v1_3}),
113 .dependencies = featureSet(&[_]Feature{
114 .v1_3,
115 }),
96116 };
97 result[@intFromEnum(Feature.arbitrary_precision_integers)] = .{
117 result[@intFromEnum(Feature.v1_0)] = .{
98118 .llvm_name = null,
99 .description = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",
100 .dependencies = featureSet(&[_]Feature{.v1_5}),
119 .description = "Enable version 1.0",
120 .dependencies = featureSet(&[_]Feature{}),
101121 };
102 result[@intFromEnum(Feature.kernel)] = .{
122 result[@intFromEnum(Feature.v1_1)] = .{
103123 .llvm_name = null,
104 .description = "Enable Kernel capability",
105 .dependencies = featureSet(&[_]Feature{.v1_0}),
124 .description = "Enable version 1.1",
125 .dependencies = featureSet(&[_]Feature{
126 .v1_0,
127 }),
106128 };
107 result[@intFromEnum(Feature.addresses)] = .{
129 result[@intFromEnum(Feature.v1_2)] = .{
108130 .llvm_name = null,
109 .description = "Enable Addresses capability",
110 .dependencies = featureSet(&[_]Feature{.v1_0}),
131 .description = "Enable version 1.2",
132 .dependencies = featureSet(&[_]Feature{
133 .v1_1,
134 }),
111135 };
112 result[@intFromEnum(Feature.generic_pointer)] = .{
136 result[@intFromEnum(Feature.v1_3)] = .{
113137 .llvm_name = null,
114 .description = "Enable GenericPointer capability",
115 .dependencies = featureSet(&[_]Feature{ .v1_0, .addresses }),
138 .description = "Enable version 1.3",
139 .dependencies = featureSet(&[_]Feature{
140 .v1_2,
141 }),
116142 };
117 result[@intFromEnum(Feature.vector16)] = .{
143 result[@intFromEnum(Feature.v1_4)] = .{
118144 .llvm_name = null,
119 .description = "Enable Vector16 capability",
120 .dependencies = featureSet(&[_]Feature{ .v1_0, .kernel }),
145 .description = "Enable version 1.4",
146 .dependencies = featureSet(&[_]Feature{
147 .v1_3,
148 }),
121149 };
122 result[@intFromEnum(Feature.shader)] = .{
150 result[@intFromEnum(Feature.v1_5)] = .{
123151 .llvm_name = null,
124 .description = "Enable Shader capability",
125 .dependencies = featureSet(&[_]Feature{ .v1_0, .matrix }),
152 .description = "Enable version 1.5",
153 .dependencies = featureSet(&[_]Feature{
154 .v1_4,
155 }),
126156 };
127 result[@intFromEnum(Feature.physical_storage_buffer)] = .{
157 result[@intFromEnum(Feature.v1_6)] = .{
158 .llvm_name = null,
159 .description = "Enable version 1.6",
160 .dependencies = featureSet(&[_]Feature{
161 .v1_5,
162 }),
163 };
164 result[@intFromEnum(Feature.variable_pointers)] = .{
128165 .llvm_name = null,
129166 .description = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",
130 .dependencies = featureSet(&[_]Feature{.v1_0}),
167 .dependencies = featureSet(&[_]Feature{
168 .v1_0,
169 }),
170 };
171 result[@intFromEnum(Feature.vector16)] = .{
172 .llvm_name = null,
173 .description = "Enable Vector16 capability",
174 .dependencies = featureSet(&[_]Feature{
175 .kernel,
176 }),
131177 };
132178 const ti = @typeInfo(Feature);
133179 for (&result, 0..) |*elem, i| {
......@@ -141,18 +187,23 @@ pub const cpu = struct {
141187 pub const generic: CpuModel = .{
142188 .name = "generic",
143189 .llvm_name = "generic",
144 .features = featureSet(&[_]Feature{.v1_0}),
190 .features = featureSet(&[_]Feature{}),
145191 };
146
147 pub const vulkan_v1_2: CpuModel = .{
148 .name = "vulkan_v1_2",
149 .llvm_name = null,
150 .features = featureSet(&[_]Feature{ .v1_5, .shader, .physical_storage_buffer }),
151 };
152
153192 pub const opencl_v2: CpuModel = .{
154193 .name = "opencl_v2",
155194 .llvm_name = null,
156 .features = featureSet(&[_]Feature{ .v1_2, .kernel, .addresses, .generic_pointer }),
195 .features = featureSet(&[_]Feature{
196 .generic_pointer,
197 .kernel,
198 .v1_2,
199 }),
200 };
201 pub const vulkan_v1_2: CpuModel = .{
202 .name = "vulkan_v1_2",
203 .llvm_name = null,
204 .features = featureSet(&[_]Feature{
205 .shader,
206 .v1_5,
207 }),
157208 };
158209};
lib/std/builtin.zig+1
......@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {
531531 uniform,
532532 push_constant,
533533 storage_buffer,
534 physical_storage_buffer,
534535
535536 // AVR address spaces.
536537 flash,
lib/std/gpu.zig+91-137
......@@ -1,81 +1,24 @@
11const std = @import("std.zig");
22
3/// Will make `ptr` contain the location of the current invocation within the
4/// global workgroup. Each component is equal to the index of the local workgroup
5/// multiplied by the size of the local workgroup plus `localInvocationId`.
6/// `ptr` must be a reference to variable or struct field.
7pub fn globalInvocationId(comptime ptr: *addrspace(.input) @Vector(3, u32)) void {
8 asm volatile (
9 \\OpDecorate %ptr BuiltIn GlobalInvocationId
10 :
11 : [ptr] "" (ptr),
12 );
13}
14
15/// Will make that variable contain the location of the current cluster
16/// culling, task, mesh, or compute shader invocation within the local
17/// workgroup. Each component ranges from zero through to the size of the
18/// workgroup in that dimension minus one.
19/// `ptr` must be a reference to variable or struct field.
20pub fn localInvocationId(comptime ptr: *addrspace(.input) @Vector(3, u32)) void {
21 asm volatile (
22 \\OpDecorate %ptr BuiltIn LocalInvocationId
23 :
24 : [ptr] "" (ptr),
25 );
26}
27
28/// Output vertex position from a `Vertex` entrypoint
29/// `ptr` must be a reference to variable or struct field.
30pub fn position(comptime ptr: *addrspace(.output) @Vector(4, f32)) void {
31 asm volatile (
32 \\OpDecorate %ptr BuiltIn Position
33 :
34 : [ptr] "" (ptr),
35 );
36}
37
38/// Will make `ptr` contain the index of the vertex that is
39/// being processed by the current vertex shader invocation.
40/// `ptr` must be a reference to variable or struct field.
41pub fn vertexIndex(comptime ptr: *addrspace(.input) u32) void {
42 asm volatile (
43 \\OpDecorate %ptr BuiltIn VertexIndex
44 :
45 : [ptr] "" (ptr),
46 );
47}
48
49/// Will make `ptr` contain the index of the instance that is
50/// being processed by the current vertex shader invocation.
51/// `ptr` must be a reference to variable or struct field.
52pub fn instanceIndex(comptime ptr: *addrspace(.input) u32) void {
53 asm volatile (
54 \\OpDecorate %ptr BuiltIn InstanceIndex
55 :
56 : [ptr] "" (ptr),
57 );
58}
59
60/// Output fragment depth from a `Fragment` entrypoint
61/// `ptr` must be a reference to variable or struct field.
62pub fn fragmentCoord(comptime ptr: *addrspace(.input) @Vector(4, f32)) void {
63 asm volatile (
64 \\OpDecorate %ptr BuiltIn FragCoord
65 :
66 : [ptr] "" (ptr),
67 );
68}
69
70/// Output fragment depth from a `Fragment` entrypoint
71/// `ptr` must be a reference to variable or struct field.
72pub fn fragmentDepth(comptime ptr: *addrspace(.output) f32) void {
73 asm volatile (
74 \\OpDecorate %ptr BuiltIn FragDepth
75 :
76 : [ptr] "" (ptr),
77 );
78}
3pub const position_in = @extern(*addrspace(.input) @Vector(4, f32), .{ .name = "position" });
4pub const position_out = @extern(*addrspace(.output) @Vector(4, f32), .{ .name = "position" });
5pub const point_size_in = @extern(*addrspace(.input) f32, .{ .name = "point_size" });
6pub const point_size_out = @extern(*addrspace(.output) f32, .{ .name = "point_size" });
7pub extern const invocation_id: u32 addrspace(.input);
8pub extern const frag_coord: @Vector(4, f32) addrspace(.input);
9pub extern const point_coord: @Vector(2, f32) addrspace(.input);
10// TODO: direct/indirect values
11// pub extern const front_facing: bool addrspace(.input);
12// TODO: runtime array
13// pub extern const sample_mask;
14pub extern var frag_depth: f32 addrspace(.output);
15pub extern const num_workgroups: @Vector(3, u32) addrspace(.input);
16pub extern const workgroup_size: @Vector(3, u32) addrspace(.input);
17pub extern const workgroup_id: @Vector(3, u32) addrspace(.input);
18pub extern const local_invocation_id: @Vector(3, u32) addrspace(.input);
19pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input);
20pub extern const vertex_index: u32 addrspace(.input);
21pub extern const instance_index: u32 addrspace(.input);
7922
8023/// Forms the main linkage for `input` and `output` address spaces.
8124/// `ptr` must be a reference to variable or struct field.
......@@ -101,74 +44,85 @@ pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) voi
10144 );
10245}
10346
104pub const Origin = enum(u32) {
105 /// Increase toward the right and downward
106 upper_left = 7,
107 /// Increase toward the right and upward
108 lower_left = 8,
109};
110
111/// The coordinates appear to originate in the specified `origin`.
112/// Only valid with the `Fragment` calling convention.
113pub fn fragmentOrigin(comptime entry_point: anytype, comptime origin: Origin) void {
114 asm volatile (
115 \\OpExecutionMode %entry_point $origin
116 :
117 : [entry_point] "" (entry_point),
118 [origin] "c" (@intFromEnum(origin)),
119 );
120}
121
122pub const DepthMode = enum(u32) {
123 /// Declares that this entry point dynamically writes the
124 /// `fragmentDepth` built in-decorated variable.
125 replacing = 12,
47pub const ExecutionMode = union(Tag) {
48 /// Sets origin of the framebuffer to the upper-left corner
49 origin_upper_left,
50 /// Sets origin of the framebuffer to the lower-left corner
51 origin_lower_left,
52 /// Indicates that the fragment shader writes to `frag_depth`,
53 /// replacing the fixed-function depth value.
54 depth_replacing,
12655 /// Indicates that per-fragment tests may assume that
127 /// any `fragmentDepth` built in-decorated value written by the shader is
56 /// any `frag_depth` built in-decorated value written by the shader is
12857 /// greater-than-or-equal to the fragment’s interpolated depth value
129 greater = 14,
58 depth_greater,
13059 /// Indicates that per-fragment tests may assume that
131 /// any `fragmentDepth` built in-decorated value written by the shader is
60 /// any `frag_depth` built in-decorated value written by the shader is
13261 /// less-than-or-equal to the fragment’s interpolated depth value
133 less = 15,
62 depth_less,
13463 /// Indicates that per-fragment tests may assume that
135 /// any `fragmentDepth` built in-decorated value written by the shader is
64 /// any `frag_depth` built in-decorated value written by the shader is
13665 /// the same as the fragment’s interpolated depth value
137 unchanged = 16,
138};
66 depth_unchanged,
67 /// Indicates the workgroup size in the x, y, and z dimensions.
68 local_size: LocalSize,
13969
140/// Only valid with the `Fragment` calling convention.
141pub fn depthMode(comptime entry_point: anytype, comptime mode: DepthMode) void {
142 asm volatile (
143 \\OpExecutionMode %entry_point $mode
144 :
145 : [entry_point] "" (entry_point),
146 [mode] "c" (mode),
147 );
148}
70 pub const Tag = enum(u32) {
71 origin_upper_left = 7,
72 origin_lower_left = 8,
73 depth_replacing = 12,
74 depth_greater = 14,
75 depth_less = 15,
76 depth_unchanged = 16,
77 local_size = 17,
78 };
14979
150/// Indicates the workgroup size in the `x`, `y`, and `z` dimensions.
151/// Only valid with the `GLCompute` or `Kernel` calling conventions.
152pub fn workgroupSize(comptime entry_point: anytype, comptime size: @Vector(3, u32)) void {
153 asm volatile (
154 \\OpExecutionMode %entry_point LocalSize %x %y %z
155 :
156 : [entry_point] "" (entry_point),
157 [x] "c" (size[0]),
158 [y] "c" (size[1]),
159 [z] "c" (size[2]),
160 );
161}
80 pub const LocalSize = struct { x: u32, y: u32, z: u32 };
81};
16282
163/// A hint to the client, which indicates the workgroup size in the `x`, `y`, and `z` dimensions.
164/// Only valid with the `GLCompute` or `Kernel` calling conventions.
165pub fn workgroupSizeHint(comptime entry_point: anytype, comptime size: @Vector(3, u32)) void {
166 asm volatile (
167 \\OpExecutionMode %entry_point LocalSizeHint %x %y %z
168 :
169 : [entry_point] "" (entry_point),
170 [x] "c" (size[0]),
171 [y] "c" (size[1]),
172 [z] "c" (size[2]),
173 );
83/// Declare the mode entry point executes in.
84pub fn executionMode(comptime entry_point: anytype, comptime mode: ExecutionMode) void {
85 const cc = @typeInfo(@TypeOf(entry_point)).@"fn".calling_convention;
86 switch (mode) {
87 .origin_upper_left,
88 .origin_lower_left,
89 .depth_replacing,
90 .depth_greater,
91 .depth_less,
92 .depth_unchanged,
93 => {
94 if (cc != .spirv_fragment) {
95 @compileError(
96 \\invalid execution mode '
97 ++ @tagName(mode) ++
98 \\' for function with '
99 ++ @tagName(cc) ++
100 \\' calling convention
101 );
102 }
103 asm volatile (
104 \\OpExecutionMode %entry_point $mode
105 :
106 : [entry_point] "" (entry_point),
107 [mode] "c" (@intFromEnum(mode)),
108 );
109 },
110 .local_size => |size| {
111 if (cc != .spirv_kernel) {
112 @compileError(
113 \\invalid execution mode 'local_size' for function with '
114 ++ @tagName(cc) ++
115 \\' calling convention
116 );
117 }
118 asm volatile (
119 \\OpExecutionMode %entry_point LocalSize $x $y $z
120 :
121 : [entry_point] "" (entry_point),
122 [x] "c" (size.x),
123 [y] "c" (size.y),
124 [z] "c" (size.z),
125 );
126 },
127 }
174128}
src/Zcu.zig+1-1
......@@ -3693,7 +3693,7 @@ pub fn errorSetBits(zcu: *const Zcu) u16 {
36933693 const target = zcu.getTarget();
36943694
36953695 if (zcu.error_limit == 0) return 0;
3696 if (target.cpu.arch == .spirv64) {
3696 if (target.cpu.arch.isSpirV()) {
36973697 if (!std.Target.spirv.featureSetHas(target.cpu.features, .storage_push_constant16)) {
36983698 return 32;
36993699 }
src/codegen/spirv.zig+282-436
......@@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig");
3030const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
3131
3232pub const zig_call_abi_ver = 3;
33pub const big_int_bits = 32;
3334
3435const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult);
3536const PtrTypeMap = std.AutoHashMapUnmanaged(
......@@ -169,12 +170,10 @@ pub const Object = struct {
169170 /// via the usual `intern_map` mechanism.
170171 ptr_types: PtrTypeMap = .{},
171172
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
173 /// For test declarations for Vulkan, we have to add a buffer.
174 /// We only need to generate this once, this holds the link information
174175 /// related to that.
175 error_push_constant: ?struct {
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
176 error_buffer: ?SpvModule.Decl.Index = null,
178177
179178 pub fn init(gpa: Allocator, target: std.Target) Object {
180179 return .{
......@@ -344,8 +343,7 @@ const NavGen = struct {
344343
345344 /// This structure is used to return information about a type typically used for
346345 /// arithmetic operations. These types may either be integers, floats, or a vector
347 /// of these. Most scalar operations also work on vectors, so we can easily represent
348 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the
346 /// of these. If the type is a scalar, 'inner type' refers to the
349347 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.
350348 const ArithmeticTypeInfo = struct {
351349 /// A classification of the inner type.
......@@ -379,7 +377,7 @@ const NavGen = struct {
379377 /// The number of bits required to store the type.
380378 /// For `integer` and `float`, this is equal to `bits`.
381379 /// For `strange_integer` and `bool` this is the size of the backing integer.
382 /// For `composite_integer` this is 0 (TODO)
380 /// For `composite_integer` this is the elements count.
383381 backing_bits: u16,
384382
385383 /// Null if this type is a scalar, or the length
......@@ -582,11 +580,13 @@ const NavGen = struct {
582580 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
583581 /// The result is valid to be used with OpTypeInt.
584582 /// TODO: Should the result of this function be cached?
585 fn backingIntBits(self: *NavGen, bits: u16) ?u16 {
583 fn backingIntBits(self: *NavGen, bits: u16) struct { u16, bool } {
586584 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
587585 assert(bits != 0);
588586
589 if (self.spv.hasFeature(.arbitrary_precision_integers) and bits <= 32) return bits;
587 if (self.spv.hasFeature(.arbitrary_precision_integers) and bits <= 32) {
588 return .{ bits, false };
589 }
590590
591591 // We require Int8 and Int16 capabilities and benefit Int64 when available.
592592 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).
......@@ -599,10 +599,11 @@ const NavGen = struct {
599599
600600 for (ints) |int| {
601601 const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true;
602 if (bits <= int.bits and has_feature) return int.bits;
602 if (bits <= int.bits and has_feature) return .{ int.bits, false };
603603 }
604604
605 return null;
605 // Big int
606 return .{ std.mem.alignForward(u16, bits, big_int_bits), true };
606607 }
607608
608609 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if
......@@ -615,41 +616,6 @@ const NavGen = struct {
615616 return if (self.spv.hasFeature(.int64)) 64 else 32;
616617 }
617618
618 /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by
619 /// arrays of largestSupportedIntBits().
620 /// Asserts `ty` is an integer.
621 fn isCompositeInt(self: *NavGen, ty: Type) bool {
622 return self.backingIntBits(ty) == null;
623 }
624
625 /// Checks whether the type can be directly translated to SPIR-V vectors
626 fn isSpvVector(self: *NavGen, ty: Type) bool {
627 const zcu = self.pt.zcu;
628 if (ty.zigTypeTag(zcu) != .vector) return false;
629
630 // TODO: This check must be expanded for types that can be represented
631 // as integers (enums / packed structs?) and types that are represented
632 // by multiple SPIR-V values.
633 const scalar_ty = ty.scalarType(zcu);
634 switch (scalar_ty.zigTypeTag(zcu)) {
635 .bool,
636 .int,
637 .float,
638 => {},
639 else => return false,
640 }
641
642 const elem_ty = ty.childType(zcu);
643 const len = ty.vectorLen(zcu);
644
645 if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) {
646 if (len > 1 and len <= 4) return true;
647 if (self.spv.hasFeature(.vector16)) return (len == 8 or len == 16);
648 }
649
650 return false;
651 }
652
653619 fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo {
654620 const zcu = self.pt.zcu;
655621 const target = self.spv.target;
......@@ -659,14 +625,14 @@ const NavGen = struct {
659625 }
660626 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
661627 return switch (scalar_ty.zigTypeTag(zcu)) {
662 .bool => ArithmeticTypeInfo{
628 .bool => .{
663629 .bits = 1, // Doesn't matter for this class.
664 .backing_bits = self.backingIntBits(1).?,
630 .backing_bits = self.backingIntBits(1).@"0",
665631 .vector_len = vector_len,
666632 .signedness = .unsigned, // Technically, but doesn't matter for this class.
667633 .class = .bool,
668634 },
669 .float => ArithmeticTypeInfo{
635 .float => .{
670636 .bits = scalar_ty.floatBits(target),
671637 .backing_bits = scalar_ty.floatBits(target), // TODO: F80?
672638 .vector_len = vector_len,
......@@ -676,19 +642,16 @@ const NavGen = struct {
676642 .int => blk: {
677643 const int_info = scalar_ty.intInfo(zcu);
678644 // TODO: Maybe it's useful to also return this value.
679 const maybe_backing_bits = self.backingIntBits(int_info.bits);
680 break :blk ArithmeticTypeInfo{
645 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
646 break :blk .{
681647 .bits = int_info.bits,
682 .backing_bits = maybe_backing_bits orelse 0,
648 .backing_bits = backing_bits,
683649 .vector_len = vector_len,
684650 .signedness = int_info.signedness,
685 .class = if (maybe_backing_bits) |backing_bits|
686 if (backing_bits == int_info.bits)
687 ArithmeticTypeInfo.Class.integer
688 else
689 ArithmeticTypeInfo.Class.strange_integer
690 else
691 .composite_integer,
651 .class = class: {
652 if (big_int) break :class .composite_integer;
653 break :class if (backing_bits == int_info.bits) .integer else .strange_integer;
654 },
692655 };
693656 },
694657 .@"enum" => unreachable,
......@@ -697,6 +660,34 @@ const NavGen = struct {
697660 };
698661 }
699662
663 /// Checks whether the type can be directly translated to SPIR-V vectors
664 fn isSpvVector(self: *NavGen, ty: Type) bool {
665 const zcu = self.pt.zcu;
666 if (ty.zigTypeTag(zcu) != .vector) return false;
667
668 // TODO: This check must be expanded for types that can be represented
669 // as integers (enums / packed structs?) and types that are represented
670 // by multiple SPIR-V values.
671 const scalar_ty = ty.scalarType(zcu);
672 switch (scalar_ty.zigTypeTag(zcu)) {
673 .bool,
674 .int,
675 .float,
676 => {},
677 else => return false,
678 }
679
680 const elem_ty = ty.childType(zcu);
681 const len = ty.vectorLen(zcu);
682
683 if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) {
684 if (len > 1 and len <= 4) return true;
685 if (self.spv.hasFeature(.vector16)) return (len == 8 or len == 16);
686 }
687
688 return false;
689 }
690
700691 /// Emits a bool constant in a particular representation.
701692 fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef {
702693 return switch (repr) {
......@@ -713,14 +704,26 @@ const NavGen = struct {
713704 const scalar_ty = ty.scalarType(zcu);
714705 const int_info = scalar_ty.intInfo(zcu);
715706 // Use backing bits so that negatives are sign extended
716 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int
707 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
717708 assert(backing_bits != 0); // u0 is comptime
718709
710 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
719711 const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {
720712 .int => |int| int.signedness,
721713 .comptime_int => if (value < 0) .signed else .unsigned,
722714 else => unreachable,
723715 };
716 if (@sizeOf(@TypeOf(value)) >= 4 and big_int) {
717 const value64: u64 = switch (signedness) {
718 .signed => @bitCast(@as(i64, @intCast(value))),
719 .unsigned => @as(u64, @intCast(value)),
720 };
721 assert(backing_bits == 64);
722 return self.constructComposite(result_ty_id, &.{
723 try self.constInt(.u32, @as(u32, @truncate(value64))),
724 try self.constInt(.u32, @as(u32, @truncate(value64 << 32))),
725 });
726 }
724727
725728 const final_value: spec.LiteralContextDependentNumber = blk: {
726729 if (self.spv.hasFeature(.kernel)) {
......@@ -738,18 +741,17 @@ const NavGen = struct {
738741 break :blk switch (backing_bits) {
739742 1...32 => .{ .uint32 = @truncate(truncated_value) },
740743 33...64 => .{ .uint64 = truncated_value },
741 else => unreachable, // TODO: Large integer constants
744 else => unreachable,
742745 };
743746 }
744747
745748 break :blk switch (backing_bits) {
746749 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) },
747750 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value },
748 else => unreachable, // TODO: Large integer constants
751 else => unreachable,
749752 };
750753 };
751754
752 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
753755 const result_id = try self.spv.constant(result_ty_id, final_value);
754756
755757 if (!ty.isVector(zcu)) return result_id;
......@@ -987,7 +989,7 @@ const NavGen = struct {
987989 // TODO: composite int
988990 // TODO: endianness
989991 const bits: u16 = @intCast(ty.bitSize(zcu));
990 const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).?, 8) / 8;
992 const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).@"0", 8) / 8;
991993 var limbs: [8]u8 = undefined;
992994 @memset(&limbs, 0);
993995 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
......@@ -1106,19 +1108,11 @@ const NavGen = struct {
11061108 const parent_ptr_id = try self.derivePtr(oac.parent.*);
11071109 const parent_ptr_ty = try oac.parent.ptrType(pt);
11081110 const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct);
1111 const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
11091112
1110 if (oac.byte_offset != 0) {
1111 const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
1112 if (oac.byte_offset % child_size != 0) {
1113 return self.fail("cannot perform pointer cast: '{}' to '{}'", .{
1114 parent_ptr_ty.fmt(pt),
1115 oac.new_ptr_ty.fmt(pt),
1116 });
1117 }
1118
1113 if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 0) {
11191114 // Vector element ptr accesses are derived as offset_and_cast.
11201115 // We can just use OpAccessChain.
1121 assert(parent_ptr_ty.childType(zcu).zigTypeTag(zcu) == .vector);
11221116 return self.accessChain(
11231117 result_ty_id,
11241118 parent_ptr_id,
......@@ -1126,15 +1120,22 @@ const NavGen = struct {
11261120 );
11271121 }
11281122
1129 // Allow changing the pointer type child only to restructure arrays.
1130 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T.
1131 const result_ptr_id = self.spv.allocId();
1132 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
1133 .id_result_type = result_ty_id,
1134 .id_result = result_ptr_id,
1135 .operand = parent_ptr_id,
1123 if (oac.byte_offset == 0) {
1124 // Allow changing the pointer type child only to restructure arrays.
1125 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T.
1126 const result_ptr_id = self.spv.allocId();
1127 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
1128 .id_result_type = result_ty_id,
1129 .id_result = result_ptr_id,
1130 .operand = parent_ptr_id,
1131 });
1132 return result_ptr_id;
1133 }
1134
1135 return self.fail("cannot perform pointer cast: '{}' to '{}'", .{
1136 parent_ptr_ty.fmt(pt),
1137 oac.new_ptr_ty.fmt(pt),
11361138 });
1137 return result_ptr_id;
11381139 },
11391140 }
11401141 }
......@@ -1255,11 +1256,14 @@ const NavGen = struct {
12551256 /// actual operations (as well as store) a Zig type of a particular number of bits. To create
12561257 /// a type with an exact size, use SpvModule.intType.
12571258 fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef {
1258 const backing_bits = self.backingIntBits(bits) orelse {
1259 // TODO: Integers too big for any native type are represented as "composite integers":
1260 // An array of largestSupportedIntBits.
1261 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });
1262 };
1259 const backing_bits, const big_int = self.backingIntBits(bits);
1260 if (big_int) {
1261 if (backing_bits > 64) {
1262 return self.fail("composite integers larger than 64bit aren't supported", .{});
1263 }
1264 const int_ty = try self.resolveType(.u32, .direct);
1265 return self.arrayType(backing_bits / big_int_bits, int_ty);
1266 }
12631267
12641268 // Kernel only supports unsigned ints.
12651269 if (self.spv.hasFeature(.kernel)) {
......@@ -1338,19 +1342,6 @@ const NavGen = struct {
13381342 return self.spv.functionType(return_ty_id, param_ids);
13391343 }
13401344
1341 fn zigScalarOrVectorTypeLike(self: *NavGen, new_ty: Type, base_ty: Type) !Type {
1342 const pt = self.pt;
1343 const new_scalar_ty = new_ty.scalarType(pt.zcu);
1344 if (!base_ty.isVector(pt.zcu)) {
1345 return new_scalar_ty;
1346 }
1347
1348 return try pt.vectorType(.{
1349 .len = base_ty.vectorLen(pt.zcu),
1350 .child = new_scalar_ty.toIntern(),
1351 });
1352 }
1353
13541345 /// Generate a union type. Union types are always generated with the
13551346 /// most aligned field active. If the tag alignment is greater
13561347 /// than that of the payload, a regular union (non-packed, with both tag and
......@@ -1560,6 +1551,17 @@ const NavGen = struct {
15601551 return result_id;
15611552 }
15621553 },
1554 .vector => {
1555 const elem_ty = ty.childType(zcu);
1556 const elem_ty_id = try self.resolveType(elem_ty, repr);
1557 const len = ty.vectorLen(zcu);
1558
1559 if (self.isSpvVector(ty)) {
1560 return try self.spv.vectorType(len, elem_ty_id);
1561 } else {
1562 return try self.arrayType(len, elem_ty_id);
1563 }
1564 },
15631565 .@"fn" => switch (repr) {
15641566 .direct => {
15651567 const fn_info = zcu.typeToFunc(ty).?;
......@@ -1628,17 +1630,6 @@ const NavGen = struct {
16281630 );
16291631 return result_id;
16301632 },
1631 .vector => {
1632 const elem_ty = ty.childType(zcu);
1633 const elem_ty_id = try self.resolveType(elem_ty, repr);
1634 const len = ty.vectorLen(zcu);
1635
1636 if (self.isSpvVector(ty)) {
1637 return try self.spv.vectorType(len, elem_ty_id);
1638 } else {
1639 return try self.arrayType(len, elem_ty_id);
1640 }
1641 },
16421633 .@"struct" => {
16431634 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
16441635 .tuple_type => |tuple| {
......@@ -1793,15 +1784,34 @@ const NavGen = struct {
17931784 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
17941785 return switch (as) {
17951786 .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function,
1787 .global => {
1788 if (self.spv.hasFeature(.kernel)) return .CrossWorkgroup;
1789 return .StorageBuffer;
1790 },
1791 .push_constant => {
1792 assert(self.spv.hasFeature(.shader));
1793 return .PushConstant;
1794 },
1795 .output => {
1796 assert(self.spv.hasFeature(.shader));
1797 return .Output;
1798 },
1799 .uniform => {
1800 assert(self.spv.hasFeature(.shader));
1801 return .Uniform;
1802 },
1803 .storage_buffer => {
1804 assert(self.spv.hasFeature(.shader));
1805 return .StorageBuffer;
1806 },
1807 .physical_storage_buffer => {
1808 assert(self.spv.hasFeature(.physical_storage_buffer));
1809 return .PhysicalStorageBuffer;
1810 },
1811 .constant => .UniformConstant,
17961812 .shared => .Workgroup,
17971813 .local => .Function,
1798 .global => if (self.spv.hasFeature(.shader)) .PhysicalStorageBuffer else .CrossWorkgroup,
1799 .constant => .UniformConstant,
1800 .push_constant => .PushConstant,
18011814 .input => .Input,
1802 .output => .Output,
1803 .uniform => .Uniform,
1804 .storage_buffer => .StorageBuffer,
18051815 .gs,
18061816 .fs,
18071817 .ss,
......@@ -2035,69 +2045,32 @@ const NavGen = struct {
20352045 const Vectorization = union(enum) {
20362046 /// This is an operation between scalars.
20372047 scalar,
2038 /// This is an operation between SPIR-V vectors.
2039 /// Value is number of components.
2040 spv_vectorized: u32,
20412048 /// This operation is unrolled into separate operations.
20422049 /// Inputs may still be SPIR-V vectors, for example,
20432050 /// when the operation can't be vectorized in SPIR-V.
20442051 /// Value is number of components.
20452052 unrolled: u32,
20462053
2047 /// Derive a vectorization from a particular type. This usually
2048 /// only checks the size, but the source-of-truth is implemented
2049 /// by `isSpvVector()`.
2054 /// Derive a vectorization from a particular type
20502055 fn fromType(ty: Type, ng: *NavGen) Vectorization {
20512056 const zcu = ng.pt.zcu;
2052 if (!ty.isVector(zcu)) {
2053 return .scalar;
2054 } else if (ng.isSpvVector(ty)) {
2055 return .{ .spv_vectorized = ty.vectorLen(zcu) };
2056 } else {
2057 return .{ .unrolled = ty.vectorLen(zcu) };
2058 }
2057 if (!ty.isVector(zcu)) return .scalar;
2058 return .{ .unrolled = ty.vectorLen(zcu) };
20592059 }
20602060
20612061 /// Given two vectorization methods, compute a "unification": a fallback
20622062 /// that works for both, according to the following rules:
20632063 /// - Scalars may broadcast
2064 /// - SPIR-V vectorized operations may unroll
2065 /// - Prefer scalar > SPIR-V vectorized > unrolled
2064 /// - SPIR-V vectorized operations will unroll
2065 /// - Prefer scalar > unrolled
20662066 fn unify(a: Vectorization, b: Vectorization) Vectorization {
2067 if (a == .scalar and b == .scalar) {
2068 return .scalar;
2069 } else if (a == .spv_vectorized and b == .spv_vectorized) {
2070 assert(a.components() == b.components());
2071 return .{ .spv_vectorized = a.components() };
2072 } else if (a == .unrolled or b == .unrolled) {
2073 if (a == .unrolled and b == .unrolled) {
2074 assert(a.components() == b.components());
2075 return .{ .unrolled = a.components() };
2076 } else if (a == .unrolled) {
2077 return .{ .unrolled = a.components() };
2078 } else if (b == .unrolled) {
2079 return .{ .unrolled = b.components() };
2080 } else {
2081 unreachable;
2082 }
2083 } else {
2084 if (a == .spv_vectorized) {
2085 return .{ .spv_vectorized = a.components() };
2086 } else if (b == .spv_vectorized) {
2087 return .{ .spv_vectorized = b.components() };
2088 } else {
2089 unreachable;
2090 }
2067 if (a == .scalar and b == .scalar) return .scalar;
2068 if (a == .unrolled or b == .unrolled) {
2069 if (a == .unrolled and b == .unrolled) assert(a.components() == b.components());
2070 if (a == .unrolled) return .{ .unrolled = a.components() };
2071 return .{ .unrolled = b.components() };
20912072 }
2092 }
2093
2094 /// Force this vectorization to be unrolled, if its
2095 /// an operation involving vectors.
2096 fn unroll(self: Vectorization) Vectorization {
2097 return switch (self) {
2098 .scalar, .unrolled => self,
2099 .spv_vectorized => |n| .{ .unrolled = n },
2100 };
2073 unreachable;
21012074 }
21022075
21032076 /// Query the number of components that inputs of this operation have.
......@@ -2106,35 +2079,10 @@ const NavGen = struct {
21062079 fn components(self: Vectorization) u32 {
21072080 return switch (self) {
21082081 .scalar => 1,
2109 .spv_vectorized => |n| n,
2110 .unrolled => |n| n,
2111 };
2112 }
2113
2114 /// Query the number of operations involving this vectorization.
2115 /// This is basically the number of components, except that SPIR-V vectorized
2116 /// operations only need a single SPIR-V instruction.
2117 fn operations(self: Vectorization) u32 {
2118 return switch (self) {
2119 .scalar, .spv_vectorized => 1,
21202082 .unrolled => |n| n,
21212083 };
21222084 }
21232085
2124 /// Turns `ty` into the result-type of an individual vector operation.
2125 /// `ty` may be a scalar or vector, it doesn't matter.
2126 fn operationType(self: Vectorization, ng: *NavGen, ty: Type) !Type {
2127 const pt = ng.pt;
2128 const scalar_ty = ty.scalarType(pt.zcu);
2129 return switch (self) {
2130 .scalar, .unrolled => scalar_ty,
2131 .spv_vectorized => |n| try pt.vectorType(.{
2132 .len = n,
2133 .child = scalar_ty.toIntern(),
2134 }),
2135 };
2136 }
2137
21382086 /// Turns `ty` into the result-type of the entire operation.
21392087 /// `ty` may be a scalar or vector, it doesn't matter.
21402088 fn resultType(self: Vectorization, ng: *NavGen, ty: Type) !Type {
......@@ -2142,10 +2090,7 @@ const NavGen = struct {
21422090 const scalar_ty = ty.scalarType(pt.zcu);
21432091 return switch (self) {
21442092 .scalar => scalar_ty,
2145 .unrolled, .spv_vectorized => |n| try pt.vectorType(.{
2146 .len = n,
2147 .child = scalar_ty.toIntern(),
2148 }),
2093 .unrolled => |n| try pt.vectorType(.{ .len = n, .child = scalar_ty.toIntern() }),
21492094 };
21502095 }
21512096
......@@ -2155,51 +2100,19 @@ const NavGen = struct {
21552100 fn prepare(self: Vectorization, ng: *NavGen, tmp: Temporary) !PreparedOperand {
21562101 const pt = ng.pt;
21572102 const is_vector = tmp.ty.isVector(pt.zcu);
2158 const is_spv_vector = ng.isSpvVector(tmp.ty);
21592103 const value: PreparedOperand.Value = switch (tmp.value) {
21602104 .singleton => |id| switch (self) {
21612105 .scalar => blk: {
21622106 assert(!is_vector);
21632107 break :blk .{ .scalar = id };
21642108 },
2165 .spv_vectorized => blk: {
2166 if (is_vector) {
2167 assert(is_spv_vector);
2168 break :blk .{ .spv_vectorwise = id };
2169 }
2170
2171 // Broadcast scalar into vector.
2172 const vector_ty = try pt.vectorType(.{
2173 .len = self.components(),
2174 .child = tmp.ty.toIntern(),
2175 });
2176
2177 const vector = try ng.constructCompositeSplat(vector_ty, id);
2178 return .{
2179 .ty = vector_ty,
2180 .value = .{ .spv_vectorwise = vector },
2181 };
2182 },
21832109 .unrolled => blk: {
2184 if (is_vector) {
2185 break :blk .{ .vector_exploded = try tmp.explode(ng) };
2186 } else {
2187 break :blk .{ .scalar_broadcast = id };
2188 }
2110 if (is_vector) break :blk .{ .vector_exploded = try tmp.explode(ng) };
2111 break :blk .{ .scalar_broadcast = id };
21892112 },
21902113 },
21912114 .exploded_vector => |range| switch (self) {
21922115 .scalar => unreachable,
2193 .spv_vectorized => |n| blk: {
2194 // We can vectorize this operation, but we have an exploded vector. This can happen
2195 // when a vectorizable operation succeeds a non-vectorizable operation. In this case,
2196 // pack up the IDs into a SPIR-V vector. This path should not be able to be hit with
2197 // a type that cannot do that.
2198 assert(is_spv_vector);
2199 assert(range.len == n);
2200 const vec = try tmp.materialize(ng);
2201 break :blk .{ .spv_vectorwise = vec };
2202 },
22032116 .unrolled => |n| blk: {
22042117 assert(range.len == n);
22052118 break :blk .{ .vector_exploded = range };
......@@ -2216,17 +2129,14 @@ const NavGen = struct {
22162129 /// Finalize the results of an operation back into a temporary. `results` is
22172130 /// a list of result-ids of the operation.
22182131 fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary {
2219 assert(self.operations() == results.len);
2220 const value: Temporary.Value = switch (self) {
2221 .scalar, .spv_vectorized => blk: {
2222 break :blk .{ .singleton = results.at(0) };
2223 },
2224 .unrolled => blk: {
2225 break :blk .{ .exploded_vector = results };
2132 assert(self.components() == results.len);
2133 return .{
2134 .ty = ty,
2135 .value = switch (self) {
2136 .scalar => .{ .singleton = results.at(0) },
2137 .unrolled => .{ .exploded_vector = results },
22262138 },
22272139 };
2228
2229 return .{ .ty = ty, .value = value };
22302140 }
22312141
22322142 /// This struct represents an operand that has gone through some setup, and is
......@@ -2242,32 +2152,20 @@ const NavGen = struct {
22422152 scalar: IdResult,
22432153 /// A single scalar that is broadcasted in an unrolled operation.
22442154 scalar_broadcast: IdResult,
2245 /// A SPIR-V vector that is used in SPIR-V vectorize operation.
2246 spv_vectorwise: IdResult,
22472155 /// A vector represented by a consecutive list of IDs that is used in an unrolled operation.
22482156 vector_exploded: IdRange,
22492157 };
22502158
22512159 /// Query the value at a particular index of the operation. Note that
2252 /// the index is *not* the component/lane, but the index of the *operation*. When
2253 /// this operation is vectorized, the return value of this function is a SPIR-V vector.
2254 /// See also `Vectorization.operations()`.
2160 /// the index is *not* the component/lane, but the index of the *operation*.
22552161 fn at(self: PreparedOperand, i: usize) IdResult {
22562162 switch (self.value) {
22572163 .scalar => |id| {
22582164 assert(i == 0);
22592165 return id;
22602166 },
2261 .scalar_broadcast => |id| {
2262 return id;
2263 },
2264 .spv_vectorwise => |id| {
2265 assert(i == 0);
2266 return id;
2267 },
2268 .vector_exploded => |range| {
2269 return range.at(i);
2270 },
2167 .scalar_broadcast => |id| return id,
2168 .vector_exploded => |range| return range.at(i),
22712169 }
22722170 }
22732171 };
......@@ -2299,7 +2197,7 @@ const NavGen = struct {
22992197
23002198 /// This function builds an OpSConvert of OpUConvert depending on the
23012199 /// signedness of the types.
2302 fn buildIntConvert(self: *NavGen, dst_ty: Type, src: Temporary) !Temporary {
2200 fn buildConvert(self: *NavGen, dst_ty: Type, src: Temporary) !Temporary {
23032201 const zcu = self.pt.zcu;
23042202
23052203 const dst_ty_id = try self.resolveType(dst_ty.scalarType(zcu), .direct);
......@@ -2318,13 +2216,17 @@ const NavGen = struct {
23182216 return src.pun(result_ty);
23192217 }
23202218
2321 const ops = v.operations();
2219 const ops = v.components();
23222220 const results = self.spv.allocIds(ops);
23232221
2324 const op_result_ty = try v.operationType(self, dst_ty);
2222 const op_result_ty = dst_ty.scalarType(zcu);
23252223 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
23262224
2327 const opcode: Opcode = if (dst_ty.isSignedInt(zcu)) .OpSConvert else .OpUConvert;
2225 const opcode: Opcode = blk: {
2226 if (dst_ty.scalarType(zcu).isAnyFloat()) break :blk .OpFConvert;
2227 if (dst_ty.scalarType(zcu).isSignedInt(zcu)) break :blk .OpSConvert;
2228 break :blk .OpUConvert;
2229 };
23282230
23292231 const op_src = try v.prepare(self, src);
23302232
......@@ -2339,13 +2241,14 @@ const NavGen = struct {
23392241 }
23402242
23412243 fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
2244 const zcu = self.pt.zcu;
23422245 const target = self.spv.target;
23432246
23442247 const v = self.vectorization(.{ a, b, c });
2345 const ops = v.operations();
2248 const ops = v.components();
23462249 const results = self.spv.allocIds(ops);
23472250
2348 const op_result_ty = try v.operationType(self, a.ty);
2251 const op_result_ty = a.ty.scalarType(zcu);
23492252 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
23502253 const result_ty = try v.resultType(self, a.ty);
23512254
......@@ -2382,10 +2285,10 @@ const NavGen = struct {
23822285 const zcu = self.pt.zcu;
23832286
23842287 const v = self.vectorization(.{ condition, lhs, rhs });
2385 const ops = v.operations();
2288 const ops = v.components();
23862289 const results = self.spv.allocIds(ops);
23872290
2388 const op_result_ty = try v.operationType(self, lhs.ty);
2291 const op_result_ty = lhs.ty.scalarType(zcu);
23892292 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
23902293 const result_ty = try v.resultType(self, lhs.ty);
23912294
......@@ -2431,10 +2334,10 @@ const NavGen = struct {
24312334
24322335 fn buildCmp(self: *NavGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {
24332336 const v = self.vectorization(.{ lhs, rhs });
2434 const ops = v.operations();
2337 const ops = v.components();
24352338 const results = self.spv.allocIds(ops);
24362339
2437 const op_result_ty = try v.operationType(self, Type.bool);
2340 const op_result_ty: Type = .bool;
24382341 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
24392342 const result_ty = try v.resultType(self, Type.bool);
24402343
......@@ -2498,22 +2401,12 @@ const NavGen = struct {
24982401 };
24992402
25002403 fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary {
2404 const zcu = self.pt.zcu;
25012405 const target = self.spv.target;
2502 const v = blk: {
2503 const v = self.vectorization(.{operand});
2504 break :blk switch (op) {
2505 // TODO: These instructions don't seem to be working
2506 // properly for LLVM-based backends on OpenCL for 8- and
2507 // 16-component vectors.
2508 .i_abs => if (self.spv.hasFeature(.vector16) and v.components() >= 8) v.unroll() else v,
2509 else => v,
2510 };
2511 };
2512
2513 const ops = v.operations();
2406 const v = self.vectorization(.{operand});
2407 const ops = v.components();
25142408 const results = self.spv.allocIds(ops);
2515
2516 const op_result_ty = try v.operationType(self, operand.ty);
2409 const op_result_ty = operand.ty.scalarType(zcu);
25172410 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
25182411 const result_ty = try v.resultType(self, operand.ty);
25192412
......@@ -2628,13 +2521,14 @@ const NavGen = struct {
26282521 };
26292522
26302523 fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
2524 const zcu = self.pt.zcu;
26312525 const target = self.spv.target;
26322526
26332527 const v = self.vectorization(.{ lhs, rhs });
2634 const ops = v.operations();
2528 const ops = v.components();
26352529 const results = self.spv.allocIds(ops);
26362530
2637 const op_result_ty = try v.operationType(self, lhs.ty);
2531 const op_result_ty = lhs.ty.scalarType(zcu);
26382532 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
26392533 const result_ty = try v.resultType(self, lhs.ty);
26402534
......@@ -2730,9 +2624,9 @@ const NavGen = struct {
27302624 const ip = &zcu.intern_pool;
27312625
27322626 const v = lhs.vectorization(self).unify(rhs.vectorization(self));
2733 const ops = v.operations();
2627 const ops = v.components();
27342628
2735 const arith_op_ty = try v.operationType(self, lhs.ty);
2629 const arith_op_ty = lhs.ty.scalarType(zcu);
27362630 const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct);
27372631
27382632 const lhs_op = try v.prepare(self, lhs);
......@@ -2883,38 +2777,32 @@ const NavGen = struct {
28832777 });
28842778 },
28852779 .vulkan, .opengl => {
2886 const ptr_ptr_anyerror_ty_id = self.spv.allocId();
2887 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2888 .id_result = ptr_ptr_anyerror_ty_id,
2889 .storage_class = .PushConstant,
2890 .type = ptr_anyerror_ty_id,
2891 });
2892
2893 if (self.object.error_push_constant == null) {
2780 if (self.object.error_buffer == null) {
28942781 const spv_err_decl_index = try self.spv.allocDecl(.global);
28952782 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
28962783
2897 const push_constant_struct_ty_id = self.spv.allocId();
2898 try self.spv.structType(push_constant_struct_ty_id, &.{ptr_anyerror_ty_id}, &.{"error_out_ptr"});
2899 try self.spv.decorate(push_constant_struct_ty_id, .Block);
2900 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
2784 const buffer_struct_ty_id = self.spv.allocId();
2785 try self.spv.structType(buffer_struct_ty_id, &.{anyerror_ty_id}, &.{"error_out"});
2786 try self.spv.decorate(buffer_struct_ty_id, .Block);
2787 try self.spv.decorateMember(buffer_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
29012788
2902 const ptr_push_constant_struct_ty_id = self.spv.allocId();
2789 const ptr_buffer_struct_ty_id = self.spv.allocId();
29032790 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2904 .id_result = ptr_push_constant_struct_ty_id,
2905 .storage_class = .PushConstant,
2906 .type = push_constant_struct_ty_id,
2791 .id_result = ptr_buffer_struct_ty_id,
2792 .storage_class = self.spvStorageClass(.global),
2793 .type = buffer_struct_ty_id,
29072794 });
29082795
2796 const buffer_struct_id = self.spv.declPtr(spv_err_decl_index).result_id;
29092797 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2910 .id_result_type = ptr_push_constant_struct_ty_id,
2911 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,
2912 .storage_class = .PushConstant,
2798 .id_result_type = ptr_buffer_struct_ty_id,
2799 .id_result = buffer_struct_id,
2800 .storage_class = self.spvStorageClass(.global),
29132801 });
2802 try self.spv.decorate(buffer_struct_id, .{ .DescriptorSet = .{ .descriptor_set = 0 } });
2803 try self.spv.decorate(buffer_struct_id, .{ .Binding = .{ .binding_point = 0 } });
29142804
2915 self.object.error_push_constant = .{
2916 .push_constant_ptr = spv_err_decl_index,
2917 };
2805 self.object.error_buffer = spv_err_decl_index;
29182806 }
29192807
29202808 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
......@@ -2937,24 +2825,16 @@ const NavGen = struct {
29372825 .id_result = self.spv.allocId(),
29382826 });
29392827
2940 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
2941 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
2828 const spv_err_decl_index = self.object.error_buffer.?;
2829 const buffer_id = self.spv.declPtr(spv_err_decl_index).result_id;
29422830 try decl_deps.append(spv_err_decl_index);
29432831
29442832 const zero_id = try self.constInt(Type.u32, 0);
2945 // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
2946 // a load.
2947 const tmp = self.spv.allocId();
29482833 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
2949 .id_result_type = ptr_ptr_anyerror_ty_id,
2950 .id_result = tmp,
2951 .base = push_constant_id,
2952 .indexes = &.{zero_id},
2953 });
2954 try section.emit(self.spv.gpa, .OpLoad, .{
29552834 .id_result_type = ptr_anyerror_ty_id,
29562835 .id_result = p_error_id,
2957 .pointer = tmp,
2836 .base = buffer_id,
2837 .indexes = &.{zero_id},
29582838 });
29592839 },
29602840 else => unreachable,
......@@ -2990,7 +2870,7 @@ const NavGen = struct {
29902870 };
29912871
29922872 try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items);
2993 try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode);
2873 try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode, null);
29942874 }
29952875
29962876 fn genNav(self: *NavGen, do_codegen: bool) !void {
......@@ -3092,6 +2972,40 @@ const NavGen = struct {
30922972 .storage_class = storage_class,
30932973 });
30942974
2975 if (nav.fqn.eqlSlice("position", ip)) {
2976 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .Position } });
2977 } else if (nav.fqn.eqlSlice("point_size", ip)) {
2978 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .PointSize } });
2979 } else if (nav.fqn.eqlSlice("invocation_id", ip)) {
2980 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InvocationId } });
2981 } else if (nav.fqn.eqlSlice("frag_coord", ip)) {
2982 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FragCoord } });
2983 } else if (nav.fqn.eqlSlice("point_coord", ip)) {
2984 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .PointCoord } });
2985 } else if (nav.fqn.eqlSlice("front_facing", ip)) {
2986 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FrontFacing } });
2987 } else if (nav.fqn.eqlSlice("sample_mask", ip)) {
2988 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .SampleMask } });
2989 } else if (nav.fqn.eqlSlice("frag_depth", ip)) {
2990 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FragDepth } });
2991 } else if (nav.fqn.eqlSlice("num_workgroups", ip)) {
2992 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .NumWorkgroups } });
2993 } else if (nav.fqn.eqlSlice("workgroup_size", ip)) {
2994 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .WorkgroupSize } });
2995 } else if (nav.fqn.eqlSlice("workgroup_id", ip)) {
2996 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .WorkgroupId } });
2997 } else if (nav.fqn.eqlSlice("local_invocation_id", ip)) {
2998 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .LocalInvocationId } });
2999 } else if (nav.fqn.eqlSlice("global_invocation_id", ip)) {
3000 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .GlobalInvocationId } });
3001 } else if (nav.fqn.eqlSlice("local_invocation_index", ip)) {
3002 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .LocalInvocationIndex } });
3003 } else if (nav.fqn.eqlSlice("vertex_index", ip)) {
3004 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .VertexIndex } });
3005 } else if (nav.fqn.eqlSlice("instance_index", ip)) {
3006 try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InstanceIndex } });
3007 }
3008
30953009 try self.spv.debugName(result_id, nav.fqn.toSlice(ip));
30963010 try self.spv.declareDeclDeps(spv_decl_index, &.{});
30973011 },
......@@ -3175,17 +3089,18 @@ const NavGen = struct {
31753089 /// Convert representation from indirect (in memory) to direct (in 'register')
31763090 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
31773091 fn convertToDirect(self: *NavGen, ty: Type, operand_id: IdRef) !IdRef {
3178 const zcu = self.pt.zcu;
3092 const pt = self.pt;
3093 const zcu = pt.zcu;
31793094 switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
31803095 .bool => {
31813096 const false_id = try self.constBool(false, .indirect);
3182 // The operation below requires inputs in direct representation, but the operand
3183 // is actually in indirect representation.
3184 // Cheekily swap out the type to the direct equivalent of the indirect type here, they have the
3185 // same representation when converted to SPIR-V.
3186 const operand_ty = try self.zigScalarOrVectorTypeLike(Type.u1, ty);
3187 // Note: We can guarantee that these are the same ID due to the SPIR-V Module's `vector_types` cache!
3188 assert(try self.resolveType(operand_ty, .direct) == try self.resolveType(ty, .indirect));
3097 const operand_ty = blk: {
3098 if (!ty.isVector(pt.zcu)) break :blk Type.u1;
3099 break :blk try pt.vectorType(.{
3100 .len = ty.vectorLen(pt.zcu),
3101 .child = Type.u1.toIntern(),
3102 });
3103 };
31893104
31903105 const result = try self.buildCmp(
31913106 .i_ne,
......@@ -3226,7 +3141,6 @@ const NavGen = struct {
32263141 }
32273142
32283143 fn extractVectorComponent(self: *NavGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef {
3229 // Whether this is an OpTypeVector or OpTypeArray, we need to emit the same instruction regardless.
32303144 const result_ty_id = try self.resolveType(result_ty, .direct);
32313145 const result_id = self.spv.allocId();
32323146 const indexes = [_]u32{field};
......@@ -3485,7 +3399,7 @@ const NavGen = struct {
34853399 // Note: The sign may differ here between the shift and the base type, in case
34863400 // of an arithmetic right shift. SPIR-V still expects the same type,
34873401 // so in that case we have to cast convert to signed.
3488 const casted_shift = try self.buildIntConvert(base.ty.scalarType(zcu), shift);
3402 const casted_shift = try self.buildConvert(base.ty.scalarType(zcu), shift);
34893403
34903404 const shifted = switch (info.signedness) {
34913405 .unsigned => try self.buildBinary(unsigned, base, casted_shift),
......@@ -3545,8 +3459,7 @@ const NavGen = struct {
35453459 const zcu = self.pt.zcu;
35463460 const ty = value.ty;
35473461 switch (info.class) {
3548 .integer, .bool, .float => return value,
3549 .composite_integer => unreachable, // TODO
3462 .composite_integer, .integer, .bool, .float => return value,
35503463 .strange_integer => switch (info.signedness) {
35513464 .unsigned => {
35523465 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
......@@ -3815,12 +3728,12 @@ const NavGen = struct {
38153728 .unsigned => blk: {
38163729 if (maybe_op_ty_bits) |op_ty_bits| {
38173730 const op_ty = try pt.intType(.unsigned, op_ty_bits);
3818 const casted_lhs = try self.buildIntConvert(op_ty, lhs);
3819 const casted_rhs = try self.buildIntConvert(op_ty, rhs);
3731 const casted_lhs = try self.buildConvert(op_ty, lhs);
3732 const casted_rhs = try self.buildConvert(op_ty, rhs);
38203733
38213734 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);
38223735
3823 const low_bits = try self.buildIntConvert(lhs.ty, full_result);
3736 const low_bits = try self.buildConvert(lhs.ty, full_result);
38243737 const result = try self.normalize(low_bits, info);
38253738
38263739 // Shift the result bits away to get the overflow bits.
......@@ -3846,9 +3759,7 @@ const NavGen = struct {
38463759 const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);
38473760
38483761 // If no overflow bits in low_bits, no extra work needs to be done.
3849 if (info.backing_bits == info.bits) {
3850 break :blk .{ result, high_overflowed };
3851 }
3762 if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };
38523763
38533764 // Shift the result bits away to get the overflow bits.
38543765 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits));
......@@ -3886,13 +3797,13 @@ const NavGen = struct {
38863797 if (maybe_op_ty_bits) |op_ty_bits| {
38873798 const op_ty = try pt.intType(.signed, op_ty_bits);
38883799 // Assume normalized; sign bit is set. We want a sign extend.
3889 const casted_lhs = try self.buildIntConvert(op_ty, lhs);
3890 const casted_rhs = try self.buildIntConvert(op_ty, rhs);
3800 const casted_lhs = try self.buildConvert(op_ty, lhs);
3801 const casted_rhs = try self.buildConvert(op_ty, rhs);
38913802
38923803 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);
38933804
38943805 // Truncate to the result type.
3895 const low_bits = try self.buildIntConvert(lhs.ty, full_result);
3806 const low_bits = try self.buildConvert(lhs.ty, full_result);
38963807 const result = try self.normalize(low_bits, info);
38973808
38983809 // Now, we need to check the overflow bits AND the sign
......@@ -3929,9 +3840,7 @@ const NavGen = struct {
39293840 // If no overflow bits in low_bits, no extra work needs to be done.
39303841 // Careful, we still have to check the sign bit, so this branch
39313842 // only goes for i33 and such.
3932 if (info.backing_bits == info.bits + 1) {
3933 break :blk .{ result, high_overflowed };
3934 }
3843 if (info.backing_bits == info.bits + 1) break :blk .{ result, high_overflowed };
39353844
39363845 // Shift the result bits away to get the overflow bits.
39373846 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1));
......@@ -3972,7 +3881,7 @@ const NavGen = struct {
39723881
39733882 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
39743883 // so just manually upcast it if required.
3975 const casted_shift = try self.buildIntConvert(base.ty.scalarType(zcu), shift);
3884 const casted_shift = try self.buildConvert(base.ty.scalarType(zcu), shift);
39763885
39773886 const left = try self.buildBinary(.sll, base, casted_shift);
39783887 const result = try self.normalize(left, info);
......@@ -4026,7 +3935,7 @@ const NavGen = struct {
40263935 // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty.
40273936 // result_ty is always large enough to hold the result, so we might have to down
40283937 // cast it.
4029 const result = try self.buildIntConvert(scalar_result_ty, count);
3938 const result = try self.buildConvert(scalar_result_ty, count);
40303939 return try result.materialize(self);
40313940 }
40323941
......@@ -4057,11 +3966,8 @@ const NavGen = struct {
40573966 const operand_ty = self.typeOf(reduce.operand);
40583967 const scalar_ty = operand_ty.scalarType(zcu);
40593968 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);
4060
40613969 const info = self.arithmeticTypeInfo(operand_ty);
4062
40633970 const len = operand_ty.vectorLen(zcu);
4064
40653971 const first = try self.extractVectorComponent(scalar_ty, operand, 0);
40663972
40673973 switch (reduce.operation) {
......@@ -4136,51 +4042,9 @@ const NavGen = struct {
41364042
41374043 // Note: number of components in the result, a, and b may differ.
41384044 const result_ty = self.typeOfIndex(inst);
4139 const a_ty = self.typeOf(extra.a);
4140 const b_ty = self.typeOf(extra.b);
4141
41424045 const scalar_ty = result_ty.scalarType(zcu);
41434046 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);
41444047
4145 // If all of the types are SPIR-V vectors, we can use OpVectorShuffle.
4146 if (self.isSpvVector(result_ty) and self.isSpvVector(a_ty) and self.isSpvVector(b_ty)) {
4147 // The SPIR-V shuffle instruction is similar to the Air instruction, except that the elements are
4148 // numbered consecutively instead of using negatives.
4149
4150 const components = try self.gpa.alloc(Word, result_ty.vectorLen(zcu));
4151 defer self.gpa.free(components);
4152
4153 const a_len = a_ty.vectorLen(zcu);
4154
4155 for (components, 0..) |*component, i| {
4156 const elem = try mask.elemValue(pt, i);
4157 if (elem.isUndef(zcu)) {
4158 // This is explicitly valid for OpVectorShuffle, it indicates undefined.
4159 component.* = 0xFFFF_FFFF;
4160 continue;
4161 }
4162
4163 const index = elem.toSignedInt(zcu);
4164 if (index >= 0) {
4165 component.* = @intCast(index);
4166 } else {
4167 component.* = @intCast(~index + a_len);
4168 }
4169 }
4170
4171 const result_id = self.spv.allocId();
4172 try self.func.body.emit(self.spv.gpa, .OpVectorShuffle, .{
4173 .id_result_type = try self.resolveType(result_ty, .direct),
4174 .id_result = result_id,
4175 .vector_1 = a,
4176 .vector_2 = b,
4177 .components = components,
4178 });
4179 return result_id;
4180 }
4181
4182 // Fall back to manually extracting and inserting components.
4183
41844048 const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
41854049 defer self.gpa.free(constituents);
41864050
......@@ -4535,9 +4399,7 @@ const NavGen = struct {
45354399 const dst_ty_id = try self.resolveType(dst_ty, .direct);
45364400
45374401 const result_id = blk: {
4538 if (src_ty_id == dst_ty_id) {
4539 break :blk src_id;
4540 }
4402 if (src_ty_id == dst_ty_id) break :blk src_id;
45414403
45424404 // TODO: Some more cases are missing here
45434405 // See fn bitCast in llvm.zig
......@@ -4618,7 +4480,7 @@ const NavGen = struct {
46184480 return try src.materialize(self);
46194481 }
46204482
4621 const converted = try self.buildIntConvert(dst_ty, src);
4483 const converted = try self.buildConvert(dst_ty, src);
46224484
46234485 // Make sure to normalize the result if shrinking.
46244486 // Because strange ints are sign extended in their backing
......@@ -4698,17 +4560,10 @@ const NavGen = struct {
46984560
46994561 fn airFloatCast(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
47004562 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4701 const operand_id = try self.resolve(ty_op.operand);
4563 const operand = try self.temporary(ty_op.operand);
47024564 const dest_ty = self.typeOfIndex(inst);
4703 const dest_ty_id = try self.resolveType(dest_ty, .direct);
4704
4705 const result_id = self.spv.allocId();
4706 try self.func.body.emit(self.spv.gpa, .OpFConvert, .{
4707 .id_result_type = dest_ty_id,
4708 .id_result = result_id,
4709 .float_value = operand_id,
4710 });
4711 return result_id;
4565 const result = try self.buildConvert(dest_ty, operand);
4566 return try result.materialize(self);
47124567 }
47134568
47144569 fn airNot(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4790,13 +4645,14 @@ const NavGen = struct {
47904645 const field_int_id = blk: {
47914646 if (field_ty.isPtrAtRuntime(zcu)) {
47924647 assert(self.spv.hasFeature(.addresses) or
4793 (self.spv.hasFeature(.physical_storage_buffer) and field_ty.ptrAddressSpace(zcu) == .storage_buffer));
4648 (self.spv.hasFeature(.physical_storage_buffer) and
4649 field_ty.ptrAddressSpace(zcu) == .storage_buffer));
47944650 break :blk try self.intFromPtr(field_id);
47954651 }
47964652 break :blk try self.bitCast(field_int_ty, field_ty, field_id);
47974653 };
47984654 const shift_rhs = try self.constInt(backing_int_ty, running_bits);
4799 const extended_int_conv = try self.buildIntConvert(backing_int_ty, .{
4655 const extended_int_conv = try self.buildConvert(backing_int_ty, .{
48004656 .ty = field_int_ty,
48014657 .value = .{ .singleton = field_int_id },
48024658 });
......@@ -5016,17 +4872,6 @@ const NavGen = struct {
50164872 const array_id = try self.resolve(bin_op.lhs);
50174873 const index_id = try self.resolve(bin_op.rhs);
50184874
5019 if (self.isSpvVector(array_ty)) {
5020 const result_id = self.spv.allocId();
5021 try self.func.body.emit(self.spv.gpa, .OpVectorExtractDynamic, .{
5022 .id_result_type = try self.resolveType(elem_ty, .direct),
5023 .id_result = result_id,
5024 .vector = array_id,
5025 .index = index_id,
5026 });
5027 return result_id;
5028 }
5029
50304875 // SPIR-V doesn't have an array indexing function for some damn reason.
50314876 // For now, just generate a temporary and use that.
50324877 // TODO: This backend probably also should use isByRef from llvm...
......@@ -5173,7 +5018,7 @@ const NavGen = struct {
51735018 return self.bitCast(ty, payload_ty, payload.?);
51745019 }
51755020
5176 const trunc = try self.buildIntConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } });
5021 const trunc = try self.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } });
51775022 return try trunc.materialize(self);
51785023 }
51795024
......@@ -5182,7 +5027,7 @@ const NavGen = struct {
51825027 try self.convertToIndirect(payload_ty, payload.?)
51835028 else
51845029 try self.bitCast(payload_int_ty, payload_ty, payload.?);
5185 const trunc = try self.buildIntConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } });
5030 const trunc = try self.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } });
51865031 return try trunc.materialize(self);
51875032 }
51885033
......@@ -5208,13 +5053,16 @@ const NavGen = struct {
52085053 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
52095054 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
52105055 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
5211 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
5212 const active_pl_ptr_id = self.spv.allocId();
5213 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
5214 .id_result_type = active_pl_ptr_ty_id,
5215 .id_result = active_pl_ptr_id,
5216 .operand = pl_ptr_id,
5217 });
5056 const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: {
5057 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
5058 const active_pl_ptr_id = self.spv.allocId();
5059 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
5060 .id_result_type = active_pl_ptr_ty_id,
5061 .id_result = active_pl_ptr_id,
5062 .operand = pl_ptr_id,
5063 });
5064 break :blk active_pl_ptr_id;
5065 } else pl_ptr_id;
52185066
52195067 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});
52205068 } else {
......@@ -5271,9 +5119,9 @@ const NavGen = struct {
52715119 const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
52725120 const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
52735121 const result_id = blk: {
5274 if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).?)
5122 if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0")
52755123 break :blk try self.bitCast(field_int_ty, object_ty, try masked.materialize(self));
5276 const trunc = try self.buildIntConvert(field_int_ty, masked);
5124 const trunc = try self.buildConvert(field_int_ty, masked);
52775125 break :blk try trunc.materialize(self);
52785126 };
52795127 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);
......@@ -5295,9 +5143,9 @@ const NavGen = struct {
52955143 .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },
52965144 );
52975145 const result_id = blk: {
5298 if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).?)
5146 if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).@"0")
52995147 break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self));
5300 const trunc = try self.buildIntConvert(int_ty, masked);
5148 const trunc = try self.buildConvert(int_ty, masked);
53015149 break :blk try trunc.materialize(self);
53025150 };
53035151 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);
......@@ -6332,17 +6180,15 @@ const NavGen = struct {
63326180 .bool, .error_set => 1,
63336181 .int => blk: {
63346182 const bits = cond_ty.intInfo(zcu).bits;
6335 const backing_bits = self.backingIntBits(bits) orelse {
6336 return self.todo("implement composite int switch", .{});
6337 };
6183 const backing_bits, const big_int = self.backingIntBits(bits);
6184 if (big_int) return self.todo("implement composite int switch", .{});
63386185 break :blk if (backing_bits <= 32) 1 else 2;
63396186 },
63406187 .@"enum" => blk: {
63416188 const int_ty = cond_ty.intTagType(zcu);
63426189 const int_info = int_ty.intInfo(zcu);
6343 const backing_bits = self.backingIntBits(int_info.bits) orelse {
6344 return self.todo("implement composite int switch", .{});
6345 };
6190 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
6191 if (big_int) return self.todo("implement composite int switch", .{});
63466192 break :blk if (backing_bits <= 32) 1 else 2;
63476193 },
63486194 .pointer => blk: {
......@@ -6752,7 +6598,7 @@ const NavGen = struct {
67526598 // TODO: Should we make these builtins return usize?
67536599 const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0);
67546600 const tmp = Temporary.init(Type.u64, result_id);
6755 const result = try self.buildIntConvert(Type.u32, tmp);
6601 const result = try self.buildConvert(Type.u32, tmp);
67566602 return try result.materialize(self);
67576603 }
67586604
......@@ -6763,7 +6609,7 @@ const NavGen = struct {
67636609 // TODO: Should we make these builtins return usize?
67646610 const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0);
67656611 const tmp = Temporary.init(Type.u64, result_id);
6766 const result = try self.buildIntConvert(Type.u32, tmp);
6612 const result = try self.buildConvert(Type.u32, tmp);
67676613 return try result.materialize(self);
67686614 }
67696615
......@@ -6774,7 +6620,7 @@ const NavGen = struct {
67746620 // TODO: Should we make these builtins return usize?
67756621 const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0);
67766622 const tmp = Temporary.init(Type.u64, result_id);
6777 const result = try self.buildIntConvert(Type.u32, tmp);
6623 const result = try self.buildConvert(Type.u32, tmp);
67786624 return try result.materialize(self);
67796625 }
67806626
src/codegen/spirv/Assembler.zig+18-4
......@@ -296,12 +296,26 @@ fn processInstruction(self: *Assembler) !void {
296296 };
297297 break :blk .{ .value = try self.spv.importInstructionSet(set_tag) };
298298 },
299 .OpExecutionMode, .OpExecutionModeId => {
300 assert(try self.processGenericInstruction() == null);
301 const entry_point_id = try self.resolveRefId(self.inst.operands.items[0].ref_id);
302 const exec_mode: spec.ExecutionMode = @enumFromInt(self.inst.operands.items[1].value);
303 const gop = try self.spv.entry_points.getOrPut(self.gpa, entry_point_id);
304 if (!gop.found_existing) {
305 gop.value_ptr.* = .{};
306 } else if (gop.value_ptr.exec_mode != null) {
307 return self.fail(
308 self.currentToken().start,
309 "cannot set execution mode more than once to any entry point",
310 .{},
311 );
312 }
313 gop.value_ptr.exec_mode = exec_mode;
314 return;
315 },
299316 else => switch (self.inst.opcode.class()) {
300317 .TypeDeclaration => try self.processTypeInstruction(),
301 else => if (try self.processGenericInstruction()) |result|
302 result
303 else
304 return,
318 else => (try self.processGenericInstruction()) orelse return,
305319 },
306320 };
307321
src/codegen/spirv/Module.zig+47-30
......@@ -92,11 +92,12 @@ pub const Decl = struct {
9292/// This models a kernel entry point.
9393pub const EntryPoint = struct {
9494 /// The declaration that should be exported.
95 decl_index: Decl.Index,
95 decl_index: ?Decl.Index = null,
9696 /// The name of the kernel to be exported.
97 name: []const u8,
97 name: ?[]const u8 = null,
9898 /// Calling Convention
99 execution_model: spec.ExecutionModel,
99 exec_model: ?spec.ExecutionModel = null,
100 exec_mode: ?spec.ExecutionMode = null,
100101};
101102
102103/// A general-purpose allocator which may be used to allocate resources for this module
......@@ -164,8 +165,6 @@ cache: struct {
164165 void_type: ?IdRef = null,
165166 int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .empty,
166167 float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .empty,
167 // This cache is required so that @Vector(X, u1) in direct representation has the
168 // same ID as @Vector(X, bool) in indirect representation.
169168 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty,
170169 array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty,
171170
......@@ -186,7 +185,7 @@ decls: std.ArrayListUnmanaged(Decl) = .empty,
186185decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty,
187186
188187/// The list of entry points that should be exported from this module.
189entry_points: std.ArrayListUnmanaged(EntryPoint) = .empty,
188entry_points: std.AutoArrayHashMapUnmanaged(IdRef, EntryPoint) = .empty,
190189
191190pub fn init(gpa: Allocator, target: std.Target) Module {
192191 const version_minor: u8 = blk: {
......@@ -306,19 +305,30 @@ fn entryPoints(self: *Module) !Section {
306305 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
307306 defer seen.deinit(self.gpa);
308307
309 for (self.entry_points.items) |entry_point| {
308 for (self.entry_points.keys(), self.entry_points.values()) |entry_point_id, entry_point| {
310309 interface.items.len = 0;
311310 seen.setRangeValue(.{ .start = 0, .end = self.decls.items.len }, false);
312311
313 try self.addEntryPointDeps(entry_point.decl_index, &seen, &interface);
314
315 const entry_point_id = self.declPtr(entry_point.decl_index).result_id;
312 try self.addEntryPointDeps(entry_point.decl_index.?, &seen, &interface);
316313 try entry_points.emit(self.gpa, .OpEntryPoint, .{
317 .execution_model = entry_point.execution_model,
314 .execution_model = entry_point.exec_model.?,
318315 .entry_point = entry_point_id,
319 .name = entry_point.name,
316 .name = entry_point.name.?,
320317 .interface = interface.items,
321318 });
319
320 if (entry_point.exec_mode == null and entry_point.exec_model == .Fragment) {
321 switch (self.target.os.tag) {
322 .vulkan, .opengl => |tag| {
323 try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{
324 .entry_point = entry_point_id,
325 .mode = if (tag == .vulkan) .OriginUpperLeft else .OriginLowerLeft,
326 });
327 },
328 .opencl => {},
329 else => unreachable,
330 }
331 }
322332 }
323333
324334 return entry_points;
......@@ -352,6 +362,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
352362 .vector16 => try self.addCapability(.Vector16),
353363 // Shader
354364 .shader => try self.addCapability(.Shader),
365 .variable_pointers => {
366 try self.addExtension("SPV_KHR_variable_pointers");
367 try self.addCapability(.VariablePointersStorageBuffer);
368 try self.addCapability(.VariablePointers);
369 },
355370 .physical_storage_buffer => {
356371 try self.addExtension("SPV_KHR_physical_storage_buffer");
357372 try self.addCapability(.PhysicalStorageBufferAddresses);
......@@ -366,20 +381,20 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
366381 // Emit memory model
367382 const addressing_model: spec.AddressingModel = blk: {
368383 if (self.hasFeature(.shader)) {
369 break :blk switch (self.target.cpu.arch) {
370 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
371 .spirv64 => .PhysicalStorageBuffer64,
372 else => unreachable,
373 };
374 } else if (self.hasFeature(.kernel)) {
375 break :blk switch (self.target.cpu.arch) {
376 .spirv32 => .Physical32,
377 .spirv64 => .Physical64,
378 else => unreachable,
379 };
384 if (self.hasFeature(.physical_storage_buffer)) {
385 assert(self.target.cpu.arch == .spirv64);
386 break :blk .PhysicalStorageBuffer64;
387 }
388 assert(self.target.cpu.arch == .spirv);
389 break :blk .Logical;
380390 }
381391
382 unreachable;
392 assert(self.hasFeature(.kernel));
393 break :blk switch (self.target.cpu.arch) {
394 .spirv32 => .Physical32,
395 .spirv64 => .Physical64,
396 else => unreachable,
397 };
383398 };
384399 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{
385400 .addressing_model = addressing_model,
......@@ -746,13 +761,15 @@ pub fn declareEntryPoint(
746761 self: *Module,
747762 decl_index: Decl.Index,
748763 name: []const u8,
749 execution_model: spec.ExecutionModel,
764 exec_model: spec.ExecutionModel,
765 exec_mode: ?spec.ExecutionMode,
750766) !void {
751 try self.entry_points.append(self.gpa, .{
752 .decl_index = decl_index,
753 .name = try self.arena.allocator().dupe(u8, name),
754 .execution_model = execution_model,
755 });
767 const gop = try self.entry_points.getOrPut(self.gpa, self.declPtr(decl_index).result_id);
768 gop.value_ptr.decl_index = decl_index;
769 gop.value_ptr.name = try self.arena.allocator().dupe(u8, name);
770 gop.value_ptr.exec_model = exec_model;
771 // Might've been set by assembler
772 if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode;
756773}
757774
758775pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void {
src/link/SpirV.zig+3-2
......@@ -162,7 +162,7 @@ pub fn updateExports(
162162 if (ip.isFunctionType(nav_ty)) {
163163 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
164164 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
165 const execution_model: spec.ExecutionModel = switch (target.os.tag) {
165 const exec_model: spec.ExecutionModel = switch (target.os.tag) {
166166 .vulkan, .opengl => switch (cc) {
167167 .spirv_vertex => .Vertex,
168168 .spirv_fragment => .Fragment,
......@@ -185,7 +185,8 @@ pub fn updateExports(
185185 try self.object.spv.declareEntryPoint(
186186 spv_decl_index,
187187 exp.opts.name.toSlice(ip),
188 execution_model,
188 exec_model,
189 null,
189190 );
190191 }
191192 }
src/target.zig+15-9
......@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(
501501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns
502502/// for a particular architecture and address space wether such pointers are logical.
503503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {
504 if (target.os.tag != .vulkan) {
505 return false;
506 }
504 if (target.os.tag != .vulkan) return false;
507505
508506 return switch (as) {
509507 // TODO: Vulkan doesn't support pointers in the generic address space, we
510508 // should remove this case but this requires a change in defaultAddressSpace().
511509 // For now, at least disable them from being regarded as physical.
512510 .generic => true,
513 // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real
514 // pointers.
511 // For now, all global pointers are represented using StorageBuffer or CrossWorkgroup,
512 // so these are real pointers.
515513 .global => false,
516 // TODO: Allowed with VK_KHR_variable_pointers.
517 .shared => true,
518 .constant, .local, .input, .output, .uniform, .push_constant, .storage_buffer => true,
514 .physical_storage_buffer => false,
515 .shared => !target.cpu.features.isEnabled(@intFromEnum(std.Target.spirv.Feature.variable_pointers)),
516 .constant,
517 .local,
518 .input,
519 .output,
520 .uniform,
521 .push_constant,
522 .storage_buffer,
523 => true,
519524 else => unreachable,
520525 };
521526}
......@@ -802,7 +807,8 @@ pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBacken
802807 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .stage2_powerpc,
803808 .riscv64 => .stage2_riscv64,
804809 .sparc64 => .stage2_sparc64,
805 .spirv64 => .stage2_spirv64,
810 .spirv32 => if (target.os.tag == .opencl) .stage2_spirv64 else .other,
811 .spirv, .spirv64 => .stage2_spirv64,
806812 .wasm32, .wasm64 => .stage2_wasm,
807813 .x86 => .stage2_x86,
808814 .x86_64 => .stage2_x86_64,
test/cases/compile_errors/@import_zon_bad_type.zig+3-3
......@@ -117,9 +117,9 @@ export fn testMutablePointer() void {
117117// tmp.zig:37:38: note: imported here
118118// neg_inf.zon:1:1: error: expected type '?u8'
119119// tmp.zig:57:28: note: imported here
120// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_499'
120// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_501'
121121// tmp.zig:62:39: note: imported here
122// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_501'
122// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_503'
123123// tmp.zig:67:44: note: imported here
124// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_504'
124// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_506'
125125// tmp.zig:72:50: note: imported here
test/cases/compile_errors/anytype_param_requires_comptime.zig+1-1
......@@ -15,6 +15,6 @@ pub export fn entry() void {
1515// error
1616//
1717// :7:25: error: unable to resolve comptime value
18// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_473.C' must be comptime-known
18// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_475.C' must be comptime-known
1919// :4:16: note: struct requires comptime because of this field
2020// :4:16: note: types are not available at runtime
test/cases/compile_errors/bogus_method_call_on_slice.zig+1-1
......@@ -16,5 +16,5 @@ pub export fn entry2() void {
1616//
1717// :3:6: error: no field or member function named 'copy' in '[]const u8'
1818// :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})'
19// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_477'
19// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_479'
2020// :12:6: note: struct declared here
test/cases/compile_errors/coerce_anon_struct.zig+1-1
......@@ -6,6 +6,6 @@ export fn foo() void {
66
77// error
88//
9// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_466'
9// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_468'
1010// :3:16: note: struct declared here
1111// :1:11: note: struct declared here
test/cases/compile_errors/redundant_try.zig+2-2
......@@ -44,9 +44,9 @@ comptime {
4444//
4545// :5:23: error: expected error union type, found 'comptime_int'
4646// :10:23: error: expected error union type, found '@TypeOf(.{})'
47// :15:23: error: expected error union type, found 'tmp.test2__struct_503'
47// :15:23: error: expected error union type, found 'tmp.test2__struct_505'
4848// :15:23: note: struct declared here
49// :20:27: error: expected error union type, found 'tmp.test3__struct_505'
49// :20:27: error: expected error union type, found 'tmp.test3__struct_507'
5050// :20:27: note: struct declared here
5151// :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }'
5252// :31:13: error: expected error union type, found 'u32'
test/tests.zig+1-1
......@@ -145,7 +145,7 @@ const test_targets = blk: {
145145 .{
146146 .target = std.Target.Query.parse(.{
147147 .arch_os_abi = "spirv64-vulkan",
148 .cpu_features = "vulkan_v1_2+int64+float16+float64",
148 .cpu_features = "vulkan_v1_2+physical_storage_buffer+int64+float16+float64",
149149 }) catch unreachable,
150150 .use_llvm = false,
151151 .use_lld = false,
tools/update_cpu_features.zig+122
......@@ -1047,6 +1047,128 @@ const targets = [_]ArchTarget{
10471047 },
10481048 },
10491049 },
1050 .{
1051 .zig_name = "spirv",
1052 .llvm = .{
1053 .name = "SPIRV",
1054 .td_name = "SPIRV",
1055 },
1056 .branch_quota = 2000,
1057 .extra_features = &.{
1058 .{
1059 .zig_name = "v1_0",
1060 .desc = "Enable version 1.0",
1061 .deps = &.{},
1062 },
1063 .{
1064 .zig_name = "v1_1",
1065 .desc = "Enable version 1.1",
1066 .deps = &.{"v1_0"},
1067 },
1068 .{
1069 .zig_name = "v1_2",
1070 .desc = "Enable version 1.2",
1071 .deps = &.{"v1_1"},
1072 },
1073 .{
1074 .zig_name = "v1_3",
1075 .desc = "Enable version 1.3",
1076 .deps = &.{"v1_2"},
1077 },
1078 .{
1079 .zig_name = "v1_4",
1080 .desc = "Enable version 1.4",
1081 .deps = &.{"v1_3"},
1082 },
1083 .{
1084 .zig_name = "v1_5",
1085 .desc = "Enable version 1.5",
1086 .deps = &.{"v1_4"},
1087 },
1088 .{
1089 .zig_name = "v1_6",
1090 .desc = "Enable version 1.6",
1091 .deps = &.{"v1_5"},
1092 },
1093 .{
1094 .zig_name = "int64",
1095 .desc = "Enable Int64 capability",
1096 .deps = &.{"v1_0"},
1097 },
1098 .{
1099 .zig_name = "float16",
1100 .desc = "Enable Float16 capability",
1101 .deps = &.{"v1_0"},
1102 },
1103 .{
1104 .zig_name = "float64",
1105 .desc = "Enable Float64 capability",
1106 .deps = &.{"v1_0"},
1107 },
1108 .{
1109 .zig_name = "matrix",
1110 .desc = "Enable Matrix capability",
1111 .deps = &.{"v1_0"},
1112 },
1113 .{
1114 .zig_name = "storage_push_constant16",
1115 .desc = "Enable SPV_KHR_16bit_storage extension and the StoragePushConstant16 capability",
1116 .deps = &.{"v1_3"},
1117 },
1118 .{
1119 .zig_name = "arbitrary_precision_integers",
1120 .desc = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",
1121 .deps = &.{"v1_5"},
1122 },
1123 .{
1124 .zig_name = "kernel",
1125 .desc = "Enable Kernel capability",
1126 .deps = &.{"v1_0"},
1127 },
1128 .{
1129 .zig_name = "addresses",
1130 .desc = "Enable Addresses capability",
1131 .deps = &.{"v1_0"},
1132 },
1133 .{
1134 .zig_name = "generic_pointer",
1135 .desc = "Enable GenericPointer capability",
1136 .deps = &.{ "v1_0", "addresses" },
1137 },
1138 .{
1139 .zig_name = "vector16",
1140 .desc = "Enable Vector16 capability",
1141 .deps = &.{ "v1_0", "kernel" },
1142 },
1143 .{
1144 .zig_name = "shader",
1145 .desc = "Enable Shader capability",
1146 .deps = &.{ "v1_0", "matrix" },
1147 },
1148 .{
1149 .zig_name = "variable_pointers",
1150 .desc = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",
1151 .deps = &.{"v1_0"},
1152 },
1153 .{
1154 .zig_name = "physical_storage_buffer",
1155 .desc = "Enable SPV_KHR_variable_pointers extension and the (VariablePointers, VariablePointersStorageBuffer) capabilities",
1156 .deps = &.{"v1_0"},
1157 },
1158 },
1159 .extra_cpus = &.{
1160 .{
1161 .llvm_name = null,
1162 .zig_name = "vulkan_v1_2",
1163 .features = &.{ "v1_5", "shader" },
1164 },
1165 .{
1166 .llvm_name = null,
1167 .zig_name = "opencl_v2",
1168 .features = &.{ "v1_2", "kernel", "addresses", "generic_pointer" },
1169 },
1170 },
1171 },
10501172 .{
10511173 .zig_name = "riscv",
10521174 .llvm = .{