authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-02-15 23:03:59+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-02-18 18:07:48+03:30
log29e46633ce29e5325049dd1835c1c0bde0f6d79e
tree90dad2c55df31e8aec3279377baf1e544eed7856
parent7bbeac7f175f5b2aff27aea9c9f64d531150aa80
signaturelock-open Commit is signed but in an unrecognized format.

spirv: cache more types & merge constructX functions


4 files changed, 219 insertions(+), 281 deletions(-)

src/codegen/spirv.zig+134-272
......@@ -159,7 +159,7 @@ pub const Object = struct {
159159 uav_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .empty,
160160
161161 /// A map that maps AIR intern pool indices to SPIR-V result-ids.
162 intern_map: InternMap = .{},
162 intern_map: InternMap = .empty,
163163
164164 /// This map serves a dual purpose:
165165 /// - It keeps track of pointers that are currently being emitted, so that we can tell
......@@ -314,7 +314,7 @@ const NavGen = struct {
314314 next_arg_index: u32 = 0,
315315
316316 /// A map keeping track of which instruction generated which result-id.
317 inst_results: InstMap = .{},
317 inst_results: InstMap = .empty,
318318
319319 /// A map that maps AIR intern pool indices to SPIR-V result-ids.
320320 /// See `Object.intern_map`.
......@@ -469,7 +469,7 @@ const NavGen = struct {
469469
470470 const zcu = self.pt.zcu;
471471 const ty = Type.fromInterned(zcu.intern_pool.typeOf(val));
472 const decl_ptr_ty_id = try self.ptrType(ty, .Generic);
472 const decl_ptr_ty_id = try self.ptrType(ty, .Generic, .indirect);
473473
474474 const spv_decl_index = blk: {
475475 const entry = try self.object.uav_link.getOrPut(self.object.gpa, .{ val, .Function });
......@@ -532,7 +532,7 @@ const NavGen = struct {
532532
533533 try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)});
534534
535 const fn_decl_ptr_ty_id = try self.ptrType(ty, .Function);
535 const fn_decl_ptr_ty_id = try self.ptrType(ty, .Function, .indirect);
536536 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{
537537 .id_result_type = fn_decl_ptr_ty_id,
538538 .id_result = result_id,
......@@ -721,36 +721,16 @@ const NavGen = struct {
721721
722722 /// Emits a bool constant in a particular representation.
723723 fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef {
724 // TODO: Cache?
725
726 const section = &self.spv.sections.types_globals_constants;
727 switch (repr) {
728 .indirect => {
729 return try self.constInt(Type.u1, @intFromBool(value), .indirect);
730 },
731 .direct => {
732 const result_ty_id = try self.resolveType(Type.bool, .direct);
733 const result_id = self.spv.allocId();
734 switch (value) {
735 inline else => |val_ct| try section.emit(
736 self.spv.gpa,
737 if (val_ct) .OpConstantTrue else .OpConstantFalse,
738 .{
739 .id_result_type = result_ty_id,
740 .id_result = result_id,
741 },
742 ),
743 }
744 return result_id;
745 },
746 }
724 return switch (repr) {
725 .indirect => self.constInt(Type.u1, @intFromBool(value)),
726 .direct => self.spv.constBool(value),
727 };
747728 }
748729
749730 /// Emits an integer constant.
750731 /// This function, unlike SpvModule.constInt, takes care to bitcast
751732 /// the value to an unsigned int first for Kernels.
752 fn constInt(self: *NavGen, ty: Type, value: anytype, repr: Repr) !IdRef {
753 // TODO: Cache?
733 fn constInt(self: *NavGen, ty: Type, value: anytype) !IdRef {
754734 const zcu = self.pt.zcu;
755735 const scalar_ty = ty.scalarType(zcu);
756736 const int_info = scalar_ty.intInfo(zcu);
......@@ -763,18 +743,18 @@ const NavGen = struct {
763743 else => unreachable,
764744 };
765745
766 const bits: u64 = switch (signedness) {
746 const value64: u64 = switch (signedness) {
767747 .signed => @bitCast(@as(i64, @intCast(value))),
768748 .unsigned => @as(u64, @intCast(value)),
769749 };
770750
771751 // Manually truncate the value to the right amount of bits.
772 const truncated_bits = if (backing_bits == 64)
773 bits
752 const truncated_value = if (backing_bits == 64)
753 value64
774754 else
775 bits & (@as(u64, 1) << @intCast(backing_bits)) - 1;
755 value64 & (@as(u64, 1) << @intCast(backing_bits)) - 1;
776756
777 const result_ty_id = try self.resolveType(scalar_ty, repr);
757 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
778758 const result_id = self.spv.allocId();
779759
780760 const section = &self.spv.sections.types_globals_constants;
......@@ -783,100 +763,42 @@ const NavGen = struct {
783763 1...32 => try section.emit(self.spv.gpa, .OpConstant, .{
784764 .id_result_type = result_ty_id,
785765 .id_result = result_id,
786 .value = .{ .uint32 = @truncate(truncated_bits) },
766 .value = .{ .uint32 = @truncate(truncated_value) },
787767 }),
788768 33...64 => try section.emit(self.spv.gpa, .OpConstant, .{
789769 .id_result_type = result_ty_id,
790770 .id_result = result_id,
791 .value = .{ .uint64 = truncated_bits },
771 .value = .{ .uint64 = truncated_value },
792772 }),
793773 else => unreachable, // TODO: Large integer constants
794774 }
795775
796 if (!ty.isVector(zcu)) {
797 return result_id;
798 }
799
800 const n = ty.vectorLen(zcu);
801 const ids = try self.gpa.alloc(IdRef, n);
802 defer self.gpa.free(ids);
803 @memset(ids, result_id);
804
805 const vec_ty_id = try self.resolveType(ty, repr);
806 const vec_result_id = self.spv.allocId();
807 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
808 .id_result_type = vec_ty_id,
809 .id_result = vec_result_id,
810 .constituents = ids,
811 });
812 return vec_result_id;
776 if (!ty.isVector(zcu)) return result_id;
777 return self.constructCompositeSplat(ty, result_id);
813778 }
814779
815 /// Construct a struct at runtime.
816 /// ty must be a struct type.
817 /// Constituents should be in `indirect` representation (as the elements of a struct should be).
818 /// Result is in `direct` representation.
819 fn constructStruct(self: *NavGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef {
820 assert(types.len == constituents.len);
821
780 pub fn constructComposite(self: *NavGen, result_ty_id: IdRef, constituents: []const IdRef) !IdRef {
822781 const result_id = self.spv.allocId();
823 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
824 .id_result_type = try self.resolveType(ty, .direct),
825 .id_result = result_id,
826 .constituents = constituents,
827 });
828 return result_id;
829 }
830
831 /// Construct a vector at runtime.
832 /// ty must be an vector type.
833 fn constructVector(self: *NavGen, ty: Type, constituents: []const IdRef) !IdRef {
834 const zcu = self.pt.zcu;
835 assert(ty.vectorLen(zcu) == constituents.len);
836
837 // Note: older versions of the Khronos SPRIV-LLVM translator crash on this instruction
838 // because it cannot construct structs which' operands are not constant.
839 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
840 // Currently this is the case for Intel OpenCL CPU runtime (2023-WW46), but the
841 // alternatives dont work properly:
842 // - using temporaries/pointers doesn't work properly with vectors of bool, causes
843 // backends that use llvm to crash
844 // - using OpVectorInsertDynamic doesn't work for non-spirv-vectors of bool.
845
846 const result_id = self.spv.allocId();
847 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
848 .id_result_type = try self.resolveType(ty, .direct),
782 try self.func.body.emit(self.gpa, .OpCompositeConstruct, .{
783 .id_result_type = result_ty_id,
849784 .id_result = result_id,
850785 .constituents = constituents,
851786 });
852787 return result_id;
853788 }
854789
855 /// Construct a vector at runtime with all lanes set to the same value.
856 /// ty must be an vector type.
857 fn constructVectorSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
790 /// Construct a composite at runtime with all lanes set to the same value.
791 /// ty must be an aggregate type.
792 fn constructCompositeSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
858793 const zcu = self.pt.zcu;
859 const n = ty.vectorLen(zcu);
794 const n = ty.arrayLen(zcu);
860795
861796 const constituents = try self.gpa.alloc(IdRef, n);
862797 defer self.gpa.free(constituents);
863798 @memset(constituents, constituent);
864799
865 return try self.constructVector(ty, constituents);
866 }
867
868 /// Construct an array at runtime.
869 /// ty must be an array type.
870 /// Constituents should be in `indirect` representation (as the elements of an array should be).
871 /// Result is in `direct` representation.
872 fn constructArray(self: *NavGen, ty: Type, constituents: []const IdRef) !IdRef {
873 const result_id = self.spv.allocId();
874 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
875 .id_result_type = try self.resolveType(ty, .direct),
876 .id_result = result_id,
877 .constituents = constituents,
878 });
879 return result_id;
800 const result_ty_id = try self.resolveType(ty, .direct);
801 return self.constructComposite(result_ty_id, constituents);
880802 }
881803
882804 /// This function generates a load for a constant in direct (ie, non-memory) representation.
......@@ -947,9 +869,9 @@ const NavGen = struct {
947869 },
948870 .int => {
949871 if (ty.isSignedInt(zcu)) {
950 break :cache try self.constInt(ty, val.toSignedInt(zcu), repr);
872 break :cache try self.constInt(ty, val.toSignedInt(zcu));
951873 } else {
952 break :cache try self.constInt(ty, val.toUnsignedInt(zcu), repr);
874 break :cache try self.constInt(ty, val.toUnsignedInt(zcu));
953875 }
954876 },
955877 .float => {
......@@ -970,7 +892,7 @@ const NavGen = struct {
970892 },
971893 .err => |err| {
972894 const value = try pt.getErrorValue(err.name);
973 break :cache try self.constInt(ty, value, repr);
895 break :cache try self.constInt(ty, value);
974896 },
975897 .error_union => |error_union| {
976898 // TODO: Error unions may be constructed with constant instructions if the payload type
......@@ -1011,7 +933,8 @@ const NavGen = struct {
1011933 types = .{ payload_ty, err_ty };
1012934 }
1013935
1014 return try self.constructStruct(ty, &types, &constituents);
936 const comp_ty_id = try self.resolveType(ty, .direct);
937 return try self.constructComposite(comp_ty_id, &constituents);
1015938 },
1016939 .enum_tag => {
1017940 const int_val = try val.intFromEnum(ty, pt);
......@@ -1020,14 +943,10 @@ const NavGen = struct {
1020943 },
1021944 .ptr => return self.constantPtr(val),
1022945 .slice => |slice| {
1023 const ptr_ty = ty.slicePtrFieldType(zcu);
1024946 const ptr_id = try self.constantPtr(Value.fromInterned(slice.ptr));
1025947 const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect);
1026 return self.constructStruct(
1027 ty,
1028 &.{ ptr_ty, Type.usize },
1029 &.{ ptr_id, len_id },
1030 );
948 const comp_ty_id = try self.resolveType(ty, .direct);
949 return try self.constructComposite(comp_ty_id, &.{ ptr_id, len_id });
1031950 },
1032951 .opt => {
1033952 const payload_ty = ty.optionalChild(zcu);
......@@ -1053,11 +972,8 @@ const NavGen = struct {
1053972 else
1054973 try self.spv.constUndef(try self.resolveType(payload_ty, .indirect));
1055974
1056 return try self.constructStruct(
1057 ty,
1058 &.{ payload_ty, Type.bool },
1059 &.{ payload_id, has_pl_id },
1060 );
975 const comp_ty_id = try self.resolveType(ty, .direct);
976 return try self.constructComposite(comp_ty_id, &.{ payload_id, has_pl_id });
1061977 },
1062978 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
1063979 inline .array_type, .vector_type => |array_type, tag| {
......@@ -1077,7 +993,7 @@ const NavGen = struct {
1077993 // TODO: This is really space inefficient, perhaps there is a better
1078994 // way to do it?
1079995 for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| {
1080 constituent.* = try self.constInt(elem_ty, byte, child_repr);
996 constituent.* = try self.constInt(elem_ty, byte);
1081997 }
1082998 },
1083999 .elems => |elems| {
......@@ -1090,11 +1006,8 @@ const NavGen = struct {
10901006 },
10911007 }
10921008
1093 switch (tag) {
1094 .array_type => return self.constructArray(ty, constituents),
1095 .vector_type => return self.constructVector(ty, constituents),
1096 else => unreachable,
1097 }
1009 const comp_ty_id = try self.resolveType(ty, .direct);
1010 return self.constructComposite(comp_ty_id, constituents);
10981011 },
10991012 .struct_type => {
11001013 const struct_type = zcu.typeToStruct(ty).?;
......@@ -1124,7 +1037,8 @@ const NavGen = struct {
11241037 try constituents.append(field_id);
11251038 }
11261039
1127 return try self.constructStruct(ty, types.items, constituents.items);
1040 const comp_ty_id = try self.resolveType(ty, .direct);
1041 return try self.constructComposite(comp_ty_id, constituents.items);
11281042 },
11291043 .tuple_type => unreachable, // TODO
11301044 else => unreachable,
......@@ -1149,8 +1063,6 @@ const NavGen = struct {
11491063 }
11501064
11511065 fn constantPtr(self: *NavGen, ptr_val: Value) Error!IdRef {
1152 // TODO: Caching??
1153
11541066 const pt = self.pt;
11551067
11561068 if (ptr_val.isUndef(pt.zcu)) {
......@@ -1201,7 +1113,7 @@ const NavGen = struct {
12011113 .elem_ptr => |elem| {
12021114 const parent_ptr_id = try self.derivePtr(elem.parent.*);
12031115 const parent_ptr_ty = try elem.parent.ptrType(pt);
1204 const index_id = try self.constInt(Type.usize, elem.elem_idx, .direct);
1116 const index_id = try self.constInt(Type.usize, elem.elem_idx);
12051117 return self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id);
12061118 },
12071119 .offset_and_cast => |oac| {
......@@ -1255,7 +1167,7 @@ const NavGen = struct {
12551167
12561168 // Uav refs are always generic.
12571169 assert(ty.ptrAddressSpace(zcu) == .generic);
1258 const decl_ptr_ty_id = try self.ptrType(uav_ty, .Generic);
1170 const decl_ptr_ty_id = try self.ptrType(uav_ty, .Generic, .indirect);
12591171 const ptr_id = try self.resolveUav(uav.val);
12601172
12611173 if (decl_ptr_ty_id != ty_id) {
......@@ -1310,7 +1222,7 @@ const NavGen = struct {
13101222 const storage_class = self.spvStorageClass(nav.getAddrspace());
13111223 try self.addFunctionDep(spv_decl_index, storage_class);
13121224
1313 const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
1225 const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class, .indirect);
13141226
13151227 const ptr_id = switch (storage_class) {
13161228 .Generic => try self.castToGeneric(decl_ptr_ty_id, decl_id),
......@@ -1359,23 +1271,11 @@ const NavGen = struct {
13591271 }
13601272
13611273 fn arrayType(self: *NavGen, len: u32, child_ty: IdRef) !IdRef {
1362 // TODO: Cache??
1363 const len_id = try self.constInt(Type.u32, len, .direct);
1364 const result_id = self.spv.allocId();
1365
1366 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeArray, .{
1367 .id_result = result_id,
1368 .element_type = child_ty,
1369 .length = len_id,
1370 });
1371 return result_id;
1372 }
1373
1374 fn ptrType(self: *NavGen, child_ty: Type, storage_class: StorageClass) !IdRef {
1375 return try self.ptrType2(child_ty, storage_class, .indirect);
1274 const len_id = try self.constInt(Type.u32, len);
1275 return self.spv.arrayType(len_id, child_ty);
13761276 }
13771277
1378 fn ptrType2(self: *NavGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef {
1278 fn ptrType(self: *NavGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef {
13791279 const key = .{ child_ty.toIntern(), storage_class, child_repr };
13801280 const entry = try self.ptr_types.getOrPut(self.gpa, key);
13811281 if (entry.found_existing) {
......@@ -1408,8 +1308,7 @@ const NavGen = struct {
14081308 }
14091309
14101310 fn functionType(self: *NavGen, return_ty: Type, param_types: []const Type) !IdRef {
1411 // TODO: Cache??
1412
1311 const return_ty_id = try self.resolveFnReturnType(return_ty);
14131312 const param_ids = try self.gpa.alloc(IdRef, param_types.len);
14141313 defer self.gpa.free(param_ids);
14151314
......@@ -1417,14 +1316,7 @@ const NavGen = struct {
14171316 param_id.* = try self.resolveType(param_ty, .direct);
14181317 }
14191318
1420 const ty_id = self.spv.allocId();
1421 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeFunction, .{
1422 .id_result = ty_id,
1423 .return_type = try self.resolveFnReturnType(return_ty),
1424 .id_ref_2 = param_ids,
1425 });
1426
1427 return ty_id;
1319 return self.spv.functionType(return_ty_id, param_ids);
14281320 }
14291321
14301322 fn zigScalarOrVectorTypeLike(self: *NavGen, new_ty: Type, base_ty: Type) !Type {
......@@ -1702,13 +1594,7 @@ const NavGen = struct {
17021594
17031595 const child_ty = Type.fromInterned(ptr_info.child);
17041596 const storage_class = self.spvStorageClass(ptr_info.flags.address_space);
1705 const ptr_ty_id = try self.ptrType(child_ty, storage_class);
1706
1707 if (target.os.tag == .vulkan and ptr_info.flags.size == .many) {
1708 try self.spv.decorate(ptr_ty_id, .{ .ArrayStride = .{
1709 .array_stride = @intCast(child_ty.abiSize(zcu)),
1710 } });
1711 }
1597 const ptr_ty_id = try self.ptrType(child_ty, storage_class, .indirect);
17121598
17131599 if (ptr_info.flags.size != .slice) {
17141600 return ptr_ty_id;
......@@ -1755,10 +1641,6 @@ const NavGen = struct {
17551641 defer self.gpa.free(type_name);
17561642 try self.spv.debugName(result_id, type_name);
17571643
1758 if (target.os.tag == .vulkan) {
1759 try self.spv.decorate(result_id, .Block); // Decorate all structs as block for now...
1760 }
1761
17621644 return result_id;
17631645 },
17641646 .struct_type => ip.loadStructType(ty.toIntern()),
......@@ -1804,10 +1686,6 @@ const NavGen = struct {
18041686 defer self.gpa.free(type_name);
18051687 try self.spv.debugName(result_id, type_name);
18061688
1807 if (target.os.tag == .vulkan) {
1808 try self.spv.decorate(result_id, .Block); // Decorate all structs as block for now...
1809 }
1810
18111689 return result_id;
18121690 },
18131691 .optional => {
......@@ -2073,12 +1951,13 @@ const NavGen = struct {
20731951 .exploded_vector => |range| {
20741952 assert(self.ty.isVector(zcu));
20751953 assert(self.ty.vectorLen(zcu) == range.len);
2076 const consituents = try ng.gpa.alloc(IdRef, range.len);
2077 defer ng.gpa.free(consituents);
2078 for (consituents, 0..range.len) |*id, i| {
1954 const constituents = try ng.gpa.alloc(IdRef, range.len);
1955 defer ng.gpa.free(constituents);
1956 for (constituents, 0..range.len) |*id, i| {
20791957 id.* = range.at(i);
20801958 }
2081 return ng.constructVector(self.ty, consituents);
1959 const result_ty_id = try ng.resolveType(self.ty, .direct);
1960 return ng.constructComposite(result_ty_id, constituents);
20821961 },
20831962 }
20841963 }
......@@ -2282,7 +2161,7 @@ const NavGen = struct {
22822161 .child = tmp.ty.toIntern(),
22832162 });
22842163
2285 const vector = try ng.constructVectorSplat(vector_ty, id);
2164 const vector = try ng.constructCompositeSplat(vector_ty, id);
22862165 return .{
22872166 .ty = vector_ty,
22882167 .value = .{ .spv_vectorwise = vector },
......@@ -3045,7 +2924,7 @@ const NavGen = struct {
30452924 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
30462925 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
30472926
3048 const zero_id = try self.constInt(Type.u32, 0, .direct);
2927 const zero_id = try self.constInt(Type.u32, 0);
30492928 // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
30502929 // a load.
30512930 const tmp = self.spv.allocId();
......@@ -3187,7 +3066,7 @@ const NavGen = struct {
31873066 const storage_class = self.spvStorageClass(nav.getAddrspace());
31883067 assert(storage_class != .Generic); // These should be instance globals
31893068
3190 const ptr_ty_id = try self.ptrType(ty, storage_class);
3069 const ptr_ty_id = try self.ptrType(ty, storage_class, .indirect);
31913070
31923071 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
31933072 .id_result_type = ptr_ty_id,
......@@ -3208,7 +3087,7 @@ const NavGen = struct {
32083087
32093088 try self.spv.declareDeclDeps(spv_decl_index, &.{});
32103089
3211 const ptr_ty_id = try self.ptrType(ty, .Function);
3090 const ptr_ty_id = try self.ptrType(ty, .Function, .indirect);
32123091
32133092 if (maybe_init_val) |init_val| {
32143093 // TODO: Combine with resolveAnonDecl?
......@@ -3265,8 +3144,8 @@ const NavGen = struct {
32653144 }
32663145
32673146 fn intFromBool2(self: *NavGen, value: Temporary, result_ty: Type) !Temporary {
3268 const zero_id = try self.constInt(result_ty, 0, .direct);
3269 const one_id = try self.constInt(result_ty, 1, .direct);
3147 const zero_id = try self.constInt(result_ty, 0);
3148 const one_id = try self.constInt(result_ty, 1);
32703149
32713150 return try self.buildSelect(
32723151 value,
......@@ -3648,12 +3527,12 @@ const NavGen = struct {
36483527 .strange_integer => switch (info.signedness) {
36493528 .unsigned => {
36503529 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
3651 const mask_id = try self.constInt(ty.scalarType(zcu), mask_value, .direct);
3530 const mask_id = try self.constInt(ty.scalarType(zcu), mask_value);
36523531 return try self.buildBinary(.bit_and, value, Temporary.init(ty.scalarType(zcu), mask_id));
36533532 },
36543533 .signed => {
36553534 // Shift left and right so that we can copy the sight bit that way.
3656 const shift_amt_id = try self.constInt(ty.scalarType(zcu), info.backing_bits - info.bits, .direct);
3535 const shift_amt_id = try self.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);
36573536 const shift_amt = Temporary.init(ty.scalarType(zcu), shift_amt_id);
36583537 const left = try self.buildBinary(.sll, value, shift_amt);
36593538 return try self.buildBinary(.sra, left, shift_amt);
......@@ -3687,7 +3566,7 @@ const NavGen = struct {
36873566 const div = try self.buildBinary(.s_div, lhs, rhs);
36883567 const rem = try self.buildBinary(.s_rem, lhs, rhs);
36893568
3690 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
3569 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
36913570
36923571 const rem_is_not_zero = try self.buildCmp(.i_ne, rem, zero);
36933572
......@@ -3863,7 +3742,7 @@ const NavGen = struct {
38633742 // = (rhs < 0) == (value < lhs)
38643743 // = (rhs < 0) == (lhs > value)
38653744 .signed => blk: {
3866 const zero = Temporary.init(rhs.ty, try self.constInt(rhs.ty, 0, .direct));
3745 const zero = Temporary.init(rhs.ty, try self.constInt(rhs.ty, 0));
38673746 const rhs_lt_zero = try self.buildCmp(.s_lt, rhs, zero);
38683747 const result_gt_lhs = try self.buildCmp(scmp, lhs, result);
38693748 break :blk try self.buildCmp(.l_eq, rhs_lt_zero, result_gt_lhs);
......@@ -3872,11 +3751,8 @@ const NavGen = struct {
38723751
38733752 const ov = try self.intFromBool(overflowed);
38743753
3875 return try self.constructStruct(
3876 result_ty,
3877 &.{ result.ty, ov.ty },
3878 &.{ try result.materialize(self), try ov.materialize(self) },
3879 );
3754 const result_ty_id = try self.resolveType(result_ty, .direct);
3755 return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
38803756 }
38813757
38823758 fn airMulOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -3928,11 +3804,11 @@ const NavGen = struct {
39283804 const result = try self.normalize(low_bits, info);
39293805
39303806 // Shift the result bits away to get the overflow bits.
3931 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits, .direct));
3807 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits));
39323808 const overflow = try self.buildBinary(.srl, full_result, shift);
39333809
39343810 // Directly check if its zero in the op_ty without converting first.
3935 const zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
3811 const zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0));
39363812 const overflowed = try self.buildCmp(.i_ne, zero, overflow);
39373813
39383814 break :blk .{ result, overflowed };
......@@ -3946,7 +3822,7 @@ const NavGen = struct {
39463822 // Overflow happened if the high-bits of the result are non-zero OR if the
39473823 // high bits of the low word of the result (those outside the range of the
39483824 // int) are nonzero.
3949 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
3825 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
39503826 const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);
39513827
39523828 // If no overflow bits in low_bits, no extra work needs to be done.
......@@ -3955,7 +3831,7 @@ const NavGen = struct {
39553831 }
39563832
39573833 // Shift the result bits away to get the overflow bits.
3958 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits, .direct));
3834 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits));
39593835 const low_overflow = try self.buildBinary(.srl, low_bits, shift);
39603836 const low_overflowed = try self.buildCmp(.i_ne, zero, low_overflow);
39613837
......@@ -3974,7 +3850,7 @@ const NavGen = struct {
39743850 // overflow should be -1 when
39753851 // (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
39763852
3977 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
3853 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
39783854 const lhs_negative = try self.buildCmp(.s_lt, lhs, zero);
39793855 const rhs_negative = try self.buildCmp(.s_lt, rhs, zero);
39803856 const lhs_positive = try self.buildCmp(.s_gt, lhs, zero);
......@@ -4003,13 +3879,13 @@ const NavGen = struct {
40033879 // bit for the expected overflow bits.
40043880 // To do that, shift out everything bit the sign bit and
40053881 // then check what remains.
4006 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits - 1, .direct));
3882 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits - 1));
40073883 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
40083884 // for negative cases.
40093885 const overflow = try self.buildBinary(.sra, full_result, shift);
40103886
4011 const long_all_set = Temporary.init(full_result.ty, try self.constInt(full_result.ty, -1, .direct));
4012 const long_zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
3887 const long_all_set = Temporary.init(full_result.ty, try self.constInt(full_result.ty, -1));
3888 const long_zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0));
40133889 const mask = try self.buildSelect(expected_overflow_bit, long_all_set, long_zero);
40143890
40153891 const overflowed = try self.buildCmp(.i_ne, mask, overflow);
......@@ -4022,7 +3898,7 @@ const NavGen = struct {
40223898 // Truncate result if required.
40233899 const result = try self.normalize(low_bits, info);
40243900
4025 const all_set = Temporary.init(lhs.ty, try self.constInt(lhs.ty, -1, .direct));
3901 const all_set = Temporary.init(lhs.ty, try self.constInt(lhs.ty, -1));
40263902 const mask = try self.buildSelect(expected_overflow_bit, all_set, zero);
40273903
40283904 // Like with unsigned, overflow happened if high_bits are not the ones we expect,
......@@ -4038,7 +3914,7 @@ const NavGen = struct {
40383914 }
40393915
40403916 // Shift the result bits away to get the overflow bits.
4041 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1, .direct));
3917 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1));
40423918 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
40433919 // for negative cases.
40443920 const low_overflow = try self.buildBinary(.sra, low_bits, shift);
......@@ -4052,11 +3928,8 @@ const NavGen = struct {
40523928
40533929 const ov = try self.intFromBool(overflowed);
40543930
4055 return try self.constructStruct(
4056 result_ty,
4057 &.{ result.ty, ov.ty },
4058 &.{ try result.materialize(self), try ov.materialize(self) },
4059 );
3931 const result_ty_id = try self.resolveType(result_ty, .direct);
3932 return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
40603933 }
40613934
40623935 fn airShlOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4092,11 +3965,8 @@ const NavGen = struct {
40923965 const overflowed = try self.buildCmp(.i_ne, base, right);
40933966 const ov = try self.intFromBool(overflowed);
40943967
4095 return try self.constructStruct(
4096 result_ty,
4097 &.{ result.ty, ov.ty },
4098 &.{ try result.materialize(self), try ov.materialize(self) },
4099 );
3968 const result_ty_id = try self.resolveType(result_ty, .direct);
3969 return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
41003970 }
41013971
41023972 fn airMulAdd(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4163,7 +4033,7 @@ const NavGen = struct {
41634033 const operand_id = try self.resolve(ty_op.operand);
41644034 const result_ty = self.typeOfIndex(inst);
41654035
4166 return try self.constructVectorSplat(result_ty, operand_id);
4036 return try self.constructCompositeSplat(result_ty, operand_id);
41674037 }
41684038
41694039 fn airReduce(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4297,10 +4167,10 @@ const NavGen = struct {
42974167
42984168 // Fall back to manually extracting and inserting components.
42994169
4300 const components = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
4301 defer self.gpa.free(components);
4170 const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
4171 defer self.gpa.free(constituents);
43024172
4303 for (components, 0..) |*id, i| {
4173 for (constituents, 0..) |*id, i| {
43044174 const elem = try mask.elemValue(pt, i);
43054175 if (elem.isUndef(zcu)) {
43064176 id.* = try self.spv.constUndef(scalar_ty_id);
......@@ -4315,14 +4185,15 @@ const NavGen = struct {
43154185 }
43164186 }
43174187
4318 return try self.constructVector(result_ty, components);
4188 const result_ty_id = try self.resolveType(result_ty, .direct);
4189 return try self.constructComposite(result_ty_id, constituents);
43194190 }
43204191
43214192 fn indicesToIds(self: *NavGen, indices: []const u32) ![]IdRef {
43224193 const ids = try self.gpa.alloc(IdRef, indices.len);
43234194 errdefer self.gpa.free(ids);
43244195 for (indices, ids) |index, *id| {
4325 id.* = try self.constInt(Type.u32, index, .direct);
4196 id.* = try self.constInt(Type.u32, index);
43264197 }
43274198
43284199 return ids;
......@@ -4676,7 +4547,7 @@ const NavGen = struct {
46764547 break :blk result_id;
46774548 }
46784549
4679 const dst_ptr_ty_id = try self.ptrType(dst_ty, .Function);
4550 const dst_ptr_ty_id = try self.ptrType(dst_ty, .Function, .indirect);
46804551
46814552 const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function });
46824553 try self.store(src_ty, tmp_id, src_id, .{});
......@@ -4851,7 +4722,7 @@ const NavGen = struct {
48514722 const elem_ptr_ty_id = try self.resolveType(elem_ptr_ty, .direct);
48524723
48534724 const array_ptr_id = try self.resolve(ty_op.operand);
4854 const len_id = try self.constInt(Type.usize, array_ty.arrayLen(zcu), .direct);
4725 const len_id = try self.constInt(Type.usize, array_ty.arrayLen(zcu));
48554726
48564727 const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu))
48574728 // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type.
......@@ -4860,11 +4731,8 @@ const NavGen = struct {
48604731 // Convert the pointer-to-array to a pointer to the first element.
48614732 try self.accessChain(elem_ptr_ty_id, array_ptr_id, &.{0});
48624733
4863 return try self.constructStruct(
4864 slice_ty,
4865 &.{ elem_ptr_ty, Type.usize },
4866 &.{ elem_ptr_id, len_id },
4867 );
4734 const slice_ty_id = try self.resolveType(slice_ty, .direct);
4735 return try self.constructComposite(slice_ty_id, &.{ elem_ptr_id, len_id });
48684736 }
48694737
48704738 fn airSlice(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4872,16 +4740,9 @@ const NavGen = struct {
48724740 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
48734741 const ptr_id = try self.resolve(bin_op.lhs);
48744742 const len_id = try self.resolve(bin_op.rhs);
4875 const ptr_ty = self.typeOf(bin_op.lhs);
48764743 const slice_ty = self.typeOfIndex(inst);
4877
4878 // Note: Types should not need to be converted to direct, these types
4879 // dont need to be converted.
4880 return try self.constructStruct(
4881 slice_ty,
4882 &.{ ptr_ty, Type.usize },
4883 &.{ ptr_id, len_id },
4884 );
4744 const slice_ty_id = try self.resolveType(slice_ty, .direct);
4745 return try self.constructComposite(slice_ty_id, &.{ ptr_id, len_id });
48854746 }
48864747
48874748 fn airAggregateInit(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -4936,11 +4797,8 @@ const NavGen = struct {
49364797 else => unreachable,
49374798 }
49384799
4939 return try self.constructStruct(
4940 result_ty,
4941 types[0..index],
4942 constituents[0..index],
4943 );
4800 const result_ty_id = try self.resolveType(result_ty, .direct);
4801 return try self.constructComposite(result_ty_id, constituents[0..index]);
49444802 },
49454803 .vector => {
49464804 const n_elems = result_ty.vectorLen(zcu);
......@@ -4951,7 +4809,8 @@ const NavGen = struct {
49514809 elem_ids[i] = try self.resolve(element);
49524810 }
49534811
4954 return try self.constructVector(result_ty, elem_ids);
4812 const result_ty_id = try self.resolveType(result_ty, .direct);
4813 return try self.constructComposite(result_ty_id, elem_ids);
49554814 },
49564815 .array => {
49574816 const array_info = result_ty.arrayInfo(zcu);
......@@ -4968,7 +4827,8 @@ const NavGen = struct {
49684827 elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect);
49694828 }
49704829
4971 return try self.constructArray(result_ty, elem_ids);
4830 const result_ty_id = try self.resolveType(result_ty, .direct);
4831 return try self.constructComposite(result_ty_id, elem_ids);
49724832 },
49734833 else => unreachable,
49744834 }
......@@ -4984,7 +4844,7 @@ const NavGen = struct {
49844844 const elem_ty = array_ty.childType(zcu);
49854845 const abi_size = elem_ty.abiSize(zcu);
49864846 const size = array_ty.arrayLenIncludingSentinel(zcu) * abi_size;
4987 return try self.constInt(Type.usize, size, .direct);
4847 return try self.constInt(Type.usize, size);
49884848 },
49894849 .many, .c => unreachable,
49904850 }
......@@ -5060,7 +4920,7 @@ const NavGen = struct {
50604920 const zcu = self.pt.zcu;
50614921 // Construct new pointer type for the resulting pointer
50624922 const elem_ty = ptr_ty.elemType2(zcu); // use elemType() so that we get T for *[N]T.
5063 const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(zcu)));
4923 const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(zcu)), .indirect);
50644924 if (ptr_ty.isSinglePointer(zcu)) {
50654925 // Pointer-to-array. In this case, the resulting pointer is not of the same type
50664926 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
......@@ -5115,8 +4975,8 @@ const NavGen = struct {
51154975 const is_vector = array_ty.isVector(zcu);
51164976
51174977 const elem_repr: Repr = if (is_vector) .direct else .indirect;
5118 const ptr_array_ty_id = try self.ptrType2(array_ty, .Function, .direct);
5119 const ptr_elem_ty_id = try self.ptrType2(elem_ty, .Function, elem_repr);
4978 const ptr_array_ty_id = try self.ptrType(array_ty, .Function, .direct);
4979 const ptr_elem_ty_id = try self.ptrType(elem_ty, .Function, elem_repr);
51204980
51214981 const tmp_id = self.spv.allocId();
51224982 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
......@@ -5171,7 +5031,7 @@ const NavGen = struct {
51715031 const scalar_ty = vector_ty.scalarType(zcu);
51725032
51735033 const storage_class = self.spvStorageClass(vector_ptr_ty.ptrAddressSpace(zcu));
5174 const scalar_ptr_ty_id = try self.ptrType(scalar_ty, storage_class);
5034 const scalar_ptr_ty_id = try self.ptrType(scalar_ty, storage_class, .indirect);
51755035
51765036 const vector_ptr = try self.resolve(data.vector_ptr);
51775037 const index = try self.resolve(extra.lhs);
......@@ -5193,7 +5053,7 @@ const NavGen = struct {
51935053 if (layout.tag_size == 0) return;
51945054
51955055 const tag_ty = un_ty.unionTagTypeSafety(zcu).?;
5196 const tag_ptr_ty_id = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(zcu)));
5056 const tag_ptr_ty_id = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(zcu)), .indirect);
51975057
51985058 const union_ptr_id = try self.resolve(bin_op.lhs);
51995059 const new_tag_id = try self.resolve(bin_op.rhs);
......@@ -5252,23 +5112,23 @@ const NavGen = struct {
52525112 } else 0;
52535113
52545114 if (!layout.has_payload) {
5255 return try self.constInt(tag_ty, tag_int, .direct);
5115 return try self.constInt(tag_ty, tag_int);
52565116 }
52575117
52585118 const tmp_id = try self.alloc(ty, .{ .storage_class = .Function });
52595119
52605120 if (layout.tag_size != 0) {
5261 const tag_ptr_ty_id = try self.ptrType(tag_ty, .Function);
5121 const tag_ptr_ty_id = try self.ptrType(tag_ty, .Function, .indirect);
52625122 const ptr_id = try self.accessChain(tag_ptr_ty_id, tmp_id, &.{@as(u32, @intCast(layout.tag_index))});
5263 const tag_id = try self.constInt(tag_ty, tag_int, .direct);
5123 const tag_id = try self.constInt(tag_ty, tag_int);
52645124 try self.store(tag_ty, ptr_id, tag_id, .{});
52655125 }
52665126
52675127 const payload_ty = Type.fromInterned(union_ty.field_types.get(ip)[active_field]);
52685128 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
5269 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function);
5129 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
52705130 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
5271 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function);
5131 const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
52725132 const active_pl_ptr_id = self.spv.allocId();
52735133 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
52745134 .id_result_type = active_pl_ptr_ty_id,
......@@ -5332,10 +5192,10 @@ const NavGen = struct {
53325192 const tmp_id = try self.alloc(object_ty, .{ .storage_class = .Function });
53335193 try self.store(object_ty, tmp_id, object_id, .{});
53345194
5335 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function);
5195 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
53365196 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
53375197
5338 const active_pl_ptr_ty_id = try self.ptrType(field_ty, .Function);
5198 const active_pl_ptr_ty_id = try self.ptrType(field_ty, .Function, .indirect);
53395199 const active_pl_ptr_id = self.spv.allocId();
53405200 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
53415201 .id_result_type = active_pl_ptr_ty_id,
......@@ -5365,7 +5225,7 @@ const NavGen = struct {
53655225 const base_ptr_int = base_ptr_int: {
53665226 if (field_offset == 0) break :base_ptr_int field_ptr_int;
53675227
5368 const field_offset_id = try self.constInt(Type.usize, field_offset, .direct);
5228 const field_offset_id = try self.constInt(Type.usize, field_offset);
53695229 const field_ptr_tmp = Temporary.init(Type.usize, field_ptr_int);
53705230 const field_offset_tmp = Temporary.init(Type.usize, field_offset_id);
53715231 const result = try self.buildBinary(.i_sub, field_ptr_tmp, field_offset_tmp);
......@@ -5415,7 +5275,7 @@ const NavGen = struct {
54155275 }
54165276
54175277 const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(zcu));
5418 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class);
5278 const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class, .indirect);
54195279 const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index});
54205280
54215281 const active_pl_ptr_id = self.spv.allocId();
......@@ -5456,7 +5316,7 @@ const NavGen = struct {
54565316 ty: Type,
54575317 options: AllocOptions,
54585318 ) !IdRef {
5459 const ptr_fn_ty_id = try self.ptrType(ty, .Function);
5319 const ptr_fn_ty_id = try self.ptrType(ty, .Function, .indirect);
54605320
54615321 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
54625322 // directly generate them into func.prologue instead of the body.
......@@ -5475,7 +5335,7 @@ const NavGen = struct {
54755335
54765336 switch (options.storage_class) {
54775337 .Generic => {
5478 const ptr_gn_ty_id = try self.ptrType(ty, .Generic);
5338 const ptr_gn_ty_id = try self.ptrType(ty, .Generic, .indirect);
54795339 // Convert to a generic pointer
54805340 return self.castToGeneric(ptr_gn_ty_id, var_id);
54815341 },
......@@ -5724,7 +5584,7 @@ const NavGen = struct {
57245584 assert(cf.block_stack.items.len > 0);
57255585
57265586 // Check if the target of the branch was this current block.
5727 const this_block = try self.constInt(Type.u32, @intFromEnum(inst), .direct);
5587 const this_block = try self.constInt(Type.u32, @intFromEnum(inst));
57285588 const jump_to_this_block_id = self.spv.allocId();
57295589 const bool_ty_id = try self.resolveType(Type.bool, .direct);
57305590 try self.func.body.emit(self.spv.gpa, .OpIEqual, .{
......@@ -5804,7 +5664,7 @@ const NavGen = struct {
58045664 try self.store(operand_ty, block_result_var_id, operand_id, .{});
58055665 }
58065666
5807 const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst), .direct);
5667 const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst));
58085668 try self.structuredBreak(next_block);
58095669 },
58105670 .unstructured => |cf| {
......@@ -5968,7 +5828,7 @@ const NavGen = struct {
59685828 // Functions with an empty error set are emitted with an error code
59695829 // return type and return zero so they can be function pointers coerced
59705830 // to functions that return anyerror.
5971 const no_err_id = try self.constInt(Type.anyerror, 0, .direct);
5831 const no_err_id = try self.constInt(Type.anyerror, 0);
59725832 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
59735833 } else {
59745834 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
......@@ -5992,7 +5852,7 @@ const NavGen = struct {
59925852 // Functions with an empty error set are emitted with an error code
59935853 // return type and return zero so they can be function pointers coerced
59945854 // to functions that return anyerror.
5995 const no_err_id = try self.constInt(Type.anyerror, 0, .direct);
5855 const no_err_id = try self.constInt(Type.anyerror, 0);
59965856 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
59975857 } else {
59985858 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
......@@ -6026,7 +5886,7 @@ const NavGen = struct {
60265886 else
60275887 err_union_id;
60285888
6029 const zero_id = try self.constInt(Type.anyerror, 0, .direct);
5889 const zero_id = try self.constInt(Type.anyerror, 0);
60305890 const is_err_id = self.spv.allocId();
60315891 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
60325892 .id_result_type = bool_ty_id,
......@@ -6134,7 +5994,8 @@ const NavGen = struct {
61345994 types[eu_layout.errorFieldIndex()] = Type.anyerror;
61355995 types[eu_layout.payloadFieldIndex()] = payload_ty;
61365996
6137 return try self.constructStruct(err_union_ty, &types, &members);
5997 const err_union_ty_id = try self.resolveType(err_union_ty, .direct);
5998 return try self.constructComposite(err_union_ty_id, &members);
61385999 }
61396000
61406001 fn airWrapErrUnionPayload(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
......@@ -6145,18 +6006,19 @@ const NavGen = struct {
61456006 const eu_layout = self.errorUnionLayout(payload_ty);
61466007
61476008 if (!eu_layout.payload_has_bits) {
6148 return try self.constInt(Type.anyerror, 0, .direct);
6009 return try self.constInt(Type.anyerror, 0);
61496010 }
61506011
61516012 var members: [2]IdRef = undefined;
6152 members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0, .direct);
6013 members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0);
61536014 members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id);
61546015
61556016 var types: [2]Type = undefined;
61566017 types[eu_layout.errorFieldIndex()] = Type.anyerror;
61576018 types[eu_layout.payloadFieldIndex()] = payload_ty;
61586019
6159 return try self.constructStruct(err_union_ty, &types, &members);
6020 const err_union_ty_id = try self.resolveType(err_union_ty, .direct);
6021 return try self.constructComposite(err_union_ty_id, &members);
61606022 }
61616023
61626024 fn airIsNull(self: *NavGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef {
......@@ -6204,7 +6066,7 @@ const NavGen = struct {
62046066 if (is_pointer) {
62056067 if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
62066068 const storage_class = self.spvStorageClass(operand_ty.ptrAddressSpace(zcu));
6207 const bool_ptr_ty_id = try self.ptrType(Type.bool, storage_class);
6069 const bool_ptr_ty_id = try self.ptrType(Type.bool, storage_class, .indirect);
62086070 const tag_ptr_id = try self.accessChain(bool_ptr_ty_id, operand_id, &.{1});
62096071 break :blk try self.load(Type.bool, tag_ptr_id, .{});
62106072 }
......@@ -6267,7 +6129,7 @@ const NavGen = struct {
62676129 .id_result_type = bool_ty_id,
62686130 .id_result = result_id,
62696131 .operand_1 = error_id,
6270 .operand_2 = try self.constInt(Type.anyerror, 0, .direct),
6132 .operand_2 = try self.constInt(Type.anyerror, 0),
62716133 },
62726134 ),
62736135 }
......@@ -6335,8 +6197,8 @@ const NavGen = struct {
63356197
63366198 const payload_id = try self.convertToIndirect(payload_ty, operand_id);
63376199 const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) };
6338 const types = [_]Type{ payload_ty, Type.bool };
6339 return try self.constructStruct(optional_ty, &types, &members);
6200 const optional_ty_id = try self.resolveType(optional_ty, .direct);
6201 return try self.constructComposite(optional_ty_id, &members);
63406202 }
63416203
63426204 fn airSwitchBr(self: *NavGen, inst: Air.Inst.Index) !void {
......@@ -6752,13 +6614,13 @@ const NavGen = struct {
67526614
67536615 fn builtin3D(self: *NavGen, result_ty: Type, builtin: spec.BuiltIn, dimension: u32, out_of_range_value: anytype) !IdRef {
67546616 if (dimension >= 3) {
6755 return try self.constInt(result_ty, out_of_range_value, .direct);
6617 return try self.constInt(result_ty, out_of_range_value);
67566618 }
67576619 const vec_ty = try self.pt.vectorType(.{
67586620 .len = 3,
67596621 .child = result_ty.toIntern(),
67606622 });
6761 const ptr_ty_id = try self.ptrType(vec_ty, .Input);
6623 const ptr_ty_id = try self.ptrType(vec_ty, .Input, .indirect);
67626624 const spv_decl_index = try self.spv.builtin(ptr_ty_id, builtin);
67636625 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
67646626 const ptr = self.spv.declPtr(spv_decl_index).result_id;
src/codegen/spirv/Module.zig+83-7
......@@ -10,6 +10,8 @@ const Module = @This();
1010const std = @import("std");
1111const Allocator = std.mem.Allocator;
1212const assert = std.debug.assert;
13const autoHashStrat = std.hash.autoHashStrat;
14const Wyhash = std.hash.Wyhash;
1315
1416const spec = @import("spec.zig");
1517const Word = spec.Word;
......@@ -19,6 +21,19 @@ const IdResultType = spec.IdResultType;
1921
2022const Section = @import("Section.zig");
2123
24/// Helper HashMap type to hash deeply
25fn DeepHashMap(K: type, V: type) type {
26 return std.HashMapUnmanaged(K, V, struct {
27 pub fn hash(ctx: @This(), key: K) u64 {
28 _ = ctx;
29 var hasher = Wyhash.init(0);
30 autoHashStrat(&hasher, key, .Deep);
31 return hasher.final();
32 }
33 pub const eql = std.hash_map.getAutoEqlFn(K, @This());
34 }, std.hash_map.default_max_load_percentage);
35}
36
2237/// This structure represents a function that isc in-progress of being emitted.
2338/// Commonly, the contents of this structure will be merged with the appropriate
2439/// sections of the module and re-used. Note that the SPIR-V module system makes
......@@ -159,8 +174,13 @@ cache: struct {
159174 // This cache is required so that @Vector(X, u1) in direct representation has the
160175 // same ID as @Vector(X, bool) in indirect representation.
161176 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty,
177 array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty,
178 function_types: DeepHashMap(struct { IdRef, []const IdRef }, IdRef) = .empty,
162179
163180 builtins: std.AutoHashMapUnmanaged(struct { IdRef, spec.BuiltIn }, Decl.Index) = .empty,
181 decorations: std.AutoHashMapUnmanaged(struct { IdRef, spec.Decoration }, void) = .empty,
182
183 bool_const: [2]?IdRef = .{ null, null },
164184} = .{},
165185
166186/// Set of Decls, referred to by Decl.Index.
......@@ -201,7 +221,10 @@ pub fn deinit(self: *Module) void {
201221 self.cache.int_types.deinit(self.gpa);
202222 self.cache.float_types.deinit(self.gpa);
203223 self.cache.vector_types.deinit(self.gpa);
224 self.cache.array_types.deinit(self.gpa);
225 self.cache.function_types.deinit(self.gpa);
204226 self.cache.builtins.deinit(self.gpa);
227 self.cache.decorations.deinit(self.gpa);
205228
206229 self.decls.deinit(self.gpa);
207230 self.decl_deps.deinit(self.gpa);
......@@ -477,20 +500,69 @@ pub fn floatType(self: *Module, bits: u16) !IdRef {
477500 return entry.value_ptr.*;
478501}
479502
480pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {
481 const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_id, len });
503pub fn vectorType(self: *Module, len: u32, child_ty_id: IdRef) !IdRef {
504 const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_ty_id, len });
482505 if (!entry.found_existing) {
483506 const result_id = self.allocId();
484507 entry.value_ptr.* = result_id;
485508 try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVector, .{
486509 .id_result = result_id,
487 .component_type = child_id,
510 .component_type = child_ty_id,
488511 .component_count = len,
489512 });
490513 }
491514 return entry.value_ptr.*;
492515}
493516
517pub fn arrayType(self: *Module, len_id: IdRef, child_ty_id: IdRef) !IdRef {
518 const entry = try self.cache.array_types.getOrPut(self.gpa, .{ child_ty_id, len_id });
519 if (!entry.found_existing) {
520 const result_id = self.allocId();
521 entry.value_ptr.* = result_id;
522 try self.sections.types_globals_constants.emit(self.gpa, .OpTypeArray, .{
523 .id_result = result_id,
524 .element_type = child_ty_id,
525 .length = len_id,
526 });
527 }
528 return entry.value_ptr.*;
529}
530
531pub fn functionType(self: *Module, return_ty_id: IdRef, param_type_ids: []const IdRef) !IdRef {
532 const entry = try self.cache.function_types.getOrPut(self.gpa, .{ return_ty_id, param_type_ids });
533 if (!entry.found_existing) {
534 const result_id = self.allocId();
535 entry.value_ptr.* = result_id;
536 try self.sections.types_globals_constants.emit(self.gpa, .OpTypeFunction, .{
537 .id_result = result_id,
538 .return_type = return_ty_id,
539 .id_ref_2 = param_type_ids,
540 });
541 }
542 return entry.value_ptr.*;
543}
544
545pub fn constBool(self: *Module, value: bool) !IdRef {
546 if (self.cache.bool_const[@intFromBool(value)]) |b| return b;
547
548 const result_ty_id = try self.boolType();
549 const result_id = self.allocId();
550 self.cache.bool_const[@intFromBool(value)] = result_id;
551
552 switch (value) {
553 inline else => |value_ct| try self.sections.types_globals_constants.emit(
554 self.gpa,
555 if (value_ct) .OpConstantTrue else .OpConstantFalse,
556 .{
557 .id_result_type = result_ty_id,
558 .id_result = result_id,
559 },
560 ),
561 }
562
563 return result_id;
564}
565
494566/// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer**
495567/// with storage class `.Input`.
496568pub fn builtin(self: *Module, result_ty_id: IdRef, spirv_builtin: spec.BuiltIn) !Decl.Index {
......@@ -534,13 +606,17 @@ pub fn decorate(
534606 target: IdRef,
535607 decoration: spec.Decoration.Extended,
536608) !void {
537 try self.sections.annotations.emit(self.gpa, .OpDecorate, .{
538 .target = target,
539 .decoration = decoration,
540 });
609 const entry = try self.cache.decorations.getOrPut(self.gpa, .{ target, decoration });
610 if (!entry.found_existing) {
611 try self.sections.annotations.emit(self.gpa, .OpDecorate, .{
612 .target = target,
613 .decoration = decoration,
614 });
615 }
541616}
542617
543618/// Decorate a result-id which is a member of some struct.
619/// We really don't have to and shouldn't need to cache this.
544620pub fn decorateMember(
545621 self: *Module,
546622 structure_type: IdRef,
src/link/SpirV/deduplicate.zig+1-1
......@@ -155,7 +155,7 @@ const ModuleInfo = struct {
155155 }
156156 }
157157
158 return ModuleInfo{
158 return .{
159159 .entities = entities.unmanaged,
160160 .operand_is_id = operand_is_id,
161161 // There may be unrelated decorations at the end, so make sure to
src/link/SpirV/prune_unused.zig+1-1
......@@ -166,7 +166,7 @@ const ModuleInfo = struct {
166166 return error.InvalidPhysicalFormat;
167167 }
168168
169 return ModuleInfo{
169 return .{
170170 .functions = functions.unmanaged,
171171 .callee_store = callee_store.items,
172172 .result_id_to_code_offset = result_id_to_code_offset.unmanaged,