| author | |
| committer | |
| log | d7d131c0503ae8a02677faa01d7d518a5441cb6f |
| tree | 2ecd47f8e08c6e63c932843868ef1448c9b98474 |
| parent | 7a9f8dc9393ff4084da6c77d874d08e26897b3cb |
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 | 5792 | <p>Returns an {#link|enum#} type with the properties specified by the arguments.</p> |
| 5793 | 5793 | {#header_close#} |
| 5794 | 5794 | |
| 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 | 5839 | {#header_open|@typeInfo#} |
| 5796 | 5840 | <pre>{#syntax#}@typeInfo(comptime T: type) std.lang.Type{#endsyntax#}</pre> |
| 5797 | 5841 | <p> |
lib/std/Target.zig+2-1| ... | ... | @@ -2341,7 +2341,8 @@ pub fn supportsAddressSpace( |
| 2341 | 2341 | .lut => arch == .propeller and std.Target.propeller.featureSetHas(target.cpu.features, .p2), |
| 2342 | 2342 | |
| 2343 | 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 | 2346 | .param => is_nvptx, |
| 2346 | 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 | 76 | switch (@typeInfo(Key)) { |
| 77 | 77 | .noreturn, |
| 78 | 78 | .@"opaque", |
| 79 | .spirv, | |
| 79 | 80 | .undefined, |
| 80 | 81 | .null, |
| 81 | 82 | .comptime_float, |
lib/std/lang.zig+54| ... | ... | @@ -578,6 +578,7 @@ pub const Type = union(enum) { |
| 578 | 578 | @"anyframe": AnyFrame, |
| 579 | 579 | vector: Vector, |
| 580 | 580 | enum_literal, |
| 581 | spirv: Spirv, | |
| 581 | 582 | |
| 582 | 583 | /// This data structure is used by the Zig language code generation and |
| 583 | 584 | /// therefore must be kept in sync with the compiler implementation. |
| ... | ... | @@ -781,6 +782,59 @@ pub const Type = union(enum) { |
| 781 | 782 | }; |
| 782 | 783 | }; |
| 783 | 784 | |
| 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 | 838 | /// This data structure is used by the Zig language code generation and |
| 785 | 839 | /// therefore must be kept in sync with the compiler implementation. |
| 786 | 840 | pub const Opaque = struct { |
lib/std/mem.zig+1| ... | ... | @@ -352,6 +352,7 @@ pub fn zeroes(comptime T: type) T { |
| 352 | 352 | .noreturn, |
| 353 | 353 | .undefined, |
| 354 | 354 | .@"opaque", |
| 355 | .spirv, | |
| 355 | 356 | .frame, |
| 356 | 357 | .@"anyframe", |
| 357 | 358 | => { |
lib/std/start.zig+3-1| ... | ... | @@ -19,7 +19,9 @@ comptime { |
| 19 | 19 | // decls there get run. |
| 20 | 20 | _ = root; |
| 21 | 21 | |
| 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 | 25 | const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup"; |
| 24 | 26 | if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) { |
| 25 | 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 | 79 | switch (@typeInfo(@TypeOf(actual))) { |
| 80 | 80 | .noreturn, |
| 81 | 81 | .@"opaque", |
| 82 | .spirv, | |
| 82 | 83 | .frame, |
| 83 | 84 | .@"anyframe", |
| 84 | 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 | 738 | switch (@typeInfo(@TypeOf(actual))) { |
| 738 | 739 | .noreturn, |
| 739 | 740 | .@"opaque", |
| 741 | .spirv, | |
| 740 | 742 | .frame, |
| 741 | 743 | .@"anyframe", |
| 742 | 744 | => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"), |
lib/std/zig/AstGen.zig+10| ... | ... | @@ -9327,6 +9327,16 @@ fn builtinCall( |
| 9327 | 9327 | }); |
| 9328 | 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 | }, | |
| 9330 | 9340 | |
| 9331 | 9341 | .panic => { |
| 9332 | 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 | 1079 | _ = try astrl.expr(args[3], block, ResultInfo.type_only); |
| 1080 | 1080 | return false; |
| 1081 | 1081 | }, |
| 1082 | .SpirvType => { | |
| 1083 | _ = try astrl.expr(args[0], block, ResultInfo.type_only); | |
| 1084 | return false; | |
| 1085 | }, | |
| 1082 | 1086 | .Vector => { |
| 1083 | 1087 | _ = try astrl.expr(args[0], block, ResultInfo.type_only); |
| 1084 | 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 | 114 | Struct, |
| 115 | 115 | Union, |
| 116 | 116 | Enum, |
| 117 | SpirvType, | |
| 117 | 118 | type_info, |
| 118 | 119 | type_name, |
| 119 | 120 | TypeOf, |
| ... | ... | @@ -971,6 +972,13 @@ pub const list = list: { |
| 971 | 972 | .param_count = 4, |
| 972 | 973 | }, |
| 973 | 974 | }, |
| 975 | .{ | |
| 976 | "@SpirvType", | |
| 977 | .{ | |
| 978 | .tag = .SpirvType, | |
| 979 | .param_count = 1, | |
| 980 | }, | |
| 981 | }, | |
| 974 | 982 | .{ |
| 975 | 983 | "@typeInfo", |
| 976 | 984 | .{ |
lib/std/zig/Zir.zig+14| ... | ... | @@ -2055,6 +2055,9 @@ pub const Inst = struct { |
| 2055 | 2055 | /// `operand` is payload index to `ReifyEnum`. |
| 2056 | 2056 | /// `small` contains `NameStrategy`. |
| 2057 | 2057 | reify_enum, |
| 2058 | /// Implements builtin `@SpirvType`. | |
| 2059 | /// `operand` is payload index to `ReifyFn`. | |
| 2060 | reify_spirv_type, | |
| 2058 | 2061 | /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins. |
| 2059 | 2062 | /// `small` 0=>weak 1=>strong |
| 2060 | 2063 | /// `operand` is payload index to `Cmpxchg`. |
| ... | ... | @@ -3269,6 +3272,14 @@ pub const Inst = struct { |
| 3269 | 3272 | field_values: Ref, |
| 3270 | 3273 | }; |
| 3271 | 3274 | |
| 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 | 3283 | /// Trailing: |
| 3273 | 3284 | /// 0. multi_cases_len: u32, // If has_multi_cases is set. |
| 3274 | 3285 | /// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set. |
| ... | ... | @@ -3584,6 +3595,7 @@ pub const Inst = struct { |
| 3584 | 3595 | fn_attributes, |
| 3585 | 3596 | container_layout, |
| 3586 | 3597 | enum_mode, |
| 3598 | spirv_type_options, | |
| 3587 | 3599 | // Values |
| 3588 | 3600 | calling_convention_c, |
| 3589 | 3601 | calling_convention_inline, |
| ... | ... | @@ -4389,6 +4401,7 @@ fn findTrackableInner( |
| 4389 | 4401 | .reify_enum, |
| 4390 | 4402 | .reify_struct, |
| 4391 | 4403 | .reify_union, |
| 4404 | .reify_spirv_type, | |
| 4392 | 4405 | => return contents.other.append(gpa, inst), |
| 4393 | 4406 | |
| 4394 | 4407 | // Type declarations need tracking. |
| ... | ... | @@ -5181,6 +5194,7 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void { |
| 5181 | 5194 | .reify_enum, |
| 5182 | 5195 | .reify_struct, |
| 5183 | 5196 | .reify_union, |
| 5197 | .reify_spirv_type, | |
| 5184 | 5198 | => {}, // tracked in order, as the owner instructions of explicit container types |
| 5185 | 5199 | else => unreachable, // assertion failure; not trackable |
| 5186 | 5200 | }, |
lib/std/zon/Serializer.zig+1| ... | ... | @@ -854,6 +854,7 @@ fn canSerializeTypeInner( |
| 854 | 854 | .frame, |
| 855 | 855 | .@"anyframe", |
| 856 | 856 | .@"opaque", |
| 857 | .spirv, | |
| 857 | 858 | => false, |
| 858 | 859 | |
| 859 | 860 | .@"enum" => |@"enum"| @"enum".mode == .exhaustive, |
lib/std/zon/parse.zig+1| ... | ... | @@ -1213,6 +1213,7 @@ fn canParseTypeInner( |
| 1213 | 1213 | .frame, |
| 1214 | 1214 | .@"anyframe", |
| 1215 | 1215 | .@"opaque", |
| 1216 | .spirv, | |
| 1216 | 1217 | .comptime_int, |
| 1217 | 1218 | .comptime_float, |
| 1218 | 1219 | .enum_literal, |
src/Air.zig+7| ... | ... | @@ -918,6 +918,11 @@ pub const Inst = struct { |
| 918 | 918 | /// Uses the `ty` field. |
| 919 | 919 | c_va_start, |
| 920 | 920 | |
| 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 | 926 | /// Implements @workItemId builtin. |
| 922 | 927 | /// Result type is always `u32` |
| 923 | 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 | 1798 | .work_item_id, |
| 1794 | 1799 | .work_group_size, |
| 1795 | 1800 | .work_group_id, |
| 1801 | .spirv_runtime_array_len, | |
| 1796 | 1802 | => return .u32, |
| 1797 | 1803 | |
| 1798 | 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 | 2062 | .work_group_size, |
| 2057 | 2063 | .work_group_id, |
| 2058 | 2064 | .legalize_vec_elem_val, |
| 2065 | .spirv_runtime_array_len, | |
| 2059 | 2066 | => false, |
| 2060 | 2067 | |
| 2061 | 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 | 908 | .legalize_vec_elem_val, |
| 909 | 909 | .legalize_vec_store_elem, |
| 910 | 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 | 673 | const extra = a.air.extraData(Air.UnionInit, inst_datas[@intFromEnum(inst)].ty_pl.payload).data; |
| 674 | 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 | 677 | const extra = a.air.extraData(Air.StructField, inst_datas[@intFromEnum(inst)].ty_pl.payload).data; |
| 678 | 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 | 191 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 192 | 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 | 195 | const ty_pl = data[@intFromEnum(inst)].ty_pl; |
| 196 | 196 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 197 | 197 | try self.verifyInstOperands(inst, .{ extra.struct_operand, .none, .none }); |
src/Air/print.zig+1| ... | ... | @@ -306,6 +306,7 @@ const Writer = struct { |
| 306 | 306 | |
| 307 | 307 | .struct_field_ptr => try w.writeStructField(s, inst), |
| 308 | 308 | .struct_field_val => try w.writeStructField(s, inst), |
| 309 | .spirv_runtime_array_len => try w.writeStructField(s, inst), | |
| 309 | 310 | .inferred_alloc => @panic("TODO"), |
| 310 | 311 | .inferred_alloc_comptime => @panic("TODO"), |
| 311 | 312 | .assembly => try w.writeAssembly(s, inst), |
src/InternPool.zig+112-1| ... | ... | @@ -1983,6 +1983,7 @@ pub const Key = union(enum) { |
| 1983 | 1983 | union_type: ContainerType, |
| 1984 | 1984 | opaque_type: ContainerType, |
| 1985 | 1985 | enum_type: ContainerType, |
| 1986 | spirv_type: SpirvType, | |
| 1986 | 1987 | func_type: FuncType, |
| 1987 | 1988 | error_set_type: ErrorSetType, |
| 1988 | 1989 | /// The payload is the function body, either a `func_decl` or `func_instance`. |
| ... | ... | @@ -2146,6 +2147,13 @@ pub const Key = union(enum) { |
| 2146 | 2147 | }; |
| 2147 | 2148 | }; |
| 2148 | 2149 | |
| 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 | 2157 | pub const FuncType = struct { |
| 2150 | 2158 | param_types: Index.Slice, |
| 2151 | 2159 | return_type: Index, |
| ... | ... | @@ -2590,6 +2598,7 @@ pub const Key = union(enum) { |
| 2590 | 2598 | .opt_type, |
| 2591 | 2599 | .anyframe_type, |
| 2592 | 2600 | .error_union_type, |
| 2601 | .spirv_type, | |
| 2593 | 2602 | .simple_type, |
| 2594 | 2603 | .simple_value, |
| 2595 | 2604 | .opt, |
| ... | ... | @@ -2841,6 +2850,10 @@ pub const Key = union(enum) { |
| 2841 | 2850 | const b_info = b.error_union_type; |
| 2842 | 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 | 2857 | .simple_type => |a_info| { |
| 2845 | 2858 | const b_info = b.simple_type; |
| 2846 | 2859 | return a_info == b_info; |
| ... | ... | @@ -3130,6 +3143,7 @@ pub const Key = union(enum) { |
| 3130 | 3143 | .simple_type, |
| 3131 | 3144 | .struct_type, |
| 3132 | 3145 | .union_type, |
| 3146 | .spirv_type, | |
| 3133 | 3147 | .opaque_type, |
| 3134 | 3148 | .enum_type, |
| 3135 | 3149 | .tuple_type, |
| ... | ... | @@ -3877,6 +3891,14 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType { |
| 3877 | 3891 | }; |
| 3878 | 3892 | } |
| 3879 | 3893 | |
| 3894 | pub 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 | ||
| 3880 | 3902 | pub const Item = struct { |
| 3881 | 3903 | tag: Tag, |
| 3882 | 3904 | /// The doc comments on the respective Tag explain how to interpret this. |
| ... | ... | @@ -4214,6 +4236,8 @@ pub const Index = enum(u32) { |
| 4214 | 4236 | type_enum_nonexhaustive: struct { data: *Tag.TypeEnum }, |
| 4215 | 4237 | type_opaque: struct { data: *Tag.TypeOpaque }, |
| 4216 | 4238 | |
| 4239 | type_spirv: struct { data: *Tag.TypeSpirv }, | |
| 4240 | ||
| 4217 | 4241 | undef: DataIsIndex, |
| 4218 | 4242 | simple_value: void, |
| 4219 | 4243 | ptr_nav: struct { data: *PtrNav }, |
| ... | ... | @@ -4841,6 +4865,10 @@ pub const Tag = enum(u8) { |
| 4841 | 4865 | /// data is extra index of `TypeEnum`. |
| 4842 | 4866 | type_enum_nonexhaustive, |
| 4843 | 4867 | |
| 4868 | /// An spirv type. | |
| 4869 | /// data is index of `TypeSpirv` in extra. | |
| 4870 | type_spirv, | |
| 4871 | ||
| 4844 | 4872 | /// An opaque type. |
| 4845 | 4873 | /// data is extra index of `TypeOpaque`. |
| 4846 | 4874 | type_opaque, |
| ... | ... | @@ -5231,6 +5259,7 @@ pub const Tag = enum(u8) { |
| 5231 | 5259 | }, |
| 5232 | 5260 | .type_enum_explicit = enum_explicit_encoding, |
| 5233 | 5261 | .type_enum_nonexhaustive = enum_explicit_encoding, |
| 5262 | .type_spirv = .{ .summary = .@"{.payload.name%summary#\"}", .payload = Tag.TypeSpirv }, | |
| 5234 | 5263 | .type_opaque = .{ |
| 5235 | 5264 | .summary = .@"{.payload.name%summary#\"}", |
| 5236 | 5265 | .payload = TypeOpaque, |
| ... | ... | @@ -5688,6 +5717,34 @@ pub const Tag = enum(u8) { |
| 5688 | 5717 | name_nav: Nav.Index.Optional, |
| 5689 | 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 | }; |
| 5692 | 5749 | |
| 5693 | 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 | 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 | 6641 | .type_opaque => .{ .opaque_type = ns: { |
| 6577 | 6642 | const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data); |
| 6578 | 6643 | break :ns .{ .declared = .{ |
| ... | ... | @@ -7345,6 +7410,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: |
| 7345 | 7410 | .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType |
| 7346 | 7411 | .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType |
| 7347 | 7412 | .opaque_type => unreachable, // instead use: getDeclaredOpaqueType |
| 7413 | .spirv_type => unreachable, // instead use: getSpirvType | |
| 7348 | 7414 | |
| 7349 | 7415 | .tuple_type => unreachable, // use getTupleType() instead |
| 7350 | 7416 | .func_type => unreachable, // use getFuncType() instead |
| ... | ... | @@ -8717,6 +8783,39 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT |
| 8717 | 8783 | } }; |
| 8718 | 8784 | } |
| 8719 | 8785 | |
| 8786 | pub 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 | ||
| 8720 | 8819 | pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { |
| 8721 | 8820 | /// The union type for which this enum is a generated tag. |
| 8722 | 8821 | union_type: Index, |
| ... | ... | @@ -9077,7 +9176,7 @@ pub fn getExtern( |
| 9077 | 9176 | }) catch unreachable; // capacity asserted above |
| 9078 | 9177 | const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) { |
| 9079 | 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 | 9180 | } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined }; |
| 9082 | 9181 | const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{ |
| 9083 | 9182 | .ty = key.ty, |
| ... | ... | @@ -9810,6 +9909,7 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { |
| 9810 | 9909 | Tag.TypeStructPacked.Bits, |
| 9811 | 9910 | Tag.TypeUnionPacked.Bits, |
| 9812 | 9911 | Tag.TypeEnum.Bits, |
| 9912 | Tag.TypeSpirv.Flags, | |
| 9813 | 9913 | => @bitCast(@field(item, field_name)), |
| 9814 | 9914 | |
| 9815 | 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 | 9977 | Tag.TypeStructPacked.Bits, |
| 9878 | 9978 | Tag.TypeUnionPacked.Bits, |
| 9879 | 9979 | Tag.TypeEnum.Bits, |
| 9980 | Tag.TypeSpirv.Flags, | |
| 9880 | 9981 | => @bitCast(extra_item), |
| 9881 | 9982 | |
| 9882 | 9983 | else => @compileError("bad field type: " ++ @typeName(field_type)), |
| ... | ... | @@ -9930,6 +10031,11 @@ pub fn childType(ip: *const InternPool, i: Index) Index { |
| 9930 | 10031 | .vector_type => |vector_type| vector_type.child, |
| 9931 | 10032 | .array_type => |array_type| array_type.child, |
| 9932 | 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 | 10039 | else => unreachable, |
| 9934 | 10040 | }; |
| 9935 | 10041 | } |
| ... | ... | @@ -10583,6 +10689,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo |
| 10583 | 10689 | .type_optional => 0, |
| 10584 | 10690 | .type_anyframe => 0, |
| 10585 | 10691 | .type_error_union => @sizeOf(Key.ErrorUnionType), |
| 10692 | .type_spirv => @sizeOf(Tag.TypeSpirv) + @sizeOf(PackedU64), | |
| 10586 | 10693 | .type_anyerror_union => 0, |
| 10587 | 10694 | .type_error_set => b: { |
| 10588 | 10695 | const info = extraData(extra_list, Tag.ErrorSet, data); |
| ... | ... | @@ -10860,6 +10967,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { |
| 10860 | 10967 | .type_enum_explicit, |
| 10861 | 10968 | .type_enum_nonexhaustive, |
| 10862 | 10969 | .type_opaque, |
| 10970 | .type_spirv, | |
| 10863 | 10971 | .undef, |
| 10864 | 10972 | .ptr_nav, |
| 10865 | 10973 | .ptr_comptime_alloc, |
| ... | ... | @@ -11598,6 +11706,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { |
| 11598 | 11706 | .type_enum_explicit, |
| 11599 | 11707 | .type_enum_nonexhaustive, |
| 11600 | 11708 | .type_opaque, |
| 11709 | .type_spirv, | |
| 11601 | 11710 | => .type_type, |
| 11602 | 11711 | |
| 11603 | 11712 | .undef, |
| ... | ... | @@ -11955,6 +12064,8 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.lang.TypeId { |
| 11955 | 12064 | .type_opaque, |
| 11956 | 12065 | => .@"opaque", |
| 11957 | 12066 | |
| 12067 | .type_spirv => .spirv, | |
| 12068 | ||
| 11958 | 12069 | .type_function => .@"fn", |
| 11959 | 12070 | |
| 11960 | 12071 | // values, not types |
src/Sema.zig+442-1| ... | ... | @@ -1434,6 +1434,7 @@ fn analyzeBodyInner( |
| 1434 | 1434 | .reify_struct => try sema.zirReifyStruct( block, extended, inst), |
| 1435 | 1435 | .reify_union => try sema.zirReifyUnion( block, extended, inst), |
| 1436 | 1436 | .reify_enum => try sema.zirReifyEnum( block, extended, inst), |
| 1437 | .reify_spirv_type => try sema.zirReifySpirvType( block, extended, inst), | |
| 1437 | 1438 | // zig fmt: on |
| 1438 | 1439 | |
| 1439 | 1440 | .set_float_mode => { |
| ... | ... | @@ -9273,6 +9274,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9273 | 9274 | .noreturn, |
| 9274 | 9275 | .null, |
| 9275 | 9276 | .@"opaque", |
| 9277 | .spirv, | |
| 9276 | 9278 | .optional, |
| 9277 | 9279 | .type, |
| 9278 | 9280 | .undefined, |
| ... | ... | @@ -9348,6 +9350,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9348 | 9350 | .noreturn, |
| 9349 | 9351 | .null, |
| 9350 | 9352 | .@"opaque", |
| 9353 | .spirv, | |
| 9351 | 9354 | .optional, |
| 9352 | 9355 | .type, |
| 9353 | 9356 | .undefined, |
| ... | ... | @@ -15531,6 +15534,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15531 | 15534 | .undefined, |
| 15532 | 15535 | .null, |
| 15533 | 15536 | .@"opaque", |
| 15537 | .spirv, | |
| 15534 | 15538 | .type, |
| 15535 | 15539 | .enum_literal, |
| 15536 | 15540 | .comptime_float, |
| ... | ... | @@ -16834,6 +16838,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 16834 | 16838 | .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(), |
| 16835 | 16839 | }))); |
| 16836 | 16840 | }, |
| 16841 | .spirv => unreachable, // TODO: ALI | |
| 16837 | 16842 | .frame => return sema.failWithUseOfAsync(block, src), |
| 16838 | 16843 | .@"anyframe" => return sema.failWithUseOfAsync(block, src), |
| 16839 | 16844 | } |
| ... | ... | @@ -20449,6 +20454,306 @@ fn zirReifyEnum( |
| 20449 | 20454 | } |
| 20450 | 20455 | } |
| 20451 | 20456 | |
| 20457 | fn 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 | ||
| 20452 | 20757 | fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { |
| 20453 | 20758 | const pt = sema.pt; |
| 20454 | 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 | 25065 | .fn_attributes, => .@"Type.Fn.Attributes", |
| 24761 | 25066 | .container_layout => .@"Type.ContainerLayout", |
| 24762 | 25067 | .enum_mode => .@"Type.Enum.Mode", |
| 25068 | .spirv_type_options => .@"Type.Spirv", | |
| 24763 | 25069 | // zig fmt: on |
| 24764 | 25070 | |
| 24765 | 25071 | // Values are handled here. |
| ... | ... | @@ -24957,6 +25263,7 @@ fn explainWhyTypeIsComptime( |
| 24957 | 25263 | .void, |
| 24958 | 25264 | .@"enum", |
| 24959 | 25265 | .@"opaque", |
| 25266 | .spirv, | |
| 24960 | 25267 | .pointer, |
| 24961 | 25268 | => unreachable, // not comptime-only |
| 24962 | 25269 | |
| ... | ... | @@ -25043,6 +25350,7 @@ pub fn explainWhyTypeIsNotExtern( |
| 25043 | 25350 | .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), |
| 25044 | 25351 | |
| 25045 | 25352 | .@"opaque", |
| 25353 | .spirv, | |
| 25046 | 25354 | .bool, |
| 25047 | 25355 | .float, |
| 25048 | 25356 | .@"anyframe", |
| ... | ... | @@ -25483,9 +25791,13 @@ fn fieldPtrLoad( |
| 25483 | 25791 | ) CompileError!Air.Inst.Ref { |
| 25484 | 25792 | const pt = sema.pt; |
| 25485 | 25793 | const zcu = pt.zcu; |
| 25794 | const ip = &zcu.intern_pool; | |
| 25486 | 25795 | const object_ptr_ty = sema.typeOf(object_ptr); |
| 25487 | 25796 | assert(object_ptr_ty.zigTypeTag(zcu) == .pointer); |
| 25488 | 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 | 25801 | try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access); |
| 25490 | 25802 | if (try pointee_ty.onePossibleValue(pt)) |opv| { |
| 25491 | 25803 | const object: Air.Inst.Ref = .fromValue(opv); |
| ... | ... | @@ -25675,11 +25987,123 @@ fn fieldVal( |
| 25675 | 25987 | } else { |
| 25676 | 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 | 26001 | else => {}, |
| 25679 | 26002 | } |
| 25680 | 26003 | return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); |
| 25681 | 26004 | } |
| 25682 | 26005 | |
| 26006 | fn 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 | ||
| 25683 | 26107 | fn fieldPtr( |
| 25684 | 26108 | sema: *Sema, |
| 25685 | 26109 | block: *Block, |
| ... | ... | @@ -26540,6 +26964,7 @@ fn elemPtr( |
| 26540 | 26964 | .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), |
| 26541 | 26965 | .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), |
| 26542 | 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 | 26968 | else => { |
| 26544 | 26969 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); |
| 26545 | 26970 | try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access); |
| ... | ... | @@ -26964,6 +27389,22 @@ fn elemPtrVector( |
| 26964 | 27389 | return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty); |
| 26965 | 27390 | } |
| 26966 | 27391 | |
| 27392 | fn 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 | 27408 | /// Asserts that the layout of the array is already resolved. |
| 26968 | 27409 | fn elemPtrArray( |
| 26969 | 27410 | sema: *Sema, |
| ... | ... | @@ -31496,7 +31937,7 @@ const PeerResolveStrategy = enum { |
| 31496 | 31937 | |
| 31497 | 31938 | fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy { |
| 31498 | 31939 | return switch (ty.zigTypeTag(zcu)) { |
| 31499 | .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact, | |
| 31940 | .type, .void, .bool, .@"opaque", .spirv, .frame, .@"anyframe" => .exact, | |
| 31500 | 31941 | .noreturn, .undefined => .unknown, |
| 31501 | 31942 | .null => .nullable, |
| 31502 | 31943 | .comptime_int => .comptime_int, |
src/Sema/LowerZon.zig+2| ... | ... | @@ -244,6 +244,7 @@ fn checkTypeInner( |
| 244 | 244 | .frame, |
| 245 | 245 | .@"anyframe", |
| 246 | 246 | .@"opaque", |
| 247 | .spirv, | |
| 247 | 248 | => return self.failUnsupportedResultType(ty, null), |
| 248 | 249 | |
| 249 | 250 | .pointer => { |
| ... | ... | @@ -408,6 +409,7 @@ fn lowerExprKnownResTyInner( |
| 408 | 409 | .error_set, |
| 409 | 410 | .@"fn", |
| 410 | 411 | .@"opaque", |
| 412 | .spirv, | |
| 411 | 413 | .frame, |
| 412 | 414 | .@"anyframe", |
| 413 | 415 | .void, |
src/Sema/bitcast.zig+1| ... | ... | @@ -249,6 +249,7 @@ const UnpackValueBits = struct { |
| 249 | 249 | .tuple_type, |
| 250 | 250 | .union_type, |
| 251 | 251 | .opaque_type, |
| 252 | .spirv_type, | |
| 252 | 253 | .enum_type, |
| 253 | 254 | .func_type, |
| 254 | 255 | .error_set_type, |
src/Sema/comptime_ptr_access.zig+2| ... | ... | @@ -422,6 +422,7 @@ fn loadComptimePtrInner( |
| 422 | 422 | .undefined, |
| 423 | 423 | .enum_literal, |
| 424 | 424 | .@"opaque", |
| 425 | .spirv, | |
| 425 | 426 | .@"fn", |
| 426 | 427 | .error_union, |
| 427 | 428 | => unreachable, // ill-defined layout |
| ... | ... | @@ -854,6 +855,7 @@ fn prepareComptimePtrStore( |
| 854 | 855 | .undefined, |
| 855 | 856 | .enum_literal, |
| 856 | 857 | .@"opaque", |
| 858 | .spirv, | |
| 857 | 859 | .@"fn", |
| 858 | 860 | .error_union, |
| 859 | 861 | => 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 | 87 | .ptr_type, |
| 88 | 88 | .anyframe_type, |
| 89 | 89 | .simple_type, |
| 90 | .spirv_type, | |
| 90 | 91 | .opaque_type, |
| 91 | 92 | .error_set_type, |
| 92 | 93 | .inferred_error_set_type, |
| ... | ... | @@ -288,10 +289,12 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { |
| 288 | 289 | } |
| 289 | 290 | |
| 290 | 291 | // Resolve the layout of all fields, and check their types are allowed. |
| 292 | const fields_len = struct_obj.field_types.len; | |
| 291 | 293 | for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { |
| 292 | 294 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 293 | 295 | assert(!field_ty.isGenericPoison()); |
| 294 | 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 | 298 | try sema.ensureLayoutResolved(field_ty, field_ty_src, .field); |
| 296 | 299 | if (field_ty.zigTypeTag(zcu) == .@"opaque") { |
| 297 | 300 | return sema.failWithOwnedErrorMsg(&block, msg: { |
| ... | ... | @@ -302,6 +305,35 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { |
| 302 | 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 | 337 | if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) { |
| 306 | 338 | return sema.failWithOwnedErrorMsg(&block, msg: { |
| 307 | 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 | 445 | const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]); |
| 414 | 446 | const offset = resolved_field_aligns[field_idx].forward(cur_offset); |
| 415 | 447 | 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 | 453 | const struct_size: u32 = switch (class) { |
| 419 | 454 | .no_possible_value => 0, |
| ... | ... | @@ -858,6 +893,14 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { |
| 858 | 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 | 904 | if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) { |
| 862 | 905 | return sema.failWithOwnedErrorMsg(&block, msg: { |
| 863 | 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 | 170 | |
| 171 | 171 | .func_type => .fully_comptime, |
| 172 | 172 | |
| 173 | .spirv_type => if (cur_ty.isSpirvRuntimeArray(zcu)) .runtime else .no_possible_value, | |
| 173 | 174 | .opaque_type => .no_possible_value, |
| 174 | 175 | |
| 175 | 176 | .error_union_type => |eu| { |
| ... | ... | @@ -323,6 +324,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { |
| 323 | 324 | .error_set, |
| 324 | 325 | .@"fn", |
| 325 | 326 | .@"opaque", |
| 327 | .spirv, | |
| 326 | 328 | .@"anyframe", |
| 327 | 329 | .@"enum", |
| 328 | 330 | .enum_literal, |
| ... | ... | @@ -617,6 +619,10 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari |
| 617 | 619 | const name = ip.loadEnumType(ty.toIntern()).name; |
| 618 | 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 | 626 | .func_type => |fn_info| { |
| 621 | 627 | if (fn_info.is_noinline) { |
| 622 | 628 | try writer.writeAll("noinline "); |
| ... | ... | @@ -704,6 +710,14 @@ pub fn toIntern(ty: Type) InternPool.Index { |
| 704 | 710 | return ty.ip_index; |
| 705 | 711 | } |
| 706 | 712 | |
| 713 | pub 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 | ||
| 707 | 721 | pub fn toValue(self: Type) Value { |
| 708 | 722 | return .fromInterned(self.toIntern()); |
| 709 | 723 | } |
| ... | ... | @@ -751,6 +765,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool { |
| 751 | 765 | .error_set_type, |
| 752 | 766 | .inferred_error_set_type, |
| 753 | 767 | .tuple_type, |
| 768 | .spirv_type, | |
| 754 | 769 | .opaque_type, |
| 755 | 770 | .anyframe_type, |
| 756 | 771 | // These are function bodies, not function pointers. |
| ... | ... | @@ -1038,6 +1053,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { |
| 1038 | 1053 | } |
| 1039 | 1054 | }, |
| 1040 | 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 | 1057 | .opaque_type => .@"1", |
| 1042 | 1058 | |
| 1043 | 1059 | // values, not types |
| ... | ... | @@ -1183,6 +1199,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { |
| 1183 | 1199 | } |
| 1184 | 1200 | }, |
| 1185 | 1201 | .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu), |
| 1202 | .spirv_type => unreachable, | |
| 1186 | 1203 | .opaque_type => unreachable, |
| 1187 | 1204 | |
| 1188 | 1205 | // values, not types |
| ... | ... | @@ -1309,7 +1326,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 { |
| 1309 | 1326 | .tuple_type, |
| 1310 | 1327 | => ty.abiSize(zcu) * 8, |
| 1311 | 1328 | |
| 1312 | .opaque_type => unreachable, | |
| 1329 | .opaque_type, .spirv_type => unreachable, | |
| 1313 | 1330 | |
| 1314 | 1331 | // values, not types |
| 1315 | 1332 | .undef, |
| ... | ... | @@ -1511,14 +1528,17 @@ pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type { |
| 1511 | 1528 | /// * `[]T` |
| 1512 | 1529 | /// * `[*]T` |
| 1513 | 1530 | /// * `[*c]T` |
| 1531 | /// * `@SpirvType(.{ .runtime_array = T })` | |
| 1514 | 1532 | pub fn indexableElem(ty: Type, zcu: *const Zcu) Type { |
| 1515 | 1533 | const ip = &zcu.intern_pool; |
| 1516 | 1534 | return switch (ip.indexToKey(ty.toIntern())) { |
| 1517 | 1535 | inline .array_type, .vector_type => |arr| .fromInterned(arr.child), |
| 1536 | .spirv_type => ty.childType(zcu), | |
| 1518 | 1537 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { |
| 1519 | 1538 | .many, .slice, .c => .fromInterned(ptr_type.child), |
| 1520 | 1539 | .one => switch (ip.indexToKey(ptr_type.child)) { |
| 1521 | 1540 | inline .array_type, .vector_type => |arr| .fromInterned(arr.child), |
| 1541 | .spirv_type => Type.fromInterned(ptr_type.child).childType(zcu), | |
| 1522 | 1542 | else => unreachable, |
| 1523 | 1543 | }, |
| 1524 | 1544 | }, |
| ... | ... | @@ -1864,6 +1884,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { |
| 1864 | 1884 | .func_type => unreachable, |
| 1865 | 1885 | .simple_type => unreachable, // handled via Index enum tag above |
| 1866 | 1886 | |
| 1887 | .spirv_type => unreachable, | |
| 1867 | 1888 | .opaque_type => unreachable, |
| 1868 | 1889 | |
| 1869 | 1890 | // values, not types |
| ... | ... | @@ -2032,6 +2053,7 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value { |
| 2032 | 2053 | .error_set_type, |
| 2033 | 2054 | .inferred_error_set_type, |
| 2034 | 2055 | .opaque_type, |
| 2056 | .spirv_type, | |
| 2035 | 2057 | => null, |
| 2036 | 2058 | |
| 2037 | 2059 | .simple_type => |t| switch (t) { |
| ... | ... | @@ -2219,10 +2241,12 @@ pub fn isIndexable(ty: Type, zcu: *const Zcu) bool { |
| 2219 | 2241 | .one => switch (ty.childType(zcu).zigTypeTag(zcu)) { |
| 2220 | 2242 | .array, .vector => true, |
| 2221 | 2243 | .@"struct" => ty.childType(zcu).isTuple(zcu), |
| 2244 | .spirv => ty.childType(zcu).isSpirvRuntimeArray(zcu), | |
| 2222 | 2245 | else => false, |
| 2223 | 2246 | }, |
| 2224 | 2247 | }, |
| 2225 | 2248 | .@"struct" => ty.isTuple(zcu), |
| 2249 | .spirv => ty.isSpirvRuntimeArray(zcu), | |
| 2226 | 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 | 2868 | .slice, .many, .c => .fromInterned(ptr_info.child), |
| 2845 | 2869 | .one => switch (ip.indexToKey(ptr_info.child)) { |
| 2846 | 2870 | .array_type => |array_type| .fromInterned(array_type.child), |
| 2871 | .spirv_type => Type.fromInterned(ptr_info.child).childType(zcu), | |
| 2847 | 2872 | else => unreachable, |
| 2848 | 2873 | }, |
| 2849 | 2874 | }; |
| ... | ... | @@ -3095,6 +3120,7 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason { |
| 3095 | 3120 | |
| 3096 | 3121 | .noreturn, |
| 3097 | 3122 | .@"opaque", |
| 3123 | .spirv, | |
| 3098 | 3124 | .error_union, |
| 3099 | 3125 | .error_set, |
| 3100 | 3126 | .frame, |
| ... | ... | @@ -3170,6 +3196,7 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool |
| 3170 | 3196 | .noreturn => position == .ret_ty, |
| 3171 | 3197 | |
| 3172 | 3198 | .@"opaque", |
| 3199 | .spirv, | |
| 3173 | 3200 | .bool, |
| 3174 | 3201 | .float, |
| 3175 | 3202 | .@"anyframe", |
| ... | ... | @@ -3261,6 +3288,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void { |
| 3261 | 3288 | .simple_type, |
| 3262 | 3289 | .opaque_type, |
| 3263 | 3290 | .error_set_type, |
| 3291 | .spirv_type, | |
| 3264 | 3292 | .inferred_error_set_type, |
| 3265 | 3293 | => {}, |
| 3266 | 3294 | .func_type => |func_type| { |
| ... | ... | @@ -3362,6 +3390,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn |
| 3362 | 3390 | .union_type, |
| 3363 | 3391 | .opaque_type, |
| 3364 | 3392 | .enum_type, |
| 3393 | .spirv_type, | |
| 3365 | 3394 | .simple_type, |
| 3366 | 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 | 2046 | |
| 2047 | 2047 | const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu); |
| 2048 | 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 | 2053 | // No refinement can happen - this pointer is presumably invalid. |
| 2051 | 2054 | // Just offset it. |
| 2052 | 2055 | const parent = try arena.create(PointerDeriveStep); |
| ... | ... | @@ -2078,6 +2081,7 @@ pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, op |
| 2078 | 2081 | .undefined, |
| 2079 | 2082 | .enum_literal, |
| 2080 | 2083 | .@"opaque", |
| 2084 | .spirv, | |
| 2081 | 2085 | .@"fn", |
| 2082 | 2086 | .error_union, |
| 2083 | 2087 | .int, |
| ... | ... | @@ -2229,6 +2233,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe |
| 2229 | 2233 | .null, |
| 2230 | 2234 | .@"fn", |
| 2231 | 2235 | .@"opaque", |
| 2236 | .spirv, | |
| 2232 | 2237 | .enum_literal, |
| 2233 | 2238 | => comptime unreachable, // comptime-only or otherwise impossible |
| 2234 | 2239 | |
| ... | ... | @@ -2332,6 +2337,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory |
| 2332 | 2337 | .null, |
| 2333 | 2338 | .@"fn", |
| 2334 | 2339 | .@"opaque", |
| 2340 | .spirv, | |
| 2335 | 2341 | .enum_literal, |
| 2336 | 2342 | => comptime unreachable, // comptime-only or otherwise impossible |
| 2337 | 2343 |
src/Zcu.zig+4-1| ... | ... | @@ -468,6 +468,7 @@ pub const StdLangDecl = enum { |
| 468 | 468 | @"Type.Struct.FieldAttributes", |
| 469 | 469 | @"Type.ContainerLayout", |
| 470 | 470 | @"Type.Opaque", |
| 471 | @"Type.Spirv", | |
| 471 | 472 | |
| 472 | 473 | panic, |
| 473 | 474 | @"panic.call", |
| ... | ... | @@ -548,6 +549,7 @@ pub const StdLangDecl = enum { |
| 548 | 549 | .@"Type.Struct.FieldAttributes", |
| 549 | 550 | .@"Type.ContainerLayout", |
| 550 | 551 | .@"Type.Opaque", |
| 552 | .@"Type.Spirv", | |
| 551 | 553 | => .type, |
| 552 | 554 | |
| 553 | 555 | .panic => .type, |
| ... | ... | @@ -601,7 +603,7 @@ pub const StdLangDecl = enum { |
| 601 | 603 | .VaList => .va_list, |
| 602 | 604 | .assembly, .@"assembly.Clobbers" => .assembly, |
| 603 | 605 | else => { |
| 604 | if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Opaque")) { | |
| 606 | if (@intFromEnum(decl) <= @intFromEnum(StdLangDecl.@"Type.Spirv")) { | |
| 605 | 607 | return .main; |
| 606 | 608 | } else { |
| 607 | 609 | return .panic; |
| ... | ... | @@ -2740,6 +2742,7 @@ pub const LazySrcLoc = struct { |
| 2740 | 2742 | .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node, |
| 2741 | 2743 | .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node, |
| 2742 | 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 | 2746 | else => unreachable, |
| 2744 | 2747 | }, |
| 2745 | 2748 | else => unreachable, |
src/codegen.zig+1| ... | ... | @@ -329,6 +329,7 @@ pub fn generateSymbol( |
| 329 | 329 | .tuple_type, |
| 330 | 330 | .union_type, |
| 331 | 331 | .opaque_type, |
| 332 | .spirv_type, | |
| 332 | 333 | .enum_type, |
| 333 | 334 | .func_type, |
| 334 | 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 | 255 | .work_item_id, |
| 256 | 256 | .work_group_size, |
| 257 | 257 | .work_group_id, |
| 258 | .spirv_runtime_array_len, | |
| 258 | 259 | => unreachable, |
| 259 | 260 | .ret_ptr => { |
| 260 | 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 | 7492 | } |
| 7492 | 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 | 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 | 62 | .null, |
| 63 | 63 | .@"fn", |
| 64 | 64 | .@"opaque", |
| 65 | .spirv, | |
| 65 | 66 | .enum_literal, |
| 66 | 67 | .array, |
| 67 | 68 | => unreachable, |
src/codegen/arm/abi.zig+1| ... | ... | @@ -113,6 +113,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 113 | 113 | .null, |
| 114 | 114 | .@"fn", |
| 115 | 115 | .@"opaque", |
| 116 | .spirv, | |
| 116 | 117 | .enum_literal, |
| 117 | 118 | .array, |
| 118 | 119 | => unreachable, |
src/codegen/c.zig+3| ... | ... | @@ -902,6 +902,7 @@ pub const DeclGen = struct { |
| 902 | 902 | .tuple_type, |
| 903 | 903 | .union_type, |
| 904 | 904 | .opaque_type, |
| 905 | .spirv_type, | |
| 905 | 906 | .enum_type, |
| 906 | 907 | .func_type, |
| 907 | 908 | .error_set_type, |
| ... | ... | @@ -1565,6 +1566,7 @@ pub const DeclGen = struct { |
| 1565 | 1566 | }, |
| 1566 | 1567 | .anyframe_type, |
| 1567 | 1568 | .opaque_type, |
| 1569 | .spirv_type, | |
| 1568 | 1570 | .func_type, |
| 1569 | 1571 | => unreachable, |
| 1570 | 1572 | |
| ... | ... | @@ -2877,6 +2879,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 2877 | 2879 | .work_item_id, |
| 2878 | 2880 | .work_group_size, |
| 2879 | 2881 | .work_group_id, |
| 2882 | .spirv_runtime_array_len, | |
| 2880 | 2883 | => unreachable, |
| 2881 | 2884 | |
| 2882 | 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 | 249 | .null, |
| 250 | 250 | .enum_literal, |
| 251 | 251 | .@"opaque", |
| 252 | .spirv, | |
| 252 | 253 | .noreturn, |
| 253 | 254 | .void, |
| 254 | 255 | => return .void, |
| ... | ... | @@ -865,6 +866,7 @@ pub const CType = union(enum) { |
| 865 | 866 | switch (ty.zigTypeTag(zcu)) { |
| 866 | 867 | .frame => unreachable, |
| 867 | 868 | .@"anyframe" => unreachable, |
| 869 | .spirv => unreachable, | |
| 868 | 870 | |
| 869 | 871 | .type => try w.writeAll("type"), |
| 870 | 872 | .void => try w.writeAll("void"), |
| ... | ... | @@ -988,6 +990,7 @@ pub const CType = union(enum) { |
| 988 | 990 | .anyframe_type, |
| 989 | 991 | .simple_type, |
| 990 | 992 | .opaque_type, |
| 993 | .spirv_type, | |
| 991 | 994 | .error_set_type, |
| 992 | 995 | .inferred_error_set_type, |
| 993 | 996 | => true, |
src/codegen/llvm.zig+3-1| ... | ... | @@ -2662,6 +2662,7 @@ pub const Object = struct { |
| 2662 | 2662 | }, |
| 2663 | 2663 | .frame => @panic("TODO implement lowerDebugType for Frame types"), |
| 2664 | 2664 | .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"), |
| 2665 | .spirv => unreachable, | |
| 2665 | 2666 | } |
| 2666 | 2667 | } |
| 2667 | 2668 | |
| ... | ... | @@ -3375,7 +3376,7 @@ pub const Object = struct { |
| 3375 | 3376 | ); |
| 3376 | 3377 | return ty; |
| 3377 | 3378 | }, |
| 3378 | .opaque_type => unreachable, // no runtime bits | |
| 3379 | .opaque_type, .spirv_type => unreachable, // no runtime bits | |
| 3379 | 3380 | .enum_type => try o.lowerType(t.intTagType(zcu)), |
| 3380 | 3381 | .func_type => |func_type| try o.lowerFnType(t, func_type), |
| 3381 | 3382 | .error_set_type, .inferred_error_set_type => try o.errorIntType(), |
| ... | ... | @@ -3497,6 +3498,7 @@ pub const Object = struct { |
| 3497 | 3498 | .tuple_type, |
| 3498 | 3499 | .union_type, |
| 3499 | 3500 | .opaque_type, |
| 3501 | .spirv_type, | |
| 3500 | 3502 | .enum_type, |
| 3501 | 3503 | .func_type, |
| 3502 | 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 | 434 | .work_item_id => try self.airWorkItemId(inst), |
| 435 | 435 | .work_group_size => try self.airWorkGroupSize(inst), |
| 436 | 436 | .work_group_id => try self.airWorkGroupId(inst), |
| 437 | .spirv_runtime_array_len => unreachable, | |
| 437 | 438 | |
| 438 | 439 | // Instructions that are known to always be `noreturn` based on their tag. |
| 439 | 440 | .br => return self.airBr(inst), |
| ... | ... | @@ -7255,6 +7256,7 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool { |
| 7255 | 7256 | .undefined, |
| 7256 | 7257 | .null, |
| 7257 | 7258 | .@"opaque", |
| 7259 | .spirv, | |
| 7258 | 7260 | => unreachable, |
| 7259 | 7261 | |
| 7260 | 7262 | .noreturn, |
src/codegen/mips/abi.zig+1| ... | ... | @@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 77 | 77 | .null, |
| 78 | 78 | .@"fn", |
| 79 | 79 | .@"opaque", |
| 80 | .spirv, | |
| 80 | 81 | .enum_literal, |
| 81 | 82 | .array, |
| 82 | 83 | => unreachable, |
src/codegen/riscv64/CodeGen.zig+1| ... | ... | @@ -1642,6 +1642,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1642 | 1642 | .work_item_id => unreachable, |
| 1643 | 1643 | .work_group_size => unreachable, |
| 1644 | 1644 | .work_group_id => unreachable, |
| 1645 | .spirv_runtime_array_len => unreachable, | |
| 1645 | 1646 | // zig fmt: on |
| 1646 | 1647 | } |
| 1647 | 1648 |
src/codegen/riscv64/abi.zig+1| ... | ... | @@ -87,6 +87,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { |
| 87 | 87 | .null, |
| 88 | 88 | .@"fn", |
| 89 | 89 | .@"opaque", |
| 90 | .spirv, | |
| 90 | 91 | .enum_literal, |
| 91 | 92 | .array, |
| 92 | 93 | => unreachable, |
src/codegen/sparc64/CodeGen.zig+1| ... | ... | @@ -709,6 +709,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 709 | 709 | .work_item_id => unreachable, |
| 710 | 710 | .work_group_size => unreachable, |
| 711 | 711 | .work_group_id => unreachable, |
| 712 | .spirv_runtime_array_len => unreachable, | |
| 712 | 713 | // zig fmt: on |
| 713 | 714 | } |
| 714 | 715 |
src/codegen/spirv/Assembler.zig+1-1| ... | ... | @@ -269,7 +269,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue { |
| 269 | 269 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 270 | 270 | const ids = try cg.id_scratch.addManyAsSlice(gpa, operands[1..].len); |
| 271 | 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 | 274 | .OpTypeImage => blk: { |
| 275 | 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 | 271 | .vulkan, .opengl => { |
| 272 | 272 | if (ty.zigTypeTag(zcu) == .@"struct") { |
| 273 | 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 | 281 | else => {}, |
| 276 | 282 | } |
| 277 | 283 | } |
| ... | ... | @@ -313,6 +319,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 313 | 319 | try cg.module.debugName(result_id, nav.fqn.toSlice(ip)); |
| 314 | 320 | }, |
| 315 | 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 | 334 | const ty_id = try cg.resolveType(ty, .indirect); |
| 317 | 335 | const ptr_ty_id = try cg.module.ptrType(ty_id, .function); |
| 318 | 336 | |
| ... | ... | @@ -360,6 +378,21 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 360 | 378 | cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len; |
| 361 | 379 | } |
| 362 | 380 | |
| 381 | fn 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 | ||
| 363 | 396 | pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error { |
| 364 | 397 | @branchHint(.cold); |
| 365 | 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 | 812 | .tuple_type, |
| 780 | 813 | .union_type, |
| 781 | 814 | .opaque_type, |
| 815 | .spirv_type, | |
| 782 | 816 | .enum_type, |
| 783 | 817 | .func_type, |
| 784 | 818 | .error_set_type, |
| ... | ... | @@ -1065,16 +1099,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1065 | 1099 | const parent_ptr_id = try cg.derivePtr(oac.parent.*); |
| 1066 | 1100 | const parent_ptr_ty = try oac.parent.ptrType(pt); |
| 1067 | 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); | |
| 1069 | 1102 | |
| 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 | 1104 | // Vector element ptr accesses are derived as offset_and_cast. |
| 1072 | 1105 | // 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 | } | |
| 1078 | 1114 | } |
| 1079 | 1115 | |
| 1080 | 1116 | if (oac.byte_offset == 0) { |
| ... | ... | @@ -1269,7 +1305,6 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id { |
| 1269 | 1305 | const result_id = try cg.module.structType( |
| 1270 | 1306 | member_types[0..layout.total_fields], |
| 1271 | 1307 | member_names[0..layout.total_fields], |
| 1272 | null, | |
| 1273 | 1308 | .none, |
| 1274 | 1309 | ); |
| 1275 | 1310 | |
| ... | ... | @@ -1450,7 +1485,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1450 | 1485 | return try cg.module.structType( |
| 1451 | 1486 | &.{ ptr_ty_id, size_ty_id }, |
| 1452 | 1487 | &.{ "ptr", "len" }, |
| 1453 | null, | |
| 1454 | 1488 | .none, |
| 1455 | 1489 | ); |
| 1456 | 1490 | }, |
| ... | ... | @@ -1472,7 +1506,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1472 | 1506 | const result_id = try cg.module.structType( |
| 1473 | 1507 | member_types[0..member_index], |
| 1474 | 1508 | null, |
| 1475 | null, | |
| 1476 | 1509 | .none, |
| 1477 | 1510 | ); |
| 1478 | 1511 | const type_name = try cg.resolveTypeName(ty); |
| ... | ... | @@ -1494,9 +1527,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1494 | 1527 | var member_names = std.array_list.Managed([]const u8).init(gpa); |
| 1495 | 1528 | defer member_names.deinit(); |
| 1496 | 1529 | |
| 1497 | var member_offsets = std.array_list.Managed(u32).init(gpa); | |
| 1498 | defer member_offsets.deinit(); | |
| 1499 | ||
| 1500 | 1530 | var it = struct_type.iterateRuntimeOrder(ip); |
| 1501 | 1531 | while (it.next()) |field_index| { |
| 1502 | 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 | 1535 | const field_name = struct_type.field_names.get(ip)[field_index]; |
| 1506 | 1536 | try member_types.append(try cg.resolveType(field_ty, .indirect)); |
| 1507 | 1537 | try member_names.append(field_name.toSlice(ip)); |
| 1508 | try member_offsets.append(@intCast(ty.structFieldOffset(field_index, zcu))); | |
| 1509 | 1538 | } |
| 1510 | 1539 | |
| 1511 | 1540 | const result_id = try cg.module.structType( |
| 1512 | 1541 | member_types.items, |
| 1513 | 1542 | member_names.items, |
| 1514 | member_offsets.items, | |
| 1515 | 1543 | ty.toIntern(), |
| 1516 | 1544 | ); |
| 1517 | 1545 | |
| ... | ... | @@ -1541,7 +1569,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1541 | 1569 | return try cg.module.structType( |
| 1542 | 1570 | &.{ payload_ty_id, bool_ty_id }, |
| 1543 | 1571 | &.{ "payload", "valid" }, |
| 1544 | null, | |
| 1545 | 1572 | .none, |
| 1546 | 1573 | ); |
| 1547 | 1574 | }, |
| ... | ... | @@ -1576,7 +1603,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1576 | 1603 | // TODO: ABI padding? |
| 1577 | 1604 | } |
| 1578 | 1605 | |
| 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 | 1608 | .@"opaque" => { |
| 1582 | 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 | 1611 | defer gpa.free(type_name); |
| 1585 | 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 | }, | |
| 1587 | 1685 | |
| 1588 | 1686 | .null, |
| 1589 | 1687 | .undefined, |
| ... | ... | @@ -2421,7 +2519,6 @@ fn generateTestEntryPoint( |
| 2421 | 2519 | const buffer_struct_ty_id = try cg.module.structType( |
| 2422 | 2520 | &.{anyerror_ty_id}, |
| 2423 | 2521 | &.{"error_out"}, |
| 2424 | null, | |
| 2425 | 2522 | .none, |
| 2426 | 2523 | ); |
| 2427 | 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 | 2805 | .memcpy => return cg.airMemcpy(inst), |
| 2709 | 2806 | .memmove => return cg.airMemmove(inst), |
| 2710 | 2807 | |
| 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), | |
| 2718 | 2816 | |
| 2719 | 2817 | .set_union_tag => return cg.airSetUnionTag(inst), |
| 2720 | 2818 | .get_union_tag => try cg.airGetUnionTag(inst), |
| ... | ... | @@ -4305,6 +4403,22 @@ fn airSliceField(cg: *CodeGen, inst: Air.Inst.Index, field: u32) !?Id { |
| 4305 | 4403 | return try cg.extractField(field_ty, operand_id, field); |
| 4306 | 4404 | } |
| 4307 | 4405 | |
| 4406 | fn 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 | ||
| 4308 | 4422 | fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4309 | 4423 | const zcu = cg.module.zcu; |
| 4310 | 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 | 5998 | .struct_type, |
| 5885 | 5999 | .union_type, |
| 5886 | 6000 | .opaque_type, |
| 6001 | .spirv_type, | |
| 5887 | 6002 | .enum_type, |
| 5888 | 6003 | .func_type, |
| 5889 | 6004 | .error_set_type, |
src/codegen/spirv/Module.zig+72-18| ... | ... | @@ -70,6 +70,8 @@ cache: struct { |
| 70 | 70 | |
| 71 | 71 | bool_const: [2]?Id = .{ null, null }, |
| 72 | 72 | constants: std.ArrayHashMapUnmanaged(Constant, Id, Constant.HashContext, true) = .empty, |
| 73 | ||
| 74 | spirv_types: std.AutoHashMapUnmanaged(InternPool.Index, Id) = .empty, | |
| 73 | 75 | } = .{}, |
| 74 | 76 | /// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module". |
| 75 | 77 | sections: struct { |
| ... | ... | @@ -229,6 +231,7 @@ pub fn deinit(module: *Module) void { |
| 229 | 231 | module.cache.array_types.deinit(module.gpa); |
| 230 | 232 | module.cache.struct_types.deinit(module.gpa); |
| 231 | 233 | module.cache.fn_types.deinit(module.gpa); |
| 234 | module.cache.spirv_types.deinit(module.gpa); | |
| 232 | 235 | module.cache.capabilities.deinit(module.gpa); |
| 233 | 236 | module.cache.extensions.deinit(module.gpa); |
| 234 | 237 | module.cache.extended_instruction_set.deinit(module.gpa); |
| ... | ... | @@ -683,10 +686,8 @@ pub fn structType( |
| 683 | 686 | module: *Module, |
| 684 | 687 | types: []const Id, |
| 685 | 688 | maybe_names: ?[]const []const u8, |
| 686 | maybe_offsets: ?[]const u32, | |
| 687 | 689 | ip_index: InternPool.Index, |
| 688 | 690 | ) !Id { |
| 689 | const target = module.zcu.getTarget(); | |
| 690 | 691 | const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index; |
| 691 | 692 | |
| 692 | 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 | 705 | } |
| 705 | 706 | } |
| 706 | 707 | |
| 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 | 708 | try module.cache.struct_types.put( |
| 724 | 709 | module.gpa, |
| 725 | 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 | 732 | return result_id; |
| 748 | 733 | } |
| 749 | 734 | |
| 735 | pub 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 | ||
| 747 | pub 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 | ||
| 778 | pub 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 | ||
| 791 | pub 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 | ||
| 750 | 804 | pub fn constant(module: *Module, ty_id: Id, value: spec.LiteralContextDependentNumber) !Id { |
| 751 | 805 | const gop = try module.cache.constants.getOrPut(module.gpa, .{ .ty = ty_id, .value = value }); |
| 752 | 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 | 1195 | .undefined, |
| 1196 | 1196 | .null, |
| 1197 | 1197 | .@"opaque", |
| 1198 | .spirv, | |
| 1198 | 1199 | => unreachable, |
| 1199 | 1200 | |
| 1200 | 1201 | .noreturn, |
| ... | ... | @@ -1882,6 +1883,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1882 | 1883 | .work_item_id, |
| 1883 | 1884 | .work_group_size, |
| 1884 | 1885 | .work_group_id, |
| 1886 | .spirv_runtime_array_len, | |
| 1885 | 1887 | => unreachable, |
| 1886 | 1888 | }; |
| 1887 | 1889 | } |
| ... | ... | @@ -4698,6 +4700,7 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue { |
| 4698 | 4700 | .tuple_type, |
| 4699 | 4701 | .union_type, |
| 4700 | 4702 | .opaque_type, |
| 4703 | .spirv_type, | |
| 4701 | 4704 | .enum_type, |
| 4702 | 4705 | .func_type, |
| 4703 | 4706 | .error_set_type, |
src/codegen/wasm/abi.zig+1| ... | ... | @@ -77,6 +77,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { |
| 77 | 77 | .null, |
| 78 | 78 | .@"fn", |
| 79 | 79 | .@"opaque", |
| 80 | .spirv, | |
| 80 | 81 | .enum_literal, |
| 81 | 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 | 173719 | // No soft-float `Legalize` features are enabled, so this instruction never appears. |
| 173720 | 173720 | .legalize_compiler_rt_call => unreachable, |
| 173721 | 173721 | |
| 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 | 173724 | try cg.resetTemps(@enumFromInt(0)); |
| 173725 | 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 | 164 | .null, |
| 165 | 165 | .@"fn", |
| 166 | 166 | .@"opaque", |
| 167 | .spirv, | |
| 167 | 168 | .enum_literal, |
| 168 | 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 | 196 | .null, |
| 197 | 197 | .error_set, |
| 198 | 198 | .@"opaque", |
| 199 | .spirv, | |
| 199 | 200 | .frame, |
| 200 | 201 | .@"anyframe", |
| 201 | 202 | .enum_literal, |
| ... | ... | @@ -241,6 +242,7 @@ fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Al |
| 241 | 242 | .null, |
| 242 | 243 | .error_set, |
| 243 | 244 | .@"opaque", |
| 245 | .spirv, | |
| 244 | 246 | .frame, |
| 245 | 247 | .@"anyframe", |
| 246 | 248 | .enum_literal, |
src/link/Dwarf.zig+2| ... | ... | @@ -3047,6 +3047,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo |
| 3047 | 3047 | .func_type, |
| 3048 | 3048 | .error_set_type, |
| 3049 | 3049 | .inferred_error_set_type, |
| 3050 | .spirv_type, | |
| 3050 | 3051 | => .alias, |
| 3051 | 3052 | |
| 3052 | 3053 | .struct_type => tag: { |
| ... | ... | @@ -3533,6 +3534,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co |
| 3533 | 3534 | switch (value_ip_key) { |
| 3534 | 3535 | .func => unreachable, // handled above |
| 3535 | 3536 | .@"extern" => unreachable, // handled above |
| 3537 | .spirv_type => unreachable, | |
| 3536 | 3538 | |
| 3537 | 3539 | .int_type => |int_type| { |
| 3538 | 3540 | try wip_nav.abbrevCode(.numeric_type); |
src/print_value.zig+1| ... | ... | @@ -60,6 +60,7 @@ pub fn print( |
| 60 | 60 | .union_type, |
| 61 | 61 | .opaque_type, |
| 62 | 62 | .enum_type, |
| 63 | .spirv_type, | |
| 63 | 64 | .func_type, |
| 64 | 65 | .error_set_type, |
| 65 | 66 | .inferred_error_set_type, |
src/print_zir.zig+10| ... | ... | @@ -685,6 +685,16 @@ const Writer = struct { |
| 685 | 685 | defer self.parent_decl_node = prev_parent_decl_node; |
| 686 | 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 | }, | |
| 688 | 698 | |
| 689 | 699 | .cmpxchg => try self.writeCmpxchg(stream, extended), |
| 690 | 700 | .ptr_cast_full => try self.writePtrCastFull(stream, extended), |
test/behavior.zig+4| ... | ... | @@ -112,6 +112,10 @@ test { |
| 112 | 112 | _ = @import("behavior/wasm.zig"); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | if (builtin.zig_backend == .stage2_spirv) { | |
| 116 | _ = @import("behavior/spirv.zig"); | |
| 117 | } | |
| 118 | ||
| 115 | 119 | if (builtin.zig_backend != .stage2_spirv and builtin.os.tag != .wasi) { |
| 116 | 120 | _ = @import("behavior/asm.zig"); |
| 117 | 121 | } |
test/behavior/spirv.zig created+47| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | const Sampler = @SpirvType(.sampler); | |
| 2 | const 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 | } }); | |
| 11 | const SampledImage = @SpirvType(.{ .sampled_image = Image }); | |
| 12 | const 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 | } }); | |
| 21 | const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); | |
| 22 | ||
| 23 | const RuntimeArrayBuf = extern struct { e: RuntimeArray }; | |
| 24 | ||
| 25 | const sampler = @extern(*addrspace(.constant) const Sampler, .{ | |
| 26 | .name = "sampler", | |
| 27 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, | |
| 28 | }); | |
| 29 | const sampled_image = @extern(*addrspace(.constant) const SampledImage, .{ | |
| 30 | .name = "sampled_image", | |
| 31 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, | |
| 32 | }); | |
| 33 | const storage_image = @extern(*addrspace(.constant) const StorageImage, .{ | |
| 34 | .name = "storage_image", | |
| 35 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } }, | |
| 36 | }); | |
| 37 | const runtime_array = @extern(*addrspace(.storage_buffer) const RuntimeArrayBuf, .{ | |
| 38 | .name = "runtime_array", | |
| 39 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 3 } }, | |
| 40 | }); | |
| 41 | ||
| 42 | test "@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 | 253 | try expect(typeinfo_info == .@"union"); |
| 254 | 254 | try expect(typeinfo_info.@"union".layout == .auto); |
| 255 | 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 | 257 | try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_types.len); |
| 258 | 258 | try expect(typeinfo_info.@"union".field_names.len == typeinfo_info.@"union".field_attrs.len); |
| 259 | 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); | |
| 261 | 261 | |
| 262 | 262 | const TestNoTagUnion = union { |
| 263 | 263 | Foo: void, |
test/cases/compile_errors/SpirvType_is_a_compile_error_in_non-SPIRV_targets.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | comptime { | |
| 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 @@ |
| 1 | comptime { | |
| 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 | ||
| 13 | comptime { | |
| 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 | ||
| 25 | comptime { | |
| 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 | ||
| 37 | comptime { | |
| 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 @@ |
| 1 | const Sampler = @SpirvType(.sampler); | |
| 2 | const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); | |
| 3 | const Foo = struct { | |
| 4 | s: Sampler, | |
| 5 | }; | |
| 6 | const Baz = struct { | |
| 7 | a: RuntimeArray, | |
| 8 | }; | |
| 9 | const Qux = extern struct { | |
| 10 | a: RuntimeArray, | |
| 11 | b: u32, | |
| 12 | }; | |
| 13 | export fn a() void { | |
| 14 | var foo: Foo = undefined; | |
| 15 | _ = &foo; | |
| 16 | } | |
| 17 | export fn c() void { | |
| 18 | var baz: Baz = undefined; | |
| 19 | _ = &baz; | |
| 20 | } | |
| 21 | export 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 |