| ... | @@ -41,6 +41,8 @@ const SpvTypeInfo = struct { | ... | @@ -41,6 +41,8 @@ const SpvTypeInfo = struct { |
| 41 | | 41 | |
| 42 | const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, SpvTypeInfo); | 42 | const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, SpvTypeInfo); |
| 43 | | 43 | |
| | 44 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult); |
| | 45 | |
| 44 | const ControlFlow = union(enum) { | 46 | const ControlFlow = union(enum) { |
| 45 | const Structured = struct { | 47 | const Structured = struct { |
| 46 | /// This type indicates the way that a block is terminated. The | 48 | /// This type indicates the way that a block is terminated. The |
| ... | @@ -171,6 +173,8 @@ pub const Object = struct { | ... | @@ -171,6 +173,8 @@ pub const Object = struct { |
| 171 | /// of the SPIR-V module. | 173 | /// of the SPIR-V module. |
| 172 | type_map: TypeMap = .{}, | 174 | type_map: TypeMap = .{}, |
| 173 | | 175 | |
| | 176 | intern_map: InternMap = .{}, |
| | 177 | |
| 174 | pub fn init(gpa: Allocator) Object { | 178 | pub fn init(gpa: Allocator) Object { |
| 175 | return .{ | 179 | return .{ |
| 176 | .gpa = gpa, | 180 | .gpa = gpa, |
| ... | @@ -183,6 +187,7 @@ pub const Object = struct { | ... | @@ -183,6 +187,7 @@ pub const Object = struct { |
| 183 | self.decl_link.deinit(self.gpa); | 187 | self.decl_link.deinit(self.gpa); |
| 184 | self.anon_decl_link.deinit(self.gpa); | 188 | self.anon_decl_link.deinit(self.gpa); |
| 185 | self.type_map.deinit(self.gpa); | 189 | self.type_map.deinit(self.gpa); |
| | 190 | self.intern_map.deinit(self.gpa); |
| 186 | } | 191 | } |
| 187 | | 192 | |
| 188 | fn genDecl( | 193 | fn genDecl( |
| ... | @@ -205,6 +210,7 @@ pub const Object = struct { | ... | @@ -205,6 +210,7 @@ pub const Object = struct { |
| 205 | .air = air, | 210 | .air = air, |
| 206 | .liveness = liveness, | 211 | .liveness = liveness, |
| 207 | .type_map = &self.type_map, | 212 | .type_map = &self.type_map, |
| | 213 | .intern_map = &self.intern_map, |
| 208 | .control_flow = switch (structured_cfg) { | 214 | .control_flow = switch (structured_cfg) { |
| 209 | true => .{ .structured = .{} }, | 215 | true => .{ .structured = .{} }, |
| 210 | false => .{ .unstructured = .{} }, | 216 | false => .{ .unstructured = .{} }, |
| ... | @@ -313,6 +319,8 @@ const DeclGen = struct { | ... | @@ -313,6 +319,8 @@ const DeclGen = struct { |
| 313 | /// See Object.type_map | 319 | /// See Object.type_map |
| 314 | type_map: *TypeMap, | 320 | type_map: *TypeMap, |
| 315 | | 321 | |
| | 322 | intern_map: *InternMap, |
| | 323 | |
| 316 | /// Child types of pointers that are currently in progress of being resolved. If a pointer | 324 | /// Child types of pointers that are currently in progress of being resolved. If a pointer |
| 317 | /// is already in this map, its recursive. | 325 | /// is already in this map, its recursive. |
| 318 | wip_pointers: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, CacheRef) = .{}, | 326 | wip_pointers: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, CacheRef) = .{}, |
| ... | @@ -696,14 +704,25 @@ const DeclGen = struct { | ... | @@ -696,14 +704,25 @@ const DeclGen = struct { |
| 696 | | 704 | |
| 697 | /// Emits a bool constant in a particular representation. | 705 | /// Emits a bool constant in a particular representation. |
| 698 | fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef { | 706 | fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef { |
| | 707 | // TODO: Cache? |
| | 708 | |
| | 709 | const section = &self.spv.sections.types_globals_constants; |
| 699 | switch (repr) { | 710 | switch (repr) { |
| 700 | .indirect => { | 711 | .indirect => { |
| 701 | const int_ty_ref = try self.intType(.unsigned, 1); | 712 | return try self.constInt(Type.u1, @intFromBool(value), .indirect); |
| 702 | return self.constInt(int_ty_ref, @intFromBool(value)); | | |
| 703 | }, | 713 | }, |
| 704 | .direct => { | 714 | .direct => { |
| 705 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); | 715 | const result_ty_id = try self.resolveType2(Type.bool, .direct); |
| 706 | return self.spv.constBool(bool_ty_ref, value); | 716 | const result_id = self.spv.allocId(); |
| | 717 | const operands = .{ |
| | 718 | .id_result_type = result_ty_id, |
| | 719 | .id_result = result_id, |
| | 720 | }; |
| | 721 | switch (value) { |
| | 722 | true => try section.emit(self.spv.gpa, .OpConstantTrue, operands), |
| | 723 | false => try section.emit(self.spv.gpa, .OpConstantFalse, operands), |
| | 724 | } |
| | 725 | return result_id; |
| 707 | }, | 726 | }, |
| 708 | } | 727 | } |
| 709 | } | 728 | } |
| ... | @@ -711,68 +730,63 @@ const DeclGen = struct { | ... | @@ -711,68 +730,63 @@ const DeclGen = struct { |
| 711 | /// Emits an integer constant. | 730 | /// Emits an integer constant. |
| 712 | /// This function, unlike SpvModule.constInt, takes care to bitcast | 731 | /// This function, unlike SpvModule.constInt, takes care to bitcast |
| 713 | /// the value to an unsigned int first for Kernels. | 732 | /// the value to an unsigned int first for Kernels. |
| 714 | fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef { | 733 | fn constInt(self: *DeclGen, ty: Type, value: anytype, repr: Repr) !IdRef { |
| 715 | switch (self.spv.cache.lookup(ty_ref)) { | 734 | // TODO: Cache? |
| 716 | .vector_type => |vec_type| { | 735 | const mod = self.module; |
| 717 | const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); | 736 | const scalar_ty = ty.scalarType(mod); |
| 718 | defer self.gpa.free(elem_ids); | 737 | const int_info = scalar_ty.intInfo(mod); |
| 719 | const int_value = try self.constInt(vec_type.component_type, value); | 738 | // Use backing bits so that negatives are sign extended |
| 720 | @memset(elem_ids, int_value); | 739 | const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int |
| 721 | | 740 | |
| 722 | const constituents_id = self.spv.allocId(); | 741 | const bits: u64 = switch (int_info.signedness) { |
| 723 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ | 742 | // Intcast needed to silence compile errors for when the wrong path is compiled. |
| 724 | .id_result_type = self.typeId(ty_ref), | 743 | // Lazy fix. |
| 725 | .id_result = constituents_id, | 744 | .signed => @bitCast(@as(i64, @intCast(value))), |
| 726 | .constituents = elem_ids, | 745 | .unsigned => @as(u64, @intCast(value)), |
| 727 | }); | 746 | }; |
| 728 | return constituents_id; | | |
| 729 | }, | | |
| 730 | else => {}, | | |
| 731 | } | | |
| 732 | | 747 | |
| 733 | if (value < 0) { | 748 | // Manually truncate the value to the right amount of bits. |
| 734 | const ty = self.spv.cache.lookup(ty_ref).int_type; | 749 | const truncated_bits = if (backing_bits == 64) |
| 735 | // Manually truncate the value so that the resulting value | 750 | bits |
| 736 | // fits within the unsigned type. | 751 | else |
| 737 | const bits: u64 = @bitCast(@as(i64, @intCast(value))); | 752 | bits & (@as(u64, 1) << @intCast(backing_bits)) - 1; |
| 738 | const truncated_bits = if (ty.bits == 64) | 753 | |
| 739 | bits | 754 | const result_ty_id = try self.resolveType2(scalar_ty, repr); |
| 740 | else | 755 | const result_id = self.spv.allocId(); |
| 741 | bits & (@as(u64, 1) << @intCast(ty.bits)) - 1; | 756 | |
| 742 | return try self.spv.constInt(ty_ref, truncated_bits); | 757 | const section = &self.spv.sections.types_globals_constants; |
| 743 | } else { | 758 | switch (backing_bits) { |
| 744 | return try self.spv.constInt(ty_ref, value); | 759 | 0 => unreachable, // u0 is comptime |
| | 760 | 1...32 => try section.emit(self.spv.gpa, .OpConstant, .{ |
| | 761 | .id_result_type = result_ty_id, |
| | 762 | .id_result = result_id, |
| | 763 | .value = .{ .uint32 = @truncate(truncated_bits) }, |
| | 764 | }), |
| | 765 | 33...64 => try section.emit(self.spv.gpa, .OpConstant, .{ |
| | 766 | .id_result_type = result_ty_id, |
| | 767 | .id_result = result_id, |
| | 768 | .value = .{ .uint64 = truncated_bits }, |
| | 769 | }), |
| | 770 | else => unreachable, // TODO: Large integer constants |
| 745 | } | 771 | } |
| 746 | } | | |
| 747 | | 772 | |
| 748 | /// Emits a float constant | 773 | if (!ty.isVector(mod)) { |
| 749 | fn constFloat(self: *DeclGen, ty_ref: CacheRef, value: f128) !IdRef { | 774 | return result_id; |
| 750 | switch (self.spv.cache.lookup(ty_ref)) { | | |
| 751 | .vector_type => |vec_type| { | | |
| 752 | const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); | | |
| 753 | defer self.gpa.free(elem_ids); | | |
| 754 | const int_value = try self.constFloat(vec_type.component_type, value); | | |
| 755 | @memset(elem_ids, int_value); | | |
| 756 | | | |
| 757 | const constituents_id = self.spv.allocId(); | | |
| 758 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ | | |
| 759 | .id_result_type = self.typeId(ty_ref), | | |
| 760 | .id_result = constituents_id, | | |
| 761 | .constituents = elem_ids, | | |
| 762 | }); | | |
| 763 | return constituents_id; | | |
| 764 | }, | | |
| 765 | else => {}, | | |
| 766 | } | 775 | } |
| 767 | | 776 | |
| 768 | const ty = self.spv.cache.lookup(ty_ref).float_type; | 777 | const n = ty.vectorLen(mod); |
| 769 | return switch (ty.bits) { | 778 | const ids = try self.gpa.alloc(IdRef, n); |
| 770 | 16 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float16 = @floatCast(value) } } }), | 779 | defer self.gpa.free(ids); |
| 771 | 32 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float32 = @floatCast(value) } } }), | 780 | @memset(ids, result_id); |
| 772 | 64 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float64 = @floatCast(value) } } }), | 781 | |
| 773 | 80, 128 => unreachable, // TODO | 782 | const vec_ty_id = try self.resolveType2(ty, repr); |
| 774 | else => unreachable, | 783 | const vec_result_id = self.spv.allocId(); |
| 775 | }; | 784 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| | 785 | .id_result_type = vec_ty_id, |
| | 786 | .id_result = vec_result_id, |
| | 787 | .constituents = ids, |
| | 788 | }); |
| | 789 | return vec_result_id; |
| 776 | } | 790 | } |
| 777 | | 791 | |
| 778 | /// Construct a struct at runtime. | 792 | /// Construct a struct at runtime. |
| ... | @@ -852,258 +866,279 @@ const DeclGen = struct { | ... | @@ -852,258 +866,279 @@ const DeclGen = struct { |
| 852 | /// is done by emitting a sequence of instructions that initialize the value. | 866 | /// is done by emitting a sequence of instructions that initialize the value. |
| 853 | // | 867 | // |
| 854 | /// This function should only be called during function code generation. | 868 | /// This function should only be called during function code generation. |
| 855 | fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef { | 869 | fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef { |
| | 870 | // Note: Using intern_map can only be used with constants that DO NOT generate any runtime code!! |
| | 871 | // Ideally that should be all constants in the future, or it should be cleaned up somehow. For |
| | 872 | // now, only use the intern_map on case-by-case basis by breaking to :cache. |
| | 873 | if (self.intern_map.get(.{ val.toIntern(), repr })) |id| { |
| | 874 | return id; |
| | 875 | } |
| | 876 | |
| 856 | const mod = self.module; | 877 | const mod = self.module; |
| 857 | const target = self.getTarget(); | 878 | const target = self.getTarget(); |
| 858 | const result_ty_ref = try self.resolveType(ty, repr); | 879 | const result_ty_ref = try self.resolveType(ty, repr); |
| | 880 | const result_ty_id = self.typeId(result_ty_ref); |
| 859 | const ip = &mod.intern_pool; | 881 | const ip = &mod.intern_pool; |
| 860 | | 882 | |
| 861 | const val = arg_val; | 883 | log.debug("lowering constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(mod) }); |
| 862 | | | |
| 863 | log.debug("constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(mod) }); | | |
| 864 | if (val.isUndefDeep(mod)) { | 884 | if (val.isUndefDeep(mod)) { |
| 865 | return self.spv.constUndef(result_ty_ref); | 885 | return self.spv.constUndef(result_ty_id); |
| 866 | } | 886 | } |
| 867 | | 887 | |
| 868 | switch (ip.indexToKey(val.toIntern())) { | 888 | const section = &self.spv.sections.types_globals_constants; |
| 869 | .int_type, | 889 | |
| 870 | .ptr_type, | 890 | const cacheable_id = cache: { |
| 871 | .array_type, | 891 | switch (ip.indexToKey(val.toIntern())) { |
| 872 | .vector_type, | 892 | .int_type, |
| 873 | .opt_type, | 893 | .ptr_type, |
| 874 | .anyframe_type, | 894 | .array_type, |
| 875 | .error_union_type, | 895 | .vector_type, |
| 876 | .simple_type, | 896 | .opt_type, |
| 877 | .struct_type, | 897 | .anyframe_type, |
| 878 | .anon_struct_type, | 898 | .error_union_type, |
| 879 | .union_type, | 899 | .simple_type, |
| 880 | .opaque_type, | 900 | .struct_type, |
| 881 | .enum_type, | 901 | .anon_struct_type, |
| 882 | .func_type, | 902 | .union_type, |
| 883 | .error_set_type, | 903 | .opaque_type, |
| 884 | .inferred_error_set_type, | 904 | .enum_type, |
| 885 | => unreachable, // types, not values | 905 | .func_type, |
| 886 | | 906 | .error_set_type, |
| 887 | .undef => unreachable, // handled above | 907 | .inferred_error_set_type, |
| 888 | | 908 | => unreachable, // types, not values |
| 889 | .variable, | 909 | |
| 890 | .extern_func, | 910 | .undef => unreachable, // handled above |
| 891 | .func, | 911 | |
| 892 | .enum_literal, | 912 | .variable, |
| 893 | .empty_enum_value, | 913 | .extern_func, |
| 894 | => unreachable, // non-runtime values | 914 | .func, |
| 895 | | 915 | .enum_literal, |
| 896 | .simple_value => |simple_value| switch (simple_value) { | 916 | .empty_enum_value, |
| 897 | .undefined, | | |
| 898 | .void, | | |
| 899 | .null, | | |
| 900 | .empty_struct, | | |
| 901 | .@"unreachable", | | |
| 902 | .generic_poison, | | |
| 903 | => unreachable, // non-runtime values | 917 | => unreachable, // non-runtime values |
| 904 | | 918 | |
| 905 | .false, .true => return try self.constBool(val.toBool(), repr), | 919 | .simple_value => |simple_value| switch (simple_value) { |
| 906 | }, | 920 | .undefined, |
| 907 | | 921 | .void, |
| 908 | .int => { | 922 | .null, |
| 909 | if (ty.isSignedInt(mod)) { | 923 | .empty_struct, |
| 910 | return try self.constInt(result_ty_ref, val.toSignedInt(mod)); | 924 | .@"unreachable", |
| 911 | } else { | 925 | .generic_poison, |
| 912 | return try self.constInt(result_ty_ref, val.toUnsignedInt(mod)); | 926 | => unreachable, // non-runtime values |
| 913 | } | | |
| 914 | }, | | |
| 915 | .float => return switch (ty.floatBits(target)) { | | |
| 916 | 16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }), | | |
| 917 | 32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }), | | |
| 918 | 64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }), | | |
| 919 | 80, 128 => unreachable, // TODO | | |
| 920 | else => unreachable, | | |
| 921 | }, | | |
| 922 | .err => |err| { | | |
| 923 | const value = try mod.getErrorValue(err.name); | | |
| 924 | return try self.constInt(result_ty_ref, value); | | |
| 925 | }, | | |
| 926 | .error_union => |error_union| { | | |
| 927 | // TODO: Error unions may be constructed with constant instructions if the payload type | | |
| 928 | // allows it. For now, just generate it here regardless. | | |
| 929 | const err_int_ty = try mod.errorIntType(); | | |
| 930 | const err_ty = switch (error_union.val) { | | |
| 931 | .err_name => ty.errorUnionSet(mod), | | |
| 932 | .payload => err_int_ty, | | |
| 933 | }; | | |
| 934 | const err_val = switch (error_union.val) { | | |
| 935 | .err_name => |err_name| Value.fromInterned((try mod.intern(.{ .err = .{ | | |
| 936 | .ty = ty.errorUnionSet(mod).toIntern(), | | |
| 937 | .name = err_name, | | |
| 938 | } }))), | | |
| 939 | .payload => try mod.intValue(err_int_ty, 0), | | |
| 940 | }; | | |
| 941 | const payload_ty = ty.errorUnionPayload(mod); | | |
| 942 | const eu_layout = self.errorUnionLayout(payload_ty); | | |
| 943 | if (!eu_layout.payload_has_bits) { | | |
| 944 | // We use the error type directly as the type. | | |
| 945 | return try self.constant(err_ty, err_val, .indirect); | | |
| 946 | } | | |
| 947 | | | |
| 948 | const payload_val = Value.fromInterned(switch (error_union.val) { | | |
| 949 | .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }), | | |
| 950 | .payload => |payload| payload, | | |
| 951 | }); | | |
| 952 | | | |
| 953 | var constituents: [2]IdRef = undefined; | | |
| 954 | var types: [2]Type = undefined; | | |
| 955 | if (eu_layout.error_first) { | | |
| 956 | constituents[0] = try self.constant(err_ty, err_val, .indirect); | | |
| 957 | constituents[1] = try self.constant(payload_ty, payload_val, .indirect); | | |
| 958 | types = .{ err_ty, payload_ty }; | | |
| 959 | } else { | | |
| 960 | constituents[0] = try self.constant(payload_ty, payload_val, .indirect); | | |
| 961 | constituents[1] = try self.constant(err_ty, err_val, .indirect); | | |
| 962 | types = .{ payload_ty, err_ty }; | | |
| 963 | } | | |
| 964 | | 927 | |
| 965 | return try self.constructStruct(ty, &types, &constituents); | 928 | .false, .true => break :cache try self.constBool(val.toBool(), repr), |
| 966 | }, | 929 | }, |
| 967 | .enum_tag => { | 930 | .int => { |
| 968 | const int_val = try val.intFromEnum(ty, mod); | 931 | if (ty.isSignedInt(mod)) { |
| 969 | const int_ty = ty.intTagType(mod); | 932 | break :cache try self.constInt(ty, val.toSignedInt(mod), repr); |
| 970 | return try self.constant(int_ty, int_val, repr); | | |
| 971 | }, | | |
| 972 | .ptr => return self.constantPtr(ty, val), | | |
| 973 | .slice => |slice| { | | |
| 974 | const ptr_ty = ty.slicePtrFieldType(mod); | | |
| 975 | const ptr_id = try self.constantPtr(ptr_ty, Value.fromInterned(slice.ptr)); | | |
| 976 | const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect); | | |
| 977 | return self.constructStruct( | | |
| 978 | ty, | | |
| 979 | &.{ ptr_ty, Type.usize }, | | |
| 980 | &.{ ptr_id, len_id }, | | |
| 981 | ); | | |
| 982 | }, | | |
| 983 | .opt => { | | |
| 984 | const payload_ty = ty.optionalChild(mod); | | |
| 985 | const maybe_payload_val = val.optionalValue(mod); | | |
| 986 | | | |
| 987 | if (!payload_ty.hasRuntimeBits(mod)) { | | |
| 988 | return try self.constBool(maybe_payload_val != null, .indirect); | | |
| 989 | } else if (ty.optionalReprIsPayload(mod)) { | | |
| 990 | // Optional representation is a nullable pointer or slice. | | |
| 991 | if (maybe_payload_val) |payload_val| { | | |
| 992 | return try self.constant(payload_ty, payload_val, .indirect); | | |
| 993 | } else { | 933 | } else { |
| 994 | const ptr_ty_ref = try self.resolveType(ty, .indirect); | 934 | break :cache try self.constInt(ty, val.toUnsignedInt(mod), repr); |
| 995 | return self.spv.constNull(ptr_ty_ref); | 935 | } |
| | 936 | }, |
| | 937 | .float => { |
| | 938 | const lit: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) { |
| | 939 | 16 => .{ .uint32 = @as(u16, @bitCast(val.toFloat(f16, mod))) }, |
| | 940 | 32 => .{ .float32 = val.toFloat(f32, mod) }, |
| | 941 | 64 => .{ .float64 = val.toFloat(f64, mod) }, |
| | 942 | 80, 128 => unreachable, // TODO |
| | 943 | else => unreachable, |
| | 944 | }; |
| | 945 | const result_id = self.spv.allocId(); |
| | 946 | try section.emit(self.spv.gpa, .OpConstant, .{ |
| | 947 | .id_result_type = result_ty_id, |
| | 948 | .id_result = result_id, |
| | 949 | .value = lit, |
| | 950 | }); |
| | 951 | break :cache result_id; |
| | 952 | }, |
| | 953 | .err => |err| { |
| | 954 | const value = try mod.getErrorValue(err.name); |
| | 955 | break :cache try self.constInt(ty, value, repr); |
| | 956 | }, |
| | 957 | .error_union => |error_union| { |
| | 958 | // TODO: Error unions may be constructed with constant instructions if the payload type |
| | 959 | // allows it. For now, just generate it here regardless. |
| | 960 | const err_int_ty = try mod.errorIntType(); |
| | 961 | const err_ty = switch (error_union.val) { |
| | 962 | .err_name => ty.errorUnionSet(mod), |
| | 963 | .payload => err_int_ty, |
| | 964 | }; |
| | 965 | const err_val = switch (error_union.val) { |
| | 966 | .err_name => |err_name| Value.fromInterned((try mod.intern(.{ .err = .{ |
| | 967 | .ty = ty.errorUnionSet(mod).toIntern(), |
| | 968 | .name = err_name, |
| | 969 | } }))), |
| | 970 | .payload => try mod.intValue(err_int_ty, 0), |
| | 971 | }; |
| | 972 | const payload_ty = ty.errorUnionPayload(mod); |
| | 973 | const eu_layout = self.errorUnionLayout(payload_ty); |
| | 974 | if (!eu_layout.payload_has_bits) { |
| | 975 | // We use the error type directly as the type. |
| | 976 | break :cache try self.constant(err_ty, err_val, .indirect); |
| 996 | } | 977 | } |
| 997 | } | | |
| 998 | | | |
| 999 | // Optional representation is a structure. | | |
| 1000 | // { Payload, Bool } | | |
| 1001 | | 978 | |
| 1002 | const has_pl_id = try self.constBool(maybe_payload_val != null, .indirect); | 979 | const payload_val = Value.fromInterned(switch (error_union.val) { |
| 1003 | const payload_id = if (maybe_payload_val) |payload_val| | 980 | .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }), |
| 1004 | try self.constant(payload_ty, payload_val, .indirect) | 981 | .payload => |payload| payload, |
| 1005 | else | 982 | }); |
| 1006 | try self.spv.constUndef(try self.resolveType(payload_ty, .indirect)); | | |
| 1007 | | 983 | |
| 1008 | return try self.constructStruct( | 984 | var constituents: [2]IdRef = undefined; |
| 1009 | ty, | 985 | var types: [2]Type = undefined; |
| 1010 | &.{ payload_ty, Type.bool }, | 986 | if (eu_layout.error_first) { |
| 1011 | &.{ payload_id, has_pl_id }, | 987 | constituents[0] = try self.constant(err_ty, err_val, .indirect); |
| 1012 | ); | 988 | constituents[1] = try self.constant(payload_ty, payload_val, .indirect); |
| 1013 | }, | 989 | types = .{ err_ty, payload_ty }; |
| 1014 | .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { | 990 | } else { |
| 1015 | inline .array_type, .vector_type => |array_type, tag| { | 991 | constituents[0] = try self.constant(payload_ty, payload_val, .indirect); |
| 1016 | const elem_ty = Type.fromInterned(array_type.child); | 992 | constituents[1] = try self.constant(err_ty, err_val, .indirect); |
| 1017 | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); | 993 | types = .{ payload_ty, err_ty }; |
| 1018 | | | |
| 1019 | const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod))); | | |
| 1020 | defer self.gpa.free(constituents); | | |
| 1021 | | | |
| 1022 | switch (aggregate.storage) { | | |
| 1023 | .bytes => |bytes| { | | |
| 1024 | // TODO: This is really space inefficient, perhaps there is a better | | |
| 1025 | // way to do it? | | |
| 1026 | for (bytes, 0..) |byte, i| { | | |
| 1027 | constituents[i] = try self.constInt(elem_ty_ref, byte); | | |
| 1028 | } | | |
| 1029 | }, | | |
| 1030 | .elems => |elems| { | | |
| 1031 | for (0..@as(usize, @intCast(array_type.len))) |i| { | | |
| 1032 | constituents[i] = try self.constant(elem_ty, Value.fromInterned(elems[i]), .indirect); | | |
| 1033 | } | | |
| 1034 | }, | | |
| 1035 | .repeated_elem => |elem| { | | |
| 1036 | const val_id = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); | | |
| 1037 | for (0..@as(usize, @intCast(array_type.len))) |i| { | | |
| 1038 | constituents[i] = val_id; | | |
| 1039 | } | | |
| 1040 | }, | | |
| 1041 | } | 994 | } |
| 1042 | | 995 | |
| 1043 | switch (tag) { | 996 | return try self.constructStruct(ty, &types, &constituents); |
| 1044 | inline .array_type => { | 997 | }, |
| 1045 | if (array_type.sentinel != .none) { | 998 | .enum_tag => { |
| 1046 | const sentinel = Value.fromInterned(array_type.sentinel); | 999 | const int_val = try val.intFromEnum(ty, mod); |
| 1047 | constituents[constituents.len - 1] = try self.constant(elem_ty, sentinel, .indirect); | 1000 | const int_ty = ty.intTagType(mod); |
| 1048 | } | 1001 | break :cache try self.constant(int_ty, int_val, repr); |
| 1049 | return self.constructArray(ty, constituents); | | |
| 1050 | }, | | |
| 1051 | inline .vector_type => return self.constructVector(ty, constituents), | | |
| 1052 | else => unreachable, | | |
| 1053 | } | | |
| 1054 | }, | 1002 | }, |
| 1055 | .struct_type => { | 1003 | .ptr => return self.constantPtr(ty, val), |
| 1056 | const struct_type = mod.typeToStruct(ty).?; | 1004 | .slice => |slice| { |
| 1057 | if (struct_type.layout == .@"packed") { | 1005 | const ptr_ty = ty.slicePtrFieldType(mod); |
| 1058 | return self.todo("packed struct constants", .{}); | 1006 | const ptr_id = try self.constantPtr(ptr_ty, Value.fromInterned(slice.ptr)); |
| | 1007 | const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect); |
| | 1008 | return self.constructStruct( |
| | 1009 | ty, |
| | 1010 | &.{ ptr_ty, Type.usize }, |
| | 1011 | &.{ ptr_id, len_id }, |
| | 1012 | ); |
| | 1013 | }, |
| | 1014 | .opt => { |
| | 1015 | const payload_ty = ty.optionalChild(mod); |
| | 1016 | const maybe_payload_val = val.optionalValue(mod); |
| | 1017 | |
| | 1018 | if (!payload_ty.hasRuntimeBits(mod)) { |
| | 1019 | break :cache try self.constBool(maybe_payload_val != null, .indirect); |
| | 1020 | } else if (ty.optionalReprIsPayload(mod)) { |
| | 1021 | // Optional representation is a nullable pointer or slice. |
| | 1022 | if (maybe_payload_val) |payload_val| { |
| | 1023 | return try self.constant(payload_ty, payload_val, .indirect); |
| | 1024 | } else { |
| | 1025 | break :cache try self.spv.constNull(result_ty_id); |
| | 1026 | } |
| 1059 | } | 1027 | } |
| 1060 | | 1028 | |
| 1061 | var types = std.ArrayList(Type).init(self.gpa); | 1029 | // Optional representation is a structure. |
| 1062 | defer types.deinit(); | 1030 | // { Payload, Bool } |
| 1063 | | 1031 | |
| 1064 | var constituents = std.ArrayList(IdRef).init(self.gpa); | 1032 | const has_pl_id = try self.constBool(maybe_payload_val != null, .indirect); |
| 1065 | defer constituents.deinit(); | 1033 | const payload_id = if (maybe_payload_val) |payload_val| |
| | 1034 | try self.constant(payload_ty, payload_val, .indirect) |
| | 1035 | else |
| | 1036 | try self.spv.constUndef(try self.resolveType2(payload_ty, .indirect)); |
| 1066 | | 1037 | |
| 1067 | var it = struct_type.iterateRuntimeOrder(ip); | 1038 | return try self.constructStruct( |
| 1068 | while (it.next()) |field_index| { | 1039 | ty, |
| 1069 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); | 1040 | &.{ payload_ty, Type.bool }, |
| 1070 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { | 1041 | &.{ payload_id, has_pl_id }, |
| 1071 | // This is a zero-bit field - we only needed it for the alignment. | 1042 | ); |
| 1072 | continue; | 1043 | }, |
| | 1044 | .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { |
| | 1045 | inline .array_type, .vector_type => |array_type, tag| { |
| | 1046 | const elem_ty = Type.fromInterned(array_type.child); |
| | 1047 | |
| | 1048 | const constituents = try self.gpa.alloc(IdRef, @as(u32, @intCast(ty.arrayLenIncludingSentinel(mod)))); |
| | 1049 | defer self.gpa.free(constituents); |
| | 1050 | |
| | 1051 | switch (aggregate.storage) { |
| | 1052 | .bytes => |bytes| { |
| | 1053 | // TODO: This is really space inefficient, perhaps there is a better |
| | 1054 | // way to do it? |
| | 1055 | for (bytes, 0..) |byte, i| { |
| | 1056 | constituents[i] = try self.constInt(elem_ty, byte, .indirect); |
| | 1057 | } |
| | 1058 | }, |
| | 1059 | .elems => |elems| { |
| | 1060 | for (0..@as(usize, @intCast(array_type.len))) |i| { |
| | 1061 | constituents[i] = try self.constant(elem_ty, Value.fromInterned(elems[i]), .indirect); |
| | 1062 | } |
| | 1063 | }, |
| | 1064 | .repeated_elem => |elem| { |
| | 1065 | const val_id = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); |
| | 1066 | for (0..@as(usize, @intCast(array_type.len))) |i| { |
| | 1067 | constituents[i] = val_id; |
| | 1068 | } |
| | 1069 | }, |
| 1073 | } | 1070 | } |
| 1074 | | 1071 | |
| 1075 | // TODO: Padding? | 1072 | switch (tag) { |
| 1076 | const field_val = try val.fieldValue(mod, field_index); | 1073 | inline .array_type => { |
| 1077 | const field_id = try self.constant(field_ty, field_val, .indirect); | 1074 | if (array_type.sentinel != .none) { |
| | 1075 | const sentinel = Value.fromInterned(array_type.sentinel); |
| | 1076 | constituents[constituents.len - 1] = try self.constant(elem_ty, sentinel, .indirect); |
| | 1077 | } |
| | 1078 | return self.constructArray(ty, constituents); |
| | 1079 | }, |
| | 1080 | inline .vector_type => return self.constructVector(ty, constituents), |
| | 1081 | else => unreachable, |
| | 1082 | } |
| | 1083 | }, |
| | 1084 | .struct_type => { |
| | 1085 | const struct_type = mod.typeToStruct(ty).?; |
| | 1086 | if (struct_type.layout == .@"packed") { |
| | 1087 | return self.todo("packed struct constants", .{}); |
| | 1088 | } |
| 1078 | | 1089 | |
| 1079 | try types.append(field_ty); | 1090 | var types = std.ArrayList(Type).init(self.gpa); |
| 1080 | try constituents.append(field_id); | 1091 | defer types.deinit(); |
| 1081 | } | 1092 | |
| | 1093 | var constituents = std.ArrayList(IdRef).init(self.gpa); |
| | 1094 | defer constituents.deinit(); |
| | 1095 | |
| | 1096 | var it = struct_type.iterateRuntimeOrder(ip); |
| | 1097 | while (it.next()) |field_index| { |
| | 1098 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); |
| | 1099 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| | 1100 | // This is a zero-bit field - we only needed it for the alignment. |
| | 1101 | continue; |
| | 1102 | } |
| | 1103 | |
| | 1104 | // TODO: Padding? |
| | 1105 | const field_val = try val.fieldValue(mod, field_index); |
| | 1106 | const field_id = try self.constant(field_ty, field_val, .indirect); |
| 1082 | | 1107 | |
| 1083 | return try self.constructStruct(ty, types.items, constituents.items); | 1108 | try types.append(field_ty); |
| | 1109 | try constituents.append(field_id); |
| | 1110 | } |
| | 1111 | |
| | 1112 | return try self.constructStruct(ty, types.items, constituents.items); |
| | 1113 | }, |
| | 1114 | .anon_struct_type => unreachable, // TODO |
| | 1115 | else => unreachable, |
| 1084 | }, | 1116 | }, |
| 1085 | .anon_struct_type => unreachable, // TODO | 1117 | .un => |un| { |
| 1086 | else => unreachable, | 1118 | const active_field = ty.unionTagFieldIndex(Value.fromInterned(un.tag), mod).?; |
| 1087 | }, | 1119 | const union_obj = mod.typeToUnion(ty).?; |
| 1088 | .un => |un| { | 1120 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[active_field]); |
| 1089 | const active_field = ty.unionTagFieldIndex(Value.fromInterned(un.tag), mod).?; | 1121 | const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 1090 | const union_obj = mod.typeToUnion(ty).?; | 1122 | try self.constant(field_ty, Value.fromInterned(un.val), .direct) |
| 1091 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[active_field]); | 1123 | else |
| 1092 | const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) | 1124 | null; |
| 1093 | try self.constant(field_ty, Value.fromInterned(un.val), .direct) | 1125 | return try self.unionInit(ty, active_field, payload); |
| 1094 | else | 1126 | }, |
| 1095 | null; | 1127 | .memoized_call => unreachable, |
| 1096 | return try self.unionInit(ty, active_field, payload); | 1128 | } |
| 1097 | }, | 1129 | }; |
| 1098 | .memoized_call => unreachable, | 1130 | |
| 1099 | } | 1131 | try self.intern_map.putNoClobber(self.gpa, .{ val.toIntern(), repr }, cacheable_id); |
| | 1132 | |
| | 1133 | return cacheable_id; |
| 1100 | } | 1134 | } |
| 1101 | | 1135 | |
| 1102 | fn constantPtr(self: *DeclGen, ptr_ty: Type, ptr_val: Value) Error!IdRef { | 1136 | fn constantPtr(self: *DeclGen, ptr_ty: Type, ptr_val: Value) Error!IdRef { |
| | 1137 | const result_ty_id = try self.resolveType2(ptr_ty, .direct); |
| 1103 | const result_ty_ref = try self.resolveType(ptr_ty, .direct); | 1138 | const result_ty_ref = try self.resolveType(ptr_ty, .direct); |
| 1104 | const mod = self.module; | 1139 | const mod = self.module; |
| 1105 | | 1140 | |
| 1106 | if (ptr_val.isUndef(mod)) return self.spv.constUndef(result_ty_ref); | 1141 | if (ptr_val.isUndef(mod)) return self.spv.constUndef(result_ty_id); |
| 1107 | | 1142 | |
| 1108 | switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { | 1143 | switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { |
| 1109 | .decl => |decl| return try self.constantDeclRef(ptr_ty, decl), | 1144 | .decl => |decl| return try self.constantDeclRef(ptr_ty, decl), |
| ... | @@ -1126,8 +1161,7 @@ const DeclGen = struct { | ... | @@ -1126,8 +1161,7 @@ const DeclGen = struct { |
| 1126 | .elem => |elem_ptr| { | 1161 | .elem => |elem_ptr| { |
| 1127 | const parent_ptr_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)); | 1162 | const parent_ptr_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)); |
| 1128 | const parent_ptr_id = try self.constantPtr(parent_ptr_ty, Value.fromInterned(elem_ptr.base)); | 1163 | const parent_ptr_id = try self.constantPtr(parent_ptr_ty, Value.fromInterned(elem_ptr.base)); |
| 1129 | const size_ty_ref = try self.sizeType(); | 1164 | const index_id = try self.constInt(Type.usize, elem_ptr.index, .direct); |
| 1130 | const index_id = try self.constInt(size_ty_ref, elem_ptr.index); | | |
| 1131 | | 1165 | |
| 1132 | const elem_ptr_id = try self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); | 1166 | const elem_ptr_id = try self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id); |
| 1133 | | 1167 | |
| ... | @@ -1181,7 +1215,7 @@ const DeclGen = struct { | ... | @@ -1181,7 +1215,7 @@ const DeclGen = struct { |
| 1181 | // const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; | 1215 | // const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; |
| 1182 | if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { | 1216 | if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { |
| 1183 | // Pointer to nothing - return undefoined | 1217 | // Pointer to nothing - return undefoined |
| 1184 | return self.spv.constUndef(ty_ref); | 1218 | return self.spv.constUndef(self.typeId(ty_ref)); |
| 1185 | } | 1219 | } |
| 1186 | | 1220 | |
| 1187 | if (decl_ty.zigTypeTag(mod) == .Fn) { | 1221 | if (decl_ty.zigTypeTag(mod) == .Fn) { |
| ... | @@ -1217,7 +1251,7 @@ const DeclGen = struct { | ... | @@ -1217,7 +1251,7 @@ const DeclGen = struct { |
| 1217 | .func => { | 1251 | .func => { |
| 1218 | // TODO: Properly lower function pointers. For now we are going to hack around it and | 1252 | // TODO: Properly lower function pointers. For now we are going to hack around it and |
| 1219 | // just generate an empty pointer. Function pointers are represented by a pointer to usize. | 1253 | // just generate an empty pointer. Function pointers are represented by a pointer to usize. |
| 1220 | return try self.spv.constUndef(ty_ref); | 1254 | return try self.spv.constUndef(ty_id); |
| 1221 | }, | 1255 | }, |
| 1222 | .extern_func => unreachable, // TODO | 1256 | .extern_func => unreachable, // TODO |
| 1223 | else => {}, | 1257 | else => {}, |
| ... | @@ -1225,7 +1259,7 @@ const DeclGen = struct { | ... | @@ -1225,7 +1259,7 @@ const DeclGen = struct { |
| 1225 | | 1259 | |
| 1226 | if (!decl.typeOf(mod).isFnOrHasRuntimeBitsIgnoreComptime(mod)) { | 1260 | if (!decl.typeOf(mod).isFnOrHasRuntimeBitsIgnoreComptime(mod)) { |
| 1227 | // Pointer to nothing - return undefined. | 1261 | // Pointer to nothing - return undefined. |
| 1228 | return self.spv.constUndef(ty_ref); | 1262 | return self.spv.constUndef(ty_id); |
| 1229 | } | 1263 | } |
| 1230 | | 1264 | |
| 1231 | const spv_decl_index = try self.object.resolveDecl(mod, decl_index); | 1265 | const spv_decl_index = try self.object.resolveDecl(mod, decl_index); |
| ... | @@ -1274,6 +1308,14 @@ const DeclGen = struct { | ... | @@ -1274,6 +1308,14 @@ const DeclGen = struct { |
| 1274 | return self.spv.resultId(type_ref); | 1308 | return self.spv.resultId(type_ref); |
| 1275 | } | 1309 | } |
| 1276 | | 1310 | |
| | 1311 | /// Turn a Zig type into a SPIR-V Type result-id. |
| | 1312 | /// This function represents the "new interface", where types handled only |
| | 1313 | /// with Type and IdResult, and CacheRef is not used. Prefer this for now. |
| | 1314 | fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) !IdResult { |
| | 1315 | const type_ref = try self.resolveType(ty, repr); |
| | 1316 | return self.typeId(type_ref); |
| | 1317 | } |
| | 1318 | |
| 1277 | fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef { | 1319 | fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef { |
| 1278 | return self.spv.resultId(ty_ref); | 1320 | return self.spv.resultId(ty_ref); |
| 1279 | } | 1321 | } |
| ... | @@ -1297,11 +1339,6 @@ const DeclGen = struct { | ... | @@ -1297,11 +1339,6 @@ const DeclGen = struct { |
| 1297 | return self.spv.intType(.unsigned, backing_bits); | 1339 | return self.spv.intType(.unsigned, backing_bits); |
| 1298 | } | 1340 | } |
| 1299 | | 1341 | |
| 1300 | /// Create an integer type that represents 'usize'. | | |
| 1301 | fn sizeType(self: *DeclGen) !CacheRef { | | |
| 1302 | return try self.intType(.unsigned, self.getTarget().ptrBitWidth()); | | |
| 1303 | } | | |
| 1304 | | | |
| 1305 | fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !CacheRef { | 1342 | fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !CacheRef { |
| 1306 | const key = .{ child_ty.toIntern(), storage_class }; | 1343 | const key = .{ child_ty.toIntern(), storage_class }; |
| 1307 | const entry = try self.wip_pointers.getOrPut(self.gpa, key); | 1344 | const entry = try self.wip_pointers.getOrPut(self.gpa, key); |
| ... | @@ -1367,7 +1404,7 @@ const DeclGen = struct { | ... | @@ -1367,7 +1404,7 @@ const DeclGen = struct { |
| 1367 | var member_types: [4]CacheRef = undefined; | 1404 | var member_types: [4]CacheRef = undefined; |
| 1368 | var member_names: [4]CacheString = undefined; | 1405 | var member_names: [4]CacheString = undefined; |
| 1369 | | 1406 | |
| 1370 | const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? | 1407 | const u8_ty_ref = try self.resolveType(Type.u8, .direct); // TODO: What if Int8Type is not enabled? |
| 1371 | | 1408 | |
| 1372 | if (layout.tag_size != 0) { | 1409 | if (layout.tag_size != 0) { |
| 1373 | const tag_ty_ref = try self.resolveType(Type.fromInterned(union_obj.enum_tag_ty), .indirect); | 1410 | const tag_ty_ref = try self.resolveType(Type.fromInterned(union_obj.enum_tag_ty), .indirect); |
| ... | @@ -1439,7 +1476,7 @@ const DeclGen = struct { | ... | @@ -1439,7 +1476,7 @@ const DeclGen = struct { |
| 1439 | }, | 1476 | }, |
| 1440 | .Bool => switch (repr) { | 1477 | .Bool => switch (repr) { |
| 1441 | .direct => return try self.spv.resolve(.bool_type), | 1478 | .direct => return try self.spv.resolve(.bool_type), |
| 1442 | .indirect => return try self.intType(.unsigned, 1), | 1479 | .indirect => return try self.resolveType(Type.u1, .indirect), |
| 1443 | }, | 1480 | }, |
| 1444 | .Int => { | 1481 | .Int => { |
| 1445 | const int_info = ty.intInfo(mod); | 1482 | const int_info = ty.intInfo(mod); |
| ... | @@ -1548,7 +1585,7 @@ const DeclGen = struct { | ... | @@ -1548,7 +1585,7 @@ const DeclGen = struct { |
| 1548 | .indirect => { | 1585 | .indirect => { |
| 1549 | // TODO: Represent function pointers properly. | 1586 | // TODO: Represent function pointers properly. |
| 1550 | // For now, just use an usize type. | 1587 | // For now, just use an usize type. |
| 1551 | return try self.sizeType(); | 1588 | return try self.resolveType(Type.usize, .indirect); |
| 1552 | }, | 1589 | }, |
| 1553 | }, | 1590 | }, |
| 1554 | .Pointer => { | 1591 | .Pointer => { |
| ... | @@ -1564,7 +1601,7 @@ const DeclGen = struct { | ... | @@ -1564,7 +1601,7 @@ const DeclGen = struct { |
| 1564 | return ptr_ty_ref; | 1601 | return ptr_ty_ref; |
| 1565 | } | 1602 | } |
| 1566 | | 1603 | |
| 1567 | const size_ty_ref = try self.sizeType(); | 1604 | const size_ty_ref = try self.resolveType(Type.usize, .direct); |
| 1568 | return self.spv.resolve(.{ .struct_type = .{ | 1605 | return self.spv.resolve(.{ .struct_type = .{ |
| 1569 | .member_types = &.{ ptr_ty_ref, size_ty_ref }, | 1606 | .member_types = &.{ ptr_ty_ref, size_ty_ref }, |
| 1570 | .member_names = &.{ | 1607 | .member_names = &.{ |
| ... | @@ -1680,7 +1717,7 @@ const DeclGen = struct { | ... | @@ -1680,7 +1717,7 @@ const DeclGen = struct { |
| 1680 | return ty_ref; | 1717 | return ty_ref; |
| 1681 | }, | 1718 | }, |
| 1682 | .Union => return try self.resolveUnionType(ty), | 1719 | .Union => return try self.resolveUnionType(ty), |
| 1683 | .ErrorSet => return try self.intType(.unsigned, 16), | 1720 | .ErrorSet => return try self.resolveType(Type.u16, repr), |
| 1684 | .ErrorUnion => { | 1721 | .ErrorUnion => { |
| 1685 | const payload_ty = ty.errorUnionPayload(mod); | 1722 | const payload_ty = ty.errorUnionPayload(mod); |
| 1686 | const error_ty_ref = try self.resolveType(Type.anyerror, .indirect); | 1723 | const error_ty_ref = try self.resolveType(Type.anyerror, .indirect); |
| ... | @@ -2202,12 +2239,12 @@ const DeclGen = struct { | ... | @@ -2202,12 +2239,12 @@ const DeclGen = struct { |
| 2202 | } | 2239 | } |
| 2203 | } | 2240 | } |
| 2204 | | 2241 | |
| 2205 | fn intFromBool(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef { | 2242 | fn intFromBool(self: *DeclGen, ty: Type, condition_id: IdRef) !IdRef { |
| 2206 | const zero_id = try self.constInt(result_ty_ref, 0); | 2243 | const zero_id = try self.constInt(ty, 0, .direct); |
| 2207 | const one_id = try self.constInt(result_ty_ref, 1); | 2244 | const one_id = try self.constInt(ty, 1, .direct); |
| 2208 | const result_id = self.spv.allocId(); | 2245 | const result_id = self.spv.allocId(); |
| 2209 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ | 2246 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2210 | .id_result_type = self.typeId(result_ty_ref), | 2247 | .id_result_type = try self.resolveType2(ty, .direct), |
| 2211 | .id_result = result_id, | 2248 | .id_result = result_id, |
| 2212 | .condition = condition_id, | 2249 | .condition = condition_id, |
| 2213 | .object_1 = one_id, | 2250 | .object_1 = one_id, |
| ... | @@ -2222,15 +2259,12 @@ const DeclGen = struct { | ... | @@ -2222,15 +2259,12 @@ const DeclGen = struct { |
| 2222 | const mod = self.module; | 2259 | const mod = self.module; |
| 2223 | return switch (ty.zigTypeTag(mod)) { | 2260 | return switch (ty.zigTypeTag(mod)) { |
| 2224 | .Bool => blk: { | 2261 | .Bool => blk: { |
| 2225 | const direct_bool_ty_ref = try self.resolveType(ty, .direct); | | |
| 2226 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); | | |
| 2227 | const zero_id = try self.constInt(indirect_bool_ty_ref, 0); | | |
| 2228 | const result_id = self.spv.allocId(); | 2262 | const result_id = self.spv.allocId(); |
| 2229 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ | 2263 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 2230 | .id_result_type = self.typeId(direct_bool_ty_ref), | 2264 | .id_result_type = try self.resolveType2(Type.bool, .direct), |
| 2231 | .id_result = result_id, | 2265 | .id_result = result_id, |
| 2232 | .operand_1 = operand_id, | 2266 | .operand_1 = operand_id, |
| 2233 | .operand_2 = zero_id, | 2267 | .operand_2 = try self.constBool(false, .indirect), |
| 2234 | }); | 2268 | }); |
| 2235 | break :blk result_id; | 2269 | break :blk result_id; |
| 2236 | }, | 2270 | }, |
| ... | @@ -2243,10 +2277,7 @@ const DeclGen = struct { | ... | @@ -2243,10 +2277,7 @@ const DeclGen = struct { |
| 2243 | fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { | 2277 | fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { |
| 2244 | const mod = self.module; | 2278 | const mod = self.module; |
| 2245 | return switch (ty.zigTypeTag(mod)) { | 2279 | return switch (ty.zigTypeTag(mod)) { |
| 2246 | .Bool => blk: { | 2280 | .Bool => try self.intFromBool(Type.u1, operand_id), |
| 2247 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); | | |
| 2248 | break :blk self.intFromBool(indirect_bool_ty_ref, operand_id); | | |
| 2249 | }, | | |
| 2250 | else => operand_id, | 2281 | else => operand_id, |
| 2251 | }; | 2282 | }; |
| 2252 | } | 2283 | } |
| ... | @@ -2529,7 +2560,7 @@ const DeclGen = struct { | ... | @@ -2529,7 +2560,7 @@ const DeclGen = struct { |
| 2529 | try self.func.body.emit(self.spv.gpa, unsigned, args); | 2560 | try self.func.body.emit(self.spv.gpa, unsigned, args); |
| 2530 | } | 2561 | } |
| 2531 | | 2562 | |
| 2532 | result_id.* = try self.normalize(wip.ty_ref, value_id, info); | 2563 | result_id.* = try self.normalize(wip.ty, value_id, info); |
| 2533 | } | 2564 | } |
| 2534 | return try wip.finalize(); | 2565 | return try wip.finalize(); |
| 2535 | } | 2566 | } |
| ... | @@ -2622,7 +2653,7 @@ const DeclGen = struct { | ... | @@ -2622,7 +2653,7 @@ const DeclGen = struct { |
| 2622 | /// - Signed integers are also sign extended if they are negative. | 2653 | /// - Signed integers are also sign extended if they are negative. |
| 2623 | /// All other values are returned unmodified (this makes strange integer | 2654 | /// All other values are returned unmodified (this makes strange integer |
| 2624 | /// wrapping easier to use in generic operations). | 2655 | /// wrapping easier to use in generic operations). |
| 2625 | fn normalize(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { | 2656 | fn normalize(self: *DeclGen, ty: Type, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { |
| 2626 | switch (info.class) { | 2657 | switch (info.class) { |
| 2627 | .integer, .bool, .float => return value_id, | 2658 | .integer, .bool, .float => return value_id, |
| 2628 | .composite_integer => unreachable, // TODO | 2659 | .composite_integer => unreachable, // TODO |
| ... | @@ -2630,9 +2661,9 @@ const DeclGen = struct { | ... | @@ -2630,9 +2661,9 @@ const DeclGen = struct { |
| 2630 | .unsigned => { | 2661 | .unsigned => { |
| 2631 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; | 2662 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; |
| 2632 | const result_id = self.spv.allocId(); | 2663 | const result_id = self.spv.allocId(); |
| 2633 | const mask_id = try self.constInt(ty_ref, mask_value); | 2664 | const mask_id = try self.constInt(ty, mask_value, .direct); |
| 2634 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ | 2665 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ |
| 2635 | .id_result_type = self.typeId(ty_ref), | 2666 | .id_result_type = try self.resolveType2(ty, .direct), |
| 2636 | .id_result = result_id, | 2667 | .id_result = result_id, |
| 2637 | .operand_1 = value_id, | 2668 | .operand_1 = value_id, |
| 2638 | .operand_2 = mask_id, | 2669 | .operand_2 = mask_id, |
| ... | @@ -2641,17 +2672,17 @@ const DeclGen = struct { | ... | @@ -2641,17 +2672,17 @@ const DeclGen = struct { |
| 2641 | }, | 2672 | }, |
| 2642 | .signed => { | 2673 | .signed => { |
| 2643 | // Shift left and right so that we can copy the sight bit that way. | 2674 | // Shift left and right so that we can copy the sight bit that way. |
| 2644 | const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits); | 2675 | const shift_amt_id = try self.constInt(ty, info.backing_bits - info.bits, .direct); |
| 2645 | const left_id = self.spv.allocId(); | 2676 | const left_id = self.spv.allocId(); |
| 2646 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ | 2677 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ |
| 2647 | .id_result_type = self.typeId(ty_ref), | 2678 | .id_result_type = try self.resolveType2(ty, .direct), |
| 2648 | .id_result = left_id, | 2679 | .id_result = left_id, |
| 2649 | .base = value_id, | 2680 | .base = value_id, |
| 2650 | .shift = shift_amt_id, | 2681 | .shift = shift_amt_id, |
| 2651 | }); | 2682 | }); |
| 2652 | const right_id = self.spv.allocId(); | 2683 | const right_id = self.spv.allocId(); |
| 2653 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ | 2684 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ |
| 2654 | .id_result_type = self.typeId(ty_ref), | 2685 | .id_result_type = try self.resolveType2(ty, .direct), |
| 2655 | .id_result = right_id, | 2686 | .id_result = right_id, |
| 2656 | .base = left_id, | 2687 | .base = left_id, |
| 2657 | .shift = shift_amt_id, | 2688 | .shift = shift_amt_id, |
| ... | @@ -2667,13 +2698,13 @@ const DeclGen = struct { | ... | @@ -2667,13 +2698,13 @@ const DeclGen = struct { |
| 2667 | const lhs_id = try self.resolve(bin_op.lhs); | 2698 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2668 | const rhs_id = try self.resolve(bin_op.rhs); | 2699 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2669 | const ty = self.typeOfIndex(inst); | 2700 | const ty = self.typeOfIndex(inst); |
| 2670 | const ty_ref = try self.resolveType(ty, .direct); | 2701 | const ty_id = try self.resolveType2(ty, .direct); |
| 2671 | const info = self.arithmeticTypeInfo(ty); | 2702 | const info = self.arithmeticTypeInfo(ty); |
| 2672 | switch (info.class) { | 2703 | switch (info.class) { |
| 2673 | .composite_integer => unreachable, // TODO | 2704 | .composite_integer => unreachable, // TODO |
| 2674 | .integer, .strange_integer => { | 2705 | .integer, .strange_integer => { |
| 2675 | const zero_id = try self.constInt(ty_ref, 0); | 2706 | const zero_id = try self.constInt(ty, 0, .direct); |
| 2676 | const one_id = try self.constInt(ty_ref, 1); | 2707 | const one_id = try self.constInt(ty, 1, .direct); |
| 2677 | | 2708 | |
| 2678 | // (a ^ b) > 0 | 2709 | // (a ^ b) > 0 |
| 2679 | const bin_bitwise_id = try self.binOpSimple(ty, lhs_id, rhs_id, .OpBitwiseXor); | 2710 | const bin_bitwise_id = try self.binOpSimple(ty, lhs_id, rhs_id, .OpBitwiseXor); |
| ... | @@ -2696,14 +2727,14 @@ const DeclGen = struct { | ... | @@ -2696,14 +2727,14 @@ const DeclGen = struct { |
| 2696 | const negative_div_id = try self.arithOp(ty, negative_div_lhs, rhs_abs, .OpFDiv, .OpSDiv, .OpUDiv); | 2727 | const negative_div_id = try self.arithOp(ty, negative_div_lhs, rhs_abs, .OpFDiv, .OpSDiv, .OpUDiv); |
| 2697 | const negated_negative_div_id = self.spv.allocId(); | 2728 | const negated_negative_div_id = self.spv.allocId(); |
| 2698 | try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ | 2729 | try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ |
| 2699 | .id_result_type = self.typeId(ty_ref), | 2730 | .id_result_type = ty_id, |
| 2700 | .id_result = negated_negative_div_id, | 2731 | .id_result = negated_negative_div_id, |
| 2701 | .operand = negative_div_id, | 2732 | .operand = negative_div_id, |
| 2702 | }); | 2733 | }); |
| 2703 | | 2734 | |
| 2704 | const result_id = self.spv.allocId(); | 2735 | const result_id = self.spv.allocId(); |
| 2705 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ | 2736 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2706 | .id_result_type = self.typeId(ty_ref), | 2737 | .id_result_type = ty_id, |
| 2707 | .id_result = result_id, | 2738 | .id_result = result_id, |
| 2708 | .condition = is_positive_id, | 2739 | .condition = is_positive_id, |
| 2709 | .object_1 = positive_div_id, | 2740 | .object_1 = positive_div_id, |
| ... | @@ -2819,7 +2850,7 @@ const DeclGen = struct { | ... | @@ -2819,7 +2850,7 @@ const DeclGen = struct { |
| 2819 | | 2850 | |
| 2820 | // TODO: Trap on overflow? Probably going to be annoying. | 2851 | // TODO: Trap on overflow? Probably going to be annoying. |
| 2821 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. | 2852 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| 2822 | result_id.* = try self.normalize(wip.ty_ref, value_id, info); | 2853 | result_id.* = try self.normalize(wip.ty, value_id, info); |
| 2823 | } | 2854 | } |
| 2824 | | 2855 | |
| 2825 | return try wip.finalize(); | 2856 | return try wip.finalize(); |
| ... | @@ -2929,7 +2960,7 @@ const DeclGen = struct { | ... | @@ -2929,7 +2960,7 @@ const DeclGen = struct { |
| 2929 | }); | 2960 | }); |
| 2930 | | 2961 | |
| 2931 | // Normalize the result so that the comparisons go well | 2962 | // Normalize the result so that the comparisons go well |
| 2932 | result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); | 2963 | result_id.* = try self.normalize(wip_result.ty, value_id, info); |
| 2933 | | 2964 | |
| 2934 | const overflowed_id = switch (info.signedness) { | 2965 | const overflowed_id = switch (info.signedness) { |
| 2935 | .unsigned => blk: { | 2966 | .unsigned => blk: { |
| ... | @@ -2963,7 +2994,7 @@ const DeclGen = struct { | ... | @@ -2963,7 +2994,7 @@ const DeclGen = struct { |
| 2963 | // = (rhs < 0) == (lhs > value) | 2994 | // = (rhs < 0) == (lhs > value) |
| 2964 | | 2995 | |
| 2965 | const rhs_lt_zero_id = self.spv.allocId(); | 2996 | const rhs_lt_zero_id = self.spv.allocId(); |
| 2966 | const zero_id = try self.constInt(wip_result.ty_ref, 0); | 2997 | const zero_id = try self.constInt(wip_result.ty, 0, .direct); |
| 2967 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ | 2998 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2968 | .id_result_type = self.typeId(cmp_ty_ref), | 2999 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2969 | .id_result = rhs_lt_zero_id, | 3000 | .id_result = rhs_lt_zero_id, |
| ... | @@ -2990,7 +3021,7 @@ const DeclGen = struct { | ... | @@ -2990,7 +3021,7 @@ const DeclGen = struct { |
| 2990 | }, | 3021 | }, |
| 2991 | }; | 3022 | }; |
| 2992 | | 3023 | |
| 2993 | ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); | 3024 | ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id); |
| 2994 | } | 3025 | } |
| 2995 | | 3026 | |
| 2996 | return try self.constructStruct( | 3027 | return try self.constructStruct( |
| ... | @@ -3022,9 +3053,9 @@ const DeclGen = struct { | ... | @@ -3022,9 +3053,9 @@ const DeclGen = struct { |
| 3022 | var wip_ov = try self.elementWise(ov_ty, true); | 3053 | var wip_ov = try self.elementWise(ov_ty, true); |
| 3023 | defer wip_ov.deinit(); | 3054 | defer wip_ov.deinit(); |
| 3024 | | 3055 | |
| 3025 | const zero_id = try self.constInt(wip_result.ty_ref, 0); | 3056 | const zero_id = try self.constInt(wip_result.ty, 0, .direct); |
| 3026 | const zero_ov_id = try self.constInt(wip_ov.ty_ref, 0); | 3057 | const zero_ov_id = try self.constInt(wip_ov.ty, 0, .direct); |
| 3027 | const one_ov_id = try self.constInt(wip_ov.ty_ref, 1); | 3058 | const one_ov_id = try self.constInt(wip_ov.ty, 1, .direct); |
| 3028 | | 3059 | |
| 3029 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { | 3060 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { |
| 3030 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); | 3061 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| ... | @@ -3109,7 +3140,7 @@ const DeclGen = struct { | ... | @@ -3109,7 +3140,7 @@ const DeclGen = struct { |
| 3109 | .base = lhs_elem_id, | 3140 | .base = lhs_elem_id, |
| 3110 | .shift = shift_id, | 3141 | .shift = shift_id, |
| 3111 | }); | 3142 | }); |
| 3112 | result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); | 3143 | result_id.* = try self.normalize(wip_result.ty, value_id, info); |
| 3113 | | 3144 | |
| 3114 | const right_shift_id = self.spv.allocId(); | 3145 | const right_shift_id = self.spv.allocId(); |
| 3115 | switch (info.signedness) { | 3146 | switch (info.signedness) { |
| ... | @@ -3139,7 +3170,7 @@ const DeclGen = struct { | ... | @@ -3139,7 +3170,7 @@ const DeclGen = struct { |
| 3139 | .operand_2 = right_shift_id, | 3170 | .operand_2 = right_shift_id, |
| 3140 | }); | 3171 | }); |
| 3141 | | 3172 | |
| 3142 | ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); | 3173 | ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id); |
| 3143 | } | 3174 | } |
| 3144 | | 3175 | |
| 3145 | return try self.constructStruct( | 3176 | return try self.constructStruct( |
| ... | @@ -3351,7 +3382,7 @@ const DeclGen = struct { | ... | @@ -3351,7 +3382,7 @@ const DeclGen = struct { |
| 3351 | for (wip.results, 0..) |*result_id, i| { | 3382 | for (wip.results, 0..) |*result_id, i| { |
| 3352 | const elem = try mask.elemValue(mod, i); | 3383 | const elem = try mask.elemValue(mod, i); |
| 3353 | if (elem.isUndef(mod)) { | 3384 | if (elem.isUndef(mod)) { |
| 3354 | result_id.* = try self.spv.constUndef(wip.ty_ref); | 3385 | result_id.* = try self.spv.constUndef(wip.ty_id); |
| 3355 | continue; | 3386 | continue; |
| 3356 | } | 3387 | } |
| 3357 | | 3388 | |
| ... | @@ -3366,11 +3397,10 @@ const DeclGen = struct { | ... | @@ -3366,11 +3397,10 @@ const DeclGen = struct { |
| 3366 | } | 3397 | } |
| 3367 | | 3398 | |
| 3368 | fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef { | 3399 | fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef { |
| 3369 | const index_ty_ref = try self.intType(.unsigned, 32); | | |
| 3370 | const ids = try self.gpa.alloc(IdRef, indices.len); | 3400 | const ids = try self.gpa.alloc(IdRef, indices.len); |
| 3371 | errdefer self.gpa.free(ids); | 3401 | errdefer self.gpa.free(ids); |
| 3372 | for (indices, ids) |index, *id| { | 3402 | for (indices, ids) |index, *id| { |
| 3373 | id.* = try self.constInt(index_ty_ref, index); | 3403 | id.* = try self.constInt(Type.u32, index, .direct); |
| 3374 | } | 3404 | } |
| 3375 | | 3405 | |
| 3376 | return ids; | 3406 | return ids; |
| ... | @@ -3502,7 +3532,7 @@ const DeclGen = struct { | ... | @@ -3502,7 +3532,7 @@ const DeclGen = struct { |
| 3502 | cmp_lhs_id = self.spv.allocId(); | 3532 | cmp_lhs_id = self.spv.allocId(); |
| 3503 | cmp_rhs_id = self.spv.allocId(); | 3533 | cmp_rhs_id = self.spv.allocId(); |
| 3504 | | 3534 | |
| 3505 | const usize_ty_id = self.typeId(try self.sizeType()); | 3535 | const usize_ty_id = try self.resolveType2(Type.usize, .direct); |
| 3506 | | 3536 | |
| 3507 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ | 3537 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 3508 | .id_result_type = usize_ty_id, | 3538 | .id_result_type = usize_ty_id, |
| ... | @@ -3761,7 +3791,7 @@ const DeclGen = struct { | ... | @@ -3761,7 +3791,7 @@ const DeclGen = struct { |
| 3761 | // should we change the representation of strange integers? | 3791 | // should we change the representation of strange integers? |
| 3762 | if (dst_ty.zigTypeTag(mod) == .Int) { | 3792 | if (dst_ty.zigTypeTag(mod) == .Int) { |
| 3763 | const info = self.arithmeticTypeInfo(dst_ty); | 3793 | const info = self.arithmeticTypeInfo(dst_ty); |
| 3764 | return try self.normalize(dst_ty_ref, result_id, info); | 3794 | return try self.normalize(dst_ty, result_id, info); |
| 3765 | } | 3795 | } |
| 3766 | | 3796 | |
| 3767 | return result_id; | 3797 | return result_id; |
| ... | @@ -3811,7 +3841,7 @@ const DeclGen = struct { | ... | @@ -3811,7 +3841,7 @@ const DeclGen = struct { |
| 3811 | // type, we don't need to normalize when growing the type. The | 3841 | // type, we don't need to normalize when growing the type. The |
| 3812 | // representation is already the same. | 3842 | // representation is already the same. |
| 3813 | if (dst_info.bits < src_info.bits) { | 3843 | if (dst_info.bits < src_info.bits) { |
| 3814 | result_id.* = try self.normalize(wip.ty_ref, value_id, dst_info); | 3844 | result_id.* = try self.normalize(wip.ty, value_id, dst_info); |
| 3815 | } else { | 3845 | } else { |
| 3816 | result_id.* = value_id; | 3846 | result_id.* = value_id; |
| 3817 | } | 3847 | } |
| ... | @@ -3898,7 +3928,7 @@ const DeclGen = struct { | ... | @@ -3898,7 +3928,7 @@ const DeclGen = struct { |
| 3898 | defer wip.deinit(); | 3928 | defer wip.deinit(); |
| 3899 | for (wip.results, 0..) |*result_id, i| { | 3929 | for (wip.results, 0..) |*result_id, i| { |
| 3900 | const elem_id = try wip.elementAt(Type.bool, operand_id, i); | 3930 | const elem_id = try wip.elementAt(Type.bool, operand_id, i); |
| 3901 | result_id.* = try self.intFromBool(wip.ty_ref, elem_id); | 3931 | result_id.* = try self.intFromBool(wip.ty, elem_id); |
| 3902 | } | 3932 | } |
| 3903 | return try wip.finalize(); | 3933 | return try wip.finalize(); |
| 3904 | } | 3934 | } |
| ... | @@ -3958,10 +3988,9 @@ const DeclGen = struct { | ... | @@ -3958,10 +3988,9 @@ const DeclGen = struct { |
| 3958 | const elem_ptr_ty = slice_ty.slicePtrFieldType(mod); | 3988 | const elem_ptr_ty = slice_ty.slicePtrFieldType(mod); |
| 3959 | | 3989 | |
| 3960 | const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct); | 3990 | const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct); |
| 3961 | const size_ty_ref = try self.sizeType(); | | |
| 3962 | | 3991 | |
| 3963 | const array_ptr_id = try self.resolve(ty_op.operand); | 3992 | const array_ptr_id = try self.resolve(ty_op.operand); |
| 3964 | const len_id = try self.constInt(size_ty_ref, array_ty.arrayLen(mod)); | 3993 | const len_id = try self.constInt(Type.usize, array_ty.arrayLen(mod), .direct); |
| 3965 | | 3994 | |
| 3966 | const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) | 3995 | const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 3967 | // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type. | 3996 | // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type. |
| ... | @@ -4092,8 +4121,8 @@ const DeclGen = struct { | ... | @@ -4092,8 +4121,8 @@ const DeclGen = struct { |
| 4092 | const array_ty = ty.childType(mod); | 4121 | const array_ty = ty.childType(mod); |
| 4093 | const elem_ty = array_ty.childType(mod); | 4122 | const elem_ty = array_ty.childType(mod); |
| 4094 | const abi_size = elem_ty.abiSize(mod); | 4123 | const abi_size = elem_ty.abiSize(mod); |
| 4095 | const usize_ty_ref = try self.resolveType(Type.usize, .direct); | 4124 | const size = array_ty.arrayLenIncludingSentinel(mod) * abi_size; |
| 4096 | return self.spv.constInt(usize_ty_ref, array_ty.arrayLenIncludingSentinel(mod) * abi_size); | 4125 | return try self.constInt(Type.usize, size, .direct); |
| 4097 | }, | 4126 | }, |
| 4098 | .Many, .C => unreachable, | 4127 | .Many, .C => unreachable, |
| 4099 | } | 4128 | } |
| ... | @@ -4298,6 +4327,8 @@ const DeclGen = struct { | ... | @@ -4298,6 +4327,8 @@ const DeclGen = struct { |
| 4298 | // union type, then get the field pointer and pointer-cast it to the | 4327 | // union type, then get the field pointer and pointer-cast it to the |
| 4299 | // right type to store it. Finally load the entire union. | 4328 | // right type to store it. Finally load the entire union. |
| 4300 | | 4329 | |
| | 4330 | // Note: The result here is not cached, because it generates runtime code. |
| | 4331 | |
| 4301 | const mod = self.module; | 4332 | const mod = self.module; |
| 4302 | const ip = &mod.intern_pool; | 4333 | const ip = &mod.intern_pool; |
| 4303 | const union_ty = mod.typeToUnion(ty).?; | 4334 | const union_ty = mod.typeToUnion(ty).?; |
| ... | @@ -4316,17 +4347,15 @@ const DeclGen = struct { | ... | @@ -4316,17 +4347,15 @@ const DeclGen = struct { |
| 4316 | } else 0; | 4347 | } else 0; |
| 4317 | | 4348 | |
| 4318 | if (!layout.has_payload) { | 4349 | if (!layout.has_payload) { |
| 4319 | const tag_ty_ref = try self.resolveType(tag_ty, .direct); | 4350 | return try self.constInt(tag_ty, tag_int, .direct); |
| 4320 | return try self.constInt(tag_ty_ref, tag_int); | | |
| 4321 | } | 4351 | } |
| 4322 | | 4352 | |
| 4323 | const tmp_id = try self.alloc(ty, .{ .storage_class = .Function }); | 4353 | const tmp_id = try self.alloc(ty, .{ .storage_class = .Function }); |
| 4324 | | 4354 | |
| 4325 | if (layout.tag_size != 0) { | 4355 | if (layout.tag_size != 0) { |
| 4326 | const tag_ty_ref = try self.resolveType(tag_ty, .direct); | | |
| 4327 | const tag_ptr_ty_ref = try self.ptrType(tag_ty, .Function); | 4356 | const tag_ptr_ty_ref = try self.ptrType(tag_ty, .Function); |
| 4328 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); | 4357 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); |
| 4329 | const tag_id = try self.constInt(tag_ty_ref, tag_int); | 4358 | const tag_id = try self.constInt(tag_ty, tag_int, .direct); |
| 4330 | try self.store(tag_ty, ptr_id, tag_id, .{}); | 4359 | try self.store(tag_ty, ptr_id, tag_id, .{}); |
| 4331 | } | 4360 | } |
| 4332 | | 4361 | |
| ... | @@ -4420,8 +4449,6 @@ const DeclGen = struct { | ... | @@ -4420,8 +4449,6 @@ const DeclGen = struct { |
| 4420 | | 4449 | |
| 4421 | const parent_ty = ty_pl.ty.toType().childType(mod); | 4450 | const parent_ty = ty_pl.ty.toType().childType(mod); |
| 4422 | const res_ty = try self.resolveType(ty_pl.ty.toType(), .indirect); | 4451 | const res_ty = try self.resolveType(ty_pl.ty.toType(), .indirect); |
| 4423 | const usize_ty = Type.usize; | | |
| 4424 | const usize_ty_ref = try self.resolveType(usize_ty, .direct); | | |
| 4425 | | 4452 | |
| 4426 | const field_ptr = try self.resolve(extra.field_ptr); | 4453 | const field_ptr = try self.resolve(extra.field_ptr); |
| 4427 | const field_ptr_int = try self.intFromPtr(field_ptr); | 4454 | const field_ptr_int = try self.intFromPtr(field_ptr); |
| ... | @@ -4430,8 +4457,8 @@ const DeclGen = struct { | ... | @@ -4430,8 +4457,8 @@ const DeclGen = struct { |
| 4430 | const base_ptr_int = base_ptr_int: { | 4457 | const base_ptr_int = base_ptr_int: { |
| 4431 | if (field_offset == 0) break :base_ptr_int field_ptr_int; | 4458 | if (field_offset == 0) break :base_ptr_int field_ptr_int; |
| 4432 | | 4459 | |
| 4433 | const field_offset_id = try self.constInt(usize_ty_ref, field_offset); | 4460 | const field_offset_id = try self.constInt(Type.usize, field_offset, .direct); |
| 4434 | break :base_ptr_int try self.binOpSimple(usize_ty, field_ptr_int, field_offset_id, .OpISub); | 4461 | break :base_ptr_int try self.binOpSimple(Type.usize, field_ptr_int, field_offset_id, .OpISub); |
| 4435 | }; | 4462 | }; |
| 4436 | | 4463 | |
| 4437 | const base_ptr = self.spv.allocId(); | 4464 | const base_ptr = self.spv.allocId(); |
| ... | @@ -4469,7 +4496,7 @@ const DeclGen = struct { | ... | @@ -4469,7 +4496,7 @@ const DeclGen = struct { |
| 4469 | if (!layout.has_payload) { | 4496 | if (!layout.has_payload) { |
| 4470 | // Asked to get a pointer to a zero-sized field. Just lower this | 4497 | // Asked to get a pointer to a zero-sized field. Just lower this |
| 4471 | // to undefined, there is no reason to make it be a valid pointer. | 4498 | // to undefined, there is no reason to make it be a valid pointer. |
| 4472 | return try self.spv.constUndef(result_ty_ref); | 4499 | return try self.spv.constUndef(self.typeId(result_ty_ref)); |
| 4473 | } | 4500 | } |
| 4474 | | 4501 | |
| 4475 | const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); | 4502 | const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); |
| ... | @@ -4563,7 +4590,7 @@ const DeclGen = struct { | ... | @@ -4563,7 +4590,7 @@ const DeclGen = struct { |
| 4563 | assert(self.control_flow == .structured); | 4590 | assert(self.control_flow == .structured); |
| 4564 | | 4591 | |
| 4565 | const result_id = self.spv.allocId(); | 4592 | const result_id = self.spv.allocId(); |
| 4566 | const block_id_ty_ref = try self.intType(.unsigned, 32); | 4593 | const block_id_ty_ref = try self.resolveType(Type.u32, .direct); |
| 4567 | try self.func.body.emitRaw(self.spv.gpa, .OpPhi, @intCast(2 + incoming.len * 2)); // result type + result + variable/parent... | 4594 | try self.func.body.emitRaw(self.spv.gpa, .OpPhi, @intCast(2 + incoming.len * 2)); // result type + result + variable/parent... |
| 4568 | self.func.body.writeOperand(spec.IdResultType, self.typeId(block_id_ty_ref)); | 4595 | self.func.body.writeOperand(spec.IdResultType, self.typeId(block_id_ty_ref)); |
| 4569 | self.func.body.writeOperand(spec.IdRef, result_id); | 4596 | self.func.body.writeOperand(spec.IdRef, result_id); |
| ... | @@ -4663,8 +4690,8 @@ const DeclGen = struct { | ... | @@ -4663,8 +4690,8 @@ const DeclGen = struct { |
| 4663 | // Make sure that we are still in a block when exiting the function. | 4690 | // Make sure that we are still in a block when exiting the function. |
| 4664 | // TODO: Can we get rid of that? | 4691 | // TODO: Can we get rid of that? |
| 4665 | try self.beginSpvBlock(self.spv.allocId()); | 4692 | try self.beginSpvBlock(self.spv.allocId()); |
| 4666 | const block_id_ty_ref = try self.intType(.unsigned, 32); | 4693 | const block_id_ty_ref = try self.resolveType(Type.u32, .direct); |
| 4667 | return try self.spv.constUndef(block_id_ty_ref); | 4694 | return try self.spv.constUndef(self.typeId(block_id_ty_ref)); |
| 4668 | } | 4695 | } |
| 4669 | | 4696 | |
| 4670 | // The top-most merge actually only has a single source, the | 4697 | // The top-most merge actually only has a single source, the |
| ... | @@ -4781,8 +4808,7 @@ const DeclGen = struct { | ... | @@ -4781,8 +4808,7 @@ const DeclGen = struct { |
| 4781 | assert(cf.block_stack.items.len > 0); | 4808 | assert(cf.block_stack.items.len > 0); |
| 4782 | | 4809 | |
| 4783 | // Check if the target of the branch was this current block. | 4810 | // Check if the target of the branch was this current block. |
| 4784 | const block_id_ty_ref = try self.intType(.unsigned, 32); | 4811 | const this_block = try self.constInt(Type.u32, @intFromEnum(inst), .direct); |
| 4785 | const this_block = try self.constInt(block_id_ty_ref, @intFromEnum(inst)); | | |
| 4786 | const jump_to_this_block_id = self.spv.allocId(); | 4812 | const jump_to_this_block_id = self.spv.allocId(); |
| 4787 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); | 4813 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 4788 | try self.func.body.emit(self.spv.gpa, .OpIEqual, .{ | 4814 | try self.func.body.emit(self.spv.gpa, .OpIEqual, .{ |
| ... | @@ -4862,8 +4888,7 @@ const DeclGen = struct { | ... | @@ -4862,8 +4888,7 @@ const DeclGen = struct { |
| 4862 | try self.store(operand_ty, block_result_var_id, operand_id, .{}); | 4888 | try self.store(operand_ty, block_result_var_id, operand_id, .{}); |
| 4863 | } | 4889 | } |
| 4864 | | 4890 | |
| 4865 | const block_id_ty_ref = try self.intType(.unsigned, 32); | 4891 | const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst), .direct); |
| 4866 | const next_block = try self.constInt(block_id_ty_ref, @intFromEnum(br.block_inst)); | | |
| 4867 | try self.structuredBreak(next_block); | 4892 | try self.structuredBreak(next_block); |
| 4868 | }, | 4893 | }, |
| 4869 | .unstructured => |cf| { | 4894 | .unstructured => |cf| { |
| ... | @@ -5026,8 +5051,7 @@ const DeclGen = struct { | ... | @@ -5026,8 +5051,7 @@ const DeclGen = struct { |
| 5026 | // Functions with an empty error set are emitted with an error code | 5051 | // Functions with an empty error set are emitted with an error code |
| 5027 | // return type and return zero so they can be function pointers coerced | 5052 | // return type and return zero so they can be function pointers coerced |
| 5028 | // to functions that return anyerror. | 5053 | // to functions that return anyerror. |
| 5029 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); | 5054 | const no_err_id = try self.constInt(Type.anyerror, 0, .direct); |
| 5030 | const no_err_id = try self.constInt(err_ty_ref, 0); | | |
| 5031 | return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); | 5055 | return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); |
| 5032 | } else { | 5056 | } else { |
| 5033 | return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); | 5057 | return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| ... | @@ -5051,8 +5075,7 @@ const DeclGen = struct { | ... | @@ -5051,8 +5075,7 @@ const DeclGen = struct { |
| 5051 | // Functions with an empty error set are emitted with an error code | 5075 | // Functions with an empty error set are emitted with an error code |
| 5052 | // return type and return zero so they can be function pointers coerced | 5076 | // return type and return zero so they can be function pointers coerced |
| 5053 | // to functions that return anyerror. | 5077 | // to functions that return anyerror. |
| 5054 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); | 5078 | const no_err_id = try self.constInt(Type.anyerror, 0, .direct); |
| 5055 | const no_err_id = try self.constInt(err_ty_ref, 0); | | |
| 5056 | return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); | 5079 | return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id }); |
| 5057 | } else { | 5080 | } else { |
| 5058 | return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); | 5081 | return try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| ... | @@ -5076,7 +5099,6 @@ const DeclGen = struct { | ... | @@ -5076,7 +5099,6 @@ const DeclGen = struct { |
| 5076 | const err_union_ty = self.typeOf(pl_op.operand); | 5099 | const err_union_ty = self.typeOf(pl_op.operand); |
| 5077 | const payload_ty = self.typeOfIndex(inst); | 5100 | const payload_ty = self.typeOfIndex(inst); |
| 5078 | | 5101 | |
| 5079 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); | | |
| 5080 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); | 5102 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 5081 | | 5103 | |
| 5082 | const eu_layout = self.errorUnionLayout(payload_ty); | 5104 | const eu_layout = self.errorUnionLayout(payload_ty); |
| ... | @@ -5087,7 +5109,7 @@ const DeclGen = struct { | ... | @@ -5087,7 +5109,7 @@ const DeclGen = struct { |
| 5087 | else | 5109 | else |
| 5088 | err_union_id; | 5110 | err_union_id; |
| 5089 | | 5111 | |
| 5090 | const zero_id = try self.constInt(err_ty_ref, 0); | 5112 | const zero_id = try self.constInt(Type.anyerror, 0, .direct); |
| 5091 | const is_err_id = self.spv.allocId(); | 5113 | const is_err_id = self.spv.allocId(); |
| 5092 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ | 5114 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 5093 | .id_result_type = self.typeId(bool_ty_ref), | 5115 | .id_result_type = self.typeId(bool_ty_ref), |
| ... | @@ -5146,7 +5168,7 @@ const DeclGen = struct { | ... | @@ -5146,7 +5168,7 @@ const DeclGen = struct { |
| 5146 | | 5168 | |
| 5147 | if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { | 5169 | if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) { |
| 5148 | // No error possible, so just return undefined. | 5170 | // No error possible, so just return undefined. |
| 5149 | return try self.spv.constUndef(err_ty_ref); | 5171 | return try self.spv.constUndef(self.typeId(err_ty_ref)); |
| 5150 | } | 5172 | } |
| 5151 | | 5173 | |
| 5152 | const payload_ty = err_union_ty.errorUnionPayload(mod); | 5174 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| ... | @@ -5189,7 +5211,7 @@ const DeclGen = struct { | ... | @@ -5189,7 +5211,7 @@ const DeclGen = struct { |
| 5189 | | 5211 | |
| 5190 | var members: [2]IdRef = undefined; | 5212 | var members: [2]IdRef = undefined; |
| 5191 | members[eu_layout.errorFieldIndex()] = operand_id; | 5213 | members[eu_layout.errorFieldIndex()] = operand_id; |
| 5192 | members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_ref); | 5214 | members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(self.typeId(payload_ty_ref)); |
| 5193 | | 5215 | |
| 5194 | var types: [2]Type = undefined; | 5216 | var types: [2]Type = undefined; |
| 5195 | types[eu_layout.errorFieldIndex()] = Type.anyerror; | 5217 | types[eu_layout.errorFieldIndex()] = Type.anyerror; |
| ... | @@ -5203,15 +5225,14 @@ const DeclGen = struct { | ... | @@ -5203,15 +5225,14 @@ const DeclGen = struct { |
| 5203 | const err_union_ty = self.typeOfIndex(inst); | 5225 | const err_union_ty = self.typeOfIndex(inst); |
| 5204 | const operand_id = try self.resolve(ty_op.operand); | 5226 | const operand_id = try self.resolve(ty_op.operand); |
| 5205 | const payload_ty = self.typeOf(ty_op.operand); | 5227 | const payload_ty = self.typeOf(ty_op.operand); |
| 5206 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); | | |
| 5207 | const eu_layout = self.errorUnionLayout(payload_ty); | 5228 | const eu_layout = self.errorUnionLayout(payload_ty); |
| 5208 | | 5229 | |
| 5209 | if (!eu_layout.payload_has_bits) { | 5230 | if (!eu_layout.payload_has_bits) { |
| 5210 | return try self.constInt(err_ty_ref, 0); | 5231 | return try self.constInt(Type.anyerror, 0, .direct); |
| 5211 | } | 5232 | } |
| 5212 | | 5233 | |
| 5213 | var members: [2]IdRef = undefined; | 5234 | var members: [2]IdRef = undefined; |
| 5214 | members[eu_layout.errorFieldIndex()] = try self.constInt(err_ty_ref, 0); | 5235 | members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0, .direct); |
| 5215 | members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); | 5236 | members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); |
| 5216 | | 5237 | |
| 5217 | var types: [2]Type = undefined; | 5238 | var types: [2]Type = undefined; |
| ... | @@ -5248,8 +5269,8 @@ const DeclGen = struct { | ... | @@ -5248,8 +5269,8 @@ const DeclGen = struct { |
| 5248 | else | 5269 | else |
| 5249 | loaded_id; | 5270 | loaded_id; |
| 5250 | | 5271 | |
| 5251 | const payload_ty_ref = try self.resolveType(ptr_ty, .direct); | 5272 | const payload_ty_id = try self.resolveType2(ptr_ty, .direct); |
| 5252 | const null_id = try self.spv.constNull(payload_ty_ref); | 5273 | const null_id = try self.spv.constNull(payload_ty_id); |
| 5253 | const op: std.math.CompareOperator = switch (pred) { | 5274 | const op: std.math.CompareOperator = switch (pred) { |
| 5254 | .is_null => .eq, | 5275 | .is_null => .eq, |
| 5255 | .is_non_null => .neq, | 5276 | .is_non_null => .neq, |
| ... | @@ -5306,7 +5327,6 @@ const DeclGen = struct { | ... | @@ -5306,7 +5327,6 @@ const DeclGen = struct { |
| 5306 | const payload_ty = err_union_ty.errorUnionPayload(mod); | 5327 | const payload_ty = err_union_ty.errorUnionPayload(mod); |
| 5307 | const eu_layout = self.errorUnionLayout(payload_ty); | 5328 | const eu_layout = self.errorUnionLayout(payload_ty); |
| 5308 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); | 5329 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 5309 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); | | |
| 5310 | | 5330 | |
| 5311 | const error_id = if (!eu_layout.payload_has_bits) | 5331 | const error_id = if (!eu_layout.payload_has_bits) |
| 5312 | operand_id | 5332 | operand_id |
| ... | @@ -5318,7 +5338,7 @@ const DeclGen = struct { | ... | @@ -5318,7 +5338,7 @@ const DeclGen = struct { |
| 5318 | .id_result_type = self.typeId(bool_ty_ref), | 5338 | .id_result_type = self.typeId(bool_ty_ref), |
| 5319 | .id_result = result_id, | 5339 | .id_result = result_id, |
| 5320 | .operand_1 = error_id, | 5340 | .operand_1 = error_id, |
| 5321 | .operand_2 = try self.constInt(err_ty_ref, 0), | 5341 | .operand_2 = try self.constInt(Type.anyerror, 0, .direct), |
| 5322 | }; | 5342 | }; |
| 5323 | switch (pred) { | 5343 | switch (pred) { |
| 5324 | .is_err => try self.func.body.emit(self.spv.gpa, .OpINotEqual, operands), | 5344 | .is_err => try self.func.body.emit(self.spv.gpa, .OpINotEqual, operands), |