authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-05-25 23:47:41+03:30
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-07 04:58:53+02:00
logd7d131c0503ae8a02677faa01d7d518a5441cb6f
tree2ecd47f8e08c6e63c932843868ef1448c9b98474
parent7a9f8dc9393ff4084da6c77d874d08e26897b3cb

add `@SpirvType` builtin

Closes #35240 Fixes #35238 Fixes #35259 Supported types are: - `OpTypeSampler` - `OpTypeImage` - `OpTypeSampledImage` - `OpTypeRuntimeArray` with indexing and `.len` field The SPIR-V backend is bit-rotted so behavior tests no longer pass (compiler crashes). However I've verified the new added tests are passing.

56 files changed, 1208 insertions(+), 60 deletions(-)

doc/langref.html.in+44
...@@ -5792,6 +5792,50 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5792,6 +5792,50 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5792 <p>Returns an {#link|enum#} type with the properties specified by the arguments.</p>5792 <p>Returns an {#link|enum#} type with the properties specified by the arguments.</p>
5793 {#header_close#}5793 {#header_close#}
57945794
5795 {#header_open|@SpirvType#}
5796 <pre>{#syntax#}@SpirvType(comptime options: std.lang.Type.Spirv) type{#endsyntax#}</pre>
5797 <p>
5798 Returns a SPIR-V type with the properties specified by the arguments.
5799 </p>
5800 <div class="table-wrapper">
5801 <table>
5802 <thead>
5803 <tr>
5804 <th scope="col">Tag</th>
5805 <th scope="col">SPIR-V Equivalent</th>
5806 <th scope="col">Description</th>
5807 </tr>
5808 </thead>
5809 <tbody>
5810 <tr>
5811 <th scope="row"><code>.sampler</code></th>
5812 <td><code>OpTypeSampler</code></td>
5813 <td>An opaque sampler</td>
5814 </tr>
5815 <tr>
5816 <th scope="row"><code>.image</code></th>
5817 <td><code>OpTypeImage</code></td>
5818 <td>An opaque image</td>
5819 </tr>
5820 <tr>
5821 <th scope="row"><code>.sampled_image</code></th>
5822 <td><code>OpTypeSampledImage</code></td>
5823 <td>An opaque image combined with a sampler</td>
5824 </tr>
5825 <tr>
5826 <th scope="row"><code>.runtime_array</code></th>
5827 <td><code>OpTypeRuntimeArray</code></td>
5828 <td>
5829 An array whose length is determined at runtime.
5830 The resulting type supports indexing and exposes a {#syntax#}.len{#endsyntax#} field.
5831 It may only appear as the last field of an {#link|extern struct#}.
5832 </td>
5833 </tr>
5834 </tbody>
5835 </table>
5836 </div>
5837 {#header_close#}
5838
5795 {#header_open|@typeInfo#}5839 {#header_open|@typeInfo#}
5796 <pre>{#syntax#}@typeInfo(comptime T: type) std.lang.Type{#endsyntax#}</pre>5840 <pre>{#syntax#}@typeInfo(comptime T: type) std.lang.Type{#endsyntax#}</pre>
5797 <p>5841 <p>
lib/std/Target.zig+2-1
...@@ -2341,7 +2341,8 @@ pub fn supportsAddressSpace(...@@ -2341,7 +2341,8 @@ pub fn supportsAddressSpace(
2341 .lut => arch == .propeller and std.Target.propeller.featureSetHas(target.cpu.features, .p2),2341 .lut => arch == .propeller and std.Target.propeller.featureSetHas(target.cpu.features, .p2),
23422342
2343 .global, .local, .shared => is_gpu,2343 .global, .local, .shared => is_gpu,
2344 .constant => is_gpu and (context == null or context == .constant),2344 .constant => (is_gpu and (context == null or context == .constant)) or
2345 (is_spirv and (context == null or context == .constant or context == .pointer)),
2345 .param => is_nvptx,2346 .param => is_nvptx,
2346 .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,2347 .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,
2347 };2348 };
lib/std/hash/auto_hash.zig+1
...@@ -76,6 +76,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -76,6 +76,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
76 switch (@typeInfo(Key)) {76 switch (@typeInfo(Key)) {
77 .noreturn,77 .noreturn,
78 .@"opaque",78 .@"opaque",
79 .spirv,
79 .undefined,80 .undefined,
80 .null,81 .null,
81 .comptime_float,82 .comptime_float,
lib/std/lang.zig+54
...@@ -578,6 +578,7 @@ pub const Type = union(enum) {...@@ -578,6 +578,7 @@ pub const Type = union(enum) {
578 @"anyframe": AnyFrame,578 @"anyframe": AnyFrame,
579 vector: Vector,579 vector: Vector,
580 enum_literal,580 enum_literal,
581 spirv: Spirv,
581582
582 /// This data structure is used by the Zig language code generation and583 /// This data structure is used by the Zig language code generation and
583 /// therefore must be kept in sync with the compiler implementation.584 /// therefore must be kept in sync with the compiler implementation.
...@@ -781,6 +782,59 @@ pub const Type = union(enum) {...@@ -781,6 +782,59 @@ pub const Type = union(enum) {
781 };782 };
782 };783 };
783784
785 /// This data structure is used by the Zig language code generation and
786 /// therefore must be kept in sync with the compiler implementation.
787 pub const Spirv = union(enum(u2)) {
788 sampler,
789 image: Image,
790 sampled_image: type,
791 runtime_array: type,
792
793 pub const Image = struct {
794 usage: Usage,
795 format: Format,
796 dim: Dimensionality,
797 depth: Depth,
798 access: Access,
799 arrayed: bool,
800 multisampled: bool,
801
802 pub const Usage = union(enum(u2)) {
803 unknown: type,
804 sampled: type,
805 storage,
806 };
807
808 pub const Format = enum(u4) {
809 unknown,
810 rgba32f,
811 rgba32i,
812 rgba32u,
813 rgba16f,
814 rgba16i,
815 rgba16u,
816 rgba8unorm,
817 rgba8snorm,
818 rgba8i,
819 rgba8u,
820 r32f,
821 r32i,
822 r32u,
823 };
824
825 pub const Dimensionality = enum(u2) {
826 @"1d",
827 @"2d",
828 @"3d",
829 cube,
830 };
831
832 pub const Depth = enum(u2) { unknown, depth, not_depth };
833
834 pub const Access = enum(u2) { unknown, read_only, write_only, read_write };
835 };
836 };
837
784 /// This data structure is used by the Zig language code generation and838 /// This data structure is used by the Zig language code generation and
785 /// therefore must be kept in sync with the compiler implementation.839 /// therefore must be kept in sync with the compiler implementation.
786 pub const Opaque = struct {840 pub const Opaque = struct {
lib/std/mem.zig+1
...@@ -352,6 +352,7 @@ pub fn zeroes(comptime T: type) T {...@@ -352,6 +352,7 @@ pub fn zeroes(comptime T: type) T {
352 .noreturn,352 .noreturn,
353 .undefined,353 .undefined,
354 .@"opaque",354 .@"opaque",
355 .spirv,
355 .frame,356 .frame,
356 .@"anyframe",357 .@"anyframe",
357 => {358 => {
lib/std/start.zig+3-1
...@@ -19,7 +19,9 @@ comptime {...@@ -19,7 +19,9 @@ comptime {
19 // decls there get run.19 // decls there get run.
20 _ = root;20 _ = root;
2121
22 if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) {22 if (builtin.zig_backend == .stage2_spirv) {
23 // Do nothing
24 } else if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) {
23 const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup";25 const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup";
24 if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) {26 if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) {
25 @export(&DllMainCRTStartup, .{ .name = dll_main_crt_startup });27 @export(&DllMainCRTStartup, .{ .name = dll_main_crt_startup });
lib/std/testing.zig+2
...@@ -79,6 +79,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -79,6 +79,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
79 switch (@typeInfo(@TypeOf(actual))) {79 switch (@typeInfo(@TypeOf(actual))) {
80 .noreturn,80 .noreturn,
81 .@"opaque",81 .@"opaque",
82 .spirv,
82 .frame,83 .frame,
83 .@"anyframe",84 .@"anyframe",
84 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),85 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
...@@ -737,6 +738,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -737,6 +738,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
737 switch (@typeInfo(@TypeOf(actual))) {738 switch (@typeInfo(@TypeOf(actual))) {
738 .noreturn,739 .noreturn,
739 .@"opaque",740 .@"opaque",
741 .spirv,
740 .frame,742 .frame,
741 .@"anyframe",743 .@"anyframe",
742 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),744 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
lib/std/zig/AstGen.zig+10
...@@ -9327,6 +9327,16 @@ fn builtinCall(...@@ -9327,6 +9327,16 @@ fn builtinCall(
9327 });9327 });
9328 return rvalue(gz, ri, result, node);9328 return rvalue(gz, ri, result, node);
9329 },9329 },
9330 .SpirvType => {
9331 const spirv_type_options_ty = try gz.addStdLangValue(node, .spirv_type_options);
9332 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = spirv_type_options_ty } }, params[0], .type);
9333 const result = try gz.addExtendedPayload(.reify_spirv_type, Zir.Inst.ReifySpirvType{
9334 .src_line = gz.astgen.source_line,
9335 .node = node,
9336 .operand = operand,
9337 });
9338 return rvalue(gz, ri, result, node);
9339 },
93309340
9331 .panic => {9341 .panic => {
9332 try emitDbgNode(gz, node);9342 try emitDbgNode(gz, node);
lib/std/zig/AstRlAnnotate.zig+4
...@@ -1079,6 +1079,10 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -1079,6 +1079,10 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
1079 _ = try astrl.expr(args[3], block, ResultInfo.type_only);1079 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1080 return false;1080 return false;
1081 },1081 },
1082 .SpirvType => {
1083 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1084 return false;
1085 },
1082 .Vector => {1086 .Vector => {
1083 _ = try astrl.expr(args[0], block, ResultInfo.type_only);1087 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1084 _ = try astrl.expr(args[1], block, ResultInfo.type_only);1088 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig+8
...@@ -114,6 +114,7 @@ pub const Tag = enum {...@@ -114,6 +114,7 @@ pub const Tag = enum {
114 Struct,114 Struct,
115 Union,115 Union,
116 Enum,116 Enum,
117 SpirvType,
117 type_info,118 type_info,
118 type_name,119 type_name,
119 TypeOf,120 TypeOf,
...@@ -971,6 +972,13 @@ pub const list = list: {...@@ -971,6 +972,13 @@ pub const list = list: {
971 .param_count = 4,972 .param_count = 4,
972 },973 },
973 },974 },
975 .{
976 "@SpirvType",
977 .{
978 .tag = .SpirvType,
979 .param_count = 1,
980 },
981 },
974 .{982 .{
975 "@typeInfo",983 "@typeInfo",
976 .{984 .{
lib/std/zig/Zir.zig+14
...@@ -2055,6 +2055,9 @@ pub const Inst = struct {...@@ -2055,6 +2055,9 @@ pub const Inst = struct {
2055 /// `operand` is payload index to `ReifyEnum`.2055 /// `operand` is payload index to `ReifyEnum`.
2056 /// `small` contains `NameStrategy`.2056 /// `small` contains `NameStrategy`.
2057 reify_enum,2057 reify_enum,
2058 /// Implements builtin `@SpirvType`.
2059 /// `operand` is payload index to `ReifyFn`.
2060 reify_spirv_type,
2058 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.2061 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
2059 /// `small` 0=>weak 1=>strong2062 /// `small` 0=>weak 1=>strong
2060 /// `operand` is payload index to `Cmpxchg`.2063 /// `operand` is payload index to `Cmpxchg`.
...@@ -3269,6 +3272,14 @@ pub const Inst = struct {...@@ -3269,6 +3272,14 @@ pub const Inst = struct {
3269 field_values: Ref,3272 field_values: Ref,
3270 };3273 };
32713274
3275 pub const ReifySpirvType = struct {
3276 src_line: u32,
3277 /// This node is absolute, because `reify` instructions are tracked across updates, and
3278 /// this simplifies the logic for getting source locations for types.
3279 node: Ast.Node.Index,
3280 operand: Ref,
3281 };
3282
3272 /// Trailing:3283 /// Trailing:
3273 /// 0. multi_cases_len: u32, // If has_multi_cases is set.3284 /// 0. multi_cases_len: u32, // If has_multi_cases is set.
3274 /// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set.3285 /// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set.
...@@ -3584,6 +3595,7 @@ pub const Inst = struct {...@@ -3584,6 +3595,7 @@ pub const Inst = struct {
3584 fn_attributes,3595 fn_attributes,
3585 container_layout,3596 container_layout,
3586 enum_mode,3597 enum_mode,
3598 spirv_type_options,
3587 // Values3599 // Values
3588 calling_convention_c,3600 calling_convention_c,
3589 calling_convention_inline,3601 calling_convention_inline,
...@@ -4389,6 +4401,7 @@ fn findTrackableInner(...@@ -4389,6 +4401,7 @@ fn findTrackableInner(
4389 .reify_enum,4401 .reify_enum,
4390 .reify_struct,4402 .reify_struct,
4391 .reify_union,4403 .reify_union,
4404 .reify_spirv_type,
4392 => return contents.other.append(gpa, inst),4405 => return contents.other.append(gpa, inst),
43934406
4394 // Type declarations need tracking.4407 // Type declarations need tracking.
...@@ -5181,6 +5194,7 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void {...@@ -5181,6 +5194,7 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void {
5181 .reify_enum,5194 .reify_enum,
5182 .reify_struct,5195 .reify_struct,
5183 .reify_union,5196 .reify_union,
5197 .reify_spirv_type,
5184 => {}, // tracked in order, as the owner instructions of explicit container types5198 => {}, // tracked in order, as the owner instructions of explicit container types
5185 else => unreachable, // assertion failure; not trackable5199 else => unreachable, // assertion failure; not trackable
5186 },5200 },
lib/std/zon/Serializer.zig+1
...@@ -854,6 +854,7 @@ fn canSerializeTypeInner(...@@ -854,6 +854,7 @@ fn canSerializeTypeInner(
854 .frame,854 .frame,
855 .@"anyframe",855 .@"anyframe",
856 .@"opaque",856 .@"opaque",
857 .spirv,
857 => false,858 => false,
858859
859 .@"enum" => |@"enum"| @"enum".mode == .exhaustive,860 .@"enum" => |@"enum"| @"enum".mode == .exhaustive,
lib/std/zon/parse.zig+1
...@@ -1213,6 +1213,7 @@ fn canParseTypeInner(...@@ -1213,6 +1213,7 @@ fn canParseTypeInner(
1213 .frame,1213 .frame,
1214 .@"anyframe",1214 .@"anyframe",
1215 .@"opaque",1215 .@"opaque",
1216 .spirv,
1216 .comptime_int,1217 .comptime_int,
1217 .comptime_float,1218 .comptime_float,
1218 .enum_literal,1219 .enum_literal,
src/Air.zig+7
...@@ -918,6 +918,11 @@ pub const Inst = struct {...@@ -918,6 +918,11 @@ pub const Inst = struct {
918 /// Uses the `ty` field.918 /// Uses the `ty` field.
919 c_va_start,919 c_va_start,
920920
921 /// Implements `.len` field for `@SpirvType(.{ .runtime_array = T })`.
922 /// Result type is always `u32`.
923 /// Uses the `ty_pl` field, payload is `StructField`.
924 spirv_runtime_array_len,
925
921 /// Implements @workItemId builtin.926 /// Implements @workItemId builtin.
922 /// Result type is always `u32`927 /// Result type is always `u32`
923 /// Uses the `pl_op` field, payload is the dimension to get the work item id for.928 /// Uses the `pl_op` field, payload is the dimension to get the work item id for.
...@@ -1793,6 +1798,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1793,6 +1798,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1793 .work_item_id,1798 .work_item_id,
1794 .work_group_size,1799 .work_group_size,
1795 .work_group_id,1800 .work_group_id,
1801 .spirv_runtime_array_len,
1796 => return .u32,1802 => return .u32,
17971803
1798 .legalize_compiler_rt_call => return datas[@intFromEnum(inst)].legalize_compiler_rt_call.func.returnType(),1804 .legalize_compiler_rt_call => return datas[@intFromEnum(inst)].legalize_compiler_rt_call.func.returnType(),
...@@ -2056,6 +2062,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -2056,6 +2062,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
2056 .work_group_size,2062 .work_group_size,
2057 .work_group_id,2063 .work_group_id,
2058 .legalize_vec_elem_val,2064 .legalize_vec_elem_val,
2065 .spirv_runtime_array_len,
2059 => false,2066 => false,
20602067
2061 .is_non_null_ptr, .is_null_ptr, .is_non_err_ptr, .is_err_ptr => air.typeOf(data.un_op, ip).isVolatilePtrIp(ip),2068 .is_non_null_ptr, .is_null_ptr, .is_non_err_ptr, .is_err_ptr => air.typeOf(data.un_op, ip).isVolatilePtrIp(ip),
src/Air/Legalize.zig+1
...@@ -908,6 +908,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -908,6 +908,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
908 .legalize_vec_elem_val,908 .legalize_vec_elem_val,
909 .legalize_vec_store_elem,909 .legalize_vec_store_elem,
910 .legalize_compiler_rt_call,910 .legalize_compiler_rt_call,
911 .spirv_runtime_array_len,
911 => {},912 => {},
912 }913 }
913 }914 }
src/Air/Liveness.zig+1-1
...@@ -673,7 +673,7 @@ fn analyzeInst(...@@ -673,7 +673,7 @@ fn analyzeInst(
673 const extra = a.air.extraData(Air.UnionInit, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;673 const extra = a.air.extraData(Air.UnionInit, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;
674 return analyzeOperands(a, pass, data, inst, .{ extra.init, .none, .none });674 return analyzeOperands(a, pass, data, inst, .{ extra.init, .none, .none });
675 },675 },
676 .struct_field_ptr, .struct_field_val => {676 .struct_field_ptr, .struct_field_val, .spirv_runtime_array_len => {
677 const extra = a.air.extraData(Air.StructField, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;677 const extra = a.air.extraData(Air.StructField, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;
678 return analyzeOperands(a, pass, data, inst, .{ extra.struct_operand, .none, .none });678 return analyzeOperands(a, pass, data, inst, .{ extra.struct_operand, .none, .none });
679 },679 },
src/Air/Liveness/Verify.zig+1-1
...@@ -191,7 +191,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -191,7 +191,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
191 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;191 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
192 try self.verifyInstOperands(inst, .{ extra.init, .none, .none });192 try self.verifyInstOperands(inst, .{ extra.init, .none, .none });
193 },193 },
194 .struct_field_ptr, .struct_field_val => {194 .struct_field_ptr, .struct_field_val, .spirv_runtime_array_len => {
195 const ty_pl = data[@intFromEnum(inst)].ty_pl;195 const ty_pl = data[@intFromEnum(inst)].ty_pl;
196 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;196 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
197 try self.verifyInstOperands(inst, .{ extra.struct_operand, .none, .none });197 try self.verifyInstOperands(inst, .{ extra.struct_operand, .none, .none });
src/Air/print.zig+1
...@@ -306,6 +306,7 @@ const Writer = struct {...@@ -306,6 +306,7 @@ const Writer = struct {
306306
307 .struct_field_ptr => try w.writeStructField(s, inst),307 .struct_field_ptr => try w.writeStructField(s, inst),
308 .struct_field_val => try w.writeStructField(s, inst),308 .struct_field_val => try w.writeStructField(s, inst),
309 .spirv_runtime_array_len => try w.writeStructField(s, inst),
309 .inferred_alloc => @panic("TODO"),310 .inferred_alloc => @panic("TODO"),
310 .inferred_alloc_comptime => @panic("TODO"),311 .inferred_alloc_comptime => @panic("TODO"),
311 .assembly => try w.writeAssembly(s, inst),312 .assembly => try w.writeAssembly(s, inst),
src/InternPool.zig+112-1
...@@ -1983,6 +1983,7 @@ pub const Key = union(enum) {...@@ -1983,6 +1983,7 @@ pub const Key = union(enum) {
1983 union_type: ContainerType,1983 union_type: ContainerType,
1984 opaque_type: ContainerType,1984 opaque_type: ContainerType,
1985 enum_type: ContainerType,1985 enum_type: ContainerType,
1986 spirv_type: SpirvType,
1986 func_type: FuncType,1987 func_type: FuncType,
1987 error_set_type: ErrorSetType,1988 error_set_type: ErrorSetType,
1988 /// The payload is the function body, either a `func_decl` or `func_instance`.1989 /// The payload is the function body, either a `func_decl` or `func_instance`.
...@@ -2146,6 +2147,13 @@ pub const Key = union(enum) {...@@ -2146,6 +2147,13 @@ pub const Key = union(enum) {
2146 };2147 };
2147 };2148 };
21482149
2150 pub const SpirvType = struct {
2151 /// A `spirv_reify` instruction.
2152 zir_index: TrackedInst.Index,
2153 /// A hash of this type's attributes generated by Sema.
2154 type_hash: u64,
2155 };
2156
2149 pub const FuncType = struct {2157 pub const FuncType = struct {
2150 param_types: Index.Slice,2158 param_types: Index.Slice,
2151 return_type: Index,2159 return_type: Index,
...@@ -2590,6 +2598,7 @@ pub const Key = union(enum) {...@@ -2590,6 +2598,7 @@ pub const Key = union(enum) {
2590 .opt_type,2598 .opt_type,
2591 .anyframe_type,2599 .anyframe_type,
2592 .error_union_type,2600 .error_union_type,
2601 .spirv_type,
2593 .simple_type,2602 .simple_type,
2594 .simple_value,2603 .simple_value,
2595 .opt,2604 .opt,
...@@ -2841,6 +2850,10 @@ pub const Key = union(enum) {...@@ -2841,6 +2850,10 @@ pub const Key = union(enum) {
2841 const b_info = b.error_union_type;2850 const b_info = b.error_union_type;
2842 return std.meta.eql(a_info, b_info);2851 return std.meta.eql(a_info, b_info);
2843 },2852 },
2853 .spirv_type => |a_info| {
2854 const b_info = b.spirv_type;
2855 return std.meta.eql(a_info, b_info);
2856 },
2844 .simple_type => |a_info| {2857 .simple_type => |a_info| {
2845 const b_info = b.simple_type;2858 const b_info = b.simple_type;
2846 return a_info == b_info;2859 return a_info == b_info;
...@@ -3130,6 +3143,7 @@ pub const Key = union(enum) {...@@ -3130,6 +3143,7 @@ pub const Key = union(enum) {
3130 .simple_type,3143 .simple_type,
3131 .struct_type,3144 .struct_type,
3132 .union_type,3145 .union_type,
3146 .spirv_type,
3133 .opaque_type,3147 .opaque_type,
3134 .enum_type,3148 .enum_type,
3135 .tuple_type,3149 .tuple_type,
...@@ -3877,6 +3891,14 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {...@@ -3877,6 +3891,14 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {
3877 };3891 };
3878}3892}
38793893
3894pub fn loadSpirvType(ip: *const InternPool, index: Index) Tag.TypeSpirv {
3895 const unwrapped_index = index.unwrap(ip);
3896 const item = unwrapped_index.getItem(ip);
3897 assert(item.tag == .type_spirv);
3898 const extra = extraData(unwrapped_index.getExtra(ip), Tag.TypeSpirv, item.data);
3899 return extra;
3900}
3901
3880pub const Item = struct {3902pub const Item = struct {
3881 tag: Tag,3903 tag: Tag,
3882 /// The doc comments on the respective Tag explain how to interpret this.3904 /// The doc comments on the respective Tag explain how to interpret this.
...@@ -4214,6 +4236,8 @@ pub const Index = enum(u32) {...@@ -4214,6 +4236,8 @@ pub const Index = enum(u32) {
4214 type_enum_nonexhaustive: struct { data: *Tag.TypeEnum },4236 type_enum_nonexhaustive: struct { data: *Tag.TypeEnum },
4215 type_opaque: struct { data: *Tag.TypeOpaque },4237 type_opaque: struct { data: *Tag.TypeOpaque },
42164238
4239 type_spirv: struct { data: *Tag.TypeSpirv },
4240
4217 undef: DataIsIndex,4241 undef: DataIsIndex,
4218 simple_value: void,4242 simple_value: void,
4219 ptr_nav: struct { data: *PtrNav },4243 ptr_nav: struct { data: *PtrNav },
...@@ -4841,6 +4865,10 @@ pub const Tag = enum(u8) {...@@ -4841,6 +4865,10 @@ pub const Tag = enum(u8) {
4841 /// data is extra index of `TypeEnum`.4865 /// data is extra index of `TypeEnum`.
4842 type_enum_nonexhaustive,4866 type_enum_nonexhaustive,
48434867
4868 /// An spirv type.
4869 /// data is index of `TypeSpirv` in extra.
4870 type_spirv,
4871
4844 /// An opaque type.4872 /// An opaque type.
4845 /// data is extra index of `TypeOpaque`.4873 /// data is extra index of `TypeOpaque`.
4846 type_opaque,4874 type_opaque,
...@@ -5231,6 +5259,7 @@ pub const Tag = enum(u8) {...@@ -5231,6 +5259,7 @@ pub const Tag = enum(u8) {
5231 },5259 },
5232 .type_enum_explicit = enum_explicit_encoding,5260 .type_enum_explicit = enum_explicit_encoding,
5233 .type_enum_nonexhaustive = enum_explicit_encoding,5261 .type_enum_nonexhaustive = enum_explicit_encoding,
5262 .type_spirv = .{ .summary = .@"{.payload.name%summary#\"}", .payload = Tag.TypeSpirv },
5234 .type_opaque = .{5263 .type_opaque = .{
5235 .summary = .@"{.payload.name%summary#\"}",5264 .summary = .@"{.payload.name%summary#\"}",
5236 .payload = TypeOpaque,5265 .payload = TypeOpaque,
...@@ -5688,6 +5717,34 @@ pub const Tag = enum(u8) {...@@ -5688,6 +5717,34 @@ pub const Tag = enum(u8) {
5688 name_nav: Nav.Index.Optional,5717 name_nav: Nav.Index.Optional,
5689 namespace: NamespaceIndex,5718 namespace: NamespaceIndex,
5690 };5719 };
5720
5721 /// Trailing:
5722 /// 0. type_hash: PackedU64
5723 pub const TypeSpirv = struct {
5724 name: NullTerminatedString,
5725 /// The index of the `reify_spirv_type` instruction.
5726 zir_index: TrackedInst.Index,
5727 /// If tag is `.image`, this is the sampled type or `.none` if `usage` is `.storage`.
5728 /// If tag is `.sampled_image`, this is the image type.
5729 /// If tag is `.runtime_array`, this is the element type.
5730 /// Otherwise this is `.none`.
5731 ty: Index,
5732 flags: Flags,
5733
5734 pub const Flags = packed struct(u32) {
5735 tag: @typeInfo(std.lang.Type.Spirv).@"union".tag_type.?,
5736 // Image type flags
5737 usage: @typeInfo(std.lang.Type.Spirv.Image.Usage).@"union".tag_type.?,
5738 format: std.lang.Type.Spirv.Image.Format,
5739 dim: std.lang.Type.Spirv.Image.Dimensionality,
5740 depth: std.lang.Type.Spirv.Image.Depth,
5741 access: std.lang.Type.Spirv.Image.Access,
5742 is_arrayed: bool,
5743 is_multisampled: bool,
5744
5745 _: u16 = 0,
5746 };
5747 };
5691};5748};
56925749
5693/// Differentiates between user-provided and compiler-generated backing types for packed and tagged types.5750/// Differentiates between user-provided and compiler-generated backing types for packed and tagged types.
...@@ -6573,6 +6630,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -6573,6 +6630,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
6573 } },6630 } },
6574 };6631 };
6575 } },6632 } },
6633 .type_spirv => .{ .spirv_type = ns: {
6634 const extra_list = unwrapped_index.getExtra(ip);
6635 const extra = extraDataTrail(extra_list, Tag.TypeSpirv, data);
6636 break :ns .{
6637 .zir_index = extra.data.zir_index,
6638 .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
6639 };
6640 } },
6576 .type_opaque => .{ .opaque_type = ns: {6641 .type_opaque => .{ .opaque_type = ns: {
6577 const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);6642 const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);
6578 break :ns .{ .declared = .{6643 break :ns .{ .declared = .{
...@@ -7345,6 +7410,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:...@@ -7345,6 +7410,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
7345 .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType7410 .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType
7346 .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType7411 .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType
7347 .opaque_type => unreachable, // instead use: getDeclaredOpaqueType7412 .opaque_type => unreachable, // instead use: getDeclaredOpaqueType
7413 .spirv_type => unreachable, // instead use: getSpirvType
73487414
7349 .tuple_type => unreachable, // use getTupleType() instead7415 .tuple_type => unreachable, // use getTupleType() instead
7350 .func_type => unreachable, // use getFuncType() instead7416 .func_type => unreachable, // use getFuncType() instead
...@@ -8717,6 +8783,39 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT...@@ -8717,6 +8783,39 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT
8717 } };8783 } };
8718}8784}
87198785
8786pub fn getReifiedSpirvType(
8787 ip: *InternPool,
8788 gpa: Allocator,
8789 io: Io,
8790 tid: Zcu.PerThread.Id,
8791 ini: struct {
8792 zir_index: TrackedInst.Index,
8793 type_hash: u64,
8794 type_spirv: Tag.TypeSpirv,
8795 },
8796) Allocator.Error!Index {
8797 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .spirv_type = .{
8798 .zir_index = ini.zir_index,
8799 .type_hash = ini.type_hash,
8800 } });
8801 defer gop.deinit();
8802 if (gop == .existing) return gop.existing;
8803
8804 const local = ip.getLocal(tid);
8805 const items = local.getMutableItems(gpa, io);
8806 const extra = local.getMutableExtra(gpa, io);
8807 try items.ensureUnusedCapacity(1);
8808
8809 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeSpirv).@"struct".field_names.len +
8810 2 // type_hash: PackedU64
8811 );
8812 const extra_index = addExtraAssumeCapacity(extra, ini.type_spirv);
8813 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash));
8814
8815 items.appendAssumeCapacity(.{ .tag = .type_spirv, .data = extra_index });
8816 return gop.put();
8817}
8818
8720pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {8819pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8721 /// The union type for which this enum is a generated tag.8820 /// The union type for which this enum is a generated tag.
8722 union_type: Index,8821 union_type: Index,
...@@ -9077,7 +9176,7 @@ pub fn getExtern(...@@ -9077,7 +9176,7 @@ pub fn getExtern(
9077 }) catch unreachable; // capacity asserted above9176 }) catch unreachable; // capacity asserted above
9078 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {9177 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {
9079 .location => |location| .{ Tag.Extern.Flags.DecorationType.location, location, undefined },9178 .location => |location| .{ Tag.Extern.Flags.DecorationType.location, location, undefined },
9080 .descriptor => |descriptor| .{ Tag.Extern.Flags.DecorationType.descriptor, descriptor.binding, descriptor.set },9179 .descriptor => |descriptor| .{ Tag.Extern.Flags.DecorationType.descriptor, descriptor.set, descriptor.binding },
9081 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };9180 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };
9082 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{9181 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
9083 .ty = key.ty,9182 .ty = key.ty,
...@@ -9810,6 +9909,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {...@@ -9810,6 +9909,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
9810 Tag.TypeStructPacked.Bits,9909 Tag.TypeStructPacked.Bits,
9811 Tag.TypeUnionPacked.Bits,9910 Tag.TypeUnionPacked.Bits,
9812 Tag.TypeEnum.Bits,9911 Tag.TypeEnum.Bits,
9912 Tag.TypeSpirv.Flags,
9813 => @bitCast(@field(item, field_name)),9913 => @bitCast(@field(item, field_name)),
98149914
9815 else => @compileError("bad field type: " ++ @typeName(field_type)),9915 else => @compileError("bad field type: " ++ @typeName(field_type)),
...@@ -9877,6 +9977,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat...@@ -9877,6 +9977,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
9877 Tag.TypeStructPacked.Bits,9977 Tag.TypeStructPacked.Bits,
9878 Tag.TypeUnionPacked.Bits,9978 Tag.TypeUnionPacked.Bits,
9879 Tag.TypeEnum.Bits,9979 Tag.TypeEnum.Bits,
9980 Tag.TypeSpirv.Flags,
9880 => @bitCast(extra_item),9981 => @bitCast(extra_item),
98819982
9882 else => @compileError("bad field type: " ++ @typeName(field_type)),9983 else => @compileError("bad field type: " ++ @typeName(field_type)),
...@@ -9930,6 +10031,11 @@ pub fn childType(ip: *const InternPool, i: Index) Index {...@@ -9930,6 +10031,11 @@ pub fn childType(ip: *const InternPool, i: Index) Index {
9930 .vector_type => |vector_type| vector_type.child,10031 .vector_type => |vector_type| vector_type.child,
9931 .array_type => |array_type| array_type.child,10032 .array_type => |array_type| array_type.child,
9932 .opt_type, .anyframe_type => |child| child,10033 .opt_type, .anyframe_type => |child| child,
10034 .spirv_type => blk: {
10035 const info = ip.loadSpirvType(i);
10036 assert(info.flags.tag == .runtime_array);
10037 break :blk info.ty;
10038 },
9933 else => unreachable,10039 else => unreachable,
9934 };10040 };
9935}10041}
...@@ -10583,6 +10689,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo...@@ -10583,6 +10689,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
10583 .type_optional => 0,10689 .type_optional => 0,
10584 .type_anyframe => 0,10690 .type_anyframe => 0,
10585 .type_error_union => @sizeOf(Key.ErrorUnionType),10691 .type_error_union => @sizeOf(Key.ErrorUnionType),
10692 .type_spirv => @sizeOf(Tag.TypeSpirv) + @sizeOf(PackedU64),
10586 .type_anyerror_union => 0,10693 .type_anyerror_union => 0,
10587 .type_error_set => b: {10694 .type_error_set => b: {
10588 const info = extraData(extra_list, Tag.ErrorSet, data);10695 const info = extraData(extra_list, Tag.ErrorSet, data);
...@@ -10860,6 +10967,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {...@@ -10860,6 +10967,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
10860 .type_enum_explicit,10967 .type_enum_explicit,
10861 .type_enum_nonexhaustive,10968 .type_enum_nonexhaustive,
10862 .type_opaque,10969 .type_opaque,
10970 .type_spirv,
10863 .undef,10971 .undef,
10864 .ptr_nav,10972 .ptr_nav,
10865 .ptr_comptime_alloc,10973 .ptr_comptime_alloc,
...@@ -11598,6 +11706,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {...@@ -11598,6 +11706,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
11598 .type_enum_explicit,11706 .type_enum_explicit,
11599 .type_enum_nonexhaustive,11707 .type_enum_nonexhaustive,
11600 .type_opaque,11708 .type_opaque,
11709 .type_spirv,
11601 => .type_type,11710 => .type_type,
1160211711
11603 .undef,11712 .undef,
...@@ -11955,6 +12064,8 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.lang.TypeId {...@@ -11955,6 +12064,8 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.lang.TypeId {
11955 .type_opaque,12064 .type_opaque,
11956 => .@"opaque",12065 => .@"opaque",
1195712066
12067 .type_spirv => .spirv,
12068
11958 .type_function => .@"fn",12069 .type_function => .@"fn",
1195912070
11960 // values, not types12071 // values, not types
src/Sema.zig+442-1
...@@ -1434,6 +1434,7 @@ fn analyzeBodyInner(...@@ -1434,6 +1434,7 @@ fn analyzeBodyInner(
1434 .reify_struct => try sema.zirReifyStruct( block, extended, inst),1434 .reify_struct => try sema.zirReifyStruct( block, extended, inst),
1435 .reify_union => try sema.zirReifyUnion( block, extended, inst),1435 .reify_union => try sema.zirReifyUnion( block, extended, inst),
1436 .reify_enum => try sema.zirReifyEnum( block, extended, inst),1436 .reify_enum => try sema.zirReifyEnum( block, extended, inst),
1437 .reify_spirv_type => try sema.zirReifySpirvType( block, extended, inst),
1437 // zig fmt: on1438 // zig fmt: on
14381439
1439 .set_float_mode => {1440 .set_float_mode => {
...@@ -9273,6 +9274,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9273,6 +9274,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9273 .noreturn,9274 .noreturn,
9274 .null,9275 .null,
9275 .@"opaque",9276 .@"opaque",
9277 .spirv,
9276 .optional,9278 .optional,
9277 .type,9279 .type,
9278 .undefined,9280 .undefined,
...@@ -9348,6 +9350,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9348,6 +9350,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9348 .noreturn,9350 .noreturn,
9349 .null,9351 .null,
9350 .@"opaque",9352 .@"opaque",
9353 .spirv,
9351 .optional,9354 .optional,
9352 .type,9355 .type,
9353 .undefined,9356 .undefined,
...@@ -15531,6 +15534,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -15531,6 +15534,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
15531 .undefined,15534 .undefined,
15532 .null,15535 .null,
15533 .@"opaque",15536 .@"opaque",
15537 .spirv,
15534 .type,15538 .type,
15535 .enum_literal,15539 .enum_literal,
15536 .comptime_float,15540 .comptime_float,
...@@ -16834,6 +16838,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -16834,6 +16838,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
16834 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(),16838 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(),
16835 })));16839 })));
16836 },16840 },
16841 .spirv => unreachable, // TODO: ALI
16837 .frame => return sema.failWithUseOfAsync(block, src),16842 .frame => return sema.failWithUseOfAsync(block, src),
16838 .@"anyframe" => return sema.failWithUseOfAsync(block, src),16843 .@"anyframe" => return sema.failWithUseOfAsync(block, src),
16839 }16844 }
...@@ -20449,6 +20454,306 @@ fn zirReifyEnum(...@@ -20449,6 +20454,306 @@ fn zirReifyEnum(
20449 }20454 }
20450}20455}
2045120456
20457fn zirReifySpirvType(
20458 sema: *Sema,
20459 block: *Block,
20460 extended: Zir.Inst.Extended.InstData,
20461 inst: Zir.Inst.Index,
20462) CompileError!Air.Inst.Ref {
20463 const pt = sema.pt;
20464 const zcu = pt.zcu;
20465 const comp = zcu.comp;
20466 const gpa = comp.gpa;
20467 const io = comp.io;
20468 const ip = &zcu.intern_pool;
20469 const target = zcu.getTarget();
20470
20471 const extra = sema.code.extraData(Zir.Inst.ReifySpirvType, extended.operand).data;
20472 const tracked_inst = try block.trackZir(inst);
20473 const src: LazySrcLoc = .{
20474 .base_node_inst = tracked_inst,
20475 .offset = .nodeOffset(.zero),
20476 };
20477 const operand_src: LazySrcLoc = .{
20478 .base_node_inst = tracked_inst,
20479 .offset = .{ .node_offset_builtin_call_arg = .{
20480 .builtin_call_node = .zero,
20481 .arg_index = 0,
20482 } },
20483 };
20484
20485 if (!target.cpu.arch.isSpirV()) {
20486 return sema.fail(
20487 block,
20488 src,
20489 "builtin @SpirvType is only available when targeting SPIR-V; targeted CPU architecture is {t}",
20490 .{target.cpu.arch},
20491 );
20492 }
20493
20494 const spirv_type_options_ty = try sema.getStdLangType(operand_src, .@"Type.Spirv");
20495 const operand_uncoerced = sema.resolveInst(extra.operand);
20496 const operand_coerced = try sema.coerce(block, spirv_type_options_ty, operand_uncoerced, operand_src);
20497 const operand_val = try sema.resolveConstDefinedValue(block, operand_src, operand_coerced, .{ .simple = .type });
20498 const union_val = ip.indexToKey(operand_val.toIntern()).un;
20499
20500 if (try sema.anyUndef(block, operand_src, .fromInterned(union_val.val))) {
20501 return sema.failWithUseOfUndef(block, operand_src, null);
20502 }
20503
20504 // TODO: use a longer hash!
20505 var hasher = std.hash.Wyhash.init(0);
20506 std.hash.autoHash(&hasher, union_val.tag);
20507
20508 const name = try ip.getOrPutStringFmt(
20509 gpa,
20510 io,
20511 pt.tid,
20512 "{f}__SpirvType_{d}",
20513 .{ block.type_name_ctx.fmt(ip), @intFromEnum(inst) },
20514 .no_embedded_nulls,
20515 );
20516 const tag = try sema.interpretStdLangType(block, src, .fromInterned(union_val.tag), @typeInfo(std.lang.Type.Spirv).@"union".tag_type.?);
20517 const ip_data: InternPool.Tag.TypeSpirv = switch (tag) {
20518 .sampler => .{
20519 .name = name,
20520 .zir_index = tracked_inst,
20521 .ty = .none,
20522 .flags = .{
20523 .tag = .sampler,
20524 .usage = .unknown,
20525 .format = .unknown,
20526 .dim = .@"1d",
20527 .depth = .unknown,
20528 .access = .unknown,
20529 .is_arrayed = false,
20530 .is_multisampled = false,
20531 },
20532 },
20533 .image => ip_data: {
20534 const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
20535 const usage_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20536 ip,
20537 try ip.getOrPutString(gpa, io, pt.tid, "usage", .no_embedded_nulls),
20538 ).?);
20539 const format_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20540 ip,
20541 try ip.getOrPutString(gpa, io, pt.tid, "format", .no_embedded_nulls),
20542 ).?);
20543 const dim_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20544 ip,
20545 try ip.getOrPutString(gpa, io, pt.tid, "dim", .no_embedded_nulls),
20546 ).?);
20547 const depth_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20548 ip,
20549 try ip.getOrPutString(gpa, io, pt.tid, "depth", .no_embedded_nulls),
20550 ).?);
20551 const access_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20552 ip,
20553 try ip.getOrPutString(gpa, io, pt.tid, "access", .no_embedded_nulls),
20554 ).?);
20555 const arrayed_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20556 ip,
20557 try ip.getOrPutString(gpa, io, pt.tid, "arrayed", .no_embedded_nulls),
20558 ).?);
20559 const multisampled_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20560 ip,
20561 try ip.getOrPutString(gpa, io, pt.tid, "multisampled", .no_embedded_nulls),
20562 ).?);
20563 const format = try sema.interpretStdLangType(block, operand_src, format_val, std.lang.Type.Spirv.Image.Format);
20564 const dim = try sema.interpretStdLangType(block, operand_src, dim_val, std.lang.Type.Spirv.Image.Dimensionality);
20565 const depth = try sema.interpretStdLangType(block, operand_src, depth_val, std.lang.Type.Spirv.Image.Depth);
20566 const access = try sema.interpretStdLangType(block, operand_src, access_val, std.lang.Type.Spirv.Image.Access);
20567
20568 switch (target.os.tag) {
20569 .opencl => if (access == .unknown) {
20570 return sema.fail(block, operand_src, "'access' field must be specified under the 'opencl' os", .{});
20571 },
20572 else => if (access != .unknown) {
20573 return sema.fail(block, operand_src, "access qualifier '.{t}' is only valid under the 'opencl' os", .{access});
20574 },
20575 }
20576
20577 const arrayed = try sema.interpretStdLangType(block, operand_src, arrayed_val, bool);
20578 const multisampled = try sema.interpretStdLangType(block, operand_src, multisampled_val, bool);
20579
20580 const usage_tag_val = usage_val.unionTag(zcu).?;
20581 const usage_tag = try sema.interpretStdLangType(block, operand_src, usage_tag_val, @typeInfo(std.lang.Type.Spirv.Image.Usage).@"union".tag_type.?);
20582
20583 switch (target.os.tag) {
20584 .vulkan => {
20585 if (usage_tag == .unknown) {
20586 return sema.fail(
20587 block,
20588 operand_src,
20589 "'usage' must be '.sampled' or '.storage' under the 'vulkan' os (Sampled == 0 is forbidden)",
20590 .{},
20591 );
20592 }
20593 },
20594 .opencl => {
20595 if (usage_tag != .unknown) {
20596 return sema.fail(block, operand_src, "'usage' must be '.unknown' under the 'opencl' os", .{});
20597 }
20598 if (multisampled) {
20599 return sema.fail(block, operand_src, "'multisampled' must be 'false' under the 'opencl' os", .{});
20600 }
20601 if (format != .unknown) {
20602 return sema.fail(block, operand_src, "'format' must be '.unknown' under the 'opencl' os", .{});
20603 }
20604 if (dim == .cube) {
20605 return sema.fail(block, operand_src, "'dim' '.cube' is not allowed under the 'opencl' os", .{});
20606 }
20607 if (arrayed and dim != .@"1d" and dim != .@"2d") {
20608 return sema.fail(block, operand_src, "'arrayed' may only be 'true' when 'dim' is '.1d' or '.2d' under the 'opencl' os", .{});
20609 }
20610 },
20611 else => {},
20612 }
20613
20614 std.hash.autoHash(&hasher, usage_tag);
20615 std.hash.autoHash(&hasher, format);
20616 std.hash.autoHash(&hasher, dim);
20617 std.hash.autoHash(&hasher, depth);
20618 std.hash.autoHash(&hasher, access);
20619 std.hash.autoHash(&hasher, arrayed);
20620 std.hash.autoHash(&hasher, multisampled);
20621
20622 break :ip_data .{
20623 .name = name,
20624 .zir_index = tracked_inst,
20625 .ty = switch (usage_tag) {
20626 .sampled, .unknown => blk: {
20627 const sampled_type = usage_val.unionPayload(zcu).toType();
20628 std.hash.autoHash(&hasher, sampled_type.toIntern());
20629
20630 if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
20631 return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
20632 }
20633 if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
20634 return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
20635 }
20636
20637 if (sampled_type.toIntern() != .void_type and
20638 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
20639 {
20640 return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
20641 }
20642
20643 if (target.os.tag == .vulkan) {
20644 const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
20645 (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
20646 if (!ok) {
20647 return sema.fail(
20648 block,
20649 operand_src,
20650 "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
20651 .{usage_tag},
20652 );
20653 }
20654
20655 if (format != .unknown) {
20656 const format_kind: enum { float, sint, uint } = switch (format) {
20657 .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
20658 .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
20659 .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
20660 .unknown => unreachable,
20661 };
20662 const matches = switch (format_kind) {
20663 .float => sampled_type.isRuntimeFloat(),
20664 .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
20665 .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
20666 };
20667 if (!matches) {
20668 return sema.fail(
20669 block,
20670 operand_src,
20671 "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
20672 .{ format, usage_tag, sampled_type.fmt(pt) },
20673 );
20674 }
20675 }
20676 }
20677
20678 break :blk sampled_type.toIntern();
20679 },
20680 .storage => .none,
20681 },
20682 .flags = .{
20683 .tag = .image,
20684 .usage = usage_tag,
20685 .format = format,
20686 .dim = dim,
20687 .depth = depth,
20688 .access = access,
20689 .is_arrayed = arrayed,
20690 .is_multisampled = multisampled,
20691 },
20692 };
20693 },
20694 .sampled_image => blk: {
20695 const image_ty = Value.fromInterned(union_val.val).toType();
20696 if (image_ty.zigTypeTag(zcu) != .spirv or ip.loadSpirvType(image_ty.toIntern()).flags.tag != .image) {
20697 return sema.fail(block, operand_src, "'sampled_image' element must be an @SpirvType image, found '{f}'", .{image_ty.fmt(pt)});
20698 }
20699 const image_info = ip.loadSpirvType(image_ty.toIntern()).flags;
20700 if (image_info.usage != .sampled) {
20701 return sema.fail(block, operand_src, "'sampled_image' element must be an image with 'usage = .sampled'", .{});
20702 }
20703 std.hash.autoHash(&hasher, union_val.val);
20704 break :blk .{
20705 .name = name,
20706 .zir_index = tracked_inst,
20707 .ty = union_val.val,
20708 .flags = .{
20709 .tag = tag,
20710 .usage = .unknown,
20711 .format = .unknown,
20712 .dim = .@"1d",
20713 .depth = .unknown,
20714 .access = .unknown,
20715 .is_arrayed = false,
20716 .is_multisampled = false,
20717 },
20718 };
20719 },
20720 .runtime_array => blk: {
20721 const elem_ty = Value.fromInterned(union_val.val).toType();
20722 if (elem_ty.toIntern() == .void_type) {
20723 return sema.fail(block, operand_src, "'runtime_array' element type must not be 'void'", .{});
20724 }
20725 if (target.os.tag == .vulkan and
20726 elem_ty.zigTypeTag(zcu) == .spirv and
20727 ip.loadSpirvType(elem_ty.toIntern()).flags.tag == .runtime_array)
20728 {
20729 return sema.fail(block, operand_src, "'runtime_array' of 'runtime_array' is not allowed under the 'vulkan' os", .{});
20730 }
20731 std.hash.autoHash(&hasher, union_val.val);
20732 break :blk .{
20733 .name = name,
20734 .zir_index = tracked_inst,
20735 .ty = union_val.val,
20736 .flags = .{
20737 .tag = tag,
20738 .usage = .unknown,
20739 .format = .unknown,
20740 .dim = .@"1d",
20741 .depth = .unknown,
20742 .access = .unknown,
20743 .is_arrayed = false,
20744 .is_multisampled = false,
20745 },
20746 };
20747 },
20748 };
20749
20750 return .fromIntern(try ip.getReifiedSpirvType(gpa, io, pt.tid, .{
20751 .zir_index = tracked_inst,
20752 .type_hash = hasher.final(),
20753 .type_spirv = ip_data,
20754 }));
20755}
20756
20452fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {20757fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
20453 const pt = sema.pt;20758 const pt = sema.pt;
20454 const va_list_ty = try sema.getStdLangType(src, .VaList);20759 const va_list_ty = try sema.getStdLangType(src, .VaList);
...@@ -24760,6 +25065,7 @@ fn zirStdLangValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD...@@ -24760,6 +25065,7 @@ fn zirStdLangValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
24760 .fn_attributes, => .@"Type.Fn.Attributes",25065 .fn_attributes, => .@"Type.Fn.Attributes",
24761 .container_layout => .@"Type.ContainerLayout",25066 .container_layout => .@"Type.ContainerLayout",
24762 .enum_mode => .@"Type.Enum.Mode",25067 .enum_mode => .@"Type.Enum.Mode",
25068 .spirv_type_options => .@"Type.Spirv",
24763 // zig fmt: on25069 // zig fmt: on
2476425070
24765 // Values are handled here.25071 // Values are handled here.
...@@ -24957,6 +25263,7 @@ fn explainWhyTypeIsComptime(...@@ -24957,6 +25263,7 @@ fn explainWhyTypeIsComptime(
24957 .void,25263 .void,
24958 .@"enum",25264 .@"enum",
24959 .@"opaque",25265 .@"opaque",
25266 .spirv,
24960 .pointer,25267 .pointer,
24961 => unreachable, // not comptime-only25268 => unreachable, // not comptime-only
2496225269
...@@ -25043,6 +25350,7 @@ pub fn explainWhyTypeIsNotExtern(...@@ -25043,6 +25350,7 @@ pub fn explainWhyTypeIsNotExtern(
25043 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),25350 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
2504425351
25045 .@"opaque",25352 .@"opaque",
25353 .spirv,
25046 .bool,25354 .bool,
25047 .float,25355 .float,
25048 .@"anyframe",25356 .@"anyframe",
...@@ -25483,9 +25791,13 @@ fn fieldPtrLoad(...@@ -25483,9 +25791,13 @@ fn fieldPtrLoad(
25483) CompileError!Air.Inst.Ref {25791) CompileError!Air.Inst.Ref {
25484 const pt = sema.pt;25792 const pt = sema.pt;
25485 const zcu = pt.zcu;25793 const zcu = pt.zcu;
25794 const ip = &zcu.intern_pool;
25486 const object_ptr_ty = sema.typeOf(object_ptr);25795 const object_ptr_ty = sema.typeOf(object_ptr);
25487 assert(object_ptr_ty.zigTypeTag(zcu) == .pointer);25796 assert(object_ptr_ty.zigTypeTag(zcu) == .pointer);
25488 const pointee_ty = object_ptr_ty.childType(zcu);25797 const pointee_ty = object_ptr_ty.childType(zcu);
25798 if (pointee_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) {
25799 return sema.analyzeSpirvRuntimeArrayLen(block, src, object_ptr, field_name_src);
25800 }
25489 try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access);25801 try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access);
25490 if (try pointee_ty.onePossibleValue(pt)) |opv| {25802 if (try pointee_ty.onePossibleValue(pt)) |opv| {
25491 const object: Air.Inst.Ref = .fromValue(opv);25803 const object: Air.Inst.Ref = .fromValue(opv);
...@@ -25675,11 +25987,123 @@ fn fieldVal(...@@ -25675,11 +25987,123 @@ fn fieldVal(
25675 } else {25987 } else {
25676 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);25988 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
25677 },25989 },
25990 .spirv => if (inner_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) {
25991 if (!is_pointer_to) {
25992 return sema.fail(
25993 block,
25994 src,
25995 "accessing 'len' field on a SPIR-V runtime_array requires a pointer to the array field",
25996 .{},
25997 );
25998 }
25999 return sema.analyzeSpirvRuntimeArrayLen(block, src, object, field_name_src);
26000 },
25678 else => {},26001 else => {},
25679 }26002 }
25680 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);26003 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
25681}26004}
2568226005
26006fn analyzeSpirvRuntimeArrayLen(
26007 sema: *Sema,
26008 block: *Block,
26009 src: LazySrcLoc,
26010 runtime_array_ptr: Air.Inst.Ref,
26011 src_for_err: LazySrcLoc,
26012) CompileError!Air.Inst.Ref {
26013 const pt = sema.pt;
26014 const zcu = pt.zcu;
26015 const ip = &zcu.intern_pool;
26016
26017 const struct_operand: Air.Inst.Ref, const field_index: u32 = sf: {
26018 if (runtime_array_ptr.toIndex()) |inst| {
26019 const tag = sema.air_instructions.items(.tag)[@intFromEnum(inst)];
26020 const data = sema.air_instructions.items(.data)[@intFromEnum(inst)];
26021 switch (tag) {
26022 .struct_field_ptr => {
26023 const extra = sema.getTmpAir().extraData(Air.StructField, data.ty_pl.payload).data;
26024 break :sf .{ extra.struct_operand, extra.field_index };
26025 },
26026 .struct_field_ptr_index_0 => break :sf .{ data.ty_op.operand, 0 },
26027 .struct_field_ptr_index_1 => break :sf .{ data.ty_op.operand, 1 },
26028 .struct_field_ptr_index_2 => break :sf .{ data.ty_op.operand, 2 },
26029 .struct_field_ptr_index_3 => break :sf .{ data.ty_op.operand, 3 },
26030 else => {},
26031 }
26032 }
26033
26034 const ptr_val = sema.resolveValue(runtime_array_ptr) orelse return sema.fail(
26035 block,
26036 src_for_err,
26037 "'len' field on a SPIR-V runtime_array requires direct struct field access",
26038 .{},
26039 );
26040 const ptr_key = ip.indexToKey(ptr_val.toIntern()).ptr;
26041 if (ptr_key.base_addr == .field and ptr_key.byte_offset == 0) {
26042 const field = ptr_key.base_addr.field;
26043 break :sf .{ .fromIntern(field.base), @intCast(field.index) };
26044 }
26045
26046 const parent_ty: Type = switch (ptr_key.base_addr) {
26047 .nav => |nav| .fromInterned(ip.getNav(nav).resolved.?.type),
26048 .uav => |uav| .fromInterned(ip.typeOf(uav.val)),
26049 .comptime_alloc,
26050 .comptime_field,
26051 .eu_payload,
26052 .opt_payload,
26053 .arr_elem,
26054 .field,
26055 .int,
26056 => return sema.fail(
26057 block,
26058 src_for_err,
26059 "'len' field on a SPIR-V runtime_array requires direct struct field access",
26060 .{},
26061 ),
26062 };
26063 if (parent_ty.zigTypeTag(zcu) != .@"struct") return sema.fail(
26064 block,
26065 src_for_err,
26066 "'len' field on a SPIR-V runtime_array requires the array to be a struct field",
26067 .{},
26068 );
26069
26070 const field_ptr_info = ip.indexToKey(ptr_key.ty).ptr_type;
26071 const rtarr_ty_ip = field_ptr_info.child;
26072 const struct_obj = ip.loadStructType(parent_ty.toIntern());
26073 const field_idx: u32 = for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, i| {
26074 if (field_ty_ip == rtarr_ty_ip and
26075 struct_obj.field_offsets.get(ip)[i] == ptr_key.byte_offset)
26076 {
26077 break @intCast(i);
26078 }
26079 } else unreachable;
26080 const struct_ptr_ty = try pt.ptrType(.{
26081 .child = parent_ty.toIntern(),
26082 .flags = field_ptr_info.flags,
26083 });
26084 const struct_ptr_val = try sema.ptrSubtract(
26085 block,
26086 src_for_err,
26087 ptr_val,
26088 ptr_key.byte_offset,
26089 struct_ptr_ty,
26090 );
26091 break :sf .{ .fromIntern(struct_ptr_val.toIntern()), field_idx };
26092 };
26093
26094 try sema.requireRuntimeBlock(block, src, null);
26095 return block.addInst(.{
26096 .tag = .spirv_runtime_array_len,
26097 .data = .{ .ty_pl = .{
26098 .ty = .u32_type,
26099 .payload = try sema.addExtra(Air.StructField{
26100 .struct_operand = struct_operand,
26101 .field_index = field_index,
26102 }),
26103 } },
26104 });
26105}
26106
25683fn fieldPtr(26107fn fieldPtr(
25684 sema: *Sema,26108 sema: *Sema,
25685 block: *Block,26109 block: *Block,
...@@ -26540,6 +26964,7 @@ fn elemPtr(...@@ -26540,6 +26964,7 @@ fn elemPtr(
26540 .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),26964 .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
26541 .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),26965 .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
26542 .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),26966 .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
26967 .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable_ptr, elem_index),
26543 else => {26968 else => {
26544 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);26969 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
26545 try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access);26970 try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access);
...@@ -26964,6 +27389,22 @@ fn elemPtrVector(...@@ -26964,6 +27389,22 @@ fn elemPtrVector(
26964 return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty);27389 return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty);
26965}27390}
2696627391
27392fn elemPtrSpirvRuntimeArray(
27393 sema: *Sema,
27394 block: *Block,
27395 array_ptr: Air.Inst.Ref,
27396 elem_index: Air.Inst.Ref,
27397) CompileError!Air.Inst.Ref {
27398 const pt = sema.pt;
27399 const zcu = pt.zcu;
27400 const array_ptr_ty = sema.typeOf(array_ptr);
27401 assert(array_ptr_ty.ptrSize(zcu) == .one);
27402 const array_ty = array_ptr_ty.childType(zcu);
27403 assert(array_ty.isSpirvRuntimeArray(zcu));
27404 const elem_ptr_ty = try array_ptr_ty.elemPtrType(null, pt);
27405 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
27406}
27407
26967/// Asserts that the layout of the array is already resolved.27408/// Asserts that the layout of the array is already resolved.
26968fn elemPtrArray(27409fn elemPtrArray(
26969 sema: *Sema,27410 sema: *Sema,
...@@ -31496,7 +31937,7 @@ const PeerResolveStrategy = enum {...@@ -31496,7 +31937,7 @@ const PeerResolveStrategy = enum {
3149631937
31497 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {31938 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {
31498 return switch (ty.zigTypeTag(zcu)) {31939 return switch (ty.zigTypeTag(zcu)) {
31499 .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact,31940 .type, .void, .bool, .@"opaque", .spirv, .frame, .@"anyframe" => .exact,
31500 .noreturn, .undefined => .unknown,31941 .noreturn, .undefined => .unknown,
31501 .null => .nullable,31942 .null => .nullable,
31502 .comptime_int => .comptime_int,31943 .comptime_int => .comptime_int,
src/Sema/LowerZon.zig+2
...@@ -244,6 +244,7 @@ fn checkTypeInner(...@@ -244,6 +244,7 @@ fn checkTypeInner(
244 .frame,244 .frame,
245 .@"anyframe",245 .@"anyframe",
246 .@"opaque",246 .@"opaque",
247 .spirv,
247 => return self.failUnsupportedResultType(ty, null),248 => return self.failUnsupportedResultType(ty, null),
248249
249 .pointer => {250 .pointer => {
...@@ -408,6 +409,7 @@ fn lowerExprKnownResTyInner(...@@ -408,6 +409,7 @@ fn lowerExprKnownResTyInner(
408 .error_set,409 .error_set,
409 .@"fn",410 .@"fn",
410 .@"opaque",411 .@"opaque",
412 .spirv,
411 .frame,413 .frame,
412 .@"anyframe",414 .@"anyframe",
413 .void,415 .void,
src/Sema/bitcast.zig+1
...@@ -249,6 +249,7 @@ const UnpackValueBits = struct {...@@ -249,6 +249,7 @@ const UnpackValueBits = struct {
249 .tuple_type,249 .tuple_type,
250 .union_type,250 .union_type,
251 .opaque_type,251 .opaque_type,
252 .spirv_type,
252 .enum_type,253 .enum_type,
253 .func_type,254 .func_type,
254 .error_set_type,255 .error_set_type,
src/Sema/comptime_ptr_access.zig+2
...@@ -422,6 +422,7 @@ fn loadComptimePtrInner(...@@ -422,6 +422,7 @@ fn loadComptimePtrInner(
422 .undefined,422 .undefined,
423 .enum_literal,423 .enum_literal,
424 .@"opaque",424 .@"opaque",
425 .spirv,
425 .@"fn",426 .@"fn",
426 .error_union,427 .error_union,
427 => unreachable, // ill-defined layout428 => unreachable, // ill-defined layout
...@@ -854,6 +855,7 @@ fn prepareComptimePtrStore(...@@ -854,6 +855,7 @@ fn prepareComptimePtrStore(
854 .undefined,855 .undefined,
855 .enum_literal,856 .enum_literal,
856 .@"opaque",857 .@"opaque",
858 .spirv,
857 .@"fn",859 .@"fn",
858 .error_union,860 .error_union,
859 => unreachable, // ill-defined layout861 => unreachable, // ill-defined layout
src/Sema/type_resolution.zig+44-1
...@@ -87,6 +87,7 @@ fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *cons...@@ -87,6 +87,7 @@ fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *cons
87 .ptr_type,87 .ptr_type,
88 .anyframe_type,88 .anyframe_type,
89 .simple_type,89 .simple_type,
90 .spirv_type,
90 .opaque_type,91 .opaque_type,
91 .error_set_type,92 .error_set_type,
92 .inferred_error_set_type,93 .inferred_error_set_type,
...@@ -288,10 +289,12 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -288,10 +289,12 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
288 }289 }
289290
290 // Resolve the layout of all fields, and check their types are allowed.291 // Resolve the layout of all fields, and check their types are allowed.
292 const fields_len = struct_obj.field_types.len;
291 for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {293 for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
292 const field_ty: Type = .fromInterned(field_ty_ip);294 const field_ty: Type = .fromInterned(field_ty_ip);
293 assert(!field_ty.isGenericPoison());295 assert(!field_ty.isGenericPoison());
294 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });296 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
297 const field_name_src = block.src(.{ .container_field_name = @intCast(field_index) });
295 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);298 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
296 if (field_ty.zigTypeTag(zcu) == .@"opaque") {299 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
297 return sema.failWithOwnedErrorMsg(&block, msg: {300 return sema.failWithOwnedErrorMsg(&block, msg: {
...@@ -302,6 +305,35 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -302,6 +305,35 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
302 break :msg msg;305 break :msg msg;
303 });306 });
304 }307 }
308 if (field_ty.zigTypeTag(zcu) == .spirv) {
309 if (field_ty.isSpirvRuntimeArray(zcu)) {
310 if (struct_obj.layout != .@"extern") {
311 return sema.failWithOwnedErrorMsg(&block, msg: {
312 const msg = try sema.errMsg(struct_ty.srcLoc(zcu), "non-extern struct cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
313 errdefer msg.destroy(gpa);
314 try sema.errNote(field_name_src, msg, "while checking this field", .{});
315 break :msg msg;
316 });
317 }
318 if (field_index != fields_len - 1) {
319 return sema.failWithOwnedErrorMsg(&block, msg: {
320 const msg = try sema.errMsg(struct_ty.srcLoc(zcu), "struct field of type '{f}' must be the last field", .{field_ty.fmt(pt)});
321 errdefer msg.destroy(gpa);
322 try sema.errNote(field_name_src, msg, "while checking this field", .{});
323 break :msg msg;
324 });
325 }
326 } else {
327 return sema.failWithOwnedErrorMsg(&block, msg: {
328 const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)});
329 errdefer msg.destroy(gpa);
330 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
331 try sema.addDeclaredHereNote(msg, field_ty);
332 break :msg msg;
333 });
334 }
335 }
336
305 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {337 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {
306 return sema.failWithOwnedErrorMsg(&block, msg: {338 return sema.failWithOwnedErrorMsg(&block, msg: {
307 const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});339 const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
...@@ -413,7 +445,10 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -413,7 +445,10 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
413 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);445 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
414 const offset = resolved_field_aligns[field_idx].forward(cur_offset);446 const offset = resolved_field_aligns[field_idx].forward(cur_offset);
415 struct_obj.field_offsets.get(ip)[field_idx] = @truncate(offset); // truncate because the overflow is handled below447 struct_obj.field_offsets.get(ip)[field_idx] = @truncate(offset); // truncate because the overflow is handled below
416 cur_offset = offset + field_ty.abiSize(zcu);448 // A SPIR-V `runtime_array` always trails the struct and
449 // contributes nothing to the struct's static size.
450 const field_size = if (field_ty.isSpirvRuntimeArray(zcu)) 0 else field_ty.abiSize(zcu);
451 cur_offset = offset + field_size;
417 }452 }
418 const struct_size: u32 = switch (class) {453 const struct_size: u32 = switch (class) {
419 .no_possible_value => 0,454 .no_possible_value => 0,
...@@ -858,6 +893,14 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {...@@ -858,6 +893,14 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
858 break :msg msg;893 break :msg msg;
859 });894 });
860 }895 }
896 if (field_ty.zigTypeTag(zcu) == .spirv) {
897 return sema.failWithOwnedErrorMsg(&block, msg: {
898 const msg = try sema.errMsg(field_ty_src, "SPIR-V type '{f}' have unknown size and therefore cannot be directly embedded in unions", .{field_ty.fmt(pt)});
899 errdefer msg.destroy(gpa);
900 try sema.addDeclaredHereNote(msg, field_ty);
901 break :msg msg;
902 });
903 }
861 if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) {904 if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) {
862 return sema.failWithOwnedErrorMsg(&block, msg: {905 return sema.failWithOwnedErrorMsg(&block, msg: {
863 const msg = try sema.errMsg(field_ty_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});906 const msg = try sema.errMsg(field_ty_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
src/Type.zig+30-1
...@@ -170,6 +170,7 @@ pub fn classify(start_ty: Type, zcu: *const Zcu) Class {...@@ -170,6 +170,7 @@ pub fn classify(start_ty: Type, zcu: *const Zcu) Class {
170170
171 .func_type => .fully_comptime,171 .func_type => .fully_comptime,
172172
173 .spirv_type => if (cur_ty.isSpirvRuntimeArray(zcu)) .runtime else .no_possible_value,
173 .opaque_type => .no_possible_value,174 .opaque_type => .no_possible_value,
174175
175 .error_union_type => |eu| {176 .error_union_type => |eu| {
...@@ -323,6 +324,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {...@@ -323,6 +324,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
323 .error_set,324 .error_set,
324 .@"fn",325 .@"fn",
325 .@"opaque",326 .@"opaque",
327 .spirv,
326 .@"anyframe",328 .@"anyframe",
327 .@"enum",329 .@"enum",
328 .enum_literal,330 .enum_literal,
...@@ -617,6 +619,10 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari...@@ -617,6 +619,10 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari
617 const name = ip.loadEnumType(ty.toIntern()).name;619 const name = ip.loadEnumType(ty.toIntern()).name;
618 try writer.print("{f}", .{name.fmt(ip)});620 try writer.print("{f}", .{name.fmt(ip)});
619 },621 },
622 .spirv_type => {
623 const name = ip.loadSpirvType(ty.toIntern()).name;
624 try writer.print("{f}", .{name.fmt(ip)});
625 },
620 .func_type => |fn_info| {626 .func_type => |fn_info| {
621 if (fn_info.is_noinline) {627 if (fn_info.is_noinline) {
622 try writer.writeAll("noinline ");628 try writer.writeAll("noinline ");
...@@ -704,6 +710,14 @@ pub fn toIntern(ty: Type) InternPool.Index {...@@ -704,6 +710,14 @@ pub fn toIntern(ty: Type) InternPool.Index {
704 return ty.ip_index;710 return ty.ip_index;
705}711}
706712
713pub fn isSpirvRuntimeArray(ty: Type, zcu: *const Zcu) bool {
714 const ip = &zcu.intern_pool;
715 return switch (ip.indexToKey(ty.toIntern())) {
716 .spirv_type => ip.loadSpirvType(ty.toIntern()).flags.tag == .runtime_array,
717 else => false,
718 };
719}
720
707pub fn toValue(self: Type) Value {721pub fn toValue(self: Type) Value {
708 return .fromInterned(self.toIntern());722 return .fromInterned(self.toIntern());
709}723}
...@@ -751,6 +765,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {...@@ -751,6 +765,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
751 .error_set_type,765 .error_set_type,
752 .inferred_error_set_type,766 .inferred_error_set_type,
753 .tuple_type,767 .tuple_type,
768 .spirv_type,
754 .opaque_type,769 .opaque_type,
755 .anyframe_type,770 .anyframe_type,
756 // These are function bodies, not function pointers.771 // These are function bodies, not function pointers.
...@@ -1038,6 +1053,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {...@@ -1038,6 +1053,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
1038 }1053 }
1039 },1054 },
1040 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu),1055 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu),
1056 .spirv_type => if (ty.isSpirvRuntimeArray(zcu)) ty.childType(zcu).abiAlignment(zcu) else .@"1",
1041 .opaque_type => .@"1",1057 .opaque_type => .@"1",
10421058
1043 // values, not types1059 // values, not types
...@@ -1183,6 +1199,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {...@@ -1183,6 +1199,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
1183 }1199 }
1184 },1200 },
1185 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),1201 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),
1202 .spirv_type => unreachable,
1186 .opaque_type => unreachable,1203 .opaque_type => unreachable,
11871204
1188 // values, not types1205 // values, not types
...@@ -1309,7 +1326,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {...@@ -1309,7 +1326,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
1309 .tuple_type,1326 .tuple_type,
1310 => ty.abiSize(zcu) * 8,1327 => ty.abiSize(zcu) * 8,
13111328
1312 .opaque_type => unreachable,1329 .opaque_type, .spirv_type => unreachable,
13131330
1314 // values, not types1331 // values, not types
1315 .undef,1332 .undef,
...@@ -1511,14 +1528,17 @@ pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {...@@ -1511,14 +1528,17 @@ pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {
1511/// * `[]T`1528/// * `[]T`
1512/// * `[*]T`1529/// * `[*]T`
1513/// * `[*c]T`1530/// * `[*c]T`
1531/// * `@SpirvType(.{ .runtime_array = T })`
1514pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {1532pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {
1515 const ip = &zcu.intern_pool;1533 const ip = &zcu.intern_pool;
1516 return switch (ip.indexToKey(ty.toIntern())) {1534 return switch (ip.indexToKey(ty.toIntern())) {
1517 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),1535 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1536 .spirv_type => ty.childType(zcu),
1518 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {1537 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1519 .many, .slice, .c => .fromInterned(ptr_type.child),1538 .many, .slice, .c => .fromInterned(ptr_type.child),
1520 .one => switch (ip.indexToKey(ptr_type.child)) {1539 .one => switch (ip.indexToKey(ptr_type.child)) {
1521 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),1540 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1541 .spirv_type => Type.fromInterned(ptr_type.child).childType(zcu),
1522 else => unreachable,1542 else => unreachable,
1523 },1543 },
1524 },1544 },
...@@ -1864,6 +1884,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {...@@ -1864,6 +1884,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
1864 .func_type => unreachable,1884 .func_type => unreachable,
1865 .simple_type => unreachable, // handled via Index enum tag above1885 .simple_type => unreachable, // handled via Index enum tag above
18661886
1887 .spirv_type => unreachable,
1867 .opaque_type => unreachable,1888 .opaque_type => unreachable,
18681889
1869 // values, not types1890 // values, not types
...@@ -2032,6 +2053,7 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {...@@ -2032,6 +2053,7 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
2032 .error_set_type,2053 .error_set_type,
2033 .inferred_error_set_type,2054 .inferred_error_set_type,
2034 .opaque_type,2055 .opaque_type,
2056 .spirv_type,
2035 => null,2057 => null,
20362058
2037 .simple_type => |t| switch (t) {2059 .simple_type => |t| switch (t) {
...@@ -2219,10 +2241,12 @@ pub fn isIndexable(ty: Type, zcu: *const Zcu) bool {...@@ -2219,10 +2241,12 @@ pub fn isIndexable(ty: Type, zcu: *const Zcu) bool {
2219 .one => switch (ty.childType(zcu).zigTypeTag(zcu)) {2241 .one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
2220 .array, .vector => true,2242 .array, .vector => true,
2221 .@"struct" => ty.childType(zcu).isTuple(zcu),2243 .@"struct" => ty.childType(zcu).isTuple(zcu),
2244 .spirv => ty.childType(zcu).isSpirvRuntimeArray(zcu),
2222 else => false,2245 else => false,
2223 },2246 },
2224 },2247 },
2225 .@"struct" => ty.isTuple(zcu),2248 .@"struct" => ty.isTuple(zcu),
2249 .spirv => ty.isSpirvRuntimeArray(zcu),
2226 else => false,2250 else => false,
2227 };2251 };
2228}2252}
...@@ -2844,6 +2868,7 @@ pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error...@@ -2844,6 +2868,7 @@ pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error
2844 .slice, .many, .c => .fromInterned(ptr_info.child),2868 .slice, .many, .c => .fromInterned(ptr_info.child),
2845 .one => switch (ip.indexToKey(ptr_info.child)) {2869 .one => switch (ip.indexToKey(ptr_info.child)) {
2846 .array_type => |array_type| .fromInterned(array_type.child),2870 .array_type => |array_type| .fromInterned(array_type.child),
2871 .spirv_type => Type.fromInterned(ptr_info.child).childType(zcu),
2847 else => unreachable,2872 else => unreachable,
2848 },2873 },
2849 };2874 };
...@@ -3095,6 +3120,7 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {...@@ -3095,6 +3120,7 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
30953120
3096 .noreturn,3121 .noreturn,
3097 .@"opaque",3122 .@"opaque",
3123 .spirv,
3098 .error_union,3124 .error_union,
3099 .error_set,3125 .error_set,
3100 .frame,3126 .frame,
...@@ -3170,6 +3196,7 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool...@@ -3170,6 +3196,7 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool
3170 .noreturn => position == .ret_ty,3196 .noreturn => position == .ret_ty,
31713197
3172 .@"opaque",3198 .@"opaque",
3199 .spirv,
3173 .bool,3200 .bool,
3174 .float,3201 .float,
3175 .@"anyframe",3202 .@"anyframe",
...@@ -3261,6 +3288,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {...@@ -3261,6 +3288,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
3261 .simple_type,3288 .simple_type,
3262 .opaque_type,3289 .opaque_type,
3263 .error_set_type,3290 .error_set_type,
3291 .spirv_type,
3264 .inferred_error_set_type,3292 .inferred_error_set_type,
3265 => {},3293 => {},
3266 .func_type => |func_type| {3294 .func_type => |func_type| {
...@@ -3362,6 +3390,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn...@@ -3362,6 +3390,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn
3362 .union_type,3390 .union_type,
3363 .opaque_type,3391 .opaque_type,
3364 .enum_type,3392 .enum_type,
3393 .spirv_type,
3365 .simple_type,3394 .simple_type,
3366 .int_type,3395 .int_type,
3367 => {},3396 => {},
src/Value.zig+7-1
...@@ -2046,7 +2046,10 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op...@@ -2046,7 +2046,10 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op
20462046
2047 const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu);2047 const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu);
2048 const need_child: Type = .fromInterned(ptr_ty_info.child);2048 const need_child: Type = .fromInterned(ptr_ty_info.child);
2049 if (need_child.comptimeOnly(zcu) or need_child.zigTypeTag(zcu) == .@"opaque") {2049 if (need_child.comptimeOnly(zcu) or
2050 need_child.zigTypeTag(zcu) == .@"opaque" or
2051 need_child.isSpirvRuntimeArray(zcu))
2052 {
2050 // No refinement can happen - this pointer is presumably invalid.2053 // No refinement can happen - this pointer is presumably invalid.
2051 // Just offset it.2054 // Just offset it.
2052 const parent = try arena.create(PointerDeriveStep);2055 const parent = try arena.create(PointerDeriveStep);
...@@ -2078,6 +2081,7 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op...@@ -2078,6 +2081,7 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op
2078 .undefined,2081 .undefined,
2079 .enum_literal,2082 .enum_literal,
2080 .@"opaque",2083 .@"opaque",
2084 .spirv,
2081 .@"fn",2085 .@"fn",
2082 .error_union,2086 .error_union,
2083 .int,2087 .int,
...@@ -2229,6 +2233,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe...@@ -2229,6 +2233,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
2229 .null,2233 .null,
2230 .@"fn",2234 .@"fn",
2231 .@"opaque",2235 .@"opaque",
2236 .spirv,
2232 .enum_literal,2237 .enum_literal,
2233 => comptime unreachable, // comptime-only or otherwise impossible2238 => comptime unreachable, // comptime-only or otherwise impossible
22342239
...@@ -2332,6 +2337,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory...@@ -2332,6 +2337,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
2332 .null,2337 .null,
2333 .@"fn",2338 .@"fn",
2334 .@"opaque",2339 .@"opaque",
2340 .spirv,
2335 .enum_literal,2341 .enum_literal,
2336 => comptime unreachable, // comptime-only or otherwise impossible2342 => comptime unreachable, // comptime-only or otherwise impossible
23372343
src/Zcu.zig+4-1
...@@ -468,6 +468,7 @@ pub const StdLangDecl = enum {...@@ -468,6 +468,7 @@ pub const StdLangDecl = enum {
468 @"Type.Struct.FieldAttributes",468 @"Type.Struct.FieldAttributes",
469 @"Type.ContainerLayout",469 @"Type.ContainerLayout",
470 @"Type.Opaque",470 @"Type.Opaque",
471 @"Type.Spirv",
471472
472 panic,473 panic,
473 @"panic.call",474 @"panic.call",
...@@ -548,6 +549,7 @@ pub const StdLangDecl = enum {...@@ -548,6 +549,7 @@ pub const StdLangDecl = enum {
548 .@"Type.Struct.FieldAttributes",549 .@"Type.Struct.FieldAttributes",
549 .@"Type.ContainerLayout",550 .@"Type.ContainerLayout",
550 .@"Type.Opaque",551 .@"Type.Opaque",
552 .@"Type.Spirv",
551 => .type,553 => .type,
552554
553 .panic => .type,555 .panic => .type,
...@@ -601,7 +603,7 @@ pub const StdLangDecl = enum {...@@ -601,7 +603,7 @@ pub const StdLangDecl = enum {
601 .VaList => .va_list,603 .VaList => .va_list,
602 .assembly, .@"assembly.Clobbers" => .assembly,604 .assembly, .@"assembly.Clobbers" => .assembly,
603 else => {605 else => {
604 if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Opaque")) {606 if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Spirv")) {
605 return .main;607 return .main;
606 } else {608 } else {
607 return .panic;609 return .panic;
...@@ -2740,6 +2742,7 @@ pub const LazySrcLoc = struct {...@@ -2740,6 +2742,7 @@ pub const LazySrcLoc = struct {
2740 .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node,2742 .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node,
2741 .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node,2743 .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node,
2742 .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node,2744 .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node,
2745 .reify_spirv_type => zir.extraData(Zir.Inst.ReifySpirvType, inst.data.extended.operand).data.node,
2743 else => unreachable,2746 else => unreachable,
2744 },2747 },
2745 else => unreachable,2748 else => unreachable,
src/codegen.zig+1
...@@ -329,6 +329,7 @@ pub fn generateSymbol(...@@ -329,6 +329,7 @@ pub fn generateSymbol(
329 .tuple_type,329 .tuple_type,
330 .union_type,330 .union_type,
331 .opaque_type,331 .opaque_type,
332 .spirv_type,
332 .enum_type,333 .enum_type,
333 .func_type,334 .func_type,
334 .error_set_type,335 .error_set_type,
src/codegen/aarch64/Select.zig+2-1
...@@ -255,6 +255,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -255,6 +255,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
255 .work_item_id,255 .work_item_id,
256 .work_group_size,256 .work_group_size,
257 .work_group_id,257 .work_group_id,
258 .spirv_runtime_array_len,
258 => unreachable,259 => unreachable,
259 .ret_ptr => {260 .ret_ptr => {
260 const ty = air_data[@intFromEnum(air_inst_index)].ty;261 const ty = air_data[@intFromEnum(air_inst_index)].ty;
...@@ -7491,7 +7492,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,...@@ -7491,7 +7492,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
7491 }7492 }
7492 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;7493 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
7493 },7494 },
7494 .work_item_id, .work_group_size, .work_group_id => unreachable,7495 .work_item_id, .work_group_size, .work_group_id, .spirv_runtime_array_len => unreachable,
7495 }7496 }
7496 assert(air.body_index == 0);7497 assert(air.body_index == 0);
7497}7498}
src/codegen/aarch64/abi.zig+1
...@@ -62,6 +62,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {...@@ -62,6 +62,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {
62 .null,62 .null,
63 .@"fn",63 .@"fn",
64 .@"opaque",64 .@"opaque",
65 .spirv,
65 .enum_literal,66 .enum_literal,
66 .array,67 .array,
67 => unreachable,68 => unreachable,
src/codegen/arm/abi.zig+1
...@@ -113,6 +113,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {...@@ -113,6 +113,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
113 .null,113 .null,
114 .@"fn",114 .@"fn",
115 .@"opaque",115 .@"opaque",
116 .spirv,
116 .enum_literal,117 .enum_literal,
117 .array,118 .array,
118 => unreachable,119 => unreachable,
src/codegen/c.zig+3
...@@ -902,6 +902,7 @@ pub const DeclGen = struct {...@@ -902,6 +902,7 @@ pub const DeclGen = struct {
902 .tuple_type,902 .tuple_type,
903 .union_type,903 .union_type,
904 .opaque_type,904 .opaque_type,
905 .spirv_type,
905 .enum_type,906 .enum_type,
906 .func_type,907 .func_type,
907 .error_set_type,908 .error_set_type,
...@@ -1565,6 +1566,7 @@ pub const DeclGen = struct {...@@ -1565,6 +1566,7 @@ pub const DeclGen = struct {
1565 },1566 },
1566 .anyframe_type,1567 .anyframe_type,
1567 .opaque_type,1568 .opaque_type,
1569 .spirv_type,
1568 .func_type,1570 .func_type,
1569 => unreachable,1571 => unreachable,
15701572
...@@ -2877,6 +2879,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2877,6 +2879,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2877 .work_item_id,2879 .work_item_id,
2878 .work_group_size,2880 .work_group_size,
2879 .work_group_id,2881 .work_group_id,
2882 .spirv_runtime_array_len,
2880 => unreachable,2883 => unreachable,
28812884
2882 // Instructions that are known to always be `noreturn` based on their tag.2885 // Instructions that are known to always be `noreturn` based on their tag.
src/codegen/c/type.zig+3
...@@ -249,6 +249,7 @@ pub const CType = union(enum) {...@@ -249,6 +249,7 @@ pub const CType = union(enum) {
249 .null,249 .null,
250 .enum_literal,250 .enum_literal,
251 .@"opaque",251 .@"opaque",
252 .spirv,
252 .noreturn,253 .noreturn,
253 .void,254 .void,
254 => return .void,255 => return .void,
...@@ -865,6 +866,7 @@ pub const CType = union(enum) {...@@ -865,6 +866,7 @@ pub const CType = union(enum) {
865 switch (ty.zigTypeTag(zcu)) {866 switch (ty.zigTypeTag(zcu)) {
866 .frame => unreachable,867 .frame => unreachable,
867 .@"anyframe" => unreachable,868 .@"anyframe" => unreachable,
869 .spirv => unreachable,
868870
869 .type => try w.writeAll("type"),871 .type => try w.writeAll("type"),
870 .void => try w.writeAll("void"),872 .void => try w.writeAll("void"),
...@@ -988,6 +990,7 @@ pub const CType = union(enum) {...@@ -988,6 +990,7 @@ pub const CType = union(enum) {
988 .anyframe_type,990 .anyframe_type,
989 .simple_type,991 .simple_type,
990 .opaque_type,992 .opaque_type,
993 .spirv_type,
991 .error_set_type,994 .error_set_type,
992 .inferred_error_set_type,995 .inferred_error_set_type,
993 => true,996 => true,
src/codegen/llvm.zig+3-1
...@@ -2662,6 +2662,7 @@ pub const Object = struct {...@@ -2662,6 +2662,7 @@ pub const Object = struct {
2662 },2662 },
2663 .frame => @panic("TODO implement lowerDebugType for Frame types"),2663 .frame => @panic("TODO implement lowerDebugType for Frame types"),
2664 .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"),2664 .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"),
2665 .spirv => unreachable,
2665 }2666 }
2666 }2667 }
26672668
...@@ -3375,7 +3376,7 @@ pub const Object = struct {...@@ -3375,7 +3376,7 @@ pub const Object = struct {
3375 );3376 );
3376 return ty;3377 return ty;
3377 },3378 },
3378 .opaque_type => unreachable, // no runtime bits3379 .opaque_type, .spirv_type => unreachable, // no runtime bits
3379 .enum_type => try o.lowerType(t.intTagType(zcu)),3380 .enum_type => try o.lowerType(t.intTagType(zcu)),
3380 .func_type => |func_type| try o.lowerFnType(t, func_type),3381 .func_type => |func_type| try o.lowerFnType(t, func_type),
3381 .error_set_type, .inferred_error_set_type => try o.errorIntType(),3382 .error_set_type, .inferred_error_set_type => try o.errorIntType(),
...@@ -3497,6 +3498,7 @@ pub const Object = struct {...@@ -3497,6 +3498,7 @@ pub const Object = struct {
3497 .tuple_type,3498 .tuple_type,
3498 .union_type,3499 .union_type,
3499 .opaque_type,3500 .opaque_type,
3501 .spirv_type,
3500 .enum_type,3502 .enum_type,
3501 .func_type,3503 .func_type,
3502 .error_set_type,3504 .error_set_type,
src/codegen/llvm/FuncGen.zig+2
...@@ -434,6 +434,7 @@ pub fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air...@@ -434,6 +434,7 @@ pub fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air
434 .work_item_id => try self.airWorkItemId(inst),434 .work_item_id => try self.airWorkItemId(inst),
435 .work_group_size => try self.airWorkGroupSize(inst),435 .work_group_size => try self.airWorkGroupSize(inst),
436 .work_group_id => try self.airWorkGroupId(inst),436 .work_group_id => try self.airWorkGroupId(inst),
437 .spirv_runtime_array_len => unreachable,
437438
438 // Instructions that are known to always be `noreturn` based on their tag.439 // Instructions that are known to always be `noreturn` based on their tag.
439 .br => return self.airBr(inst),440 .br => return self.airBr(inst),
...@@ -7255,6 +7256,7 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool {...@@ -7255,6 +7256,7 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool {
7255 .undefined,7256 .undefined,
7256 .null,7257 .null,
7257 .@"opaque",7258 .@"opaque",
7259 .spirv,
7258 => unreachable,7260 => unreachable,
72597261
7260 .noreturn,7262 .noreturn,
src/codegen/mips/abi.zig+1
...@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {...@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
77 .null,77 .null,
78 .@"fn",78 .@"fn",
79 .@"opaque",79 .@"opaque",
80 .spirv,
80 .enum_literal,81 .enum_literal,
81 .array,82 .array,
82 => unreachable,83 => unreachable,
src/codegen/riscv64/CodeGen.zig+1
...@@ -1642,6 +1642,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1642,6 +1642,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1642 .work_item_id => unreachable,1642 .work_item_id => unreachable,
1643 .work_group_size => unreachable,1643 .work_group_size => unreachable,
1644 .work_group_id => unreachable,1644 .work_group_id => unreachable,
1645 .spirv_runtime_array_len => unreachable,
1645 // zig fmt: on1646 // zig fmt: on
1646 }1647 }
16471648
src/codegen/riscv64/abi.zig+1
...@@ -87,6 +87,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {...@@ -87,6 +87,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {
87 .null,87 .null,
88 .@"fn",88 .@"fn",
89 .@"opaque",89 .@"opaque",
90 .spirv,
90 .enum_literal,91 .enum_literal,
91 .array,92 .array,
92 => unreachable,93 => unreachable,
src/codegen/sparc64/CodeGen.zig+1
...@@ -709,6 +709,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -709,6 +709,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
709 .work_item_id => unreachable,709 .work_item_id => unreachable,
710 .work_group_size => unreachable,710 .work_group_size => unreachable,
711 .work_group_id => unreachable,711 .work_group_id => unreachable,
712 .spirv_runtime_array_len => unreachable,
712 // zig fmt: on713 // zig fmt: on
713 }714 }
714715
src/codegen/spirv/Assembler.zig+1-1
...@@ -269,7 +269,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {...@@ -269,7 +269,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {
269 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);269 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
270 const ids = try cg.id_scratch.addManyAsSlice(gpa, operands[1..].len);270 const ids = try cg.id_scratch.addManyAsSlice(gpa, operands[1..].len);
271 for (operands[1..], ids) |op, *id| id.* = try ass.resolveRefId(op.ref_id);271 for (operands[1..], ids) |op, *id| id.* = try ass.resolveRefId(op.ref_id);
272 break :blk try module.structType(ids, null, null, .none);272 break :blk try module.structType(ids, null, .none);
273 },273 },
274 .OpTypeImage => blk: {274 .OpTypeImage => blk: {
275 const sampled_type = try ass.resolveRefId(operands[1].ref_id);275 const sampled_type = try ass.resolveRefId(operands[1].ref_id);
src/codegen/spirv/CodeGen.zig+141-26
...@@ -271,7 +271,13 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -271,7 +271,13 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
271 .vulkan, .opengl => {271 .vulkan, .opengl => {
272 if (ty.zigTypeTag(zcu) == .@"struct") {272 if (ty.zigTypeTag(zcu) == .@"struct") {
273 switch (storage_class) {273 switch (storage_class) {
274 .uniform, .push_constant => try cg.module.decorate(ty_id, .block),274 .uniform,
275 .push_constant,
276 .storage_buffer,
277 => {
278 try cg.module.decorate(ty_id, .block);
279 try cg.decorateBlockOffsets(ty, ty_id);
280 },
275 else => {},281 else => {},
276 }282 }
277 }283 }
...@@ -313,6 +319,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -313,6 +319,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
313 try cg.module.debugName(result_id, nav.fqn.toSlice(ip));319 try cg.module.debugName(result_id, nav.fqn.toSlice(ip));
314 },320 },
315 .invocation_global => {321 .invocation_global => {
322 // `@extern()` produces an invocation_global whose value is a
323 // comptime-known pointer to an underlying extern symbol's Nav.
324 // The pointer is inlined at use sites so we don't need a Function-scope wrapper here.
325 if (ip.indexToKey(val.toIntern()) == .ptr) alias: {
326 const ptr_key = ip.indexToKey(val.toIntern()).ptr;
327 if (ptr_key.base_addr != .nav or ptr_key.byte_offset != 0) break :alias;
328 const underlying_nav = ip.getNav(ptr_key.base_addr.nav);
329 if (!underlying_nav.resolved.?.is_extern_decl) break :alias;
330 cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len;
331 return;
332 }
333
316 const ty_id = try cg.resolveType(ty, .indirect);334 const ty_id = try cg.resolveType(ty, .indirect);
317 const ptr_ty_id = try cg.module.ptrType(ty_id, .function);335 const ptr_ty_id = try cg.module.ptrType(ty_id, .function);
318336
...@@ -360,6 +378,21 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -360,6 +378,21 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
360 cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len;378 cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len;
361}379}
362380
381fn decorateBlockOffsets(cg: *CodeGen, ty: Type, ty_id: spec.Id) !void {
382 const zcu = cg.module.zcu;
383 const ip = &zcu.intern_pool;
384 const struct_type = ip.loadStructType(ty.toIntern());
385 var it = struct_type.iterateRuntimeOrder(ip);
386 var member: u32 = 0;
387 while (it.next()) |field_index| {
388 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
389 if (!field_ty.hasRuntimeBits(zcu)) continue;
390 const offset: u32 = @intCast(ty.structFieldOffset(field_index, zcu));
391 try cg.module.decorateMember(ty_id, member, .{ .offset = .{ .byte_offset = offset } });
392 member += 1;
393 }
394}
395
363pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error {396pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error {
364 @branchHint(.cold);397 @branchHint(.cold);
365 return cg.module.zcu.codegenFail(cg.owner_nav, format, args);398 return cg.module.zcu.codegenFail(cg.owner_nav, format, args);
...@@ -779,6 +812,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -779,6 +812,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
779 .tuple_type,812 .tuple_type,
780 .union_type,813 .union_type,
781 .opaque_type,814 .opaque_type,
815 .spirv_type,
782 .enum_type,816 .enum_type,
783 .func_type,817 .func_type,
784 .error_set_type,818 .error_set_type,
...@@ -1065,16 +1099,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {...@@ -1065,16 +1099,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
1065 const parent_ptr_id = try cg.derivePtr(oac.parent.*);1099 const parent_ptr_id = try cg.derivePtr(oac.parent.*);
1066 const parent_ptr_ty = try oac.parent.ptrType(pt);1100 const parent_ptr_ty = try oac.parent.ptrType(pt);
1067 const result_ty_id = try cg.resolveType(oac.new_ptr_ty, .direct);1101 const result_ty_id = try cg.resolveType(oac.new_ptr_ty, .direct);
1068 const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
10691102
1070 if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 0) {1103 if (parent_ptr_ty.childType(zcu).isVector(zcu)) {
1071 // Vector element ptr accesses are derived as offset_and_cast.1104 // Vector element ptr accesses are derived as offset_and_cast.
1072 // We can just use OpAccessChain.1105 // We can just use OpAccessChain.
1073 return cg.accessChain(1106 const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
1074 result_ty_id,1107 if (oac.byte_offset % child_size == 0) {
1075 parent_ptr_id,1108 return cg.accessChain(
1076 &.{@intCast(@divExact(oac.byte_offset, child_size))},1109 result_ty_id,
1077 );1110 parent_ptr_id,
1111 &.{@intCast(@divExact(oac.byte_offset, child_size))},
1112 );
1113 }
1078 }1114 }
10791115
1080 if (oac.byte_offset == 0) {1116 if (oac.byte_offset == 0) {
...@@ -1269,7 +1305,6 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id {...@@ -1269,7 +1305,6 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id {
1269 const result_id = try cg.module.structType(1305 const result_id = try cg.module.structType(
1270 member_types[0..layout.total_fields],1306 member_types[0..layout.total_fields],
1271 member_names[0..layout.total_fields],1307 member_names[0..layout.total_fields],
1272 null,
1273 .none,1308 .none,
1274 );1309 );
12751310
...@@ -1450,7 +1485,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1450,7 +1485,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1450 return try cg.module.structType(1485 return try cg.module.structType(
1451 &.{ ptr_ty_id, size_ty_id },1486 &.{ ptr_ty_id, size_ty_id },
1452 &.{ "ptr", "len" },1487 &.{ "ptr", "len" },
1453 null,
1454 .none,1488 .none,
1455 );1489 );
1456 },1490 },
...@@ -1472,7 +1506,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1472,7 +1506,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1472 const result_id = try cg.module.structType(1506 const result_id = try cg.module.structType(
1473 member_types[0..member_index],1507 member_types[0..member_index],
1474 null,1508 null,
1475 null,
1476 .none,1509 .none,
1477 );1510 );
1478 const type_name = try cg.resolveTypeName(ty);1511 const type_name = try cg.resolveTypeName(ty);
...@@ -1494,9 +1527,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1494,9 +1527,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1494 var member_names = std.array_list.Managed([]const u8).init(gpa);1527 var member_names = std.array_list.Managed([]const u8).init(gpa);
1495 defer member_names.deinit();1528 defer member_names.deinit();
14961529
1497 var member_offsets = std.array_list.Managed(u32).init(gpa);
1498 defer member_offsets.deinit();
1499
1500 var it = struct_type.iterateRuntimeOrder(ip);1530 var it = struct_type.iterateRuntimeOrder(ip);
1501 while (it.next()) |field_index| {1531 while (it.next()) |field_index| {
1502 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);1532 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
...@@ -1505,13 +1535,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1505,13 +1535,11 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1505 const field_name = struct_type.field_names.get(ip)[field_index];1535 const field_name = struct_type.field_names.get(ip)[field_index];
1506 try member_types.append(try cg.resolveType(field_ty, .indirect));1536 try member_types.append(try cg.resolveType(field_ty, .indirect));
1507 try member_names.append(field_name.toSlice(ip));1537 try member_names.append(field_name.toSlice(ip));
1508 try member_offsets.append(@intCast(ty.structFieldOffset(field_index, zcu)));
1509 }1538 }
15101539
1511 const result_id = try cg.module.structType(1540 const result_id = try cg.module.structType(
1512 member_types.items,1541 member_types.items,
1513 member_names.items,1542 member_names.items,
1514 member_offsets.items,
1515 ty.toIntern(),1543 ty.toIntern(),
1516 );1544 );
15171545
...@@ -1541,7 +1569,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1541,7 +1569,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1541 return try cg.module.structType(1569 return try cg.module.structType(
1542 &.{ payload_ty_id, bool_ty_id },1570 &.{ payload_ty_id, bool_ty_id },
1543 &.{ "payload", "valid" },1571 &.{ "payload", "valid" },
1544 null,
1545 .none,1572 .none,
1546 );1573 );
1547 },1574 },
...@@ -1576,7 +1603,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1576,7 +1603,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1576 // TODO: ABI padding?1603 // TODO: ABI padding?
1577 }1604 }
15781605
1579 return try cg.module.structType(&member_types, &member_names, null, .none);1606 return try cg.module.structType(&member_types, &member_names, .none);
1580 },1607 },
1581 .@"opaque" => {1608 .@"opaque" => {
1582 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});1609 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
...@@ -1584,6 +1611,77 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -1584,6 +1611,77 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
1584 defer gpa.free(type_name);1611 defer gpa.free(type_name);
1585 return try cg.module.opaqueType(type_name);1612 return try cg.module.opaqueType(type_name);
1586 },1613 },
1614 .spirv => {
1615 const ip_index = ty.toIntern();
1616 const spirv_type = ip.loadSpirvType(ip_index);
1617 switch (spirv_type.flags.tag) {
1618 .sampler => return try cg.module.samplerType(ip_index),
1619 .image => {
1620 const sampled_type_id = blk: {
1621 if (spirv_type.ty == .none) break :blk try cg.module.intType(.unsigned, 32);
1622 break :blk try cg.resolveType(Type.fromInterned(spirv_type.ty), .direct);
1623 };
1624 return try cg.module.imageType(
1625 ip_index,
1626 sampled_type_id,
1627 switch (spirv_type.flags.dim) {
1628 .@"1d" => .@"1d",
1629 .@"2d" => .@"2d",
1630 .@"3d" => .@"3d",
1631 .cube => .cube,
1632 },
1633 switch (spirv_type.flags.depth) {
1634 .not_depth => 0,
1635 .depth => 1,
1636 .unknown => 2,
1637 },
1638 @intFromBool(spirv_type.flags.is_arrayed),
1639 @intFromBool(spirv_type.flags.is_multisampled),
1640 switch (spirv_type.flags.usage) {
1641 .unknown => 1,
1642 .sampled => 1,
1643 .storage => 2,
1644 },
1645 switch (spirv_type.flags.format) {
1646 .unknown => .unknown,
1647 .rgba32f => .rgba32f,
1648 .rgba32i => .rgba32i,
1649 .rgba32u => .rgba32ui,
1650 .rgba16f => .rgba16f,
1651 .rgba16i => .rgba16i,
1652 .rgba16u => .rgba16ui,
1653 .rgba8unorm => .rgba8,
1654 .rgba8snorm => .rgba8snorm,
1655 .rgba8i => .rgba8i,
1656 .rgba8u => .rgba8ui,
1657 .r32f => .r32f,
1658 .r32i => .r32i,
1659 .r32u => .r32ui,
1660 },
1661 switch (spirv_type.flags.access) {
1662 .unknown => null,
1663 .read_only => .read_only,
1664 .write_only => .write_only,
1665 .read_write => .read_write,
1666 },
1667 );
1668 },
1669 .sampled_image => {
1670 const image_ty_id = try cg.resolveType(.fromInterned(spirv_type.ty), .indirect);
1671 return try cg.module.sampledImageType(ip_index, image_ty_id);
1672 },
1673 .runtime_array => {
1674 const elem_ty: Type = .fromInterned(spirv_type.ty);
1675 const elem_ty_id = try cg.resolveType(elem_ty, .indirect);
1676 const result_id = try cg.module.runtimeArrayType(ip_index, elem_ty_id);
1677 try cg.module.decorate(
1678 result_id,
1679 .{ .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) } },
1680 );
1681 return result_id;
1682 },
1683 }
1684 },
15871685
1588 .null,1686 .null,
1589 .undefined,1687 .undefined,
...@@ -2421,7 +2519,6 @@ fn generateTestEntryPoint(...@@ -2421,7 +2519,6 @@ fn generateTestEntryPoint(
2421 const buffer_struct_ty_id = try cg.module.structType(2519 const buffer_struct_ty_id = try cg.module.structType(
2422 &.{anyerror_ty_id},2520 &.{anyerror_ty_id},
2423 &.{"error_out"},2521 &.{"error_out"},
2424 null,
2425 .none,2522 .none,
2426 );2523 );
2427 try cg.module.decorate(buffer_struct_ty_id, .block);2524 try cg.module.decorate(buffer_struct_ty_id, .block);
...@@ -2708,13 +2805,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -2708,13 +2805,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
2708 .memcpy => return cg.airMemcpy(inst),2805 .memcpy => return cg.airMemcpy(inst),
2709 .memmove => return cg.airMemmove(inst),2806 .memmove => return cg.airMemmove(inst),
27102807
2711 .slice_ptr => try cg.airSliceField(inst, 0),2808 .slice_ptr => try cg.airSliceField(inst, 0),
2712 .slice_len => try cg.airSliceField(inst, 1),2809 .slice_len => try cg.airSliceField(inst, 1),
2713 .slice_elem_ptr => try cg.airSliceElemPtr(inst),2810 .spirv_runtime_array_len => try cg.airSpirvRuntimeArrayLen(inst),
2714 .slice_elem_val => try cg.airSliceElemVal(inst),2811 .slice_elem_ptr => try cg.airSliceElemPtr(inst),
2715 .ptr_elem_ptr => try cg.airPtrElemPtr(inst),2812 .slice_elem_val => try cg.airSliceElemVal(inst),
2716 .ptr_elem_val => try cg.airPtrElemVal(inst),2813 .ptr_elem_ptr => try cg.airPtrElemPtr(inst),
2717 .array_elem_val => try cg.airArrayElemVal(inst),2814 .ptr_elem_val => try cg.airPtrElemVal(inst),
2815 .array_elem_val => try cg.airArrayElemVal(inst),
27182816
2719 .set_union_tag => return cg.airSetUnionTag(inst),2817 .set_union_tag => return cg.airSetUnionTag(inst),
2720 .get_union_tag => try cg.airGetUnionTag(inst),2818 .get_union_tag => try cg.airGetUnionTag(inst),
...@@ -4305,6 +4403,22 @@ fn airSliceField(cg: *CodeGen, inst: Air.Inst.Index, field: u32) !?Id {...@@ -4305,6 +4403,22 @@ fn airSliceField(cg: *CodeGen, inst: Air.Inst.Index, field: u32) !?Id {
4305 return try cg.extractField(field_ty, operand_id, field);4403 return try cg.extractField(field_ty, operand_id, field);
4306}4404}
43074405
4406fn airSpirvRuntimeArrayLen(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4407 const gpa = cg.module.gpa;
4408 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4409 const extra = cg.air.extraData(Air.StructField, ty_pl.payload).data;
4410 const struct_ptr_id = try cg.resolve(extra.struct_operand);
4411 const u32_ty_id = try cg.module.intType(.unsigned, 32);
4412 const result_id = cg.module.allocId();
4413 try cg.body.emit(gpa, .OpArrayLength, .{
4414 .id_result_type = u32_ty_id,
4415 .id_result = result_id,
4416 .structure = struct_ptr_id,
4417 .array_member = extra.field_index,
4418 });
4419 return result_id;
4420}
4421
4308fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {4422fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4309 const zcu = cg.module.zcu;4423 const zcu = cg.module.zcu;
4310 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4424 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -5884,6 +5998,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -5884,6 +5998,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
5884 .struct_type,5998 .struct_type,
5885 .union_type,5999 .union_type,
5886 .opaque_type,6000 .opaque_type,
6001 .spirv_type,
5887 .enum_type,6002 .enum_type,
5888 .func_type,6003 .func_type,
5889 .error_set_type,6004 .error_set_type,
src/codegen/spirv/Module.zig+72-18
...@@ -70,6 +70,8 @@ cache: struct {...@@ -70,6 +70,8 @@ cache: struct {
7070
71 bool_const: [2]?Id = .{ null, null },71 bool_const: [2]?Id = .{ null, null },
72 constants: std.ArrayHashMapUnmanaged(Constant, Id, Constant.HashContext, true) = .empty,72 constants: std.ArrayHashMapUnmanaged(Constant, Id, Constant.HashContext, true) = .empty,
73
74 spirv_types: std.AutoHashMapUnmanaged(InternPool.Index, Id) = .empty,
73} = .{},75} = .{},
74/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".76/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
75sections: struct {77sections: struct {
...@@ -229,6 +231,7 @@ pub fn deinit(module: *Module) void {...@@ -229,6 +231,7 @@ pub fn deinit(module: *Module) void {
229 module.cache.array_types.deinit(module.gpa);231 module.cache.array_types.deinit(module.gpa);
230 module.cache.struct_types.deinit(module.gpa);232 module.cache.struct_types.deinit(module.gpa);
231 module.cache.fn_types.deinit(module.gpa);233 module.cache.fn_types.deinit(module.gpa);
234 module.cache.spirv_types.deinit(module.gpa);
232 module.cache.capabilities.deinit(module.gpa);235 module.cache.capabilities.deinit(module.gpa);
233 module.cache.extensions.deinit(module.gpa);236 module.cache.extensions.deinit(module.gpa);
234 module.cache.extended_instruction_set.deinit(module.gpa);237 module.cache.extended_instruction_set.deinit(module.gpa);
...@@ -683,10 +686,8 @@ pub fn structType(...@@ -683,10 +686,8 @@ pub fn structType(
683 module: *Module,686 module: *Module,
684 types: []const Id,687 types: []const Id,
685 maybe_names: ?[]const []const u8,688 maybe_names: ?[]const []const u8,
686 maybe_offsets: ?[]const u32,
687 ip_index: InternPool.Index,689 ip_index: InternPool.Index,
688) !Id {690) !Id {
689 const target = module.zcu.getTarget();
690 const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index;691 const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index;
691692
692 if (module.cache.struct_types.get(.{ .fields = types, .ip_index = actual_ip_index })) |id| return id;693 if (module.cache.struct_types.get(.{ .fields = types, .ip_index = actual_ip_index })) |id| return id;
...@@ -704,22 +705,6 @@ pub fn structType(...@@ -704,22 +705,6 @@ pub fn structType(
704 }705 }
705 }706 }
706707
707 switch (target.os.tag) {
708 .vulkan, .opengl => {
709 if (maybe_offsets) |offsets| {
710 assert(offsets.len == types.len);
711 for (offsets, 0..) |offset, i| {
712 try module.decorateMember(
713 result_id,
714 @intCast(i),
715 .{ .offset = .{ .byte_offset = offset } },
716 );
717 }
718 }
719 },
720 else => {},
721 }
722
723 try module.cache.struct_types.put(708 try module.cache.struct_types.put(
724 module.gpa,709 module.gpa,
725 .{ .fields = types_dup, .ip_index = actual_ip_index },710 .{ .fields = types_dup, .ip_index = actual_ip_index },
...@@ -747,6 +732,75 @@ pub fn functionType(module: *Module, return_ty_id: Id, param_type_ids: []const I...@@ -747,6 +732,75 @@ pub fn functionType(module: *Module, return_ty_id: Id, param_type_ids: []const I
747 return result_id;732 return result_id;
748}733}
749734
735pub fn samplerType(module: *Module, ip_index: InternPool.Index) !Id {
736 const entry = try module.cache.spirv_types.getOrPut(module.gpa, ip_index);
737 if (!entry.found_existing) {
738 const result_id = module.allocId();
739 entry.value_ptr.* = result_id;
740 try module.sections.globals.emit(module.gpa, .OpTypeSampler, .{
741 .id_result = result_id,
742 });
743 }
744 return entry.value_ptr.*;
745}
746
747pub fn imageType(
748 module: *Module,
749 ip_index: InternPool.Index,
750 sampled_ty_id: Id,
751 dim: spec.Dim,
752 depth: spec.LiteralInteger,
753 arrayed: spec.LiteralInteger,
754 ms: spec.LiteralInteger,
755 sampled: spec.LiteralInteger,
756 image_format: spec.ImageFormat,
757 access_qualifier: ?spec.AccessQualifier,
758) !Id {
759 const entry = try module.cache.spirv_types.getOrPut(module.gpa, ip_index);
760 if (!entry.found_existing) {
761 const result_id = module.allocId();
762 entry.value_ptr.* = result_id;
763 try module.sections.globals.emit(module.gpa, .OpTypeImage, .{
764 .id_result = result_id,
765 .sampled_type = sampled_ty_id,
766 .dim = dim,
767 .depth = depth,
768 .arrayed = arrayed,
769 .ms = ms,
770 .sampled = sampled,
771 .image_format = image_format,
772 .access_qualifier = access_qualifier,
773 });
774 }
775 return entry.value_ptr.*;
776}
777
778pub fn sampledImageType(module: *Module, ip_index: InternPool.Index, image_ty_id: Id) !Id {
779 const entry = try module.cache.spirv_types.getOrPut(module.gpa, ip_index);
780 if (!entry.found_existing) {
781 const result_id = module.allocId();
782 entry.value_ptr.* = result_id;
783 try module.sections.globals.emit(module.gpa, .OpTypeSampledImage, .{
784 .id_result = result_id,
785 .image_type = image_ty_id,
786 });
787 }
788 return entry.value_ptr.*;
789}
790
791pub fn runtimeArrayType(module: *Module, ip_index: InternPool.Index, elem_ty_id: Id) !Id {
792 const entry = try module.cache.spirv_types.getOrPut(module.gpa, ip_index);
793 if (!entry.found_existing) {
794 const result_id = module.allocId();
795 entry.value_ptr.* = result_id;
796 try module.sections.globals.emit(module.gpa, .OpTypeRuntimeArray, .{
797 .id_result = result_id,
798 .element_type = elem_ty_id,
799 });
800 }
801 return entry.value_ptr.*;
802}
803
750pub fn constant(module: *Module, ty_id: Id, value: spec.LiteralContextDependentNumber) !Id {804pub fn constant(module: *Module, ty_id: Id, value: spec.LiteralContextDependentNumber) !Id {
751 const gop = try module.cache.constants.getOrPut(module.gpa, .{ .ty = ty_id, .value = value });805 const gop = try module.cache.constants.getOrPut(module.gpa, .{ .ty = ty_id, .value = value });
752 if (!gop.found_existing) {806 if (!gop.found_existing) {
src/codegen/wasm/CodeGen.zig+3
...@@ -1195,6 +1195,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {...@@ -1195,6 +1195,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
1195 .undefined,1195 .undefined,
1196 .null,1196 .null,
1197 .@"opaque",1197 .@"opaque",
1198 .spirv,
1198 => unreachable,1199 => unreachable,
11991200
1200 .noreturn,1201 .noreturn,
...@@ -1882,6 +1883,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1882,6 +1883,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1882 .work_item_id,1883 .work_item_id,
1883 .work_group_size,1884 .work_group_size,
1884 .work_group_id,1885 .work_group_id,
1886 .spirv_runtime_array_len,
1885 => unreachable,1887 => unreachable,
1886 };1888 };
1887}1889}
...@@ -4698,6 +4700,7 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {...@@ -4698,6 +4700,7 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {
4698 .tuple_type,4700 .tuple_type,
4699 .union_type,4701 .union_type,
4700 .opaque_type,4702 .opaque_type,
4703 .spirv_type,
4701 .enum_type,4704 .enum_type,
4702 .func_type,4705 .func_type,
4703 .error_set_type,4706 .error_set_type,
src/codegen/wasm/abi.zig+1
...@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {...@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
77 .null,77 .null,
78 .@"fn",78 .@"fn",
79 .@"opaque",79 .@"opaque",
80 .spirv,
80 .enum_literal,81 .enum_literal,
81 => unreachable,82 => unreachable,
82 }83 }
src/codegen/x86_64/CodeGen.zig+1-1
...@@ -173719,7 +173719,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -173719,7 +173719,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173719 // No soft-float `Legalize` features are enabled, so this instruction never appears.173719 // No soft-float `Legalize` features are enabled, so this instruction never appears.
173720 .legalize_compiler_rt_call => unreachable,173720 .legalize_compiler_rt_call => unreachable,
173721173721
173722 .work_item_id, .work_group_size, .work_group_id => unreachable,173722 .work_item_id, .work_group_size, .work_group_id, .spirv_runtime_array_len => unreachable,
173723 }173723 }
173724 try cg.resetTemps(@enumFromInt(0));173724 try cg.resetTemps(@enumFromInt(0));
173725 cg.checkInvariantsAfterAirInst();173725 cg.checkInvariantsAfterAirInst();
src/codegen/x86_64/abi.zig+1
...@@ -164,6 +164,7 @@ pub fn classifyWindows(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont...@@ -164,6 +164,7 @@ pub fn classifyWindows(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont
164 .null,164 .null,
165 .@"fn",165 .@"fn",
166 .@"opaque",166 .@"opaque",
167 .spirv,
167 .enum_literal,168 .enum_literal,
168 => unreachable,169 => unreachable,
169 };170 };
src/link/ConstPool.zig+2
...@@ -196,6 +196,7 @@ fn checkType(pool: *const ConstPool, ty: Type, zcu: *const Zcu) bool {...@@ -196,6 +196,7 @@ fn checkType(pool: *const ConstPool, ty: Type, zcu: *const Zcu) bool {
196 .null,196 .null,
197 .error_set,197 .error_set,
198 .@"opaque",198 .@"opaque",
199 .spirv,
199 .frame,200 .frame,
200 .@"anyframe",201 .@"anyframe",
201 .enum_literal,202 .enum_literal,
...@@ -241,6 +242,7 @@ fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Al...@@ -241,6 +242,7 @@ fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Al
241 .null,242 .null,
242 .error_set,243 .error_set,
243 .@"opaque",244 .@"opaque",
245 .spirv,
244 .frame,246 .frame,
245 .@"anyframe",247 .@"anyframe",
246 .enum_literal,248 .enum_literal,
src/link/Dwarf.zig+2
...@@ -3047,6 +3047,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo...@@ -3047,6 +3047,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
3047 .func_type,3047 .func_type,
3048 .error_set_type,3048 .error_set_type,
3049 .inferred_error_set_type,3049 .inferred_error_set_type,
3050 .spirv_type,
3050 => .alias,3051 => .alias,
30513052
3052 .struct_type => tag: {3053 .struct_type => tag: {
...@@ -3533,6 +3534,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co...@@ -3533,6 +3534,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
3533 switch (value_ip_key) {3534 switch (value_ip_key) {
3534 .func => unreachable, // handled above3535 .func => unreachable, // handled above
3535 .@"extern" => unreachable, // handled above3536 .@"extern" => unreachable, // handled above
3537 .spirv_type => unreachable,
35363538
3537 .int_type => |int_type| {3539 .int_type => |int_type| {
3538 try wip_nav.abbrevCode(.numeric_type);3540 try wip_nav.abbrevCode(.numeric_type);
src/print_value.zig+1
...@@ -60,6 +60,7 @@ pub fn print(...@@ -60,6 +60,7 @@ pub fn print(
60 .union_type,60 .union_type,
61 .opaque_type,61 .opaque_type,
62 .enum_type,62 .enum_type,
63 .spirv_type,
63 .func_type,64 .func_type,
64 .error_set_type,65 .error_set_type,
65 .inferred_error_set_type,66 .inferred_error_set_type,
src/print_zir.zig+10
...@@ -685,6 +685,16 @@ const Writer = struct {...@@ -685,6 +685,16 @@ const Writer = struct {
685 defer self.parent_decl_node = prev_parent_decl_node;685 defer self.parent_decl_node = prev_parent_decl_node;
686 try self.writeSrcNode(stream, .zero);686 try self.writeSrcNode(stream, .zero);
687 },687 },
688 .reify_spirv_type => {
689 const extra = self.code.extraData(Zir.Inst.ReifySpirvType, extended.operand).data;
690 try stream.print("line({d}), ", .{extra.src_line});
691 try self.writeInstRef(stream, extra.operand);
692 try stream.writeAll(")) ");
693 const prev_parent_decl_node = self.parent_decl_node;
694 self.parent_decl_node = extra.node;
695 defer self.parent_decl_node = prev_parent_decl_node;
696 try self.writeSrcNode(stream, .zero);
697 },
688698
689 .cmpxchg => try self.writeCmpxchg(stream, extended),699 .cmpxchg => try self.writeCmpxchg(stream, extended),
690 .ptr_cast_full => try self.writePtrCastFull(stream, extended),700 .ptr_cast_full => try self.writePtrCastFull(stream, extended),
test/behavior.zig+4
...@@ -112,6 +112,10 @@ test {...@@ -112,6 +112,10 @@ test {
112 _ = @import("behavior/wasm.zig");112 _ = @import("behavior/wasm.zig");
113 }113 }
114114
115 if (builtin.zig_backend == .stage2_spirv) {
116 _ = @import("behavior/spirv.zig");
117 }
118
115 if (builtin.zig_backend != .stage2_spirv and builtin.os.tag != .wasi) {119 if (builtin.zig_backend != .stage2_spirv and builtin.os.tag != .wasi) {
116 _ = @import("behavior/asm.zig");120 _ = @import("behavior/asm.zig");
117 }121 }
test/behavior/spirv.zig created+47
...@@ -0,0 +1,47 @@
1const Sampler = @SpirvType(.sampler);
2const Image = @SpirvType(.{ .image = .{
3 .usage = .{ .sampled = u32 },
4 .format = .unknown,
5 .dim = .@"2d",
6 .depth = .unknown,
7 .arrayed = false,
8 .multisampled = false,
9 .access = .unknown,
10} });
11const SampledImage = @SpirvType(.{ .sampled_image = Image });
12const StorageImage = @SpirvType(.{ .image = .{
13 .usage = .storage,
14 .format = .unknown,
15 .dim = .@"2d",
16 .depth = .unknown,
17 .arrayed = false,
18 .multisampled = false,
19 .access = .unknown,
20} });
21const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
22
23const RuntimeArrayBuf = extern struct { e: RuntimeArray };
24
25const sampler = @extern(*addrspace(.constant) const Sampler, .{
26 .name = "sampler",
27 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
28});
29const sampled_image = @extern(*addrspace(.constant) const SampledImage, .{
30 .name = "sampled_image",
31 .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },
32});
33const storage_image = @extern(*addrspace(.constant) const StorageImage, .{
34 .name = "storage_image",
35 .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } },
36});
37const runtime_array = @extern(*addrspace(.storage_buffer) const RuntimeArrayBuf, .{
38 .name = "runtime_array",
39 .decoration = .{ .descriptor = .{ .set = 0, .binding = 3 } },
40});
41
42test "@SpirvType" {
43 _ = sampler;
44 _ = sampled_image;
45 _ = storage_image;
46 _ = runtime_array;
47}
test/behavior/type_info.zig+2-2
...@@ -253,11 +253,11 @@ fn testUnion() !void {...@@ -253,11 +253,11 @@ fn testUnion() !void {
253 try expect(typeinfo_info == .@"union");253 try expect(typeinfo_info == .@"union");
254 try expect(typeinfo_info.@"union".layout == .auto);254 try expect(typeinfo_info.@"union".layout == .auto);
255 try expect(typeinfo_info.@"union".tag_type.? == TypeId);255 try expect(typeinfo_info.@"union".tag_type.? == TypeId);
256 try expect(typeinfo_info.@"union".field_names.len == 24);256 try expect(typeinfo_info.@"union".field_names.len == 25);
257 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_types.len);257 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_types.len);
258 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_attrs.len);258 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_attrs.len);
259 try expect(typeinfo_info.@"union".field_types[4] == @TypeOf(@typeInfo(u8).int));259 try expect(typeinfo_info.@"union".field_types[4] == @TypeOf(@typeInfo(u8).int));
260 try expect(typeinfo_info.@"union".decl_names.len == 16);260 try expect(typeinfo_info.@"union".decl_names.len == 17);
261261
262 const TestNoTagUnion = union {262 const TestNoTagUnion = union {
263 Foo: void,263 Foo: void,
test/cases/compile_errors/SpirvType_is_a_compile_error_in_non-SPIRV_targets.zig created+9
...@@ -0,0 +1,9 @@
1comptime {
2 _ = @SpirvType(.{ .runtime_array = u32 });
3}
4
5// error
6// backend=selfhosted
7// target=x86_64-native
8//
9// :2:9: error: builtin @SpirvType is only available when targeting SPIR-V; targeted CPU architecture is x86_64
test/cases/compile_errors/SpirvType_vulkan_target.zig created+56
...@@ -0,0 +1,56 @@
1comptime {
2 _ = @SpirvType(.{ .image = .{
3 .usage = .storage,
4 .format = .unknown,
5 .dim = .@"2d",
6 .depth = .unknown,
7 .arrayed = false,
8 .multisampled = false,
9 .access = .read_only,
10 } });
11}
12
13comptime {
14 _ = @SpirvType(.{ .image = .{
15 .usage = .{ .sampled = bool },
16 .format = .unknown,
17 .dim = .@"2d",
18 .depth = .unknown,
19 .arrayed = false,
20 .multisampled = false,
21 .access = .unknown,
22 } });
23}
24
25comptime {
26 _ = @SpirvType(.{ .image = .{
27 .usage = .{ .sampled = void },
28 .format = .unknown,
29 .dim = .@"2d",
30 .depth = .unknown,
31 .arrayed = false,
32 .multisampled = false,
33 .access = .unknown,
34 } });
35}
36
37comptime {
38 _ = @SpirvType(.{ .image = .{
39 .usage = .{ .sampled = u24 },
40 .format = .unknown,
41 .dim = .@"2d",
42 .depth = .unknown,
43 .arrayed = false,
44 .multisampled = false,
45 .access = .unknown,
46 } });
47}
48
49// error
50// backend=selfhosted
51// target=spirv64-vulkan
52//
53// :2:21: error: access qualifier '.read_only' is only valid under the 'opencl' os
54// :14:21: error: invalid 'sampled' field value 'bool'
55// :26:21: error: 'void' type for 'sampled' field is only valid under the 'opencl' os
56// :38:21: error: 'sampled' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os
test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig created+35
...@@ -0,0 +1,35 @@
1const Sampler = @SpirvType(.sampler);
2const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
3const Foo = struct {
4 s: Sampler,
5};
6const Baz = struct {
7 a: RuntimeArray,
8};
9const Qux = extern struct {
10 a: RuntimeArray,
11 b: u32,
12};
13export fn a() void {
14 var foo: Foo = undefined;
15 _ = &foo;
16}
17export fn c() void {
18 var baz: Baz = undefined;
19 _ = &baz;
20}
21export fn d() void {
22 var qux: Qux = undefined;
23 _ = &qux;
24}
25
26// error
27// backend=selfhosted
28// target=spirv64-vulkan
29//
30// :4:8: error: cannot directly embed SPIR-V type 'tmp.Sampler__SpirvType_4' in struct
31// :4:8: note: opaque types have unknown size
32// :6:13: error: non-extern struct cannot contain fields of type 'tmp.RuntimeArray__SpirvType_11'
33// :7:5: note: while checking this field
34// :9:20: error: struct field of type 'tmp.RuntimeArray__SpirvType_11' must be the last field
35// :10:5: note: while checking this field