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
57925792 <p>Returns an {#link|enum#} type with the properties specified by the arguments.</p>
57935793 {#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
57955839 {#header_open|@typeInfo#}
57965840 <pre>{#syntax#}@typeInfo(comptime T: type) std.lang.Type{#endsyntax#}</pre>
57975841 <p>
lib/std/Target.zig+2-1
......@@ -2341,7 +2341,8 @@ pub fn supportsAddressSpace(
23412341 .lut => arch == .propeller and std.Target.propeller.featureSetHas(target.cpu.features, .p2),
23422342
23432343 .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)),
23452346 .param => is_nvptx,
23462347 .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,
23472348 };
lib/std/hash/auto_hash.zig+1
......@@ -76,6 +76,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
7676 switch (@typeInfo(Key)) {
7777 .noreturn,
7878 .@"opaque",
79 .spirv,
7980 .undefined,
8081 .null,
8182 .comptime_float,
lib/std/lang.zig+54
......@@ -578,6 +578,7 @@ pub const Type = union(enum) {
578578 @"anyframe": AnyFrame,
579579 vector: Vector,
580580 enum_literal,
581 spirv: Spirv,
581582
582583 /// This data structure is used by the Zig language code generation and
583584 /// therefore must be kept in sync with the compiler implementation.
......@@ -781,6 +782,59 @@ pub const Type = union(enum) {
781782 };
782783 };
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
784838 /// This data structure is used by the Zig language code generation and
785839 /// therefore must be kept in sync with the compiler implementation.
786840 pub const Opaque = struct {
lib/std/mem.zig+1
......@@ -352,6 +352,7 @@ pub fn zeroes(comptime T: type) T {
352352 .noreturn,
353353 .undefined,
354354 .@"opaque",
355 .spirv,
355356 .frame,
356357 .@"anyframe",
357358 => {
lib/std/start.zig+3-1
......@@ -19,7 +19,9 @@ comptime {
1919 // decls there get run.
2020 _ = 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) {
2325 const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup";
2426 if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) {
2527 @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 {
7979 switch (@typeInfo(@TypeOf(actual))) {
8080 .noreturn,
8181 .@"opaque",
82 .spirv,
8283 .frame,
8384 .@"anyframe",
8485 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
......@@ -737,6 +738,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
737738 switch (@typeInfo(@TypeOf(actual))) {
738739 .noreturn,
739740 .@"opaque",
741 .spirv,
740742 .frame,
741743 .@"anyframe",
742744 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
lib/std/zig/AstGen.zig+10
......@@ -9327,6 +9327,16 @@ fn builtinCall(
93279327 });
93289328 return rvalue(gz, ri, result, node);
93299329 },
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
93319341 .panic => {
93329342 try emitDbgNode(gz, node);
lib/std/zig/AstRlAnnotate.zig+4
......@@ -1079,6 +1079,10 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
10791079 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
10801080 return false;
10811081 },
1082 .SpirvType => {
1083 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1084 return false;
1085 },
10821086 .Vector => {
10831087 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
10841088 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig+8
......@@ -114,6 +114,7 @@ pub const Tag = enum {
114114 Struct,
115115 Union,
116116 Enum,
117 SpirvType,
117118 type_info,
118119 type_name,
119120 TypeOf,
......@@ -971,6 +972,13 @@ pub const list = list: {
971972 .param_count = 4,
972973 },
973974 },
975 .{
976 "@SpirvType",
977 .{
978 .tag = .SpirvType,
979 .param_count = 1,
980 },
981 },
974982 .{
975983 "@typeInfo",
976984 .{
lib/std/zig/Zir.zig+14
......@@ -2055,6 +2055,9 @@ pub const Inst = struct {
20552055 /// `operand` is payload index to `ReifyEnum`.
20562056 /// `small` contains `NameStrategy`.
20572057 reify_enum,
2058 /// Implements builtin `@SpirvType`.
2059 /// `operand` is payload index to `ReifyFn`.
2060 reify_spirv_type,
20582061 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
20592062 /// `small` 0=>weak 1=>strong
20602063 /// `operand` is payload index to `Cmpxchg`.
......@@ -3269,6 +3272,14 @@ pub const Inst = struct {
32693272 field_values: Ref,
32703273 };
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
32723283 /// Trailing:
32733284 /// 0. multi_cases_len: u32, // If has_multi_cases is set.
32743285 /// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set.
......@@ -3584,6 +3595,7 @@ pub const Inst = struct {
35843595 fn_attributes,
35853596 container_layout,
35863597 enum_mode,
3598 spirv_type_options,
35873599 // Values
35883600 calling_convention_c,
35893601 calling_convention_inline,
......@@ -4389,6 +4401,7 @@ fn findTrackableInner(
43894401 .reify_enum,
43904402 .reify_struct,
43914403 .reify_union,
4404 .reify_spirv_type,
43924405 => return contents.other.append(gpa, inst),
43934406
43944407 // Type declarations need tracking.
......@@ -5181,6 +5194,7 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void {
51815194 .reify_enum,
51825195 .reify_struct,
51835196 .reify_union,
5197 .reify_spirv_type,
51845198 => {}, // tracked in order, as the owner instructions of explicit container types
51855199 else => unreachable, // assertion failure; not trackable
51865200 },
lib/std/zon/Serializer.zig+1
......@@ -854,6 +854,7 @@ fn canSerializeTypeInner(
854854 .frame,
855855 .@"anyframe",
856856 .@"opaque",
857 .spirv,
857858 => false,
858859
859860 .@"enum" => |@"enum"| @"enum".mode == .exhaustive,
lib/std/zon/parse.zig+1
......@@ -1213,6 +1213,7 @@ fn canParseTypeInner(
12131213 .frame,
12141214 .@"anyframe",
12151215 .@"opaque",
1216 .spirv,
12161217 .comptime_int,
12171218 .comptime_float,
12181219 .enum_literal,
src/Air.zig+7
......@@ -918,6 +918,11 @@ pub const Inst = struct {
918918 /// Uses the `ty` field.
919919 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
921926 /// Implements @workItemId builtin.
922927 /// Result type is always `u32`
923928 /// 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)
17931798 .work_item_id,
17941799 .work_group_size,
17951800 .work_group_id,
1801 .spirv_runtime_array_len,
17961802 => return .u32,
17971803
17981804 .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 {
20562062 .work_group_size,
20572063 .work_group_id,
20582064 .legalize_vec_elem_val,
2065 .spirv_runtime_array_len,
20592066 => false,
20602067
20612068 .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 {
908908 .legalize_vec_elem_val,
909909 .legalize_vec_store_elem,
910910 .legalize_compiler_rt_call,
911 .spirv_runtime_array_len,
911912 => {},
912913 }
913914 }
src/Air/Liveness.zig+1-1
......@@ -673,7 +673,7 @@ fn analyzeInst(
673673 const extra = a.air.extraData(Air.UnionInit, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;
674674 return analyzeOperands(a, pass, data, inst, .{ extra.init, .none, .none });
675675 },
676 .struct_field_ptr, .struct_field_val => {
676 .struct_field_ptr, .struct_field_val, .spirv_runtime_array_len => {
677677 const extra = a.air.extraData(Air.StructField, inst_datas[@intFromEnum(inst)].ty_pl.payload).data;
678678 return analyzeOperands(a, pass, data, inst, .{ extra.struct_operand, .none, .none });
679679 },
src/Air/Liveness/Verify.zig+1-1
......@@ -191,7 +191,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
191191 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
192192 try self.verifyInstOperands(inst, .{ extra.init, .none, .none });
193193 },
194 .struct_field_ptr, .struct_field_val => {
194 .struct_field_ptr, .struct_field_val, .spirv_runtime_array_len => {
195195 const ty_pl = data[@intFromEnum(inst)].ty_pl;
196196 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
197197 try self.verifyInstOperands(inst, .{ extra.struct_operand, .none, .none });
src/Air/print.zig+1
......@@ -306,6 +306,7 @@ const Writer = struct {
306306
307307 .struct_field_ptr => try w.writeStructField(s, inst),
308308 .struct_field_val => try w.writeStructField(s, inst),
309 .spirv_runtime_array_len => try w.writeStructField(s, inst),
309310 .inferred_alloc => @panic("TODO"),
310311 .inferred_alloc_comptime => @panic("TODO"),
311312 .assembly => try w.writeAssembly(s, inst),
src/InternPool.zig+112-1
......@@ -1983,6 +1983,7 @@ pub const Key = union(enum) {
19831983 union_type: ContainerType,
19841984 opaque_type: ContainerType,
19851985 enum_type: ContainerType,
1986 spirv_type: SpirvType,
19861987 func_type: FuncType,
19871988 error_set_type: ErrorSetType,
19881989 /// The payload is the function body, either a `func_decl` or `func_instance`.
......@@ -2146,6 +2147,13 @@ pub const Key = union(enum) {
21462147 };
21472148 };
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
21492157 pub const FuncType = struct {
21502158 param_types: Index.Slice,
21512159 return_type: Index,
......@@ -2590,6 +2598,7 @@ pub const Key = union(enum) {
25902598 .opt_type,
25912599 .anyframe_type,
25922600 .error_union_type,
2601 .spirv_type,
25932602 .simple_type,
25942603 .simple_value,
25952604 .opt,
......@@ -2841,6 +2850,10 @@ pub const Key = union(enum) {
28412850 const b_info = b.error_union_type;
28422851 return std.meta.eql(a_info, b_info);
28432852 },
2853 .spirv_type => |a_info| {
2854 const b_info = b.spirv_type;
2855 return std.meta.eql(a_info, b_info);
2856 },
28442857 .simple_type => |a_info| {
28452858 const b_info = b.simple_type;
28462859 return a_info == b_info;
......@@ -3130,6 +3143,7 @@ pub const Key = union(enum) {
31303143 .simple_type,
31313144 .struct_type,
31323145 .union_type,
3146 .spirv_type,
31333147 .opaque_type,
31343148 .enum_type,
31353149 .tuple_type,
......@@ -3877,6 +3891,14 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {
38773891 };
38783892}
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
38803902pub const Item = struct {
38813903 tag: Tag,
38823904 /// The doc comments on the respective Tag explain how to interpret this.
......@@ -4214,6 +4236,8 @@ pub const Index = enum(u32) {
42144236 type_enum_nonexhaustive: struct { data: *Tag.TypeEnum },
42154237 type_opaque: struct { data: *Tag.TypeOpaque },
42164238
4239 type_spirv: struct { data: *Tag.TypeSpirv },
4240
42174241 undef: DataIsIndex,
42184242 simple_value: void,
42194243 ptr_nav: struct { data: *PtrNav },
......@@ -4841,6 +4865,10 @@ pub const Tag = enum(u8) {
48414865 /// data is extra index of `TypeEnum`.
48424866 type_enum_nonexhaustive,
48434867
4868 /// An spirv type.
4869 /// data is index of `TypeSpirv` in extra.
4870 type_spirv,
4871
48444872 /// An opaque type.
48454873 /// data is extra index of `TypeOpaque`.
48464874 type_opaque,
......@@ -5231,6 +5259,7 @@ pub const Tag = enum(u8) {
52315259 },
52325260 .type_enum_explicit = enum_explicit_encoding,
52335261 .type_enum_nonexhaustive = enum_explicit_encoding,
5262 .type_spirv = .{ .summary = .@"{.payload.name%summary#\"}", .payload = Tag.TypeSpirv },
52345263 .type_opaque = .{
52355264 .summary = .@"{.payload.name%summary#\"}",
52365265 .payload = TypeOpaque,
......@@ -5688,6 +5717,34 @@ pub const Tag = enum(u8) {
56885717 name_nav: Nav.Index.Optional,
56895718 namespace: NamespaceIndex,
56905719 };
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 };
56915748};
56925749
56935750/// 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 {
65736630 } },
65746631 };
65756632 } },
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 } },
65766641 .type_opaque => .{ .opaque_type = ns: {
65776642 const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);
65786643 break :ns .{ .declared = .{
......@@ -7345,6 +7410,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
73457410 .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType
73467411 .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType
73477412 .opaque_type => unreachable, // instead use: getDeclaredOpaqueType
7413 .spirv_type => unreachable, // instead use: getSpirvType
73487414
73497415 .tuple_type => unreachable, // use getTupleType() instead
73507416 .func_type => unreachable, // use getFuncType() instead
......@@ -8717,6 +8783,39 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT
87178783 } };
87188784}
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
87208819pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
87218820 /// The union type for which this enum is a generated tag.
87228821 union_type: Index,
......@@ -9077,7 +9176,7 @@ pub fn getExtern(
90779176 }) catch unreachable; // capacity asserted above
90789177 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {
90799178 .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 },
90819180 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };
90829181 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
90839182 .ty = key.ty,
......@@ -9810,6 +9909,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
98109909 Tag.TypeStructPacked.Bits,
98119910 Tag.TypeUnionPacked.Bits,
98129911 Tag.TypeEnum.Bits,
9912 Tag.TypeSpirv.Flags,
98139913 => @bitCast(@field(item, field_name)),
98149914
98159915 else => @compileError("bad field type: " ++ @typeName(field_type)),
......@@ -9877,6 +9977,7 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
98779977 Tag.TypeStructPacked.Bits,
98789978 Tag.TypeUnionPacked.Bits,
98799979 Tag.TypeEnum.Bits,
9980 Tag.TypeSpirv.Flags,
98809981 => @bitCast(extra_item),
98819982
98829983 else => @compileError("bad field type: " ++ @typeName(field_type)),
......@@ -9930,6 +10031,11 @@ pub fn childType(ip: *const InternPool, i: Index) Index {
993010031 .vector_type => |vector_type| vector_type.child,
993110032 .array_type => |array_type| array_type.child,
993210033 .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 },
993310039 else => unreachable,
993410040 };
993510041}
......@@ -10583,6 +10689,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
1058310689 .type_optional => 0,
1058410690 .type_anyframe => 0,
1058510691 .type_error_union => @sizeOf(Key.ErrorUnionType),
10692 .type_spirv => @sizeOf(Tag.TypeSpirv) + @sizeOf(PackedU64),
1058610693 .type_anyerror_union => 0,
1058710694 .type_error_set => b: {
1058810695 const info = extraData(extra_list, Tag.ErrorSet, data);
......@@ -10860,6 +10967,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
1086010967 .type_enum_explicit,
1086110968 .type_enum_nonexhaustive,
1086210969 .type_opaque,
10970 .type_spirv,
1086310971 .undef,
1086410972 .ptr_nav,
1086510973 .ptr_comptime_alloc,
......@@ -11598,6 +11706,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
1159811706 .type_enum_explicit,
1159911707 .type_enum_nonexhaustive,
1160011708 .type_opaque,
11709 .type_spirv,
1160111710 => .type_type,
1160211711
1160311712 .undef,
......@@ -11955,6 +12064,8 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.lang.TypeId {
1195512064 .type_opaque,
1195612065 => .@"opaque",
1195712066
12067 .type_spirv => .spirv,
12068
1195812069 .type_function => .@"fn",
1195912070
1196012071 // values, not types
src/Sema.zig+442-1
......@@ -1434,6 +1434,7 @@ fn analyzeBodyInner(
14341434 .reify_struct => try sema.zirReifyStruct( block, extended, inst),
14351435 .reify_union => try sema.zirReifyUnion( block, extended, inst),
14361436 .reify_enum => try sema.zirReifyEnum( block, extended, inst),
1437 .reify_spirv_type => try sema.zirReifySpirvType( block, extended, inst),
14371438 // zig fmt: on
14381439
14391440 .set_float_mode => {
......@@ -9273,6 +9274,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
92739274 .noreturn,
92749275 .null,
92759276 .@"opaque",
9277 .spirv,
92769278 .optional,
92779279 .type,
92789280 .undefined,
......@@ -9348,6 +9350,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
93489350 .noreturn,
93499351 .null,
93509352 .@"opaque",
9353 .spirv,
93519354 .optional,
93529355 .type,
93539356 .undefined,
......@@ -15531,6 +15534,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1553115534 .undefined,
1553215535 .null,
1553315536 .@"opaque",
15537 .spirv,
1553415538 .type,
1553515539 .enum_literal,
1553615540 .comptime_float,
......@@ -16834,6 +16838,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1683416838 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(),
1683516839 })));
1683616840 },
16841 .spirv => unreachable, // TODO: ALI
1683716842 .frame => return sema.failWithUseOfAsync(block, src),
1683816843 .@"anyframe" => return sema.failWithUseOfAsync(block, src),
1683916844 }
......@@ -20449,6 +20454,306 @@ fn zirReifyEnum(
2044920454 }
2045020455}
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
2045220757fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
2045320758 const pt = sema.pt;
2045420759 const va_list_ty = try sema.getStdLangType(src, .VaList);
......@@ -24760,6 +25065,7 @@ fn zirStdLangValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
2476025065 .fn_attributes, => .@"Type.Fn.Attributes",
2476125066 .container_layout => .@"Type.ContainerLayout",
2476225067 .enum_mode => .@"Type.Enum.Mode",
25068 .spirv_type_options => .@"Type.Spirv",
2476325069 // zig fmt: on
2476425070
2476525071 // Values are handled here.
......@@ -24957,6 +25263,7 @@ fn explainWhyTypeIsComptime(
2495725263 .void,
2495825264 .@"enum",
2495925265 .@"opaque",
25266 .spirv,
2496025267 .pointer,
2496125268 => unreachable, // not comptime-only
2496225269
......@@ -25043,6 +25350,7 @@ pub fn explainWhyTypeIsNotExtern(
2504325350 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
2504425351
2504525352 .@"opaque",
25353 .spirv,
2504625354 .bool,
2504725355 .float,
2504825356 .@"anyframe",
......@@ -25483,9 +25791,13 @@ fn fieldPtrLoad(
2548325791) CompileError!Air.Inst.Ref {
2548425792 const pt = sema.pt;
2548525793 const zcu = pt.zcu;
25794 const ip = &zcu.intern_pool;
2548625795 const object_ptr_ty = sema.typeOf(object_ptr);
2548725796 assert(object_ptr_ty.zigTypeTag(zcu) == .pointer);
2548825797 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 }
2548925801 try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access);
2549025802 if (try pointee_ty.onePossibleValue(pt)) |opv| {
2549125803 const object: Air.Inst.Ref = .fromValue(opv);
......@@ -25675,11 +25987,123 @@ fn fieldVal(
2567525987 } else {
2567625988 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
2567725989 },
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 },
2567826001 else => {},
2567926002 }
2568026003 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
2568126004}
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
2568326107fn fieldPtr(
2568426108 sema: *Sema,
2568526109 block: *Block,
......@@ -26540,6 +26964,7 @@ fn elemPtr(
2654026964 .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
2654126965 .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
2654226966 .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
26967 .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable_ptr, elem_index),
2654326968 else => {
2654426969 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
2654526970 try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access);
......@@ -26964,6 +27389,22 @@ fn elemPtrVector(
2696427389 return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty);
2696527390}
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
2696727408/// Asserts that the layout of the array is already resolved.
2696827409fn elemPtrArray(
2696927410 sema: *Sema,
......@@ -31496,7 +31937,7 @@ const PeerResolveStrategy = enum {
3149631937
3149731938 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {
3149831939 return switch (ty.zigTypeTag(zcu)) {
31499 .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact,
31940 .type, .void, .bool, .@"opaque", .spirv, .frame, .@"anyframe" => .exact,
3150031941 .noreturn, .undefined => .unknown,
3150131942 .null => .nullable,
3150231943 .comptime_int => .comptime_int,
src/Sema/LowerZon.zig+2
......@@ -244,6 +244,7 @@ fn checkTypeInner(
244244 .frame,
245245 .@"anyframe",
246246 .@"opaque",
247 .spirv,
247248 => return self.failUnsupportedResultType(ty, null),
248249
249250 .pointer => {
......@@ -408,6 +409,7 @@ fn lowerExprKnownResTyInner(
408409 .error_set,
409410 .@"fn",
410411 .@"opaque",
412 .spirv,
411413 .frame,
412414 .@"anyframe",
413415 .void,
src/Sema/bitcast.zig+1
......@@ -249,6 +249,7 @@ const UnpackValueBits = struct {
249249 .tuple_type,
250250 .union_type,
251251 .opaque_type,
252 .spirv_type,
252253 .enum_type,
253254 .func_type,
254255 .error_set_type,
src/Sema/comptime_ptr_access.zig+2
......@@ -422,6 +422,7 @@ fn loadComptimePtrInner(
422422 .undefined,
423423 .enum_literal,
424424 .@"opaque",
425 .spirv,
425426 .@"fn",
426427 .error_union,
427428 => unreachable, // ill-defined layout
......@@ -854,6 +855,7 @@ fn prepareComptimePtrStore(
854855 .undefined,
855856 .enum_literal,
856857 .@"opaque",
858 .spirv,
857859 .@"fn",
858860 .error_union,
859861 => 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
8787 .ptr_type,
8888 .anyframe_type,
8989 .simple_type,
90 .spirv_type,
9091 .opaque_type,
9192 .error_set_type,
9293 .inferred_error_set_type,
......@@ -288,10 +289,12 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
288289 }
289290
290291 // Resolve the layout of all fields, and check their types are allowed.
292 const fields_len = struct_obj.field_types.len;
291293 for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
292294 const field_ty: Type = .fromInterned(field_ty_ip);
293295 assert(!field_ty.isGenericPoison());
294296 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) });
295298 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
296299 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
297300 return sema.failWithOwnedErrorMsg(&block, msg: {
......@@ -302,6 +305,35 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
302305 break :msg msg;
303306 });
304307 }
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
305337 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {
306338 return sema.failWithOwnedErrorMsg(&block, msg: {
307339 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 {
413445 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
414446 const offset = resolved_field_aligns[field_idx].forward(cur_offset);
415447 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;
417452 }
418453 const struct_size: u32 = switch (class) {
419454 .no_possible_value => 0,
......@@ -858,6 +893,14 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
858893 break :msg msg;
859894 });
860895 }
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 }
861904 if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) {
862905 return sema.failWithOwnedErrorMsg(&block, msg: {
863906 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 {
170170
171171 .func_type => .fully_comptime,
172172
173 .spirv_type => if (cur_ty.isSpirvRuntimeArray(zcu)) .runtime else .no_possible_value,
173174 .opaque_type => .no_possible_value,
174175
175176 .error_union_type => |eu| {
......@@ -323,6 +324,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
323324 .error_set,
324325 .@"fn",
325326 .@"opaque",
327 .spirv,
326328 .@"anyframe",
327329 .@"enum",
328330 .enum_literal,
......@@ -617,6 +619,10 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari
617619 const name = ip.loadEnumType(ty.toIntern()).name;
618620 try writer.print("{f}", .{name.fmt(ip)});
619621 },
622 .spirv_type => {
623 const name = ip.loadSpirvType(ty.toIntern()).name;
624 try writer.print("{f}", .{name.fmt(ip)});
625 },
620626 .func_type => |fn_info| {
621627 if (fn_info.is_noinline) {
622628 try writer.writeAll("noinline ");
......@@ -704,6 +710,14 @@ pub fn toIntern(ty: Type) InternPool.Index {
704710 return ty.ip_index;
705711}
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
707721pub fn toValue(self: Type) Value {
708722 return .fromInterned(self.toIntern());
709723}
......@@ -751,6 +765,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
751765 .error_set_type,
752766 .inferred_error_set_type,
753767 .tuple_type,
768 .spirv_type,
754769 .opaque_type,
755770 .anyframe_type,
756771 // These are function bodies, not function pointers.
......@@ -1038,6 +1053,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
10381053 }
10391054 },
10401055 .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",
10411057 .opaque_type => .@"1",
10421058
10431059 // values, not types
......@@ -1183,6 +1199,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
11831199 }
11841200 },
11851201 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),
1202 .spirv_type => unreachable,
11861203 .opaque_type => unreachable,
11871204
11881205 // values, not types
......@@ -1309,7 +1326,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
13091326 .tuple_type,
13101327 => ty.abiSize(zcu) * 8,
13111328
1312 .opaque_type => unreachable,
1329 .opaque_type, .spirv_type => unreachable,
13131330
13141331 // values, not types
13151332 .undef,
......@@ -1511,14 +1528,17 @@ pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {
15111528/// * `[]T`
15121529/// * `[*]T`
15131530/// * `[*c]T`
1531/// * `@SpirvType(.{ .runtime_array = T })`
15141532pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {
15151533 const ip = &zcu.intern_pool;
15161534 return switch (ip.indexToKey(ty.toIntern())) {
15171535 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1536 .spirv_type => ty.childType(zcu),
15181537 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
15191538 .many, .slice, .c => .fromInterned(ptr_type.child),
15201539 .one => switch (ip.indexToKey(ptr_type.child)) {
15211540 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1541 .spirv_type => Type.fromInterned(ptr_type.child).childType(zcu),
15221542 else => unreachable,
15231543 },
15241544 },
......@@ -1864,6 +1884,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
18641884 .func_type => unreachable,
18651885 .simple_type => unreachable, // handled via Index enum tag above
18661886
1887 .spirv_type => unreachable,
18671888 .opaque_type => unreachable,
18681889
18691890 // values, not types
......@@ -2032,6 +2053,7 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
20322053 .error_set_type,
20332054 .inferred_error_set_type,
20342055 .opaque_type,
2056 .spirv_type,
20352057 => null,
20362058
20372059 .simple_type => |t| switch (t) {
......@@ -2219,10 +2241,12 @@ pub fn isIndexable(ty: Type, zcu: *const Zcu) bool {
22192241 .one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
22202242 .array, .vector => true,
22212243 .@"struct" => ty.childType(zcu).isTuple(zcu),
2244 .spirv => ty.childType(zcu).isSpirvRuntimeArray(zcu),
22222245 else => false,
22232246 },
22242247 },
22252248 .@"struct" => ty.isTuple(zcu),
2249 .spirv => ty.isSpirvRuntimeArray(zcu),
22262250 else => false,
22272251 };
22282252}
......@@ -2844,6 +2868,7 @@ pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error
28442868 .slice, .many, .c => .fromInterned(ptr_info.child),
28452869 .one => switch (ip.indexToKey(ptr_info.child)) {
28462870 .array_type => |array_type| .fromInterned(array_type.child),
2871 .spirv_type => Type.fromInterned(ptr_info.child).childType(zcu),
28472872 else => unreachable,
28482873 },
28492874 };
......@@ -3095,6 +3120,7 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
30953120
30963121 .noreturn,
30973122 .@"opaque",
3123 .spirv,
30983124 .error_union,
30993125 .error_set,
31003126 .frame,
......@@ -3170,6 +3196,7 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool
31703196 .noreturn => position == .ret_ty,
31713197
31723198 .@"opaque",
3199 .spirv,
31733200 .bool,
31743201 .float,
31753202 .@"anyframe",
......@@ -3261,6 +3288,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
32613288 .simple_type,
32623289 .opaque_type,
32633290 .error_set_type,
3291 .spirv_type,
32643292 .inferred_error_set_type,
32653293 => {},
32663294 .func_type => |func_type| {
......@@ -3362,6 +3390,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn
33623390 .union_type,
33633391 .opaque_type,
33643392 .enum_type,
3393 .spirv_type,
33653394 .simple_type,
33663395 .int_type,
33673396 => {},
src/Value.zig+7-1
......@@ -2046,7 +2046,10 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op
20462046
20472047 const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu);
20482048 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 {
20502053 // No refinement can happen - this pointer is presumably invalid.
20512054 // Just offset it.
20522055 const parent = try arena.create(PointerDeriveStep);
......@@ -2078,6 +2081,7 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op
20782081 .undefined,
20792082 .enum_literal,
20802083 .@"opaque",
2084 .spirv,
20812085 .@"fn",
20822086 .error_union,
20832087 .int,
......@@ -2229,6 +2233,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
22292233 .null,
22302234 .@"fn",
22312235 .@"opaque",
2236 .spirv,
22322237 .enum_literal,
22332238 => comptime unreachable, // comptime-only or otherwise impossible
22342239
......@@ -2332,6 +2337,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
23322337 .null,
23332338 .@"fn",
23342339 .@"opaque",
2340 .spirv,
23352341 .enum_literal,
23362342 => comptime unreachable, // comptime-only or otherwise impossible
23372343
src/Zcu.zig+4-1
......@@ -468,6 +468,7 @@ pub const StdLangDecl = enum {
468468 @"Type.Struct.FieldAttributes",
469469 @"Type.ContainerLayout",
470470 @"Type.Opaque",
471 @"Type.Spirv",
471472
472473 panic,
473474 @"panic.call",
......@@ -548,6 +549,7 @@ pub const StdLangDecl = enum {
548549 .@"Type.Struct.FieldAttributes",
549550 .@"Type.ContainerLayout",
550551 .@"Type.Opaque",
552 .@"Type.Spirv",
551553 => .type,
552554
553555 .panic => .type,
......@@ -601,7 +603,7 @@ pub const StdLangDecl = enum {
601603 .VaList => .va_list,
602604 .assembly, .@"assembly.Clobbers" => .assembly,
603605 else => {
604 if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Opaque")) {
606 if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Spirv")) {
605607 return .main;
606608 } else {
607609 return .panic;
......@@ -2740,6 +2742,7 @@ pub const LazySrcLoc = struct {
27402742 .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node,
27412743 .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node,
27422744 .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,
27432746 else => unreachable,
27442747 },
27452748 else => unreachable,
src/codegen.zig+1
......@@ -329,6 +329,7 @@ pub fn generateSymbol(
329329 .tuple_type,
330330 .union_type,
331331 .opaque_type,
332 .spirv_type,
332333 .enum_type,
333334 .func_type,
334335 .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 {
255255 .work_item_id,
256256 .work_group_size,
257257 .work_group_id,
258 .spirv_runtime_array_len,
258259 => unreachable,
259260 .ret_ptr => {
260261 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,
74917492 }
74927493 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
74937494 },
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,
74957496 }
74967497 assert(air.body_index == 0);
74977498}
src/codegen/aarch64/abi.zig+1
......@@ -62,6 +62,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {
6262 .null,
6363 .@"fn",
6464 .@"opaque",
65 .spirv,
6566 .enum_literal,
6667 .array,
6768 => unreachable,
src/codegen/arm/abi.zig+1
......@@ -113,6 +113,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
113113 .null,
114114 .@"fn",
115115 .@"opaque",
116 .spirv,
116117 .enum_literal,
117118 .array,
118119 => unreachable,
src/codegen/c.zig+3
......@@ -902,6 +902,7 @@ pub const DeclGen = struct {
902902 .tuple_type,
903903 .union_type,
904904 .opaque_type,
905 .spirv_type,
905906 .enum_type,
906907 .func_type,
907908 .error_set_type,
......@@ -1565,6 +1566,7 @@ pub const DeclGen = struct {
15651566 },
15661567 .anyframe_type,
15671568 .opaque_type,
1569 .spirv_type,
15681570 .func_type,
15691571 => unreachable,
15701572
......@@ -2877,6 +2879,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
28772879 .work_item_id,
28782880 .work_group_size,
28792881 .work_group_id,
2882 .spirv_runtime_array_len,
28802883 => unreachable,
28812884
28822885 // 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) {
249249 .null,
250250 .enum_literal,
251251 .@"opaque",
252 .spirv,
252253 .noreturn,
253254 .void,
254255 => return .void,
......@@ -865,6 +866,7 @@ pub const CType = union(enum) {
865866 switch (ty.zigTypeTag(zcu)) {
866867 .frame => unreachable,
867868 .@"anyframe" => unreachable,
869 .spirv => unreachable,
868870
869871 .type => try w.writeAll("type"),
870872 .void => try w.writeAll("void"),
......@@ -988,6 +990,7 @@ pub const CType = union(enum) {
988990 .anyframe_type,
989991 .simple_type,
990992 .opaque_type,
993 .spirv_type,
991994 .error_set_type,
992995 .inferred_error_set_type,
993996 => true,
src/codegen/llvm.zig+3-1
......@@ -2662,6 +2662,7 @@ pub const Object = struct {
26622662 },
26632663 .frame => @panic("TODO implement lowerDebugType for Frame types"),
26642664 .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"),
2665 .spirv => unreachable,
26652666 }
26662667 }
26672668
......@@ -3375,7 +3376,7 @@ pub const Object = struct {
33753376 );
33763377 return ty;
33773378 },
3378 .opaque_type => unreachable, // no runtime bits
3379 .opaque_type, .spirv_type => unreachable, // no runtime bits
33793380 .enum_type => try o.lowerType(t.intTagType(zcu)),
33803381 .func_type => |func_type| try o.lowerFnType(t, func_type),
33813382 .error_set_type, .inferred_error_set_type => try o.errorIntType(),
......@@ -3497,6 +3498,7 @@ pub const Object = struct {
34973498 .tuple_type,
34983499 .union_type,
34993500 .opaque_type,
3501 .spirv_type,
35003502 .enum_type,
35013503 .func_type,
35023504 .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
434434 .work_item_id => try self.airWorkItemId(inst),
435435 .work_group_size => try self.airWorkGroupSize(inst),
436436 .work_group_id => try self.airWorkGroupId(inst),
437 .spirv_runtime_array_len => unreachable,
437438
438439 // Instructions that are known to always be `noreturn` based on their tag.
439440 .br => return self.airBr(inst),
......@@ -7255,6 +7256,7 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool {
72557256 .undefined,
72567257 .null,
72577258 .@"opaque",
7259 .spirv,
72587260 => unreachable,
72597261
72607262 .noreturn,
src/codegen/mips/abi.zig+1
......@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
7777 .null,
7878 .@"fn",
7979 .@"opaque",
80 .spirv,
8081 .enum_literal,
8182 .array,
8283 => unreachable,
src/codegen/riscv64/CodeGen.zig+1
......@@ -1642,6 +1642,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
16421642 .work_item_id => unreachable,
16431643 .work_group_size => unreachable,
16441644 .work_group_id => unreachable,
1645 .spirv_runtime_array_len => unreachable,
16451646 // zig fmt: on
16461647 }
16471648
src/codegen/riscv64/abi.zig+1
......@@ -87,6 +87,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class {
8787 .null,
8888 .@"fn",
8989 .@"opaque",
90 .spirv,
9091 .enum_literal,
9192 .array,
9293 => unreachable,
src/codegen/sparc64/CodeGen.zig+1
......@@ -709,6 +709,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
709709 .work_item_id => unreachable,
710710 .work_group_size => unreachable,
711711 .work_group_id => unreachable,
712 .spirv_runtime_array_len => unreachable,
712713 // zig fmt: on
713714 }
714715
src/codegen/spirv/Assembler.zig+1-1
......@@ -269,7 +269,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {
269269 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
270270 const ids = try cg.id_scratch.addManyAsSlice(gpa, operands[1..].len);
271271 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);
273273 },
274274 .OpTypeImage => blk: {
275275 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 {
271271 .vulkan, .opengl => {
272272 if (ty.zigTypeTag(zcu) == .@"struct") {
273273 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 },
275281 else => {},
276282 }
277283 }
......@@ -313,6 +319,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
313319 try cg.module.debugName(result_id, nav.fqn.toSlice(ip));
314320 },
315321 .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
316334 const ty_id = try cg.resolveType(ty, .indirect);
317335 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 {
360378 cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len;
361379}
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
363396pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error {
364397 @branchHint(.cold);
365398 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 {
779812 .tuple_type,
780813 .union_type,
781814 .opaque_type,
815 .spirv_type,
782816 .enum_type,
783817 .func_type,
784818 .error_set_type,
......@@ -1065,16 +1099,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
10651099 const parent_ptr_id = try cg.derivePtr(oac.parent.*);
10661100 const parent_ptr_ty = try oac.parent.ptrType(pt);
10671101 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)) {
10711104 // Vector element ptr accesses are derived as offset_and_cast.
10721105 // We can just use OpAccessChain.
1073 return cg.accessChain(
1074 result_ty_id,
1075 parent_ptr_id,
1076 &.{@intCast(@divExact(oac.byte_offset, child_size))},
1077 );
1106 const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
1107 if (oac.byte_offset % child_size == 0) {
1108 return cg.accessChain(
1109 result_ty_id,
1110 parent_ptr_id,
1111 &.{@intCast(@divExact(oac.byte_offset, child_size))},
1112 );
1113 }
10781114 }
10791115
10801116 if (oac.byte_offset == 0) {
......@@ -1269,7 +1305,6 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id {
12691305 const result_id = try cg.module.structType(
12701306 member_types[0..layout.total_fields],
12711307 member_names[0..layout.total_fields],
1272 null,
12731308 .none,
12741309 );
12751310
......@@ -1450,7 +1485,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
14501485 return try cg.module.structType(
14511486 &.{ ptr_ty_id, size_ty_id },
14521487 &.{ "ptr", "len" },
1453 null,
14541488 .none,
14551489 );
14561490 },
......@@ -1472,7 +1506,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
14721506 const result_id = try cg.module.structType(
14731507 member_types[0..member_index],
14741508 null,
1475 null,
14761509 .none,
14771510 );
14781511 const type_name = try cg.resolveTypeName(ty);
......@@ -1494,9 +1527,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
14941527 var member_names = std.array_list.Managed([]const u8).init(gpa);
14951528 defer member_names.deinit();
14961529
1497 var member_offsets = std.array_list.Managed(u32).init(gpa);
1498 defer member_offsets.deinit();
1499
15001530 var it = struct_type.iterateRuntimeOrder(ip);
15011531 while (it.next()) |field_index| {
15021532 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 {
15051535 const field_name = struct_type.field_names.get(ip)[field_index];
15061536 try member_types.append(try cg.resolveType(field_ty, .indirect));
15071537 try member_names.append(field_name.toSlice(ip));
1508 try member_offsets.append(@intCast(ty.structFieldOffset(field_index, zcu)));
15091538 }
15101539
15111540 const result_id = try cg.module.structType(
15121541 member_types.items,
15131542 member_names.items,
1514 member_offsets.items,
15151543 ty.toIntern(),
15161544 );
15171545
......@@ -1541,7 +1569,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
15411569 return try cg.module.structType(
15421570 &.{ payload_ty_id, bool_ty_id },
15431571 &.{ "payload", "valid" },
1544 null,
15451572 .none,
15461573 );
15471574 },
......@@ -1576,7 +1603,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
15761603 // TODO: ABI padding?
15771604 }
15781605
1579 return try cg.module.structType(&member_types, &member_names, null, .none);
1606 return try cg.module.structType(&member_types, &member_names, .none);
15801607 },
15811608 .@"opaque" => {
15821609 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 {
15841611 defer gpa.free(type_name);
15851612 return try cg.module.opaqueType(type_name);
15861613 },
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
15881686 .null,
15891687 .undefined,
......@@ -2421,7 +2519,6 @@ fn generateTestEntryPoint(
24212519 const buffer_struct_ty_id = try cg.module.structType(
24222520 &.{anyerror_ty_id},
24232521 &.{"error_out"},
2424 null,
24252522 .none,
24262523 );
24272524 try cg.module.decorate(buffer_struct_ty_id, .block);
......@@ -2708,13 +2805,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
27082805 .memcpy => return cg.airMemcpy(inst),
27092806 .memmove => return cg.airMemmove(inst),
27102807
2711 .slice_ptr => try cg.airSliceField(inst, 0),
2712 .slice_len => try cg.airSliceField(inst, 1),
2713 .slice_elem_ptr => try cg.airSliceElemPtr(inst),
2714 .slice_elem_val => try cg.airSliceElemVal(inst),
2715 .ptr_elem_ptr => try cg.airPtrElemPtr(inst),
2716 .ptr_elem_val => try cg.airPtrElemVal(inst),
2717 .array_elem_val => try cg.airArrayElemVal(inst),
2808 .slice_ptr => try cg.airSliceField(inst, 0),
2809 .slice_len => try cg.airSliceField(inst, 1),
2810 .spirv_runtime_array_len => try cg.airSpirvRuntimeArrayLen(inst),
2811 .slice_elem_ptr => try cg.airSliceElemPtr(inst),
2812 .slice_elem_val => try cg.airSliceElemVal(inst),
2813 .ptr_elem_ptr => try cg.airPtrElemPtr(inst),
2814 .ptr_elem_val => try cg.airPtrElemVal(inst),
2815 .array_elem_val => try cg.airArrayElemVal(inst),
27182816
27192817 .set_union_tag => return cg.airSetUnionTag(inst),
27202818 .get_union_tag => try cg.airGetUnionTag(inst),
......@@ -4305,6 +4403,22 @@ fn airSliceField(cg: *CodeGen, inst: Air.Inst.Index, field: u32) !?Id {
43054403 return try cg.extractField(field_ty, operand_id, field);
43064404}
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
43084422fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
43094423 const zcu = cg.module.zcu;
43104424 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 {
58845998 .struct_type,
58855999 .union_type,
58866000 .opaque_type,
6001 .spirv_type,
58876002 .enum_type,
58886003 .func_type,
58896004 .error_set_type,
src/codegen/spirv/Module.zig+72-18
......@@ -70,6 +70,8 @@ cache: struct {
7070
7171 bool_const: [2]?Id = .{ null, null },
7272 constants: std.ArrayHashMapUnmanaged(Constant, Id, Constant.HashContext, true) = .empty,
73
74 spirv_types: std.AutoHashMapUnmanaged(InternPool.Index, Id) = .empty,
7375} = .{},
7476/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
7577sections: struct {
......@@ -229,6 +231,7 @@ pub fn deinit(module: *Module) void {
229231 module.cache.array_types.deinit(module.gpa);
230232 module.cache.struct_types.deinit(module.gpa);
231233 module.cache.fn_types.deinit(module.gpa);
234 module.cache.spirv_types.deinit(module.gpa);
232235 module.cache.capabilities.deinit(module.gpa);
233236 module.cache.extensions.deinit(module.gpa);
234237 module.cache.extended_instruction_set.deinit(module.gpa);
......@@ -683,10 +686,8 @@ pub fn structType(
683686 module: *Module,
684687 types: []const Id,
685688 maybe_names: ?[]const []const u8,
686 maybe_offsets: ?[]const u32,
687689 ip_index: InternPool.Index,
688690) !Id {
689 const target = module.zcu.getTarget();
690691 const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index;
691692
692693 if (module.cache.struct_types.get(.{ .fields = types, .ip_index = actual_ip_index })) |id| return id;
......@@ -704,22 +705,6 @@ pub fn structType(
704705 }
705706 }
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
723708 try module.cache.struct_types.put(
724709 module.gpa,
725710 .{ .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
747732 return result_id;
748733}
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
750804pub fn constant(module: *Module, ty_id: Id, value: spec.LiteralContextDependentNumber) !Id {
751805 const gop = try module.cache.constants.getOrPut(module.gpa, .{ .ty = ty_id, .value = value });
752806 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 {
11951195 .undefined,
11961196 .null,
11971197 .@"opaque",
1198 .spirv,
11981199 => unreachable,
11991200
12001201 .noreturn,
......@@ -1882,6 +1883,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18821883 .work_item_id,
18831884 .work_group_size,
18841885 .work_group_id,
1886 .spirv_runtime_array_len,
18851887 => unreachable,
18861888 };
18871889}
......@@ -4698,6 +4700,7 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {
46984700 .tuple_type,
46994701 .union_type,
47004702 .opaque_type,
4703 .spirv_type,
47014704 .enum_type,
47024705 .func_type,
47034706 .error_set_type,
src/codegen/wasm/abi.zig+1
......@@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
7777 .null,
7878 .@"fn",
7979 .@"opaque",
80 .spirv,
8081 .enum_literal,
8182 => unreachable,
8283 }
src/codegen/x86_64/CodeGen.zig+1-1
......@@ -173719,7 +173719,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173719173719 // No soft-float `Legalize` features are enabled, so this instruction never appears.
173720173720 .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,
173723173723 }
173724173724 try cg.resetTemps(@enumFromInt(0));
173725173725 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
164164 .null,
165165 .@"fn",
166166 .@"opaque",
167 .spirv,
167168 .enum_literal,
168169 => unreachable,
169170 };
src/link/ConstPool.zig+2
......@@ -196,6 +196,7 @@ fn checkType(pool: *const ConstPool, ty: Type, zcu: *const Zcu) bool {
196196 .null,
197197 .error_set,
198198 .@"opaque",
199 .spirv,
199200 .frame,
200201 .@"anyframe",
201202 .enum_literal,
......@@ -241,6 +242,7 @@ fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Al
241242 .null,
242243 .error_set,
243244 .@"opaque",
245 .spirv,
244246 .frame,
245247 .@"anyframe",
246248 .enum_literal,
src/link/Dwarf.zig+2
......@@ -3047,6 +3047,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
30473047 .func_type,
30483048 .error_set_type,
30493049 .inferred_error_set_type,
3050 .spirv_type,
30503051 => .alias,
30513052
30523053 .struct_type => tag: {
......@@ -3533,6 +3534,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
35333534 switch (value_ip_key) {
35343535 .func => unreachable, // handled above
35353536 .@"extern" => unreachable, // handled above
3537 .spirv_type => unreachable,
35363538
35373539 .int_type => |int_type| {
35383540 try wip_nav.abbrevCode(.numeric_type);
src/print_value.zig+1
......@@ -60,6 +60,7 @@ pub fn print(
6060 .union_type,
6161 .opaque_type,
6262 .enum_type,
63 .spirv_type,
6364 .func_type,
6465 .error_set_type,
6566 .inferred_error_set_type,
src/print_zir.zig+10
......@@ -685,6 +685,16 @@ const Writer = struct {
685685 defer self.parent_decl_node = prev_parent_decl_node;
686686 try self.writeSrcNode(stream, .zero);
687687 },
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
689699 .cmpxchg => try self.writeCmpxchg(stream, extended),
690700 .ptr_cast_full => try self.writePtrCastFull(stream, extended),
test/behavior.zig+4
......@@ -112,6 +112,10 @@ test {
112112 _ = @import("behavior/wasm.zig");
113113 }
114114
115 if (builtin.zig_backend == .stage2_spirv) {
116 _ = @import("behavior/spirv.zig");
117 }
118
115119 if (builtin.zig_backend != .stage2_spirv and builtin.os.tag != .wasi) {
116120 _ = @import("behavior/asm.zig");
117121 }
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 {
253253 try expect(typeinfo_info == .@"union");
254254 try expect(typeinfo_info.@"union".layout == .auto);
255255 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);
257257 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_types.len);
258258 try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_attrs.len);
259259 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
262262 const TestNoTagUnion = union {
263263 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