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 {...@@ -2014,7 +2014,7 @@ pub const Cpu = struct {
2014 .global, .local, .shared => is_gpu,2014 .global, .local, .shared => is_gpu,
2015 .constant => is_gpu and (context == null or context == .constant),2015 .constant => is_gpu and (context == null or context == .constant),
2016 .param => is_nvptx,2016 .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,
2018 };2018 };
2019 }2019 }
2020};2020};
lib/std/Target/spirv.zig+127-76
...@@ -1,8 +1,21 @@...@@ -1,8 +1,21 @@
1//! This file is auto-generated by tools/update_cpu_features.zig.
2
1const std = @import("../std.zig");3const std = @import("../std.zig");
2const CpuFeature = std.Target.Cpu.Feature;4const CpuFeature = std.Target.Cpu.Feature;
3const CpuModel = std.Target.Cpu.Model;5const CpuModel = std.Target.Cpu.Model;
46
5pub const Feature = enum {7pub 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,
6 v1_0,19 v1_0,
7 v1_1,20 v1_1,
8 v1_2,21 v1_2,
...@@ -10,18 +23,8 @@ pub const Feature = enum {...@@ -10,18 +23,8 @@ pub const Feature = enum {
10 v1_4,23 v1_4,
11 v1_5,24 v1_5,
12 v1_6,25 v1_6,
13 int64,26 variable_pointers,
14 float16,
15 float64,
16 matrix,
17 storage_push_constant16,
18 arbitrary_precision_integers,
19 kernel,
20 addresses,
21 generic_pointer,
22 vector16,27 vector16,
23 shader,
24 physical_storage_buffer,
25};28};
2629
27pub const featureSet = CpuFeature.FeatureSetFns(Feature).featureSet;30pub const featureSet = CpuFeature.FeatureSetFns(Feature).featureSet;
...@@ -34,100 +37,143 @@ pub const all_features = blk: {...@@ -34,100 +37,143 @@ pub const all_features = blk: {
34 const len = @typeInfo(Feature).@"enum".fields.len;37 const len = @typeInfo(Feature).@"enum".fields.len;
35 std.debug.assert(len <= CpuFeature.Set.needed_bit_count);38 std.debug.assert(len <= CpuFeature.Set.needed_bit_count);
36 var result: [len]CpuFeature = undefined;39 var result: [len]CpuFeature = undefined;
37 result[@intFromEnum(Feature.v1_0)] = .{40 result[@intFromEnum(Feature.addresses)] = .{
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)] = .{
48 .llvm_name = null,41 .llvm_name = null,
49 .description = "Enable version 1.2",42 .description = "Enable Addresses capability",
50 .dependencies = featureSet(&[_]Feature{.v1_1}),43 .dependencies = featureSet(&[_]Feature{
44 .v1_0,
45 }),
51 };46 };
52 result[@intFromEnum(Feature.v1_3)] = .{47 result[@intFromEnum(Feature.arbitrary_precision_integers)] = .{
53 .llvm_name = null,48 .llvm_name = null,
54 .description = "Enable version 1.3",49 .description = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",
55 .dependencies = featureSet(&[_]Feature{.v1_2}),50 .dependencies = featureSet(&[_]Feature{
51 .v1_5,
52 }),
56 };53 };
57 result[@intFromEnum(Feature.v1_4)] = .{54 result[@intFromEnum(Feature.float16)] = .{
58 .llvm_name = null,55 .llvm_name = null,
59 .description = "Enable version 1.4",56 .description = "Enable Float16 capability",
60 .dependencies = featureSet(&[_]Feature{.v1_3}),57 .dependencies = featureSet(&[_]Feature{
58 .v1_0,
59 }),
61 };60 };
62 result[@intFromEnum(Feature.v1_5)] = .{61 result[@intFromEnum(Feature.float64)] = .{
63 .llvm_name = null,62 .llvm_name = null,
64 .description = "Enable version 1.5",63 .description = "Enable Float64 capability",
65 .dependencies = featureSet(&[_]Feature{.v1_4}),64 .dependencies = featureSet(&[_]Feature{
65 .v1_0,
66 }),
66 };67 };
67 result[@intFromEnum(Feature.v1_6)] = .{68 result[@intFromEnum(Feature.generic_pointer)] = .{
68 .llvm_name = null,69 .llvm_name = null,
69 .description = "Enable version 1.6",70 .description = "Enable GenericPointer capability",
70 .dependencies = featureSet(&[_]Feature{.v1_5}),71 .dependencies = featureSet(&[_]Feature{
72 .addresses,
73 }),
71 };74 };
72 result[@intFromEnum(Feature.int64)] = .{75 result[@intFromEnum(Feature.int64)] = .{
73 .llvm_name = null,76 .llvm_name = null,
74 .description = "Enable Int64 capability",77 .description = "Enable Int64 capability",
75 .dependencies = featureSet(&[_]Feature{.v1_0}),78 .dependencies = featureSet(&[_]Feature{
76 };79 .v1_0,
77 result[@intFromEnum(Feature.float16)] = .{80 }),
78 .llvm_name = null,
79 .description = "Enable Float16 capability",
80 .dependencies = featureSet(&[_]Feature{.v1_0}),
81 };81 };
82 result[@intFromEnum(Feature.float64)] = .{82 result[@intFromEnum(Feature.kernel)] = .{
83 .llvm_name = null,83 .llvm_name = null,
84 .description = "Enable Float64 capability",84 .description = "Enable Kernel capability",
85 .dependencies = featureSet(&[_]Feature{.v1_0}),85 .dependencies = featureSet(&[_]Feature{
86 .v1_0,
87 }),
86 };88 };
87 result[@intFromEnum(Feature.matrix)] = .{89 result[@intFromEnum(Feature.matrix)] = .{
88 .llvm_name = null,90 .llvm_name = null,
89 .description = "Enable Matrix capability",91 .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 }),
91 };109 };
92 result[@intFromEnum(Feature.storage_push_constant16)] = .{110 result[@intFromEnum(Feature.storage_push_constant16)] = .{
93 .llvm_name = null,111 .llvm_name = null,
94 .description = "Enable SPV_KHR_16bit_storage extension and the StoragePushConstant16 capability",112 .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 }),
96 };116 };
97 result[@intFromEnum(Feature.arbitrary_precision_integers)] = .{117 result[@intFromEnum(Feature.v1_0)] = .{
98 .llvm_name = null,118 .llvm_name = null,
99 .description = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",119 .description = "Enable version 1.0",
100 .dependencies = featureSet(&[_]Feature{.v1_5}),120 .dependencies = featureSet(&[_]Feature{}),
101 };121 };
102 result[@intFromEnum(Feature.kernel)] = .{122 result[@intFromEnum(Feature.v1_1)] = .{
103 .llvm_name = null,123 .llvm_name = null,
104 .description = "Enable Kernel capability",124 .description = "Enable version 1.1",
105 .dependencies = featureSet(&[_]Feature{.v1_0}),125 .dependencies = featureSet(&[_]Feature{
126 .v1_0,
127 }),
106 };128 };
107 result[@intFromEnum(Feature.addresses)] = .{129 result[@intFromEnum(Feature.v1_2)] = .{
108 .llvm_name = null,130 .llvm_name = null,
109 .description = "Enable Addresses capability",131 .description = "Enable version 1.2",
110 .dependencies = featureSet(&[_]Feature{.v1_0}),132 .dependencies = featureSet(&[_]Feature{
133 .v1_1,
134 }),
111 };135 };
112 result[@intFromEnum(Feature.generic_pointer)] = .{136 result[@intFromEnum(Feature.v1_3)] = .{
113 .llvm_name = null,137 .llvm_name = null,
114 .description = "Enable GenericPointer capability",138 .description = "Enable version 1.3",
115 .dependencies = featureSet(&[_]Feature{ .v1_0, .addresses }),139 .dependencies = featureSet(&[_]Feature{
140 .v1_2,
141 }),
116 };142 };
117 result[@intFromEnum(Feature.vector16)] = .{143 result[@intFromEnum(Feature.v1_4)] = .{
118 .llvm_name = null,144 .llvm_name = null,
119 .description = "Enable Vector16 capability",145 .description = "Enable version 1.4",
120 .dependencies = featureSet(&[_]Feature{ .v1_0, .kernel }),146 .dependencies = featureSet(&[_]Feature{
147 .v1_3,
148 }),
121 };149 };
122 result[@intFromEnum(Feature.shader)] = .{150 result[@intFromEnum(Feature.v1_5)] = .{
123 .llvm_name = null,151 .llvm_name = null,
124 .description = "Enable Shader capability",152 .description = "Enable version 1.5",
125 .dependencies = featureSet(&[_]Feature{ .v1_0, .matrix }),153 .dependencies = featureSet(&[_]Feature{
154 .v1_4,
155 }),
126 };156 };
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)] = .{
128 .llvm_name = null,165 .llvm_name = null,
129 .description = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",166 .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 }),
131 };177 };
132 const ti = @typeInfo(Feature);178 const ti = @typeInfo(Feature);
133 for (&result, 0..) |*elem, i| {179 for (&result, 0..) |*elem, i| {
...@@ -141,18 +187,23 @@ pub const cpu = struct {...@@ -141,18 +187,23 @@ pub const cpu = struct {
141 pub const generic: CpuModel = .{187 pub const generic: CpuModel = .{
142 .name = "generic",188 .name = "generic",
143 .llvm_name = "generic",189 .llvm_name = "generic",
144 .features = featureSet(&[_]Feature{.v1_0}),190 .features = featureSet(&[_]Feature{}),
145 };191 };
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
153 pub const opencl_v2: CpuModel = .{192 pub const opencl_v2: CpuModel = .{
154 .name = "opencl_v2",193 .name = "opencl_v2",
155 .llvm_name = null,194 .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 }),
157 };208 };
158};209};
lib/std/builtin.zig+1
...@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {...@@ -531,6 +531,7 @@ pub const AddressSpace = enum(u5) {
531 uniform,531 uniform,
532 push_constant,532 push_constant,
533 storage_buffer,533 storage_buffer,
534 physical_storage_buffer,
534535
535 // AVR address spaces.536 // AVR address spaces.
536 flash,537 flash,
lib/std/gpu.zig+91-137
...@@ -1,81 +1,24 @@...@@ -1,81 +1,24 @@
1const std = @import("std.zig");1const std = @import("std.zig");
22
3/// Will make `ptr` contain the location of the current invocation within the3pub const position_in = @extern(*addrspace(.input) @Vector(4, f32), .{ .name = "position" });
4/// global workgroup. Each component is equal to the index of the local workgroup4pub const position_out = @extern(*addrspace(.output) @Vector(4, f32), .{ .name = "position" });
5/// multiplied by the size of the local workgroup plus `localInvocationId`.5pub const point_size_in = @extern(*addrspace(.input) f32, .{ .name = "point_size" });
6/// `ptr` must be a reference to variable or struct field.6pub const point_size_out = @extern(*addrspace(.output) f32, .{ .name = "point_size" });
7pub fn globalInvocationId(comptime ptr: *addrspace(.input) @Vector(3, u32)) void {7pub extern const invocation_id: u32 addrspace(.input);
8 asm volatile (8pub extern const frag_coord: @Vector(4, f32) addrspace(.input);
9 \\OpDecorate %ptr BuiltIn GlobalInvocationId9pub extern const point_coord: @Vector(2, f32) addrspace(.input);
10 :10// TODO: direct/indirect values
11 : [ptr] "" (ptr),11// pub extern const front_facing: bool addrspace(.input);
12 );12// TODO: runtime array
13}13// pub extern const sample_mask;
1414pub extern var frag_depth: f32 addrspace(.output);
15/// Will make that variable contain the location of the current cluster15pub extern const num_workgroups: @Vector(3, u32) addrspace(.input);
16/// culling, task, mesh, or compute shader invocation within the local16pub extern const workgroup_size: @Vector(3, u32) addrspace(.input);
17/// workgroup. Each component ranges from zero through to the size of the17pub extern const workgroup_id: @Vector(3, u32) addrspace(.input);
18/// workgroup in that dimension minus one.18pub extern const local_invocation_id: @Vector(3, u32) addrspace(.input);
19/// `ptr` must be a reference to variable or struct field.19pub extern const global_invocation_id: @Vector(3, u32) addrspace(.input);
20pub fn localInvocationId(comptime ptr: *addrspace(.input) @Vector(3, u32)) void {20pub extern const vertex_index: u32 addrspace(.input);
21 asm volatile (21pub extern const instance_index: u32 addrspace(.input);
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}
7922
80/// Forms the main linkage for `input` and `output` address spaces.23/// Forms the main linkage for `input` and `output` address spaces.
81/// `ptr` must be a reference to variable or struct field.24/// `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...@@ -101,74 +44,85 @@ pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) voi
101 );44 );
102}45}
10346
104pub const Origin = enum(u32) {47pub const ExecutionMode = union(Tag) {
105 /// Increase toward the right and downward48 /// Sets origin of the framebuffer to the upper-left corner
106 upper_left = 7,49 origin_upper_left,
107 /// Increase toward the right and upward50 /// Sets origin of the framebuffer to the lower-left corner
108 lower_left = 8,51 origin_lower_left,
109};52 /// Indicates that the fragment shader writes to `frag_depth`,
11053 /// replacing the fixed-function depth value.
111/// The coordinates appear to originate in the specified `origin`.54 depth_replacing,
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,
126 /// Indicates that per-fragment tests may assume that55 /// Indicates that per-fragment tests may assume that
127 /// any `fragmentDepth` built in-decorated value written by the shader is56 /// any `frag_depth` built in-decorated value written by the shader is
128 /// greater-than-or-equal to the fragment’s interpolated depth value57 /// greater-than-or-equal to the fragment’s interpolated depth value
129 greater = 14,58 depth_greater,
130 /// Indicates that per-fragment tests may assume that59 /// Indicates that per-fragment tests may assume that
131 /// any `fragmentDepth` built in-decorated value written by the shader is60 /// any `frag_depth` built in-decorated value written by the shader is
132 /// less-than-or-equal to the fragment’s interpolated depth value61 /// less-than-or-equal to the fragment’s interpolated depth value
133 less = 15,62 depth_less,
134 /// Indicates that per-fragment tests may assume that63 /// Indicates that per-fragment tests may assume that
135 /// any `fragmentDepth` built in-decorated value written by the shader is64 /// any `frag_depth` built in-decorated value written by the shader is
136 /// the same as the fragment’s interpolated depth value65 /// the same as the fragment’s interpolated depth value
137 unchanged = 16,66 depth_unchanged,
138};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.70 pub const Tag = enum(u32) {
141pub fn depthMode(comptime entry_point: anytype, comptime mode: DepthMode) void {71 origin_upper_left = 7,
142 asm volatile (72 origin_lower_left = 8,
143 \\OpExecutionMode %entry_point $mode73 depth_replacing = 12,
144 :74 depth_greater = 14,
145 : [entry_point] "" (entry_point),75 depth_less = 15,
146 [mode] "c" (mode),76 depth_unchanged = 16,
147 );77 local_size = 17,
148}78 };
14979
150/// Indicates the workgroup size in the `x`, `y`, and `z` dimensions.80 pub const LocalSize = struct { x: u32, y: u32, z: u32 };
151/// Only valid with the `GLCompute` or `Kernel` calling conventions.81};
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}
16282
163/// A hint to the client, which indicates the workgroup size in the `x`, `y`, and `z` dimensions.83/// Declare the mode entry point executes in.
164/// Only valid with the `GLCompute` or `Kernel` calling conventions.84pub fn executionMode(comptime entry_point: anytype, comptime mode: ExecutionMode) void {
165pub fn workgroupSizeHint(comptime entry_point: anytype, comptime size: @Vector(3, u32)) void {85 const cc = @typeInfo(@TypeOf(entry_point)).@"fn".calling_convention;
166 asm volatile (86 switch (mode) {
167 \\OpExecutionMode %entry_point LocalSizeHint %x %y %z87 .origin_upper_left,
168 :88 .origin_lower_left,
169 : [entry_point] "" (entry_point),89 .depth_replacing,
170 [x] "c" (size[0]),90 .depth_greater,
171 [y] "c" (size[1]),91 .depth_less,
172 [z] "c" (size[2]),92 .depth_unchanged,
173 );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 }
174}128}
src/Zcu.zig+1-1
...@@ -3693,7 +3693,7 @@ pub fn errorSetBits(zcu: *const Zcu) u16 {...@@ -3693,7 +3693,7 @@ pub fn errorSetBits(zcu: *const Zcu) u16 {
3693 const target = zcu.getTarget();3693 const target = zcu.getTarget();
36943694
3695 if (zcu.error_limit == 0) return 0;3695 if (zcu.error_limit == 0) return 0;
3696 if (target.cpu.arch == .spirv64) {3696 if (target.cpu.arch.isSpirV()) {
3697 if (!std.Target.spirv.featureSetHas(target.cpu.features, .storage_push_constant16)) {3697 if (!std.Target.spirv.featureSetHas(target.cpu.features, .storage_push_constant16)) {
3698 return 32;3698 return 32;
3699 }3699 }
src/codegen/spirv.zig+282-436
...@@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig");...@@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig");
30const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);30const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
3131
32pub const zig_call_abi_ver = 3;32pub const zig_call_abi_ver = 3;
33pub const big_int_bits = 32;
3334
34const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult);35const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult);
35const PtrTypeMap = std.AutoHashMapUnmanaged(36const PtrTypeMap = std.AutoHashMapUnmanaged(
...@@ -169,12 +170,10 @@ pub const Object = struct {...@@ -169,12 +170,10 @@ pub const Object = struct {
169 /// via the usual `intern_map` mechanism.170 /// via the usual `intern_map` mechanism.
170 ptr_types: PtrTypeMap = .{},171 ptr_types: PtrTypeMap = .{},
171172
172 /// For test declarations for Vulkan, we have to add a push constant with a pointer to a173 /// For test declarations for Vulkan, we have to add a buffer.
173 /// buffer that we can use. We only need to generate this once, this holds the link information174 /// We only need to generate this once, this holds the link information
174 /// related to that.175 /// related to that.
175 error_push_constant: ?struct {176 error_buffer: ?SpvModule.Decl.Index = null,
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
178177
179 pub fn init(gpa: Allocator, target: std.Target) Object {178 pub fn init(gpa: Allocator, target: std.Target) Object {
180 return .{179 return .{
...@@ -344,8 +343,7 @@ const NavGen = struct {...@@ -344,8 +343,7 @@ const NavGen = struct {
344343
345 /// This structure is used to return information about a type typically used for344 /// This structure is used to return information about a type typically used for
346 /// arithmetic operations. These types may either be integers, floats, or a vector345 /// 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 represent346 /// of these. If the type is a scalar, 'inner type' refers to the
348 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the
349 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.347 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.
350 const ArithmeticTypeInfo = struct {348 const ArithmeticTypeInfo = struct {
351 /// A classification of the inner type.349 /// A classification of the inner type.
...@@ -379,7 +377,7 @@ const NavGen = struct {...@@ -379,7 +377,7 @@ const NavGen = struct {
379 /// The number of bits required to store the type.377 /// The number of bits required to store the type.
380 /// For `integer` and `float`, this is equal to `bits`.378 /// For `integer` and `float`, this is equal to `bits`.
381 /// For `strange_integer` and `bool` this is the size of the backing integer.379 /// 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.
383 backing_bits: u16,381 backing_bits: u16,
384382
385 /// Null if this type is a scalar, or the length383 /// Null if this type is a scalar, or the length
...@@ -582,11 +580,13 @@ const NavGen = struct {...@@ -582,11 +580,13 @@ const NavGen = struct {
582 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.580 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
583 /// The result is valid to be used with OpTypeInt.581 /// The result is valid to be used with OpTypeInt.
584 /// TODO: Should the result of this function be cached?582 /// 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 } {
586 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.584 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
587 assert(bits != 0);585 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
591 // We require Int8 and Int16 capabilities and benefit Int64 when available.591 // We require Int8 and Int16 capabilities and benefit Int64 when available.
592 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).592 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).
...@@ -599,10 +599,11 @@ const NavGen = struct {...@@ -599,10 +599,11 @@ const NavGen = struct {
599599
600 for (ints) |int| {600 for (ints) |int| {
601 const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true;601 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 };
603 }603 }
604604
605 return null;605 // Big int
606 return .{ std.mem.alignForward(u16, bits, big_int_bits), true };
606 }607 }
607608
608 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if609 /// 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 {...@@ -615,41 +616,6 @@ const NavGen = struct {
615 return if (self.spv.hasFeature(.int64)) 64 else 32;616 return if (self.spv.hasFeature(.int64)) 64 else 32;
616 }617 }
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
653 fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo {619 fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo {
654 const zcu = self.pt.zcu;620 const zcu = self.pt.zcu;
655 const target = self.spv.target;621 const target = self.spv.target;
...@@ -659,14 +625,14 @@ const NavGen = struct {...@@ -659,14 +625,14 @@ const NavGen = struct {
659 }625 }
660 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;626 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
661 return switch (scalar_ty.zigTypeTag(zcu)) {627 return switch (scalar_ty.zigTypeTag(zcu)) {
662 .bool => ArithmeticTypeInfo{628 .bool => .{
663 .bits = 1, // Doesn't matter for this class.629 .bits = 1, // Doesn't matter for this class.
664 .backing_bits = self.backingIntBits(1).?,630 .backing_bits = self.backingIntBits(1).@"0",
665 .vector_len = vector_len,631 .vector_len = vector_len,
666 .signedness = .unsigned, // Technically, but doesn't matter for this class.632 .signedness = .unsigned, // Technically, but doesn't matter for this class.
667 .class = .bool,633 .class = .bool,
668 },634 },
669 .float => ArithmeticTypeInfo{635 .float => .{
670 .bits = scalar_ty.floatBits(target),636 .bits = scalar_ty.floatBits(target),
671 .backing_bits = scalar_ty.floatBits(target), // TODO: F80?637 .backing_bits = scalar_ty.floatBits(target), // TODO: F80?
672 .vector_len = vector_len,638 .vector_len = vector_len,
...@@ -676,19 +642,16 @@ const NavGen = struct {...@@ -676,19 +642,16 @@ const NavGen = struct {
676 .int => blk: {642 .int => blk: {
677 const int_info = scalar_ty.intInfo(zcu);643 const int_info = scalar_ty.intInfo(zcu);
678 // TODO: Maybe it's useful to also return this value.644 // TODO: Maybe it's useful to also return this value.
679 const maybe_backing_bits = self.backingIntBits(int_info.bits);645 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
680 break :blk ArithmeticTypeInfo{646 break :blk .{
681 .bits = int_info.bits,647 .bits = int_info.bits,
682 .backing_bits = maybe_backing_bits orelse 0,648 .backing_bits = backing_bits,
683 .vector_len = vector_len,649 .vector_len = vector_len,
684 .signedness = int_info.signedness,650 .signedness = int_info.signedness,
685 .class = if (maybe_backing_bits) |backing_bits|651 .class = class: {
686 if (backing_bits == int_info.bits)652 if (big_int) break :class .composite_integer;
687 ArithmeticTypeInfo.Class.integer653 break :class if (backing_bits == int_info.bits) .integer else .strange_integer;
688 else654 },
689 ArithmeticTypeInfo.Class.strange_integer
690 else
691 .composite_integer,
692 };655 };
693 },656 },
694 .@"enum" => unreachable,657 .@"enum" => unreachable,
...@@ -697,6 +660,34 @@ const NavGen = struct {...@@ -697,6 +660,34 @@ const NavGen = struct {
697 };660 };
698 }661 }
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
700 /// Emits a bool constant in a particular representation.691 /// Emits a bool constant in a particular representation.
701 fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef {692 fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef {
702 return switch (repr) {693 return switch (repr) {
...@@ -713,14 +704,26 @@ const NavGen = struct {...@@ -713,14 +704,26 @@ const NavGen = struct {
713 const scalar_ty = ty.scalarType(zcu);704 const scalar_ty = ty.scalarType(zcu);
714 const int_info = scalar_ty.intInfo(zcu);705 const int_info = scalar_ty.intInfo(zcu);
715 // Use backing bits so that negatives are sign extended706 // Use backing bits so that negatives are sign extended
716 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int707 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
717 assert(backing_bits != 0); // u0 is comptime708 assert(backing_bits != 0); // u0 is comptime
718709
710 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
719 const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {711 const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {
720 .int => |int| int.signedness,712 .int => |int| int.signedness,
721 .comptime_int => if (value < 0) .signed else .unsigned,713 .comptime_int => if (value < 0) .signed else .unsigned,
722 else => unreachable,714 else => unreachable,
723 };715 };
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
725 const final_value: spec.LiteralContextDependentNumber = blk: {728 const final_value: spec.LiteralContextDependentNumber = blk: {
726 if (self.spv.hasFeature(.kernel)) {729 if (self.spv.hasFeature(.kernel)) {
...@@ -738,18 +741,17 @@ const NavGen = struct {...@@ -738,18 +741,17 @@ const NavGen = struct {
738 break :blk switch (backing_bits) {741 break :blk switch (backing_bits) {
739 1...32 => .{ .uint32 = @truncate(truncated_value) },742 1...32 => .{ .uint32 = @truncate(truncated_value) },
740 33...64 => .{ .uint64 = truncated_value },743 33...64 => .{ .uint64 = truncated_value },
741 else => unreachable, // TODO: Large integer constants744 else => unreachable,
742 };745 };
743 }746 }
744747
745 break :blk switch (backing_bits) {748 break :blk switch (backing_bits) {
746 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) },749 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) },
747 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value },750 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value },
748 else => unreachable, // TODO: Large integer constants751 else => unreachable,
749 };752 };
750 };753 };
751754
752 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
753 const result_id = try self.spv.constant(result_ty_id, final_value);755 const result_id = try self.spv.constant(result_ty_id, final_value);
754756
755 if (!ty.isVector(zcu)) return result_id;757 if (!ty.isVector(zcu)) return result_id;
...@@ -987,7 +989,7 @@ const NavGen = struct {...@@ -987,7 +989,7 @@ const NavGen = struct {
987 // TODO: composite int989 // TODO: composite int
988 // TODO: endianness990 // TODO: endianness
989 const bits: u16 = @intCast(ty.bitSize(zcu));991 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;
991 var limbs: [8]u8 = undefined;993 var limbs: [8]u8 = undefined;
992 @memset(&limbs, 0);994 @memset(&limbs, 0);
993 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;995 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
...@@ -1106,19 +1108,11 @@ const NavGen = struct {...@@ -1106,19 +1108,11 @@ const NavGen = struct {
1106 const parent_ptr_id = try self.derivePtr(oac.parent.*);1108 const parent_ptr_id = try self.derivePtr(oac.parent.*);
1107 const parent_ptr_ty = try oac.parent.ptrType(pt);1109 const parent_ptr_ty = try oac.parent.ptrType(pt);
1108 const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct);1110 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) {1113 if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 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
1119 // Vector element ptr accesses are derived as offset_and_cast.1114 // Vector element ptr accesses are derived as offset_and_cast.
1120 // We can just use OpAccessChain.1115 // We can just use OpAccessChain.
1121 assert(parent_ptr_ty.childType(zcu).zigTypeTag(zcu) == .vector);
1122 return self.accessChain(1116 return self.accessChain(
1123 result_ty_id,1117 result_ty_id,
1124 parent_ptr_id,1118 parent_ptr_id,
...@@ -1126,15 +1120,22 @@ const NavGen = struct {...@@ -1126,15 +1120,22 @@ const NavGen = struct {
1126 );1120 );
1127 }1121 }
11281122
1129 // Allow changing the pointer type child only to restructure arrays.1123 if (oac.byte_offset == 0) {
1130 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T.1124 // Allow changing the pointer type child only to restructure arrays.
1131 const result_ptr_id = self.spv.allocId();1125 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T.
1132 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{1126 const result_ptr_id = self.spv.allocId();
1133 .id_result_type = result_ty_id,1127 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
1134 .id_result = result_ptr_id,1128 .id_result_type = result_ty_id,
1135 .operand = parent_ptr_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),
1136 });1138 });
1137 return result_ptr_id;
1138 },1139 },
1139 }1140 }
1140 }1141 }
...@@ -1255,11 +1256,14 @@ const NavGen = struct {...@@ -1255,11 +1256,14 @@ const NavGen = struct {
1255 /// actual operations (as well as store) a Zig type of a particular number of bits. To create1256 /// actual operations (as well as store) a Zig type of a particular number of bits. To create
1256 /// a type with an exact size, use SpvModule.intType.1257 /// a type with an exact size, use SpvModule.intType.
1257 fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef {1258 fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef {
1258 const backing_bits = self.backingIntBits(bits) orelse {1259 const backing_bits, const big_int = self.backingIntBits(bits);
1259 // TODO: Integers too big for any native type are represented as "composite integers":1260 if (big_int) {
1260 // An array of largestSupportedIntBits.1261 if (backing_bits > 64) {
1261 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });1262 return self.fail("composite integers larger than 64bit aren't supported", .{});
1262 };1263 }
1264 const int_ty = try self.resolveType(.u32, .direct);
1265 return self.arrayType(backing_bits / big_int_bits, int_ty);
1266 }
12631267
1264 // Kernel only supports unsigned ints.1268 // Kernel only supports unsigned ints.
1265 if (self.spv.hasFeature(.kernel)) {1269 if (self.spv.hasFeature(.kernel)) {
...@@ -1338,19 +1342,6 @@ const NavGen = struct {...@@ -1338,19 +1342,6 @@ const NavGen = struct {
1338 return self.spv.functionType(return_ty_id, param_ids);1342 return self.spv.functionType(return_ty_id, param_ids);
1339 }1343 }
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
1354 /// Generate a union type. Union types are always generated with the1345 /// Generate a union type. Union types are always generated with the
1355 /// most aligned field active. If the tag alignment is greater1346 /// most aligned field active. If the tag alignment is greater
1356 /// than that of the payload, a regular union (non-packed, with both tag and1347 /// than that of the payload, a regular union (non-packed, with both tag and
...@@ -1560,6 +1551,17 @@ const NavGen = struct {...@@ -1560,6 +1551,17 @@ const NavGen = struct {
1560 return result_id;1551 return result_id;
1561 }1552 }
1562 },1553 },
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 },
1563 .@"fn" => switch (repr) {1565 .@"fn" => switch (repr) {
1564 .direct => {1566 .direct => {
1565 const fn_info = zcu.typeToFunc(ty).?;1567 const fn_info = zcu.typeToFunc(ty).?;
...@@ -1628,17 +1630,6 @@ const NavGen = struct {...@@ -1628,17 +1630,6 @@ const NavGen = struct {
1628 );1630 );
1629 return result_id;1631 return result_id;
1630 },1632 },
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 },
1642 .@"struct" => {1633 .@"struct" => {
1643 const struct_type = switch (ip.indexToKey(ty.toIntern())) {1634 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
1644 .tuple_type => |tuple| {1635 .tuple_type => |tuple| {
...@@ -1793,15 +1784,34 @@ const NavGen = struct {...@@ -1793,15 +1784,34 @@ const NavGen = struct {
1793 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {1784 fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
1794 return switch (as) {1785 return switch (as) {
1795 .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function,1786 .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,
1796 .shared => .Workgroup,1812 .shared => .Workgroup,
1797 .local => .Function,1813 .local => .Function,
1798 .global => if (self.spv.hasFeature(.shader)) .PhysicalStorageBuffer else .CrossWorkgroup,
1799 .constant => .UniformConstant,
1800 .push_constant => .PushConstant,
1801 .input => .Input,1814 .input => .Input,
1802 .output => .Output,
1803 .uniform => .Uniform,
1804 .storage_buffer => .StorageBuffer,
1805 .gs,1815 .gs,
1806 .fs,1816 .fs,
1807 .ss,1817 .ss,
...@@ -2035,69 +2045,32 @@ const NavGen = struct {...@@ -2035,69 +2045,32 @@ const NavGen = struct {
2035 const Vectorization = union(enum) {2045 const Vectorization = union(enum) {
2036 /// This is an operation between scalars.2046 /// This is an operation between scalars.
2037 scalar,2047 scalar,
2038 /// This is an operation between SPIR-V vectors.
2039 /// Value is number of components.
2040 spv_vectorized: u32,
2041 /// This operation is unrolled into separate operations.2048 /// This operation is unrolled into separate operations.
2042 /// Inputs may still be SPIR-V vectors, for example,2049 /// Inputs may still be SPIR-V vectors, for example,
2043 /// when the operation can't be vectorized in SPIR-V.2050 /// when the operation can't be vectorized in SPIR-V.
2044 /// Value is number of components.2051 /// Value is number of components.
2045 unrolled: u32,2052 unrolled: u32,
20462053
2047 /// Derive a vectorization from a particular type. This usually2054 /// Derive a vectorization from a particular type
2048 /// only checks the size, but the source-of-truth is implemented
2049 /// by `isSpvVector()`.
2050 fn fromType(ty: Type, ng: *NavGen) Vectorization {2055 fn fromType(ty: Type, ng: *NavGen) Vectorization {
2051 const zcu = ng.pt.zcu;2056 const zcu = ng.pt.zcu;
2052 if (!ty.isVector(zcu)) {2057 if (!ty.isVector(zcu)) return .scalar;
2053 return .scalar;2058 return .{ .unrolled = ty.vectorLen(zcu) };
2054 } else if (ng.isSpvVector(ty)) {
2055 return .{ .spv_vectorized = ty.vectorLen(zcu) };
2056 } else {
2057 return .{ .unrolled = ty.vectorLen(zcu) };
2058 }
2059 }2059 }
20602060
2061 /// Given two vectorization methods, compute a "unification": a fallback2061 /// Given two vectorization methods, compute a "unification": a fallback
2062 /// that works for both, according to the following rules:2062 /// that works for both, according to the following rules:
2063 /// - Scalars may broadcast2063 /// - Scalars may broadcast
2064 /// - SPIR-V vectorized operations may unroll2064 /// - SPIR-V vectorized operations will unroll
2065 /// - Prefer scalar > SPIR-V vectorized > unrolled2065 /// - Prefer scalar > unrolled
2066 fn unify(a: Vectorization, b: Vectorization) Vectorization {2066 fn unify(a: Vectorization, b: Vectorization) Vectorization {
2067 if (a == .scalar and b == .scalar) {2067 if (a == .scalar and b == .scalar) return .scalar;
2068 return .scalar;2068 if (a == .unrolled or b == .unrolled) {
2069 } else if (a == .spv_vectorized and b == .spv_vectorized) {2069 if (a == .unrolled and b == .unrolled) assert(a.components() == b.components());
2070 assert(a.components() == b.components());2070 if (a == .unrolled) return .{ .unrolled = a.components() };
2071 return .{ .spv_vectorized = a.components() };2071 return .{ .unrolled = b.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 }
2091 }2072 }
2092 }2073 unreachable;
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 };
2101 }2074 }
21022075
2103 /// Query the number of components that inputs of this operation have.2076 /// Query the number of components that inputs of this operation have.
...@@ -2106,35 +2079,10 @@ const NavGen = struct {...@@ -2106,35 +2079,10 @@ const NavGen = struct {
2106 fn components(self: Vectorization) u32 {2079 fn components(self: Vectorization) u32 {
2107 return switch (self) {2080 return switch (self) {
2108 .scalar => 1,2081 .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,
2120 .unrolled => |n| n,2082 .unrolled => |n| n,
2121 };2083 };
2122 }2084 }
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
2138 /// Turns `ty` into the result-type of the entire operation.2086 /// Turns `ty` into the result-type of the entire operation.
2139 /// `ty` may be a scalar or vector, it doesn't matter.2087 /// `ty` may be a scalar or vector, it doesn't matter.
2140 fn resultType(self: Vectorization, ng: *NavGen, ty: Type) !Type {2088 fn resultType(self: Vectorization, ng: *NavGen, ty: Type) !Type {
...@@ -2142,10 +2090,7 @@ const NavGen = struct {...@@ -2142,10 +2090,7 @@ const NavGen = struct {
2142 const scalar_ty = ty.scalarType(pt.zcu);2090 const scalar_ty = ty.scalarType(pt.zcu);
2143 return switch (self) {2091 return switch (self) {
2144 .scalar => scalar_ty,2092 .scalar => scalar_ty,
2145 .unrolled, .spv_vectorized => |n| try pt.vectorType(.{2093 .unrolled => |n| try pt.vectorType(.{ .len = n, .child = scalar_ty.toIntern() }),
2146 .len = n,
2147 .child = scalar_ty.toIntern(),
2148 }),
2149 };2094 };
2150 }2095 }
21512096
...@@ -2155,51 +2100,19 @@ const NavGen = struct {...@@ -2155,51 +2100,19 @@ const NavGen = struct {
2155 fn prepare(self: Vectorization, ng: *NavGen, tmp: Temporary) !PreparedOperand {2100 fn prepare(self: Vectorization, ng: *NavGen, tmp: Temporary) !PreparedOperand {
2156 const pt = ng.pt;2101 const pt = ng.pt;
2157 const is_vector = tmp.ty.isVector(pt.zcu);2102 const is_vector = tmp.ty.isVector(pt.zcu);
2158 const is_spv_vector = ng.isSpvVector(tmp.ty);
2159 const value: PreparedOperand.Value = switch (tmp.value) {2103 const value: PreparedOperand.Value = switch (tmp.value) {
2160 .singleton => |id| switch (self) {2104 .singleton => |id| switch (self) {
2161 .scalar => blk: {2105 .scalar => blk: {
2162 assert(!is_vector);2106 assert(!is_vector);
2163 break :blk .{ .scalar = id };2107 break :blk .{ .scalar = id };
2164 },2108 },
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 },
2183 .unrolled => blk: {2109 .unrolled => blk: {
2184 if (is_vector) {2110 if (is_vector) break :blk .{ .vector_exploded = try tmp.explode(ng) };
2185 break :blk .{ .vector_exploded = try tmp.explode(ng) };2111 break :blk .{ .scalar_broadcast = id };
2186 } else {
2187 break :blk .{ .scalar_broadcast = id };
2188 }
2189 },2112 },
2190 },2113 },
2191 .exploded_vector => |range| switch (self) {2114 .exploded_vector => |range| switch (self) {
2192 .scalar => unreachable,2115 .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 },
2203 .unrolled => |n| blk: {2116 .unrolled => |n| blk: {
2204 assert(range.len == n);2117 assert(range.len == n);
2205 break :blk .{ .vector_exploded = range };2118 break :blk .{ .vector_exploded = range };
...@@ -2216,17 +2129,14 @@ const NavGen = struct {...@@ -2216,17 +2129,14 @@ const NavGen = struct {
2216 /// Finalize the results of an operation back into a temporary. `results` is2129 /// Finalize the results of an operation back into a temporary. `results` is
2217 /// a list of result-ids of the operation.2130 /// a list of result-ids of the operation.
2218 fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary {2131 fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary {
2219 assert(self.operations() == results.len);2132 assert(self.components() == results.len);
2220 const value: Temporary.Value = switch (self) {2133 return .{
2221 .scalar, .spv_vectorized => blk: {2134 .ty = ty,
2222 break :blk .{ .singleton = results.at(0) };2135 .value = switch (self) {
2223 },2136 .scalar => .{ .singleton = results.at(0) },
2224 .unrolled => blk: {2137 .unrolled => .{ .exploded_vector = results },
2225 break :blk .{ .exploded_vector = results };
2226 },2138 },
2227 };2139 };
2228
2229 return .{ .ty = ty, .value = value };
2230 }2140 }
22312141
2232 /// This struct represents an operand that has gone through some setup, and is2142 /// This struct represents an operand that has gone through some setup, and is
...@@ -2242,32 +2152,20 @@ const NavGen = struct {...@@ -2242,32 +2152,20 @@ const NavGen = struct {
2242 scalar: IdResult,2152 scalar: IdResult,
2243 /// A single scalar that is broadcasted in an unrolled operation.2153 /// A single scalar that is broadcasted in an unrolled operation.
2244 scalar_broadcast: IdResult,2154 scalar_broadcast: IdResult,
2245 /// A SPIR-V vector that is used in SPIR-V vectorize operation.
2246 spv_vectorwise: IdResult,
2247 /// A vector represented by a consecutive list of IDs that is used in an unrolled operation.2155 /// A vector represented by a consecutive list of IDs that is used in an unrolled operation.
2248 vector_exploded: IdRange,2156 vector_exploded: IdRange,
2249 };2157 };
22502158
2251 /// Query the value at a particular index of the operation. Note that2159 /// 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*. When2160 /// the index is *not* the component/lane, but the index of the *operation*.
2253 /// this operation is vectorized, the return value of this function is a SPIR-V vector.
2254 /// See also `Vectorization.operations()`.
2255 fn at(self: PreparedOperand, i: usize) IdResult {2161 fn at(self: PreparedOperand, i: usize) IdResult {
2256 switch (self.value) {2162 switch (self.value) {
2257 .scalar => |id| {2163 .scalar => |id| {
2258 assert(i == 0);2164 assert(i == 0);
2259 return id;2165 return id;
2260 },2166 },
2261 .scalar_broadcast => |id| {2167 .scalar_broadcast => |id| return id,
2262 return id;2168 .vector_exploded => |range| return range.at(i),
2263 },
2264 .spv_vectorwise => |id| {
2265 assert(i == 0);
2266 return id;
2267 },
2268 .vector_exploded => |range| {
2269 return range.at(i);
2270 },
2271 }2169 }
2272 }2170 }
2273 };2171 };
...@@ -2299,7 +2197,7 @@ const NavGen = struct {...@@ -2299,7 +2197,7 @@ const NavGen = struct {
22992197
2300 /// This function builds an OpSConvert of OpUConvert depending on the2198 /// This function builds an OpSConvert of OpUConvert depending on the
2301 /// signedness of the types.2199 /// 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 {
2303 const zcu = self.pt.zcu;2201 const zcu = self.pt.zcu;
23042202
2305 const dst_ty_id = try self.resolveType(dst_ty.scalarType(zcu), .direct);2203 const dst_ty_id = try self.resolveType(dst_ty.scalarType(zcu), .direct);
...@@ -2318,13 +2216,17 @@ const NavGen = struct {...@@ -2318,13 +2216,17 @@ const NavGen = struct {
2318 return src.pun(result_ty);2216 return src.pun(result_ty);
2319 }2217 }
23202218
2321 const ops = v.operations();2219 const ops = v.components();
2322 const results = self.spv.allocIds(ops);2220 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);
2325 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2223 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
2329 const op_src = try v.prepare(self, src);2231 const op_src = try v.prepare(self, src);
23302232
...@@ -2339,13 +2241,14 @@ const NavGen = struct {...@@ -2339,13 +2241,14 @@ const NavGen = struct {
2339 }2241 }
23402242
2341 fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {2243 fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
2244 const zcu = self.pt.zcu;
2342 const target = self.spv.target;2245 const target = self.spv.target;
23432246
2344 const v = self.vectorization(.{ a, b, c });2247 const v = self.vectorization(.{ a, b, c });
2345 const ops = v.operations();2248 const ops = v.components();
2346 const results = self.spv.allocIds(ops);2249 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);
2349 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2252 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2350 const result_ty = try v.resultType(self, a.ty);2253 const result_ty = try v.resultType(self, a.ty);
23512254
...@@ -2382,10 +2285,10 @@ const NavGen = struct {...@@ -2382,10 +2285,10 @@ const NavGen = struct {
2382 const zcu = self.pt.zcu;2285 const zcu = self.pt.zcu;
23832286
2384 const v = self.vectorization(.{ condition, lhs, rhs });2287 const v = self.vectorization(.{ condition, lhs, rhs });
2385 const ops = v.operations();2288 const ops = v.components();
2386 const results = self.spv.allocIds(ops);2289 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);
2389 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2292 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2390 const result_ty = try v.resultType(self, lhs.ty);2293 const result_ty = try v.resultType(self, lhs.ty);
23912294
...@@ -2431,10 +2334,10 @@ const NavGen = struct {...@@ -2431,10 +2334,10 @@ const NavGen = struct {
24312334
2432 fn buildCmp(self: *NavGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {2335 fn buildCmp(self: *NavGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {
2433 const v = self.vectorization(.{ lhs, rhs });2336 const v = self.vectorization(.{ lhs, rhs });
2434 const ops = v.operations();2337 const ops = v.components();
2435 const results = self.spv.allocIds(ops);2338 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;
2438 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2341 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2439 const result_ty = try v.resultType(self, Type.bool);2342 const result_ty = try v.resultType(self, Type.bool);
24402343
...@@ -2498,22 +2401,12 @@ const NavGen = struct {...@@ -2498,22 +2401,12 @@ const NavGen = struct {
2498 };2401 };
24992402
2500 fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary {2403 fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary {
2404 const zcu = self.pt.zcu;
2501 const target = self.spv.target;2405 const target = self.spv.target;
2502 const v = blk: {2406 const v = self.vectorization(.{operand});
2503 const v = self.vectorization(.{operand});2407 const ops = v.components();
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();
2514 const results = self.spv.allocIds(ops);2408 const results = self.spv.allocIds(ops);
25152409 const op_result_ty = operand.ty.scalarType(zcu);
2516 const op_result_ty = try v.operationType(self, operand.ty);
2517 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2410 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2518 const result_ty = try v.resultType(self, operand.ty);2411 const result_ty = try v.resultType(self, operand.ty);
25192412
...@@ -2628,13 +2521,14 @@ const NavGen = struct {...@@ -2628,13 +2521,14 @@ const NavGen = struct {
2628 };2521 };
26292522
2630 fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {2523 fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
2524 const zcu = self.pt.zcu;
2631 const target = self.spv.target;2525 const target = self.spv.target;
26322526
2633 const v = self.vectorization(.{ lhs, rhs });2527 const v = self.vectorization(.{ lhs, rhs });
2634 const ops = v.operations();2528 const ops = v.components();
2635 const results = self.spv.allocIds(ops);2529 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);
2638 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);2532 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2639 const result_ty = try v.resultType(self, lhs.ty);2533 const result_ty = try v.resultType(self, lhs.ty);
26402534
...@@ -2730,9 +2624,9 @@ const NavGen = struct {...@@ -2730,9 +2624,9 @@ const NavGen = struct {
2730 const ip = &zcu.intern_pool;2624 const ip = &zcu.intern_pool;
27312625
2732 const v = lhs.vectorization(self).unify(rhs.vectorization(self));2626 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);
2736 const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct);2630 const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct);
27372631
2738 const lhs_op = try v.prepare(self, lhs);2632 const lhs_op = try v.prepare(self, lhs);
...@@ -2883,38 +2777,32 @@ const NavGen = struct {...@@ -2883,38 +2777,32 @@ const NavGen = struct {
2883 });2777 });
2884 },2778 },
2885 .vulkan, .opengl => {2779 .vulkan, .opengl => {
2886 const ptr_ptr_anyerror_ty_id = self.spv.allocId();2780 if (self.object.error_buffer == null) {
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) {
2894 const spv_err_decl_index = try self.spv.allocDecl(.global);2781 const spv_err_decl_index = try self.spv.allocDecl(.global);
2895 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});2782 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
28962783
2897 const push_constant_struct_ty_id = self.spv.allocId();2784 const buffer_struct_ty_id = self.spv.allocId();
2898 try self.spv.structType(push_constant_struct_ty_id, &.{ptr_anyerror_ty_id}, &.{"error_out_ptr"});2785 try self.spv.structType(buffer_struct_ty_id, &.{anyerror_ty_id}, &.{"error_out"});
2899 try self.spv.decorate(push_constant_struct_ty_id, .Block);2786 try self.spv.decorate(buffer_struct_ty_id, .Block);
2900 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });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();
2903 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{2790 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2904 .id_result = ptr_push_constant_struct_ty_id,2791 .id_result = ptr_buffer_struct_ty_id,
2905 .storage_class = .PushConstant,2792 .storage_class = self.spvStorageClass(.global),
2906 .type = push_constant_struct_ty_id,2793 .type = buffer_struct_ty_id,
2907 });2794 });
29082795
2796 const buffer_struct_id = self.spv.declPtr(spv_err_decl_index).result_id;
2909 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{2797 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2910 .id_result_type = ptr_push_constant_struct_ty_id,2798 .id_result_type = ptr_buffer_struct_ty_id,
2911 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,2799 .id_result = buffer_struct_id,
2912 .storage_class = .PushConstant,2800 .storage_class = self.spvStorageClass(.global),
2913 });2801 });
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 = .{2805 self.object.error_buffer = spv_err_decl_index;
2916 .push_constant_ptr = spv_err_decl_index,
2917 };
2918 }2806 }
29192807
2920 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{2808 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
...@@ -2937,24 +2825,16 @@ const NavGen = struct {...@@ -2937,24 +2825,16 @@ const NavGen = struct {
2937 .id_result = self.spv.allocId(),2825 .id_result = self.spv.allocId(),
2938 });2826 });
29392827
2940 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;2828 const spv_err_decl_index = self.object.error_buffer.?;
2941 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;2829 const buffer_id = self.spv.declPtr(spv_err_decl_index).result_id;
2942 try decl_deps.append(spv_err_decl_index);2830 try decl_deps.append(spv_err_decl_index);
29432831
2944 const zero_id = try self.constInt(Type.u32, 0);2832 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();
2948 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{2833 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, .{
2955 .id_result_type = ptr_anyerror_ty_id,2834 .id_result_type = ptr_anyerror_ty_id,
2956 .id_result = p_error_id,2835 .id_result = p_error_id,
2957 .pointer = tmp,2836 .base = buffer_id,
2837 .indexes = &.{zero_id},
2958 });2838 });
2959 },2839 },
2960 else => unreachable,2840 else => unreachable,
...@@ -2990,7 +2870,7 @@ const NavGen = struct {...@@ -2990,7 +2870,7 @@ const NavGen = struct {
2990 };2870 };
29912871
2992 try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items);2872 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);
2994 }2874 }
29952875
2996 fn genNav(self: *NavGen, do_codegen: bool) !void {2876 fn genNav(self: *NavGen, do_codegen: bool) !void {
...@@ -3092,6 +2972,40 @@ const NavGen = struct {...@@ -3092,6 +2972,40 @@ const NavGen = struct {
3092 .storage_class = storage_class,2972 .storage_class = storage_class,
3093 });2973 });
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
3095 try self.spv.debugName(result_id, nav.fqn.toSlice(ip));3009 try self.spv.debugName(result_id, nav.fqn.toSlice(ip));
3096 try self.spv.declareDeclDeps(spv_decl_index, &.{});3010 try self.spv.declareDeclDeps(spv_decl_index, &.{});
3097 },3011 },
...@@ -3175,17 +3089,18 @@ const NavGen = struct {...@@ -3175,17 +3089,18 @@ const NavGen = struct {
3175 /// Convert representation from indirect (in memory) to direct (in 'register')3089 /// Convert representation from indirect (in memory) to direct (in 'register')
3176 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).3090 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
3177 fn convertToDirect(self: *NavGen, ty: Type, operand_id: IdRef) !IdRef {3091 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;
3179 switch (ty.scalarType(zcu).zigTypeTag(zcu)) {3094 switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
3180 .bool => {3095 .bool => {
3181 const false_id = try self.constBool(false, .indirect);3096 const false_id = try self.constBool(false, .indirect);
3182 // The operation below requires inputs in direct representation, but the operand3097 const operand_ty = blk: {
3183 // is actually in indirect representation.3098 if (!ty.isVector(pt.zcu)) break :blk Type.u1;
3184 // Cheekily swap out the type to the direct equivalent of the indirect type here, they have the3099 break :blk try pt.vectorType(.{
3185 // same representation when converted to SPIR-V.3100 .len = ty.vectorLen(pt.zcu),
3186 const operand_ty = try self.zigScalarOrVectorTypeLike(Type.u1, ty);3101 .child = Type.u1.toIntern(),
3187 // Note: We can guarantee that these are the same ID due to the SPIR-V Module's `vector_types` cache!3102 });
3188 assert(try self.resolveType(operand_ty, .direct) == try self.resolveType(ty, .indirect));3103 };
31893104
3190 const result = try self.buildCmp(3105 const result = try self.buildCmp(
3191 .i_ne,3106 .i_ne,
...@@ -3226,7 +3141,6 @@ const NavGen = struct {...@@ -3226,7 +3141,6 @@ const NavGen = struct {
3226 }3141 }
32273142
3228 fn extractVectorComponent(self: *NavGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef {3143 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.
3230 const result_ty_id = try self.resolveType(result_ty, .direct);3144 const result_ty_id = try self.resolveType(result_ty, .direct);
3231 const result_id = self.spv.allocId();3145 const result_id = self.spv.allocId();
3232 const indexes = [_]u32{field};3146 const indexes = [_]u32{field};
...@@ -3485,7 +3399,7 @@ const NavGen = struct {...@@ -3485,7 +3399,7 @@ const NavGen = struct {
3485 // Note: The sign may differ here between the shift and the base type, in case3399 // Note: The sign may differ here between the shift and the base type, in case
3486 // of an arithmetic right shift. SPIR-V still expects the same type,3400 // of an arithmetic right shift. SPIR-V still expects the same type,
3487 // so in that case we have to cast convert to signed.3401 // 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
3490 const shifted = switch (info.signedness) {3404 const shifted = switch (info.signedness) {
3491 .unsigned => try self.buildBinary(unsigned, base, casted_shift),3405 .unsigned => try self.buildBinary(unsigned, base, casted_shift),
...@@ -3545,8 +3459,7 @@ const NavGen = struct {...@@ -3545,8 +3459,7 @@ const NavGen = struct {
3545 const zcu = self.pt.zcu;3459 const zcu = self.pt.zcu;
3546 const ty = value.ty;3460 const ty = value.ty;
3547 switch (info.class) {3461 switch (info.class) {
3548 .integer, .bool, .float => return value,3462 .composite_integer, .integer, .bool, .float => return value,
3549 .composite_integer => unreachable, // TODO
3550 .strange_integer => switch (info.signedness) {3463 .strange_integer => switch (info.signedness) {
3551 .unsigned => {3464 .unsigned => {
3552 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;3465 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 {...@@ -3815,12 +3728,12 @@ const NavGen = struct {
3815 .unsigned => blk: {3728 .unsigned => blk: {
3816 if (maybe_op_ty_bits) |op_ty_bits| {3729 if (maybe_op_ty_bits) |op_ty_bits| {
3817 const op_ty = try pt.intType(.unsigned, op_ty_bits);3730 const op_ty = try pt.intType(.unsigned, op_ty_bits);
3818 const casted_lhs = try self.buildIntConvert(op_ty, lhs);3731 const casted_lhs = try self.buildConvert(op_ty, lhs);
3819 const casted_rhs = try self.buildIntConvert(op_ty, rhs);3732 const casted_rhs = try self.buildConvert(op_ty, rhs);
38203733
3821 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);3734 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);
3824 const result = try self.normalize(low_bits, info);3737 const result = try self.normalize(low_bits, info);
38253738
3826 // Shift the result bits away to get the overflow bits.3739 // Shift the result bits away to get the overflow bits.
...@@ -3846,9 +3759,7 @@ const NavGen = struct {...@@ -3846,9 +3759,7 @@ const NavGen = struct {
3846 const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);3759 const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);
38473760
3848 // If no overflow bits in low_bits, no extra work needs to be done.3761 // If no overflow bits in low_bits, no extra work needs to be done.
3849 if (info.backing_bits == info.bits) {3762 if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };
3850 break :blk .{ result, high_overflowed };
3851 }
38523763
3853 // Shift the result bits away to get the overflow bits.3764 // Shift the result bits away to get the overflow bits.
3854 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits));3765 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits));
...@@ -3886,13 +3797,13 @@ const NavGen = struct {...@@ -3886,13 +3797,13 @@ const NavGen = struct {
3886 if (maybe_op_ty_bits) |op_ty_bits| {3797 if (maybe_op_ty_bits) |op_ty_bits| {
3887 const op_ty = try pt.intType(.signed, op_ty_bits);3798 const op_ty = try pt.intType(.signed, op_ty_bits);
3888 // Assume normalized; sign bit is set. We want a sign extend.3799 // Assume normalized; sign bit is set. We want a sign extend.
3889 const casted_lhs = try self.buildIntConvert(op_ty, lhs);3800 const casted_lhs = try self.buildConvert(op_ty, lhs);
3890 const casted_rhs = try self.buildIntConvert(op_ty, rhs);3801 const casted_rhs = try self.buildConvert(op_ty, rhs);
38913802
3892 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);3803 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);
38933804
3894 // Truncate to the result type.3805 // 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);
3896 const result = try self.normalize(low_bits, info);3807 const result = try self.normalize(low_bits, info);
38973808
3898 // Now, we need to check the overflow bits AND the sign3809 // Now, we need to check the overflow bits AND the sign
...@@ -3929,9 +3840,7 @@ const NavGen = struct {...@@ -3929,9 +3840,7 @@ const NavGen = struct {
3929 // If no overflow bits in low_bits, no extra work needs to be done.3840 // If no overflow bits in low_bits, no extra work needs to be done.
3930 // Careful, we still have to check the sign bit, so this branch3841 // Careful, we still have to check the sign bit, so this branch
3931 // only goes for i33 and such.3842 // only goes for i33 and such.
3932 if (info.backing_bits == info.bits + 1) {3843 if (info.backing_bits == info.bits + 1) break :blk .{ result, high_overflowed };
3933 break :blk .{ result, high_overflowed };
3934 }
39353844
3936 // Shift the result bits away to get the overflow bits.3845 // Shift the result bits away to get the overflow bits.
3937 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1));3846 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1));
...@@ -3972,7 +3881,7 @@ const NavGen = struct {...@@ -3972,7 +3881,7 @@ const NavGen = struct {
39723881
3973 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,3882 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
3974 // so just manually upcast it if required.3883 // 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
3977 const left = try self.buildBinary(.sll, base, casted_shift);3886 const left = try self.buildBinary(.sll, base, casted_shift);
3978 const result = try self.normalize(left, info);3887 const result = try self.normalize(left, info);
...@@ -4026,7 +3935,7 @@ const NavGen = struct {...@@ -4026,7 +3935,7 @@ const NavGen = struct {
4026 // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty.3935 // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty.
4027 // result_ty is always large enough to hold the result, so we might have to down3936 // result_ty is always large enough to hold the result, so we might have to down
4028 // cast it.3937 // cast it.
4029 const result = try self.buildIntConvert(scalar_result_ty, count);3938 const result = try self.buildConvert(scalar_result_ty, count);
4030 return try result.materialize(self);3939 return try result.materialize(self);
4031 }3940 }
40323941
...@@ -4057,11 +3966,8 @@ const NavGen = struct {...@@ -4057,11 +3966,8 @@ const NavGen = struct {
4057 const operand_ty = self.typeOf(reduce.operand);3966 const operand_ty = self.typeOf(reduce.operand);
4058 const scalar_ty = operand_ty.scalarType(zcu);3967 const scalar_ty = operand_ty.scalarType(zcu);
4059 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);3968 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);
4060
4061 const info = self.arithmeticTypeInfo(operand_ty);3969 const info = self.arithmeticTypeInfo(operand_ty);
4062
4063 const len = operand_ty.vectorLen(zcu);3970 const len = operand_ty.vectorLen(zcu);
4064
4065 const first = try self.extractVectorComponent(scalar_ty, operand, 0);3971 const first = try self.extractVectorComponent(scalar_ty, operand, 0);
40663972
4067 switch (reduce.operation) {3973 switch (reduce.operation) {
...@@ -4136,51 +4042,9 @@ const NavGen = struct {...@@ -4136,51 +4042,9 @@ const NavGen = struct {
41364042
4137 // Note: number of components in the result, a, and b may differ.4043 // Note: number of components in the result, a, and b may differ.
4138 const result_ty = self.typeOfIndex(inst);4044 const result_ty = self.typeOfIndex(inst);
4139 const a_ty = self.typeOf(extra.a);
4140 const b_ty = self.typeOf(extra.b);
4141
4142 const scalar_ty = result_ty.scalarType(zcu);4045 const scalar_ty = result_ty.scalarType(zcu);
4143 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);4046 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
4184 const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));4048 const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
4185 defer self.gpa.free(constituents);4049 defer self.gpa.free(constituents);
41864050
...@@ -4535,9 +4399,7 @@ const NavGen = struct {...@@ -4535,9 +4399,7 @@ const NavGen = struct {
4535 const dst_ty_id = try self.resolveType(dst_ty, .direct);4399 const dst_ty_id = try self.resolveType(dst_ty, .direct);
45364400
4537 const result_id = blk: {4401 const result_id = blk: {
4538 if (src_ty_id == dst_ty_id) {4402 if (src_ty_id == dst_ty_id) break :blk src_id;
4539 break :blk src_id;
4540 }
45414403
4542 // TODO: Some more cases are missing here4404 // TODO: Some more cases are missing here
4543 // See fn bitCast in llvm.zig4405 // See fn bitCast in llvm.zig
...@@ -4618,7 +4480,7 @@ const NavGen = struct {...@@ -4618,7 +4480,7 @@ const NavGen = struct {
4618 return try src.materialize(self);4480 return try src.materialize(self);
4619 }4481 }
46204482
4621 const converted = try self.buildIntConvert(dst_ty, src);4483 const converted = try self.buildConvert(dst_ty, src);
46224484
4623 // Make sure to normalize the result if shrinking.4485 // Make sure to normalize the result if shrinking.
4624 // Because strange ints are sign extended in their backing4486 // Because strange ints are sign extended in their backing
...@@ -4698,17 +4560,10 @@ const NavGen = struct {...@@ -4698,17 +4560,10 @@ const NavGen = struct {
46984560
4699 fn airFloatCast(self: *NavGen, inst: Air.Inst.Index) !?IdRef {4561 fn airFloatCast(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
4700 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4562 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);
4702 const dest_ty = self.typeOfIndex(inst);4564 const dest_ty = self.typeOfIndex(inst);
4703 const dest_ty_id = try self.resolveType(dest_ty, .direct);4565 const result = try self.buildConvert(dest_ty, operand);
47044566 return try result.materialize(self);
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;
4712 }4567 }
47134568
4714 fn airNot(self: *NavGen, inst: Air.Inst.Index) !?IdRef {4569 fn airNot(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
...@@ -4790,13 +4645,14 @@ const NavGen = struct {...@@ -4790,13 +4645,14 @@ const NavGen = struct {
4790 const field_int_id = blk: {4645 const field_int_id = blk: {
4791 if (field_ty.isPtrAtRuntime(zcu)) {4646 if (field_ty.isPtrAtRuntime(zcu)) {
4792 assert(self.spv.hasFeature(.addresses) or4647 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));
4794 break :blk try self.intFromPtr(field_id);4650 break :blk try self.intFromPtr(field_id);
4795 }4651 }
4796 break :blk try self.bitCast(field_int_ty, field_ty, field_id);4652 break :blk try self.bitCast(field_int_ty, field_ty, field_id);
4797 };4653 };
4798 const shift_rhs = try self.constInt(backing_int_ty, running_bits);4654 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, .{
4800 .ty = field_int_ty,4656 .ty = field_int_ty,
4801 .value = .{ .singleton = field_int_id },4657 .value = .{ .singleton = field_int_id },
4802 });4658 });
...@@ -5016,17 +4872,6 @@ const NavGen = struct {...@@ -5016,17 +4872,6 @@ const NavGen = struct {
5016 const array_id = try self.resolve(bin_op.lhs);4872 const array_id = try self.resolve(bin_op.lhs);
5017 const index_id = try self.resolve(bin_op.rhs);4873 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
5030 // SPIR-V doesn't have an array indexing function for some damn reason.4875 // SPIR-V doesn't have an array indexing function for some damn reason.
5031 // For now, just generate a temporary and use that.4876 // For now, just generate a temporary and use that.
5032 // TODO: This backend probably also should use isByRef from llvm...4877 // TODO: This backend probably also should use isByRef from llvm...
...@@ -5173,7 +5018,7 @@ const NavGen = struct {...@@ -5173,7 +5018,7 @@ const NavGen = struct {
5173 return self.bitCast(ty, payload_ty, payload.?);5018 return self.bitCast(ty, payload_ty, payload.?);
5174 }5019 }
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.? } });
5177 return try trunc.materialize(self);5022 return try trunc.materialize(self);
5178 }5023 }
51795024
...@@ -5182,7 +5027,7 @@ const NavGen = struct {...@@ -5182,7 +5027,7 @@ const NavGen = struct {
5182 try self.convertToIndirect(payload_ty, payload.?)5027 try self.convertToIndirect(payload_ty, payload.?)
5183 else5028 else
5184 try self.bitCast(payload_int_ty, payload_ty, payload.?);5029 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 } });
5186 return try trunc.materialize(self);5031 return try trunc.materialize(self);
5187 }5032 }
51885033
...@@ -5208,13 +5053,16 @@ const NavGen = struct {...@@ -5208,13 +5053,16 @@ const NavGen = struct {
5208 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {5053 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5209 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);5054 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
5210 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});5055 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);5056 const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: {
5212 const active_pl_ptr_id = self.spv.allocId();5057 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
5213 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{5058 const active_pl_ptr_id = self.spv.allocId();
5214 .id_result_type = active_pl_ptr_ty_id,5059 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
5215 .id_result = active_pl_ptr_id,5060 .id_result_type = active_pl_ptr_ty_id,
5216 .operand = pl_ptr_id,5061 .id_result = active_pl_ptr_id,
5217 });5062 .operand = pl_ptr_id,
5063 });
5064 break :blk active_pl_ptr_id;
5065 } else pl_ptr_id;
52185066
5219 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});5067 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});
5220 } else {5068 } else {
...@@ -5271,9 +5119,9 @@ const NavGen = struct {...@@ -5271,9 +5119,9 @@ const NavGen = struct {
5271 const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);5119 const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
5272 const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });5120 const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
5273 const result_id = blk: {5121 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")
5275 break :blk try self.bitCast(field_int_ty, object_ty, try masked.materialize(self));5123 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);
5277 break :blk try trunc.materialize(self);5125 break :blk try trunc.materialize(self);
5278 };5126 };
5279 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);5127 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);
...@@ -5295,9 +5143,9 @@ const NavGen = struct {...@@ -5295,9 +5143,9 @@ const NavGen = struct {
5295 .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },5143 .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },
5296 );5144 );
5297 const result_id = blk: {5145 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")
5299 break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self));5147 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);
5301 break :blk try trunc.materialize(self);5149 break :blk try trunc.materialize(self);
5302 };5150 };
5303 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);5151 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);
...@@ -6332,17 +6180,15 @@ const NavGen = struct {...@@ -6332,17 +6180,15 @@ const NavGen = struct {
6332 .bool, .error_set => 1,6180 .bool, .error_set => 1,
6333 .int => blk: {6181 .int => blk: {
6334 const bits = cond_ty.intInfo(zcu).bits;6182 const bits = cond_ty.intInfo(zcu).bits;
6335 const backing_bits = self.backingIntBits(bits) orelse {6183 const backing_bits, const big_int = self.backingIntBits(bits);
6336 return self.todo("implement composite int switch", .{});6184 if (big_int) return self.todo("implement composite int switch", .{});
6337 };
6338 break :blk if (backing_bits <= 32) 1 else 2;6185 break :blk if (backing_bits <= 32) 1 else 2;
6339 },6186 },
6340 .@"enum" => blk: {6187 .@"enum" => blk: {
6341 const int_ty = cond_ty.intTagType(zcu);6188 const int_ty = cond_ty.intTagType(zcu);
6342 const int_info = int_ty.intInfo(zcu);6189 const int_info = int_ty.intInfo(zcu);
6343 const backing_bits = self.backingIntBits(int_info.bits) orelse {6190 const backing_bits, const big_int = self.backingIntBits(int_info.bits);
6344 return self.todo("implement composite int switch", .{});6191 if (big_int) return self.todo("implement composite int switch", .{});
6345 };
6346 break :blk if (backing_bits <= 32) 1 else 2;6192 break :blk if (backing_bits <= 32) 1 else 2;
6347 },6193 },
6348 .pointer => blk: {6194 .pointer => blk: {
...@@ -6752,7 +6598,7 @@ const NavGen = struct {...@@ -6752,7 +6598,7 @@ const NavGen = struct {
6752 // TODO: Should we make these builtins return usize?6598 // TODO: Should we make these builtins return usize?
6753 const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0);6599 const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0);
6754 const tmp = Temporary.init(Type.u64, result_id);6600 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);
6756 return try result.materialize(self);6602 return try result.materialize(self);
6757 }6603 }
67586604
...@@ -6763,7 +6609,7 @@ const NavGen = struct {...@@ -6763,7 +6609,7 @@ const NavGen = struct {
6763 // TODO: Should we make these builtins return usize?6609 // TODO: Should we make these builtins return usize?
6764 const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0);6610 const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0);
6765 const tmp = Temporary.init(Type.u64, result_id);6611 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);
6767 return try result.materialize(self);6613 return try result.materialize(self);
6768 }6614 }
67696615
...@@ -6774,7 +6620,7 @@ const NavGen = struct {...@@ -6774,7 +6620,7 @@ const NavGen = struct {
6774 // TODO: Should we make these builtins return usize?6620 // TODO: Should we make these builtins return usize?
6775 const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0);6621 const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0);
6776 const tmp = Temporary.init(Type.u64, result_id);6622 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);
6778 return try result.materialize(self);6624 return try result.materialize(self);
6779 }6625 }
67806626
src/codegen/spirv/Assembler.zig+18-4
...@@ -296,12 +296,26 @@ fn processInstruction(self: *Assembler) !void {...@@ -296,12 +296,26 @@ fn processInstruction(self: *Assembler) !void {
296 };296 };
297 break :blk .{ .value = try self.spv.importInstructionSet(set_tag) };297 break :blk .{ .value = try self.spv.importInstructionSet(set_tag) };
298 },298 },
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 },
299 else => switch (self.inst.opcode.class()) {316 else => switch (self.inst.opcode.class()) {
300 .TypeDeclaration => try self.processTypeInstruction(),317 .TypeDeclaration => try self.processTypeInstruction(),
301 else => if (try self.processGenericInstruction()) |result|318 else => (try self.processGenericInstruction()) orelse return,
302 result
303 else
304 return,
305 },319 },
306 };320 };
307321
src/codegen/spirv/Module.zig+47-30
...@@ -92,11 +92,12 @@ pub const Decl = struct {...@@ -92,11 +92,12 @@ pub const Decl = struct {
92/// This models a kernel entry point.92/// This models a kernel entry point.
93pub const EntryPoint = struct {93pub const EntryPoint = struct {
94 /// The declaration that should be exported.94 /// The declaration that should be exported.
95 decl_index: Decl.Index,95 decl_index: ?Decl.Index = null,
96 /// The name of the kernel to be exported.96 /// The name of the kernel to be exported.
97 name: []const u8,97 name: ?[]const u8 = null,
98 /// Calling Convention98 /// Calling Convention
99 execution_model: spec.ExecutionModel,99 exec_model: ?spec.ExecutionModel = null,
100 exec_mode: ?spec.ExecutionMode = null,
100};101};
101102
102/// A general-purpose allocator which may be used to allocate resources for this module103/// A general-purpose allocator which may be used to allocate resources for this module
...@@ -164,8 +165,6 @@ cache: struct {...@@ -164,8 +165,6 @@ cache: struct {
164 void_type: ?IdRef = null,165 void_type: ?IdRef = null,
165 int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .empty,166 int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .empty,
166 float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .empty,167 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.
169 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty,168 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty,
170 array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty,169 array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty,
171170
...@@ -186,7 +185,7 @@ decls: std.ArrayListUnmanaged(Decl) = .empty,...@@ -186,7 +185,7 @@ decls: std.ArrayListUnmanaged(Decl) = .empty,
186decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty,185decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty,
187186
188/// The list of entry points that should be exported from this module.187/// 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
191pub fn init(gpa: Allocator, target: std.Target) Module {190pub fn init(gpa: Allocator, target: std.Target) Module {
192 const version_minor: u8 = blk: {191 const version_minor: u8 = blk: {
...@@ -306,19 +305,30 @@ fn entryPoints(self: *Module) !Section {...@@ -306,19 +305,30 @@ fn entryPoints(self: *Module) !Section {
306 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);305 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
307 defer seen.deinit(self.gpa);306 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| {
310 interface.items.len = 0;309 interface.items.len = 0;
311 seen.setRangeValue(.{ .start = 0, .end = self.decls.items.len }, false);310 seen.setRangeValue(.{ .start = 0, .end = self.decls.items.len }, false);
312311
313 try self.addEntryPointDeps(entry_point.decl_index, &seen, &interface);312 try self.addEntryPointDeps(entry_point.decl_index.?, &seen, &interface);
314
315 const entry_point_id = self.declPtr(entry_point.decl_index).result_id;
316 try entry_points.emit(self.gpa, .OpEntryPoint, .{313 try entry_points.emit(self.gpa, .OpEntryPoint, .{
317 .execution_model = entry_point.execution_model,314 .execution_model = entry_point.exec_model.?,
318 .entry_point = entry_point_id,315 .entry_point = entry_point_id,
319 .name = entry_point.name,316 .name = entry_point.name.?,
320 .interface = interface.items,317 .interface = interface.items,
321 });318 });
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 }
322 }332 }
323333
324 return entry_points;334 return entry_points;
...@@ -352,6 +362,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {...@@ -352,6 +362,11 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
352 .vector16 => try self.addCapability(.Vector16),362 .vector16 => try self.addCapability(.Vector16),
353 // Shader363 // Shader
354 .shader => try self.addCapability(.Shader),364 .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 },
355 .physical_storage_buffer => {370 .physical_storage_buffer => {
356 try self.addExtension("SPV_KHR_physical_storage_buffer");371 try self.addExtension("SPV_KHR_physical_storage_buffer");
357 try self.addCapability(.PhysicalStorageBufferAddresses);372 try self.addCapability(.PhysicalStorageBufferAddresses);
...@@ -366,20 +381,20 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {...@@ -366,20 +381,20 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
366 // Emit memory model381 // Emit memory model
367 const addressing_model: spec.AddressingModel = blk: {382 const addressing_model: spec.AddressingModel = blk: {
368 if (self.hasFeature(.shader)) {383 if (self.hasFeature(.shader)) {
369 break :blk switch (self.target.cpu.arch) {384 if (self.hasFeature(.physical_storage_buffer)) {
370 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.385 assert(self.target.cpu.arch == .spirv64);
371 .spirv64 => .PhysicalStorageBuffer64,386 break :blk .PhysicalStorageBuffer64;
372 else => unreachable,387 }
373 };388 assert(self.target.cpu.arch == .spirv);
374 } else if (self.hasFeature(.kernel)) {389 break :blk .Logical;
375 break :blk switch (self.target.cpu.arch) {
376 .spirv32 => .Physical32,
377 .spirv64 => .Physical64,
378 else => unreachable,
379 };
380 }390 }
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 };
383 };398 };
384 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{399 try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{
385 .addressing_model = addressing_model,400 .addressing_model = addressing_model,
...@@ -746,13 +761,15 @@ pub fn declareEntryPoint(...@@ -746,13 +761,15 @@ pub fn declareEntryPoint(
746 self: *Module,761 self: *Module,
747 decl_index: Decl.Index,762 decl_index: Decl.Index,
748 name: []const u8,763 name: []const u8,
749 execution_model: spec.ExecutionModel,764 exec_model: spec.ExecutionModel,
765 exec_mode: ?spec.ExecutionMode,
750) !void {766) !void {
751 try self.entry_points.append(self.gpa, .{767 const gop = try self.entry_points.getOrPut(self.gpa, self.declPtr(decl_index).result_id);
752 .decl_index = decl_index,768 gop.value_ptr.decl_index = decl_index;
753 .name = try self.arena.allocator().dupe(u8, name),769 gop.value_ptr.name = try self.arena.allocator().dupe(u8, name);
754 .execution_model = execution_model,770 gop.value_ptr.exec_model = exec_model;
755 });771 // Might've been set by assembler
772 if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode;
756}773}
757774
758pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void {775pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void {
src/link/SpirV.zig+3-2
...@@ -162,7 +162,7 @@ pub fn updateExports(...@@ -162,7 +162,7 @@ pub fn updateExports(
162 if (ip.isFunctionType(nav_ty)) {162 if (ip.isFunctionType(nav_ty)) {
163 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);163 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
164 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);164 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) {
166 .vulkan, .opengl => switch (cc) {166 .vulkan, .opengl => switch (cc) {
167 .spirv_vertex => .Vertex,167 .spirv_vertex => .Vertex,
168 .spirv_fragment => .Fragment,168 .spirv_fragment => .Fragment,
...@@ -185,7 +185,8 @@ pub fn updateExports(...@@ -185,7 +185,8 @@ pub fn updateExports(
185 try self.object.spv.declareEntryPoint(185 try self.object.spv.declareEntryPoint(
186 spv_decl_index,186 spv_decl_index,
187 exp.opts.name.toSlice(ip),187 exp.opts.name.toSlice(ip),
188 execution_model,188 exec_model,
189 null,
189 );190 );
190 }191 }
191 }192 }
src/target.zig+15-9
...@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(...@@ -501,21 +501,26 @@ pub fn addrSpaceCastIsValid(
501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns501/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns
502/// for a particular architecture and address space wether such pointers are logical.502/// for a particular architecture and address space wether such pointers are logical.
503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {503pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {
504 if (target.os.tag != .vulkan) {504 if (target.os.tag != .vulkan) return false;
505 return false;
506 }
507505
508 return switch (as) {506 return switch (as) {
509 // TODO: Vulkan doesn't support pointers in the generic address space, we507 // TODO: Vulkan doesn't support pointers in the generic address space, we
510 // should remove this case but this requires a change in defaultAddressSpace().508 // should remove this case but this requires a change in defaultAddressSpace().
511 // For now, at least disable them from being regarded as physical.509 // For now, at least disable them from being regarded as physical.
512 .generic => true,510 .generic => true,
513 // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real511 // For now, all global pointers are represented using StorageBuffer or CrossWorkgroup,
514 // pointers.512 // so these are real pointers.
515 .global => false,513 .global => false,
516 // TODO: Allowed with VK_KHR_variable_pointers.514 .physical_storage_buffer => false,
517 .shared => true,515 .shared => !target.cpu.features.isEnabled(@intFromEnum(std.Target.spirv.Feature.variable_pointers)),
518 .constant, .local, .input, .output, .uniform, .push_constant, .storage_buffer => true,516 .constant,
517 .local,
518 .input,
519 .output,
520 .uniform,
521 .push_constant,
522 .storage_buffer,
523 => true,
519 else => unreachable,524 else => unreachable,
520 };525 };
521}526}
...@@ -802,7 +807,8 @@ pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBacken...@@ -802,7 +807,8 @@ pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBacken
802 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .stage2_powerpc,807 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .stage2_powerpc,
803 .riscv64 => .stage2_riscv64,808 .riscv64 => .stage2_riscv64,
804 .sparc64 => .stage2_sparc64,809 .sparc64 => .stage2_sparc64,
805 .spirv64 => .stage2_spirv64,810 .spirv32 => if (target.os.tag == .opencl) .stage2_spirv64 else .other,
811 .spirv, .spirv64 => .stage2_spirv64,
806 .wasm32, .wasm64 => .stage2_wasm,812 .wasm32, .wasm64 => .stage2_wasm,
807 .x86 => .stage2_x86,813 .x86 => .stage2_x86,
808 .x86_64 => .stage2_x86_64,814 .x86_64 => .stage2_x86_64,
test/cases/compile_errors/@import_zon_bad_type.zig+3-3
...@@ -117,9 +117,9 @@ export fn testMutablePointer() void {...@@ -117,9 +117,9 @@ export fn testMutablePointer() void {
117// tmp.zig:37:38: note: imported here117// tmp.zig:37:38: note: imported here
118// neg_inf.zon:1:1: error: expected type '?u8'118// neg_inf.zon:1:1: error: expected type '?u8'
119// tmp.zig:57:28: note: imported here119// 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'
121// tmp.zig:62:39: note: imported here121// 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'
123// tmp.zig:67:44: note: imported here123// 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'
125// tmp.zig:72:50: note: imported here125// 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 {...@@ -15,6 +15,6 @@ pub export fn entry() void {
15// error15// error
16//16//
17// :7:25: error: unable to resolve comptime value17// :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-known18// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_475.C' must be comptime-known
19// :4:16: note: struct requires comptime because of this field19// :4:16: note: struct requires comptime because of this field
20// :4:16: note: types are not available at runtime20// :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 {...@@ -16,5 +16,5 @@ pub export fn entry2() void {
16//16//
17// :3:6: error: no field or member function named 'copy' in '[]const u8'17// :3:6: error: no field or member function named 'copy' in '[]const u8'
18// :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})'18// :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'
20// :12:6: note: struct declared here20// :12:6: note: struct declared here
test/cases/compile_errors/coerce_anon_struct.zig+1-1
...@@ -6,6 +6,6 @@ export fn foo() void {...@@ -6,6 +6,6 @@ export fn foo() void {
66
7// error7// error
8//8//
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'
10// :3:16: note: struct declared here10// :3:16: note: struct declared here
11// :1:11: note: struct declared here11// :1:11: note: struct declared here
test/cases/compile_errors/redundant_try.zig+2-2
...@@ -44,9 +44,9 @@ comptime {...@@ -44,9 +44,9 @@ comptime {
44//44//
45// :5:23: error: expected error union type, found 'comptime_int'45// :5:23: error: expected error union type, found 'comptime_int'
46// :10:23: error: expected error union type, found '@TypeOf(.{})'46// :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'
48// :15:23: note: struct declared here48// :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'
50// :20:27: note: struct declared here50// :20:27: note: struct declared here
51// :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }'51// :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }'
52// :31:13: error: expected error union type, found 'u32'52// :31:13: error: expected error union type, found 'u32'
test/tests.zig+1-1
...@@ -145,7 +145,7 @@ const test_targets = blk: {...@@ -145,7 +145,7 @@ const test_targets = blk: {
145 .{145 .{
146 .target = std.Target.Query.parse(.{146 .target = std.Target.Query.parse(.{
147 .arch_os_abi = "spirv64-vulkan",147 .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",
149 }) catch unreachable,149 }) catch unreachable,
150 .use_llvm = false,150 .use_llvm = false,
151 .use_lld = false,151 .use_lld = false,
tools/update_cpu_features.zig+122
...@@ -1047,6 +1047,128 @@ const targets = [_]ArchTarget{...@@ -1047,6 +1047,128 @@ const targets = [_]ArchTarget{
1047 },1047 },
1048 },1048 },
1049 },1049 },
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 },
1050 .{1172 .{
1051 .zig_name = "riscv",1173 .zig_name = "riscv",
1052 .llvm = .{1174 .llvm = .{