| ... | ... | @@ -209,6 +209,10 @@ const DeclGen = struct { |
| 209 | 209 | /// See Object.type_map |
| 210 | 210 | type_map: *TypeMap, |
| 211 | 211 | |
| 212 | /// Child types of pointers that are currently in progress of being resolved. If a pointer |
| 213 | /// is already in this map, its recursive. |
| 214 | wip_pointers: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, CacheRef) = .{}, |
| 215 | |
| 212 | 216 | /// We need to keep track of result ids for block labels, as well as the 'incoming' |
| 213 | 217 | /// blocks for a block. |
| 214 | 218 | blocks: BlockMap = .{}, |
| ... | ... | @@ -295,6 +299,7 @@ const DeclGen = struct { |
| 295 | 299 | pub fn deinit(self: *DeclGen) void { |
| 296 | 300 | self.args.deinit(self.gpa); |
| 297 | 301 | self.inst_results.deinit(self.gpa); |
| 302 | self.wip_pointers.deinit(self.gpa); |
| 298 | 303 | self.blocks.deinit(self.gpa); |
| 299 | 304 | self.func.deinit(self.gpa); |
| 300 | 305 | self.base_line_stack.deinit(self.gpa); |
| ... | ... | @@ -358,8 +363,7 @@ const DeclGen = struct { |
| 358 | 363 | |
| 359 | 364 | const mod = self.module; |
| 360 | 365 | const ty = mod.intern_pool.typeOf(val).toType(); |
| 361 | | const ty_ref = try self.resolveType(ty, .indirect); |
| 362 | | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class); |
| 366 | const ptr_ty_ref = try self.ptrType(ty, storage_class); |
| 363 | 367 | |
| 364 | 368 | const var_id = self.spv.declPtr(spv_decl_index).result_id; |
| 365 | 369 | |
| ... | ... | @@ -582,66 +586,41 @@ const DeclGen = struct { |
| 582 | 586 | } |
| 583 | 587 | |
| 584 | 588 | /// Construct a struct at runtime. |
| 585 | | /// result_ty_ref must be a struct type. |
| 589 | /// ty must be a struct type. |
| 586 | 590 | /// Constituents should be in `indirect` representation (as the elements of a struct should be). |
| 587 | 591 | /// Result is in `direct` representation. |
| 588 | | fn constructStruct(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { |
| 592 | fn constructStruct(self: *DeclGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef { |
| 593 | assert(types.len == constituents.len); |
| 589 | 594 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' |
| 590 | 595 | // operands are not constant. |
| 591 | 596 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| 592 | 597 | // For now, just initialize the struct by setting the fields manually... |
| 593 | 598 | // TODO: Make this OpCompositeConstruct when we can |
| 594 | | const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); |
| 595 | | const ptr_composite_id = self.spv.allocId(); |
| 596 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 597 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 598 | | .id_result = ptr_composite_id, |
| 599 | | .storage_class = .Function, |
| 600 | | }); |
| 601 | | |
| 602 | | const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type; |
| 603 | | const member_types = spv_composite_ty.member_types; |
| 604 | | |
| 605 | | for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| { |
| 606 | | const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Function); |
| 599 | const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); |
| 600 | for (constituents, types, 0..) |constitent_id, member_ty, index| { |
| 601 | const ptr_member_ty_ref = try self.ptrType(member_ty, .Function); |
| 607 | 602 | const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); |
| 608 | 603 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 609 | 604 | .pointer = ptr_id, |
| 610 | 605 | .object = constitent_id, |
| 611 | 606 | }); |
| 612 | 607 | } |
| 613 | | const result_id = self.spv.allocId(); |
| 614 | | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 615 | | .id_result_type = self.typeId(result_ty_ref), |
| 616 | | .id_result = result_id, |
| 617 | | .pointer = ptr_composite_id, |
| 618 | | }); |
| 619 | | return result_id; |
| 608 | return try self.load(ty, ptr_composite_id, .{}); |
| 620 | 609 | } |
| 621 | 610 | |
| 622 | 611 | /// Construct an array at runtime. |
| 623 | | /// result_ty_ref must be an array type. |
| 612 | /// ty must be an array type. |
| 624 | 613 | /// Constituents should be in `indirect` representation (as the elements of an array should be). |
| 625 | 614 | /// Result is in `direct` representation. |
| 626 | | fn constructArray(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { |
| 615 | fn constructArray(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { |
| 627 | 616 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' |
| 628 | 617 | // operands are not constant. |
| 629 | 618 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| 630 | 619 | // For now, just initialize the struct by setting the fields manually... |
| 631 | 620 | // TODO: Make this OpCompositeConstruct when we can |
| 632 | | // TODO: Make this Function storage type |
| 633 | | const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); |
| 634 | | const ptr_composite_id = self.spv.allocId(); |
| 635 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 636 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 637 | | .id_result = ptr_composite_id, |
| 638 | | .storage_class = .Function, |
| 639 | | }); |
| 640 | | |
| 641 | | const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type; |
| 642 | | const elem_ty_ref = spv_composite_ty.element_type; |
| 643 | | const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function); |
| 644 | | |
| 621 | const mod = self.module; |
| 622 | const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); |
| 623 | const ptr_elem_ty_ref = try self.ptrType(ty.elemType2(mod), .Function); |
| 645 | 624 | for (constituents, 0..) |constitent_id, index| { |
| 646 | 625 | const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); |
| 647 | 626 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| ... | ... | @@ -649,13 +628,8 @@ const DeclGen = struct { |
| 649 | 628 | .object = constitent_id, |
| 650 | 629 | }); |
| 651 | 630 | } |
| 652 | | const result_id = self.spv.allocId(); |
| 653 | | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 654 | | .id_result_type = self.typeId(result_ty_ref), |
| 655 | | .id_result = result_id, |
| 656 | | .pointer = ptr_composite_id, |
| 657 | | }); |
| 658 | | return result_id; |
| 631 | |
| 632 | return try self.load(ty, ptr_composite_id, .{}); |
| 659 | 633 | } |
| 660 | 634 | |
| 661 | 635 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| ... | ... | @@ -766,15 +740,18 @@ const DeclGen = struct { |
| 766 | 740 | }.toValue(); |
| 767 | 741 | |
| 768 | 742 | var constituents: [2]IdRef = undefined; |
| 743 | var types: [2]Type = undefined; |
| 769 | 744 | if (eu_layout.error_first) { |
| 770 | 745 | constituents[0] = try self.constant(err_ty, err_val, .indirect); |
| 771 | 746 | constituents[1] = try self.constant(payload_ty, payload_val, .indirect); |
| 747 | types = .{ err_ty, payload_ty }; |
| 772 | 748 | } else { |
| 773 | 749 | constituents[0] = try self.constant(payload_ty, payload_val, .indirect); |
| 774 | 750 | constituents[1] = try self.constant(err_ty, err_val, .indirect); |
| 751 | types = .{ payload_ty, err_ty }; |
| 775 | 752 | } |
| 776 | 753 | |
| 777 | | return try self.constructStruct(result_ty_ref, &constituents); |
| 754 | return try self.constructStruct(ty, &types, &constituents); |
| 778 | 755 | }, |
| 779 | 756 | .enum_tag => { |
| 780 | 757 | const int_val = try val.intFromEnum(ty, mod); |
| ... | ... | @@ -792,7 +769,11 @@ const DeclGen = struct { |
| 792 | 769 | } |
| 793 | 770 | |
| 794 | 771 | const len_id = try self.constant(Type.usize, ptr.len.toValue(), .indirect); |
| 795 | | return try self.constructStruct(result_ty_ref, &.{ ptr_id, len_id }); |
| 772 | return try self.constructStruct( |
| 773 | ty, |
| 774 | &.{ ptr_ty, Type.usize }, |
| 775 | &.{ ptr_id, len_id }, |
| 776 | ); |
| 796 | 777 | }, |
| 797 | 778 | .opt => { |
| 798 | 779 | const payload_ty = ty.optionalChild(mod); |
| ... | ... | @@ -819,7 +800,11 @@ const DeclGen = struct { |
| 819 | 800 | else |
| 820 | 801 | try self.spv.constUndef(try self.resolveType(payload_ty, .indirect)); |
| 821 | 802 | |
| 822 | | return try self.constructStruct(result_ty_ref, &.{ payload_id, has_pl_id }); |
| 803 | return try self.constructStruct( |
| 804 | ty, |
| 805 | &.{ payload_ty, Type.bool }, |
| 806 | &.{ payload_id, has_pl_id }, |
| 807 | ); |
| 823 | 808 | }, |
| 824 | 809 | .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { |
| 825 | 810 | inline .array_type, .vector_type => |array_type, tag| { |
| ... | ... | @@ -857,7 +842,7 @@ const DeclGen = struct { |
| 857 | 842 | else => {}, |
| 858 | 843 | } |
| 859 | 844 | |
| 860 | | return try self.constructArray(result_ty_ref, constituents); |
| 845 | return try self.constructArray(ty, constituents); |
| 861 | 846 | }, |
| 862 | 847 | .struct_type => { |
| 863 | 848 | const struct_type = mod.typeToStruct(ty).?; |
| ... | ... | @@ -865,6 +850,9 @@ const DeclGen = struct { |
| 865 | 850 | return self.todo("packed struct constants", .{}); |
| 866 | 851 | } |
| 867 | 852 | |
| 853 | var types = std.ArrayList(Type).init(self.gpa); |
| 854 | defer types.deinit(); |
| 855 | |
| 868 | 856 | var constituents = std.ArrayList(IdRef).init(self.gpa); |
| 869 | 857 | defer constituents.deinit(); |
| 870 | 858 | |
| ... | ... | @@ -880,22 +868,23 @@ const DeclGen = struct { |
| 880 | 868 | const field_val = try val.fieldValue(mod, field_index); |
| 881 | 869 | const field_id = try self.constant(field_ty, field_val, .indirect); |
| 882 | 870 | |
| 871 | try types.append(field_ty); |
| 883 | 872 | try constituents.append(field_id); |
| 884 | 873 | } |
| 885 | 874 | |
| 886 | | return try self.constructStruct(result_ty_ref, constituents.items); |
| 875 | return try self.constructStruct(ty, types.items, constituents.items); |
| 887 | 876 | }, |
| 888 | 877 | .anon_struct_type => unreachable, // TODO |
| 889 | 878 | else => unreachable, |
| 890 | 879 | }, |
| 891 | 880 | .un => |un| { |
| 892 | 881 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| 893 | | const layout = self.unionLayout(ty, active_field); |
| 894 | | const payload = if (layout.active_field_size != 0) |
| 895 | | try self.constant(layout.active_field_ty, un.val.toValue(), .indirect) |
| 882 | const union_obj = mod.typeToUnion(ty).?; |
| 883 | const field_ty = union_obj.field_types.get(ip)[active_field].toType(); |
| 884 | const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 885 | try self.constant(field_ty, un.val.toValue(), .direct) |
| 896 | 886 | else |
| 897 | 887 | null; |
| 898 | | |
| 899 | 888 | return try self.unionInit(ty, active_field, payload); |
| 900 | 889 | }, |
| 901 | 890 | .memoized_call => unreachable, |
| ... | ... | @@ -934,8 +923,7 @@ const DeclGen = struct { |
| 934 | 923 | |
| 935 | 924 | // TODO: Can we consolidate this in ptrElemPtr? |
| 936 | 925 | const elem_ty = parent_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. |
| 937 | | const elem_ty_ref = try self.resolveType(elem_ty, .direct); |
| 938 | | const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); |
| 926 | const elem_ptr_ty_ref = try self.ptrType(elem_ty, spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); |
| 939 | 927 | |
| 940 | 928 | if (elem_ptr_ty_ref == result_ty_ref) { |
| 941 | 929 | return elem_ptr_id; |
| ... | ... | @@ -997,8 +985,7 @@ const DeclGen = struct { |
| 997 | 985 | }; |
| 998 | 986 | |
| 999 | 987 | const decl_id = try self.resolveAnonDecl(decl_val, actual_storage_class); |
| 1000 | | const decl_ty_ref = try self.resolveType(decl_ty, .indirect); |
| 1001 | | const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class); |
| 988 | const decl_ptr_ty_ref = try self.ptrType(decl_ty, final_storage_class); |
| 1002 | 989 | |
| 1003 | 990 | const ptr_id = switch (final_storage_class) { |
| 1004 | 991 | .Generic => blk: { |
| ... | ... | @@ -1054,8 +1041,7 @@ const DeclGen = struct { |
| 1054 | 1041 | |
| 1055 | 1042 | const final_storage_class = spvStorageClass(decl.@"addrspace"); |
| 1056 | 1043 | |
| 1057 | | const decl_ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1058 | | const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class); |
| 1044 | const decl_ptr_ty_ref = try self.ptrType(decl.ty, final_storage_class); |
| 1059 | 1045 | |
| 1060 | 1046 | const ptr_id = switch (final_storage_class) { |
| 1061 | 1047 | .Generic => blk: { |
| ... | ... | @@ -1123,29 +1109,52 @@ const DeclGen = struct { |
| 1123 | 1109 | return try self.intType(.unsigned, self.getTarget().ptrBitWidth()); |
| 1124 | 1110 | } |
| 1125 | 1111 | |
| 1126 | | /// Generate a union type, optionally with a known field. If the tag alignment is greater |
| 1127 | | /// than that of the payload, a regular union (non-packed, with both tag and payload), will |
| 1128 | | /// be generated as follows: |
| 1129 | | /// If the active field is known: |
| 1112 | fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !CacheRef { |
| 1113 | const key = .{ child_ty.toIntern(), storage_class }; |
| 1114 | const entry = try self.wip_pointers.getOrPut(self.gpa, key); |
| 1115 | if (entry.found_existing) { |
| 1116 | const fwd_ref = entry.value_ptr.*; |
| 1117 | try self.spv.cache.recursive_ptrs.put(self.spv.gpa, fwd_ref, {}); |
| 1118 | return fwd_ref; |
| 1119 | } |
| 1120 | |
| 1121 | const fwd_ref = try self.spv.resolve(.{ .fwd_ptr_type = .{ |
| 1122 | .zig_child_type = child_ty.toIntern(), |
| 1123 | .storage_class = storage_class, |
| 1124 | } }); |
| 1125 | entry.value_ptr.* = fwd_ref; |
| 1126 | |
| 1127 | const child_ty_ref = try self.resolveType(child_ty, .indirect); |
| 1128 | _ = try self.spv.resolve(.{ .ptr_type = .{ |
| 1129 | .storage_class = storage_class, |
| 1130 | .child_type = child_ty_ref, |
| 1131 | .fwd = fwd_ref, |
| 1132 | } }); |
| 1133 | |
| 1134 | assert(self.wip_pointers.remove(key)); |
| 1135 | |
| 1136 | return fwd_ref; |
| 1137 | } |
| 1138 | |
| 1139 | /// Generate a union type. Union types are always generated with the |
| 1140 | /// most aligned field active. If the tag alignment is greater |
| 1141 | /// than that of the payload, a regular union (non-packed, with both tag and |
| 1142 | /// payload), will be generated as follows: |
| 1130 | 1143 | /// struct { |
| 1131 | 1144 | /// tag: TagType, |
| 1132 | | /// payload: ActivePayloadType, |
| 1133 | | /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, |
| 1145 | /// payload: MostAlignedFieldType, |
| 1146 | /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8, |
| 1134 | 1147 | /// padding: [padding_size]u8, |
| 1135 | 1148 | /// } |
| 1136 | 1149 | /// If the payload alignment is greater than that of the tag: |
| 1137 | 1150 | /// struct { |
| 1138 | | /// payload: ActivePayloadType, |
| 1139 | | /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, |
| 1151 | /// payload: MostAlignedFieldType, |
| 1152 | /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8, |
| 1140 | 1153 | /// tag: TagType, |
| 1141 | 1154 | /// padding: [padding_size]u8, |
| 1142 | 1155 | /// } |
| 1143 | | /// If the active payload is unknown, it will default back to the most aligned field. This is |
| 1144 | | /// to make sure that the overal struct has the correct alignment in spir-v. |
| 1145 | 1156 | /// If any of the fields' size is 0, it will be omitted. |
| 1146 | | /// NOTE: When the active field is set to something other than the most aligned field, the |
| 1147 | | /// resulting struct will be *underaligned*. |
| 1148 | | fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { |
| 1157 | fn resolveUnionType(self: *DeclGen, ty: Type) !CacheRef { |
| 1149 | 1158 | const mod = self.module; |
| 1150 | 1159 | const ip = &mod.intern_pool; |
| 1151 | 1160 | const union_obj = mod.typeToUnion(ty).?; |
| ... | ... | @@ -1154,17 +1163,13 @@ const DeclGen = struct { |
| 1154 | 1163 | return self.todo("packed union types", .{}); |
| 1155 | 1164 | } |
| 1156 | 1165 | |
| 1157 | | const layout = self.unionLayout(ty, maybe_active_field); |
| 1158 | | |
| 1159 | | if (layout.payload_size == 0) { |
| 1166 | const layout = self.unionLayout(ty); |
| 1167 | if (!layout.has_payload) { |
| 1160 | 1168 | // No payload, so represent this as just the tag type. |
| 1161 | 1169 | return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); |
| 1162 | 1170 | } |
| 1163 | 1171 | |
| 1164 | | // TODO: We need to add the active field to the key, somehow. |
| 1165 | | if (maybe_active_field == null) { |
| 1166 | | if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; |
| 1167 | | } |
| 1172 | if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref; |
| 1168 | 1173 | |
| 1169 | 1174 | var member_types: [4]CacheRef = undefined; |
| 1170 | 1175 | var member_names: [4]CacheString = undefined; |
| ... | ... | @@ -1177,10 +1182,10 @@ const DeclGen = struct { |
| 1177 | 1182 | member_names[layout.tag_index] = try self.spv.resolveString("(tag)"); |
| 1178 | 1183 | } |
| 1179 | 1184 | |
| 1180 | | if (layout.active_field_size != 0) { |
| 1181 | | const active_payload_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 1182 | | member_types[layout.active_field_index] = active_payload_ty_ref; |
| 1183 | | member_names[layout.active_field_index] = try self.spv.resolveString("(payload)"); |
| 1185 | if (layout.payload_size != 0) { |
| 1186 | const payload_ty_ref = try self.resolveType(layout.payload_ty, .indirect); |
| 1187 | member_types[layout.payload_index] = payload_ty_ref; |
| 1188 | member_names[layout.payload_index] = try self.spv.resolveString("(payload)"); |
| 1184 | 1189 | } |
| 1185 | 1190 | |
| 1186 | 1191 | if (layout.payload_padding_size != 0) { |
| ... | ... | @@ -1201,9 +1206,7 @@ const DeclGen = struct { |
| 1201 | 1206 | .member_names = member_names[0..layout.total_fields], |
| 1202 | 1207 | } }); |
| 1203 | 1208 | |
| 1204 | | if (maybe_active_field == null) { |
| 1205 | | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); |
| 1206 | | } |
| 1209 | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); |
| 1207 | 1210 | return ty_ref; |
| 1208 | 1211 | } |
| 1209 | 1212 | |
| ... | ... | @@ -1351,12 +1354,12 @@ const DeclGen = struct { |
| 1351 | 1354 | .Pointer => { |
| 1352 | 1355 | const ptr_info = ty.ptrInfo(mod); |
| 1353 | 1356 | |
| 1357 | // Note: Don't cache this pointer type, it would mess up the recursive pointer functionality |
| 1358 | // in ptrType()! |
| 1359 | |
| 1354 | 1360 | const storage_class = spvStorageClass(ptr_info.flags.address_space); |
| 1355 | | const child_ty_ref = try self.resolveType(ptr_info.child.toType(), .indirect); |
| 1356 | | const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{ |
| 1357 | | .storage_class = storage_class, |
| 1358 | | .child_type = child_ty_ref, |
| 1359 | | } }); |
| 1361 | const ptr_ty_ref = try self.ptrType(ptr_info.child.toType(), storage_class); |
| 1362 | |
| 1360 | 1363 | if (ptr_info.flags.size != .Slice) { |
| 1361 | 1364 | return ptr_ty_ref; |
| 1362 | 1365 | } |
| ... | ... | @@ -1471,7 +1474,7 @@ const DeclGen = struct { |
| 1471 | 1474 | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); |
| 1472 | 1475 | return ty_ref; |
| 1473 | 1476 | }, |
| 1474 | | .Union => return try self.resolveUnionType(ty, null), |
| 1477 | .Union => return try self.resolveUnionType(ty), |
| 1475 | 1478 | .ErrorSet => return try self.intType(.unsigned, 16), |
| 1476 | 1479 | .ErrorUnion => { |
| 1477 | 1480 | const payload_ty = ty.errorUnionPayload(mod); |
| ... | ... | @@ -1585,14 +1588,16 @@ const DeclGen = struct { |
| 1585 | 1588 | } |
| 1586 | 1589 | |
| 1587 | 1590 | const UnionLayout = struct { |
| 1588 | | active_field: u32, |
| 1589 | | active_field_ty: Type, |
| 1590 | | payload_size: u32, |
| 1591 | | |
| 1591 | /// If false, this union is represented |
| 1592 | /// by only an integer of the tag type. |
| 1593 | has_payload: bool, |
| 1592 | 1594 | tag_size: u32, |
| 1593 | 1595 | tag_index: u32, |
| 1594 | | active_field_size: u32, |
| 1595 | | active_field_index: u32, |
| 1596 | /// Note: This is the size of the payload type itself, NOT the size of the ENTIRE payload. |
| 1597 | /// Use `has_payload` instead!! |
| 1598 | payload_ty: Type, |
| 1599 | payload_size: u32, |
| 1600 | payload_index: u32, |
| 1596 | 1601 | payload_padding_size: u32, |
| 1597 | 1602 | payload_padding_index: u32, |
| 1598 | 1603 | padding_size: u32, |
| ... | ... | @@ -1600,23 +1605,19 @@ const DeclGen = struct { |
| 1600 | 1605 | total_fields: u32, |
| 1601 | 1606 | }; |
| 1602 | 1607 | |
| 1603 | | fn unionLayout(self: *DeclGen, ty: Type, maybe_active_field: ?usize) UnionLayout { |
| 1608 | fn unionLayout(self: *DeclGen, ty: Type) UnionLayout { |
| 1604 | 1609 | const mod = self.module; |
| 1605 | 1610 | const ip = &mod.intern_pool; |
| 1606 | 1611 | const layout = ty.unionGetLayout(self.module); |
| 1607 | 1612 | const union_obj = mod.typeToUnion(ty).?; |
| 1608 | 1613 | |
| 1609 | | const active_field = maybe_active_field orelse layout.most_aligned_field; |
| 1610 | | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); |
| 1611 | | |
| 1612 | 1614 | var union_layout = UnionLayout{ |
| 1613 | | .active_field = @intCast(active_field), |
| 1614 | | .active_field_ty = active_field_ty, |
| 1615 | | .payload_size = @intCast(layout.payload_size), |
| 1615 | .has_payload = layout.payload_size != 0, |
| 1616 | 1616 | .tag_size = @intCast(layout.tag_size), |
| 1617 | 1617 | .tag_index = undefined, |
| 1618 | | .active_field_size = undefined, |
| 1619 | | .active_field_index = undefined, |
| 1618 | .payload_ty = undefined, |
| 1619 | .payload_size = undefined, |
| 1620 | .payload_index = undefined, |
| 1620 | 1621 | .payload_padding_size = undefined, |
| 1621 | 1622 | .payload_padding_index = undefined, |
| 1622 | 1623 | .padding_size = @intCast(layout.padding), |
| ... | ... | @@ -1624,11 +1625,16 @@ const DeclGen = struct { |
| 1624 | 1625 | .total_fields = undefined, |
| 1625 | 1626 | }; |
| 1626 | 1627 | |
| 1627 | | union_layout.active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 1628 | | @intCast(active_field_ty.abiSize(mod)) |
| 1629 | | else |
| 1630 | | 0; |
| 1631 | | union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.active_field_size); |
| 1628 | if (union_layout.has_payload) { |
| 1629 | const most_aligned_field = layout.most_aligned_field; |
| 1630 | const most_aligned_field_ty = union_obj.field_types.get(ip)[most_aligned_field].toType(); |
| 1631 | union_layout.payload_ty = most_aligned_field_ty; |
| 1632 | union_layout.payload_size = @intCast(most_aligned_field_ty.abiSize(mod)); |
| 1633 | } else { |
| 1634 | union_layout.payload_size = 0; |
| 1635 | } |
| 1636 | |
| 1637 | union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.payload_size); |
| 1632 | 1638 | |
| 1633 | 1639 | const tag_first = layout.tag_align.compare(.gte, layout.payload_align); |
| 1634 | 1640 | var field_index: u32 = 0; |
| ... | ... | @@ -1638,8 +1644,8 @@ const DeclGen = struct { |
| 1638 | 1644 | field_index += 1; |
| 1639 | 1645 | } |
| 1640 | 1646 | |
| 1641 | | if (union_layout.active_field_size != 0) { |
| 1642 | | union_layout.active_field_index = field_index; |
| 1647 | if (union_layout.payload_size != 0) { |
| 1648 | union_layout.payload_index = field_index; |
| 1643 | 1649 | field_index += 1; |
| 1644 | 1650 | } |
| 1645 | 1651 | |
| ... | ... | @@ -1683,7 +1689,7 @@ const DeclGen = struct { |
| 1683 | 1689 | /// the name of an error in the text executor. |
| 1684 | 1690 | fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void { |
| 1685 | 1691 | const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct); |
| 1686 | | const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup); |
| 1692 | const ptr_anyerror_ty_ref = try self.ptrType(Type.anyerror, .CrossWorkgroup); |
| 1687 | 1693 | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| 1688 | 1694 | |
| 1689 | 1695 | const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ |
| ... | ... | @@ -1718,6 +1724,7 @@ const DeclGen = struct { |
| 1718 | 1724 | .id_result = error_id, |
| 1719 | 1725 | .function = test_id, |
| 1720 | 1726 | }); |
| 1727 | // Note: Convert to direct not required. |
| 1721 | 1728 | try section.emit(self.spv.gpa, .OpStore, .{ |
| 1722 | 1729 | .pointer = p_error_id, |
| 1723 | 1730 | .object = error_id, |
| ... | ... | @@ -1822,8 +1829,7 @@ const DeclGen = struct { |
| 1822 | 1829 | else => final_storage_class, |
| 1823 | 1830 | }; |
| 1824 | 1831 | |
| 1825 | | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1826 | | const ptr_ty_ref = try self.spv.ptrType(ty_ref, actual_storage_class); |
| 1832 | const ptr_ty_ref = try self.ptrType(decl.ty, actual_storage_class); |
| 1827 | 1833 | |
| 1828 | 1834 | const begin = self.spv.beginGlobal(); |
| 1829 | 1835 | try self.spv.globals.section.emit(self.spv.gpa, .OpVariable, .{ |
| ... | ... | @@ -1928,11 +1934,15 @@ const DeclGen = struct { |
| 1928 | 1934 | return try self.convertToDirect(result_ty, result_id); |
| 1929 | 1935 | } |
| 1930 | 1936 | |
| 1931 | | fn load(self: *DeclGen, value_ty: Type, ptr_id: IdRef, is_volatile: bool) !IdRef { |
| 1937 | const MemoryOptions = struct { |
| 1938 | is_volatile: bool = false, |
| 1939 | }; |
| 1940 | |
| 1941 | fn load(self: *DeclGen, value_ty: Type, ptr_id: IdRef, options: MemoryOptions) !IdRef { |
| 1932 | 1942 | const indirect_value_ty_ref = try self.resolveType(value_ty, .indirect); |
| 1933 | 1943 | const result_id = self.spv.allocId(); |
| 1934 | 1944 | const access = spec.MemoryAccess.Extended{ |
| 1935 | | .Volatile = is_volatile, |
| 1945 | .Volatile = options.is_volatile, |
| 1936 | 1946 | }; |
| 1937 | 1947 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 1938 | 1948 | .id_result_type = self.typeId(indirect_value_ty_ref), |
| ... | ... | @@ -1943,10 +1953,10 @@ const DeclGen = struct { |
| 1943 | 1953 | return try self.convertToDirect(value_ty, result_id); |
| 1944 | 1954 | } |
| 1945 | 1955 | |
| 1946 | | fn store(self: *DeclGen, value_ty: Type, ptr_id: IdRef, value_id: IdRef, is_volatile: bool) !void { |
| 1956 | fn store(self: *DeclGen, value_ty: Type, ptr_id: IdRef, value_id: IdRef, options: MemoryOptions) !void { |
| 1947 | 1957 | const indirect_value_id = try self.convertToIndirect(value_ty, value_id); |
| 1948 | 1958 | const access = spec.MemoryAccess.Extended{ |
| 1949 | | .Volatile = is_volatile, |
| 1959 | .Volatile = options.is_volatile, |
| 1950 | 1960 | }; |
| 1951 | 1961 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 1952 | 1962 | .pointer = ptr_id, |
| ... | ... | @@ -2118,9 +2128,7 @@ const DeclGen = struct { |
| 2118 | 2128 | constituent.* = try self.convertToIndirect(child_ty, result_id); |
| 2119 | 2129 | } |
| 2120 | 2130 | |
| 2121 | | const result_ty = try self.resolveType(child_ty, .indirect); |
| 2122 | | const result_ty_ref = try self.spv.arrayType(vector_len, result_ty); |
| 2123 | | return try self.constructArray(result_ty_ref, constituents); |
| 2131 | return try self.constructArray(ty, constituents); |
| 2124 | 2132 | } |
| 2125 | 2133 | |
| 2126 | 2134 | const result_id = self.spv.allocId(); |
| ... | ... | @@ -2181,7 +2189,7 @@ const DeclGen = struct { |
| 2181 | 2189 | |
| 2182 | 2190 | const info = try self.arithmeticTypeInfo(result_ty); |
| 2183 | 2191 | // TODO: Use fmin for OpenCL |
| 2184 | | const cmp_id = try self.cmp(op, result_ty, lhs_id, rhs_id); |
| 2192 | const cmp_id = try self.cmp(op, Type.bool, result_ty, lhs_id, rhs_id); |
| 2185 | 2193 | const selection_id = switch (info.class) { |
| 2186 | 2194 | .float => blk: { |
| 2187 | 2195 | // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, |
| ... | ... | @@ -2316,7 +2324,7 @@ const DeclGen = struct { |
| 2316 | 2324 | constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular); |
| 2317 | 2325 | } |
| 2318 | 2326 | |
| 2319 | | return self.constructArray(result_ty_ref, constituents); |
| 2327 | return self.constructArray(ty, constituents); |
| 2320 | 2328 | } |
| 2321 | 2329 | |
| 2322 | 2330 | // Binary operations are generally applicable to both scalar and vector operations |
| ... | ... | @@ -2472,11 +2480,11 @@ const DeclGen = struct { |
| 2472 | 2480 | // Construct the struct that Zig wants as result. |
| 2473 | 2481 | // The value should already be the correct type. |
| 2474 | 2482 | const ov_id = try self.intFromBool(ov_ty_ref, overflowed_id); |
| 2475 | | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| 2476 | | return try self.constructStruct(result_ty_ref, &.{ |
| 2477 | | value_id, |
| 2478 | | ov_id, |
| 2479 | | }); |
| 2483 | return try self.constructStruct( |
| 2484 | result_ty, |
| 2485 | &.{ operand_ty, ov_ty }, |
| 2486 | &.{ value_id, ov_id }, |
| 2487 | ); |
| 2480 | 2488 | } |
| 2481 | 2489 | |
| 2482 | 2490 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2634,6 +2642,7 @@ const DeclGen = struct { |
| 2634 | 2642 | fn cmp( |
| 2635 | 2643 | self: *DeclGen, |
| 2636 | 2644 | op: std.math.CompareOperator, |
| 2645 | result_ty: Type, |
| 2637 | 2646 | ty: Type, |
| 2638 | 2647 | lhs_id: IdRef, |
| 2639 | 2648 | rhs_id: IdRef, |
| ... | ... | @@ -2674,7 +2683,7 @@ const DeclGen = struct { |
| 2674 | 2683 | if (ty.optionalReprIsPayload(mod)) { |
| 2675 | 2684 | assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod)); |
| 2676 | 2685 | assert(!payload_ty.isSlice(mod)); |
| 2677 | | return self.cmp(op, payload_ty, lhs_id, rhs_id); |
| 2686 | return self.cmp(op, Type.bool, payload_ty, lhs_id, rhs_id); |
| 2678 | 2687 | } |
| 2679 | 2688 | |
| 2680 | 2689 | const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| ... | ... | @@ -2687,7 +2696,7 @@ const DeclGen = struct { |
| 2687 | 2696 | else |
| 2688 | 2697 | try self.convertToDirect(Type.bool, rhs_id); |
| 2689 | 2698 | |
| 2690 | | const valid_cmp_id = try self.cmp(op, Type.bool, lhs_valid_id, rhs_valid_id); |
| 2699 | const valid_cmp_id = try self.cmp(op, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id); |
| 2691 | 2700 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 2692 | 2701 | return valid_cmp_id; |
| 2693 | 2702 | } |
| ... | ... | @@ -2698,7 +2707,7 @@ const DeclGen = struct { |
| 2698 | 2707 | const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0); |
| 2699 | 2708 | const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0); |
| 2700 | 2709 | |
| 2701 | | const pl_cmp_id = try self.cmp(op, payload_ty, lhs_pl_id, rhs_pl_id); |
| 2710 | const pl_cmp_id = try self.cmp(op, Type.bool, payload_ty, lhs_pl_id, rhs_pl_id); |
| 2702 | 2711 | |
| 2703 | 2712 | // op == .eq => lhs_valid == rhs_valid && lhs_pl == rhs_pl |
| 2704 | 2713 | // op == .neq => lhs_valid != rhs_valid || lhs_pl != rhs_pl |
| ... | ... | @@ -2720,7 +2729,6 @@ const DeclGen = struct { |
| 2720 | 2729 | .Vector => { |
| 2721 | 2730 | const child_ty = ty.childType(mod); |
| 2722 | 2731 | const vector_len = ty.vectorLen(mod); |
| 2723 | | const bool_ty_ref_indirect = try self.resolveType(Type.bool, .indirect); |
| 2724 | 2732 | |
| 2725 | 2733 | var constituents = try self.gpa.alloc(IdRef, vector_len); |
| 2726 | 2734 | defer self.gpa.free(constituents); |
| ... | ... | @@ -2728,12 +2736,11 @@ const DeclGen = struct { |
| 2728 | 2736 | for (constituents, 0..) |*constituent, i| { |
| 2729 | 2737 | const lhs_index_id = try self.extractField(child_ty, cmp_lhs_id, @intCast(i)); |
| 2730 | 2738 | const rhs_index_id = try self.extractField(child_ty, cmp_rhs_id, @intCast(i)); |
| 2731 | | const result_id = try self.cmp(op, child_ty, lhs_index_id, rhs_index_id); |
| 2739 | const result_id = try self.cmp(op, Type.bool, child_ty, lhs_index_id, rhs_index_id); |
| 2732 | 2740 | constituent.* = try self.convertToIndirect(Type.bool, result_id); |
| 2733 | 2741 | } |
| 2734 | 2742 | |
| 2735 | | const result_ty_ref = try self.spv.arrayType(vector_len, bool_ty_ref_indirect); |
| 2736 | | return try self.constructArray(result_ty_ref, constituents); |
| 2743 | return try self.constructArray(result_ty, constituents); |
| 2737 | 2744 | }, |
| 2738 | 2745 | else => unreachable, |
| 2739 | 2746 | }; |
| ... | ... | @@ -2806,8 +2813,9 @@ const DeclGen = struct { |
| 2806 | 2813 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2807 | 2814 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2808 | 2815 | const ty = self.typeOf(bin_op.lhs); |
| 2816 | const result_ty = self.typeOfIndex(inst); |
| 2809 | 2817 | |
| 2810 | | return try self.cmp(op, ty, lhs_id, rhs_id); |
| 2818 | return try self.cmp(op, result_ty, ty, lhs_id, rhs_id); |
| 2811 | 2819 | } |
| 2812 | 2820 | |
| 2813 | 2821 | fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2819,8 +2827,9 @@ const DeclGen = struct { |
| 2819 | 2827 | const rhs_id = try self.resolve(vec_cmp.rhs); |
| 2820 | 2828 | const op = vec_cmp.compareOperator(); |
| 2821 | 2829 | const ty = self.typeOf(vec_cmp.lhs); |
| 2830 | const result_ty = self.typeOfIndex(inst); |
| 2822 | 2831 | |
| 2823 | | return try self.cmp(op, ty, lhs_id, rhs_id); |
| 2832 | return try self.cmp(op, result_ty, ty, lhs_id, rhs_id); |
| 2824 | 2833 | } |
| 2825 | 2834 | |
| 2826 | 2835 | fn bitCast( |
| ... | ... | @@ -2865,23 +2874,17 @@ const DeclGen = struct { |
| 2865 | 2874 | return result_id; |
| 2866 | 2875 | } |
| 2867 | 2876 | |
| 2868 | | const src_ptr_ty_ref = try self.spv.ptrType(src_ty_ref, .Function); |
| 2869 | | const dst_ptr_ty_ref = try self.spv.ptrType(dst_ty_ref, .Function); |
| 2877 | const dst_ptr_ty_ref = try self.ptrType(dst_ty, .Function); |
| 2870 | 2878 | |
| 2871 | | const tmp_id = self.spv.allocId(); |
| 2872 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 2873 | | .id_result_type = self.typeId(src_ptr_ty_ref), |
| 2874 | | .id_result = tmp_id, |
| 2875 | | .storage_class = .Function, |
| 2876 | | }); |
| 2877 | | try self.store(src_ty, tmp_id, src_id, false); |
| 2879 | const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function }); |
| 2880 | try self.store(src_ty, tmp_id, src_id, .{}); |
| 2878 | 2881 | const casted_ptr_id = self.spv.allocId(); |
| 2879 | 2882 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 2880 | 2883 | .id_result_type = self.typeId(dst_ptr_ty_ref), |
| 2881 | 2884 | .id_result = casted_ptr_id, |
| 2882 | 2885 | .operand = tmp_id, |
| 2883 | 2886 | }); |
| 2884 | | return try self.load(dst_ty, casted_ptr_id, false); |
| 2887 | return try self.load(dst_ty, casted_ptr_id, .{}); |
| 2885 | 2888 | } |
| 2886 | 2889 | |
| 2887 | 2890 | fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3060,7 +3063,6 @@ const DeclGen = struct { |
| 3060 | 3063 | const elem_ptr_ty = slice_ty.slicePtrFieldType(mod); |
| 3061 | 3064 | |
| 3062 | 3065 | const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct); |
| 3063 | | const slice_ty_ref = try self.resolveType(slice_ty, .direct); |
| 3064 | 3066 | const size_ty_ref = try self.sizeType(); |
| 3065 | 3067 | |
| 3066 | 3068 | const array_ptr_id = try self.resolve(ty_op.operand); |
| ... | ... | @@ -3073,7 +3075,11 @@ const DeclGen = struct { |
| 3073 | 3075 | // Convert the pointer-to-array to a pointer to the first element. |
| 3074 | 3076 | try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0}); |
| 3075 | 3077 | |
| 3076 | | return try self.constructStruct(slice_ty_ref, &.{ elem_ptr_id, len_id }); |
| 3078 | return try self.constructStruct( |
| 3079 | slice_ty, |
| 3080 | &.{ elem_ptr_ty, Type.usize }, |
| 3081 | &.{ elem_ptr_id, len_id }, |
| 3082 | ); |
| 3077 | 3083 | } |
| 3078 | 3084 | |
| 3079 | 3085 | fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3083,13 +3089,16 @@ const DeclGen = struct { |
| 3083 | 3089 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3084 | 3090 | const ptr_id = try self.resolve(bin_op.lhs); |
| 3085 | 3091 | const len_id = try self.resolve(bin_op.rhs); |
| 3092 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 3086 | 3093 | const slice_ty = self.typeOfIndex(inst); |
| 3087 | | const slice_ty_ref = try self.resolveType(slice_ty, .direct); |
| 3088 | 3094 | |
| 3089 | | return try self.constructStruct(slice_ty_ref, &.{ |
| 3090 | | ptr_id, // Note: Type should not need to be converted to direct. |
| 3091 | | len_id, // Note: Type should not need to be converted to direct. |
| 3092 | | }); |
| 3095 | // Note: Types should not need to be converted to direct, these types |
| 3096 | // dont need to be converted. |
| 3097 | return try self.constructStruct( |
| 3098 | slice_ty, |
| 3099 | &.{ ptr_ty, Type.usize }, |
| 3100 | &.{ ptr_id, len_id }, |
| 3101 | ); |
| 3093 | 3102 | } |
| 3094 | 3103 | |
| 3095 | 3104 | fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3099,7 +3108,6 @@ const DeclGen = struct { |
| 3099 | 3108 | const ip = &mod.intern_pool; |
| 3100 | 3109 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3101 | 3110 | const result_ty = self.typeOfIndex(inst); |
| 3102 | | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| 3103 | 3111 | const len: usize = @intCast(result_ty.arrayLen(mod)); |
| 3104 | 3112 | const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); |
| 3105 | 3113 | |
| ... | ... | @@ -3111,6 +3119,8 @@ const DeclGen = struct { |
| 3111 | 3119 | unreachable; // TODO |
| 3112 | 3120 | } |
| 3113 | 3121 | |
| 3122 | const types = try self.gpa.alloc(Type, elements.len); |
| 3123 | defer self.gpa.free(types); |
| 3114 | 3124 | const constituents = try self.gpa.alloc(IdRef, elements.len); |
| 3115 | 3125 | defer self.gpa.free(constituents); |
| 3116 | 3126 | var index: usize = 0; |
| ... | ... | @@ -3122,6 +3132,7 @@ const DeclGen = struct { |
| 3122 | 3132 | assert(field_ty.toType().hasRuntimeBits(mod)); |
| 3123 | 3133 | |
| 3124 | 3134 | const id = try self.resolve(element); |
| 3135 | types[index] = field_ty.toType(); |
| 3125 | 3136 | constituents[index] = try self.convertToIndirect(field_ty.toType(), id); |
| 3126 | 3137 | index += 1; |
| 3127 | 3138 | } |
| ... | ... | @@ -3135,6 +3146,7 @@ const DeclGen = struct { |
| 3135 | 3146 | assert(field_ty.hasRuntimeBitsIgnoreComptime(mod)); |
| 3136 | 3147 | |
| 3137 | 3148 | const id = try self.resolve(element); |
| 3149 | types[index] = field_ty; |
| 3138 | 3150 | constituents[index] = try self.convertToIndirect(field_ty, id); |
| 3139 | 3151 | index += 1; |
| 3140 | 3152 | } |
| ... | ... | @@ -3142,7 +3154,11 @@ const DeclGen = struct { |
| 3142 | 3154 | else => unreachable, |
| 3143 | 3155 | } |
| 3144 | 3156 | |
| 3145 | | return try self.constructStruct(result_ty_ref, constituents[0..index]); |
| 3157 | return try self.constructStruct( |
| 3158 | result_ty, |
| 3159 | types[0..index], |
| 3160 | constituents[0..index], |
| 3161 | ); |
| 3146 | 3162 | }, |
| 3147 | 3163 | .Array => { |
| 3148 | 3164 | const array_info = result_ty.arrayInfo(mod); |
| ... | ... | @@ -3159,7 +3175,7 @@ const DeclGen = struct { |
| 3159 | 3175 | elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect); |
| 3160 | 3176 | } |
| 3161 | 3177 | |
| 3162 | | return try self.constructArray(result_ty_ref, elem_ids); |
| 3178 | return try self.constructArray(result_ty, elem_ids); |
| 3163 | 3179 | }, |
| 3164 | 3180 | else => unreachable, |
| 3165 | 3181 | } |
| ... | ... | @@ -3244,15 +3260,14 @@ const DeclGen = struct { |
| 3244 | 3260 | |
| 3245 | 3261 | const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); |
| 3246 | 3262 | const elem_ptr = try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{}); |
| 3247 | | return try self.load(slice_ty.childType(mod), elem_ptr, slice_ty.isVolatilePtr(mod)); |
| 3263 | return try self.load(slice_ty.childType(mod), elem_ptr, .{ .is_volatile = slice_ty.isVolatilePtr(mod) }); |
| 3248 | 3264 | } |
| 3249 | 3265 | |
| 3250 | 3266 | fn ptrElemPtr(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, index_id: IdRef) !IdRef { |
| 3251 | 3267 | const mod = self.module; |
| 3252 | 3268 | // Construct new pointer type for the resulting pointer |
| 3253 | 3269 | const elem_ty = ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. |
| 3254 | | const elem_ty_ref = try self.resolveType(elem_ty, .direct); |
| 3255 | | const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace(mod))); |
| 3270 | const elem_ptr_ty_ref = try self.ptrType(elem_ty, spvStorageClass(ptr_ty.ptrAddressSpace(mod))); |
| 3256 | 3271 | if (ptr_ty.isSinglePointer(mod)) { |
| 3257 | 3272 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 3258 | 3273 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| ... | ... | @@ -3288,9 +3303,7 @@ const DeclGen = struct { |
| 3288 | 3303 | const mod = self.module; |
| 3289 | 3304 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3290 | 3305 | const array_ty = self.typeOf(bin_op.lhs); |
| 3291 | | const array_ty_ref = try self.resolveType(array_ty, .direct); |
| 3292 | 3306 | const elem_ty = array_ty.childType(mod); |
| 3293 | | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); |
| 3294 | 3307 | const array_id = try self.resolve(bin_op.lhs); |
| 3295 | 3308 | const index_id = try self.resolve(bin_op.rhs); |
| 3296 | 3309 | |
| ... | ... | @@ -3298,22 +3311,12 @@ const DeclGen = struct { |
| 3298 | 3311 | // For now, just generate a temporary and use that. |
| 3299 | 3312 | // TODO: This backend probably also should use isByRef from llvm... |
| 3300 | 3313 | |
| 3301 | | const array_ptr_ty_ref = try self.spv.ptrType(array_ty_ref, .Function); |
| 3302 | | const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function); |
| 3303 | | |
| 3304 | | const tmp_id = self.spv.allocId(); |
| 3305 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 3306 | | .id_result_type = self.typeId(array_ptr_ty_ref), |
| 3307 | | .id_result = tmp_id, |
| 3308 | | .storage_class = .Function, |
| 3309 | | }); |
| 3310 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 3311 | | .pointer = tmp_id, |
| 3312 | | .object = array_id, |
| 3313 | | }); |
| 3314 | const elem_ptr_ty_ref = try self.ptrType(elem_ty, .Function); |
| 3314 | 3315 | |
| 3316 | const tmp_id = try self.alloc(array_ty, .{ .storage_class = .Function }); |
| 3317 | try self.store(array_ty, tmp_id, array_id, .{}); |
| 3315 | 3318 | const elem_ptr_id = try self.accessChainId(elem_ptr_ty_ref, tmp_id, &.{index_id}); |
| 3316 | | return try self.load(elem_ty, elem_ptr_id, false); |
| 3319 | return try self.load(elem_ty, elem_ptr_id, .{}); |
| 3317 | 3320 | } |
| 3318 | 3321 | |
| 3319 | 3322 | fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3326,7 +3329,7 @@ const DeclGen = struct { |
| 3326 | 3329 | const ptr_id = try self.resolve(bin_op.lhs); |
| 3327 | 3330 | const index_id = try self.resolve(bin_op.rhs); |
| 3328 | 3331 | const elem_ptr_id = try self.ptrElemPtr(ptr_ty, ptr_id, index_id); |
| 3329 | | return try self.load(elem_ty, elem_ptr_id, ptr_ty.isVolatilePtr(mod)); |
| 3332 | return try self.load(elem_ty, elem_ptr_id, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); |
| 3330 | 3333 | } |
| 3331 | 3334 | |
| 3332 | 3335 | fn airSetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3334,22 +3337,21 @@ const DeclGen = struct { |
| 3334 | 3337 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3335 | 3338 | const un_ptr_ty = self.typeOf(bin_op.lhs); |
| 3336 | 3339 | const un_ty = un_ptr_ty.childType(mod); |
| 3337 | | const layout = self.unionLayout(un_ty, null); |
| 3340 | const layout = self.unionLayout(un_ty); |
| 3338 | 3341 | |
| 3339 | 3342 | if (layout.tag_size == 0) return; |
| 3340 | 3343 | |
| 3341 | 3344 | const tag_ty = un_ty.unionTagTypeSafety(mod).?; |
| 3342 | | const tag_ty_ref = try self.resolveType(tag_ty, .indirect); |
| 3343 | | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); |
| 3345 | const tag_ptr_ty_ref = try self.ptrType(tag_ty, spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); |
| 3344 | 3346 | |
| 3345 | 3347 | const union_ptr_id = try self.resolve(bin_op.lhs); |
| 3346 | 3348 | const new_tag_id = try self.resolve(bin_op.rhs); |
| 3347 | 3349 | |
| 3348 | | if (layout.payload_size == 0) { |
| 3349 | | try self.store(tag_ty, union_ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); |
| 3350 | if (!layout.has_payload) { |
| 3351 | try self.store(tag_ty, union_ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) }); |
| 3350 | 3352 | } else { |
| 3351 | 3353 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index}); |
| 3352 | | try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); |
| 3354 | try self.store(tag_ty, ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) }); |
| 3353 | 3355 | } |
| 3354 | 3356 | } |
| 3355 | 3357 | |
| ... | ... | @@ -3360,11 +3362,11 @@ const DeclGen = struct { |
| 3360 | 3362 | const un_ty = self.typeOf(ty_op.operand); |
| 3361 | 3363 | |
| 3362 | 3364 | const mod = self.module; |
| 3363 | | const layout = self.unionLayout(un_ty, null); |
| 3365 | const layout = self.unionLayout(un_ty); |
| 3364 | 3366 | if (layout.tag_size == 0) return null; |
| 3365 | 3367 | |
| 3366 | 3368 | const union_handle = try self.resolve(ty_op.operand); |
| 3367 | | if (layout.payload_size == 0) return union_handle; |
| 3369 | if (!layout.has_payload) return union_handle; |
| 3368 | 3370 | |
| 3369 | 3371 | const tag_ty = un_ty.unionTagTypeSafety(mod).?; |
| 3370 | 3372 | return try self.extractField(tag_ty, union_handle, layout.tag_index); |
| ... | ... | @@ -3377,8 +3379,8 @@ const DeclGen = struct { |
| 3377 | 3379 | payload: ?IdRef, |
| 3378 | 3380 | ) !IdRef { |
| 3379 | 3381 | // To initialize a union, generate a temporary variable with the |
| 3380 | | // type that has the right field active, then pointer-cast and store |
| 3381 | | // the active field, and finally load and return the entire union. |
| 3382 | // union type, then get the field pointer and pointer-cast it to the |
| 3383 | // right type to store it. Finally load the entire union. |
| 3382 | 3384 | |
| 3383 | 3385 | const mod = self.module; |
| 3384 | 3386 | const ip = &mod.intern_pool; |
| ... | ... | @@ -3389,7 +3391,7 @@ const DeclGen = struct { |
| 3389 | 3391 | } |
| 3390 | 3392 | |
| 3391 | 3393 | const maybe_tag_ty = ty.unionTagTypeSafety(mod); |
| 3392 | | const layout = self.unionLayout(ty, active_field); |
| 3394 | const layout = self.unionLayout(ty); |
| 3393 | 3395 | |
| 3394 | 3396 | const tag_int = if (layout.tag_size != 0) blk: { |
| 3395 | 3397 | const tag_ty = maybe_tag_ty.?; |
| ... | ... | @@ -3400,42 +3402,34 @@ const DeclGen = struct { |
| 3400 | 3402 | break :blk tag_int_val.toUnsignedInt(mod); |
| 3401 | 3403 | } else 0; |
| 3402 | 3404 | |
| 3403 | | if (layout.payload_size == 0) { |
| 3405 | if (!layout.has_payload) { |
| 3404 | 3406 | const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); |
| 3405 | 3407 | return try self.constInt(tag_ty_ref, tag_int); |
| 3406 | 3408 | } |
| 3407 | 3409 | |
| 3408 | | const un_active_ty_ref = try self.resolveUnionType(ty, active_field); |
| 3409 | | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); |
| 3410 | | const un_general_ty_ref = try self.resolveType(ty, .direct); |
| 3411 | | const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function); |
| 3412 | | |
| 3413 | | const tmp_id = self.spv.allocId(); |
| 3414 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 3415 | | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 3416 | | .id_result = tmp_id, |
| 3417 | | .storage_class = .Function, |
| 3418 | | }); |
| 3410 | const tmp_id = try self.alloc(ty, .{ .storage_class = .Function }); |
| 3419 | 3411 | |
| 3420 | 3412 | if (layout.tag_size != 0) { |
| 3421 | 3413 | const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); |
| 3422 | | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); |
| 3414 | const tag_ptr_ty_ref = try self.ptrType(maybe_tag_ty.?, .Function); |
| 3423 | 3415 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); |
| 3424 | 3416 | const tag_id = try self.constInt(tag_ty_ref, tag_int); |
| 3425 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 3426 | | .pointer = ptr_id, |
| 3427 | | .object = tag_id, |
| 3428 | | }); |
| 3417 | try self.store(maybe_tag_ty.?, ptr_id, tag_id, .{}); |
| 3429 | 3418 | } |
| 3430 | 3419 | |
| 3431 | | if (layout.active_field_size != 0) { |
| 3432 | | const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 3433 | | const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); |
| 3434 | | const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))}); |
| 3435 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 3436 | | .pointer = ptr_id, |
| 3437 | | .object = payload.?, |
| 3420 | const payload_ty = union_ty.field_types.get(ip)[active_field].toType(); |
| 3421 | if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 3422 | const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, .Function); |
| 3423 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, tmp_id, &.{layout.payload_index}); |
| 3424 | const active_pl_ptr_ty_ref = try self.ptrType(payload_ty, .Function); |
| 3425 | const active_pl_ptr_id = self.spv.allocId(); |
| 3426 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 3427 | .id_result_type = self.typeId(active_pl_ptr_ty_ref), |
| 3428 | .id_result = active_pl_ptr_id, |
| 3429 | .operand = pl_ptr_id, |
| 3438 | 3430 | }); |
| 3431 | |
| 3432 | try self.store(payload_ty, active_pl_ptr_id, payload.?, .{}); |
| 3439 | 3433 | } else { |
| 3440 | 3434 | assert(payload == null); |
| 3441 | 3435 | } |
| ... | ... | @@ -3443,34 +3437,21 @@ const DeclGen = struct { |
| 3443 | 3437 | // Just leave the padding fields uninitialized... |
| 3444 | 3438 | // TODO: Or should we initialize them with undef explicitly? |
| 3445 | 3439 | |
| 3446 | | // Now cast the pointer and load it as the 'generic' union type. |
| 3447 | | |
| 3448 | | const casted_var_id = self.spv.allocId(); |
| 3449 | | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 3450 | | .id_result_type = self.typeId(un_general_ptr_ty_ref), |
| 3451 | | .id_result = casted_var_id, |
| 3452 | | .operand = tmp_id, |
| 3453 | | }); |
| 3454 | | |
| 3455 | | const result_id = self.spv.allocId(); |
| 3456 | | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 3457 | | .id_result_type = self.typeId(un_general_ty_ref), |
| 3458 | | .id_result = result_id, |
| 3459 | | .pointer = casted_var_id, |
| 3460 | | }); |
| 3461 | | |
| 3462 | | return result_id; |
| 3440 | return try self.load(ty, tmp_id, .{}); |
| 3463 | 3441 | } |
| 3464 | 3442 | |
| 3465 | 3443 | fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 3466 | 3444 | if (self.liveness.isUnused(inst)) return null; |
| 3467 | 3445 | |
| 3446 | const mod = self.module; |
| 3447 | const ip = &mod.intern_pool; |
| 3468 | 3448 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3469 | 3449 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 3470 | 3450 | const ty = self.typeOfIndex(inst); |
| 3471 | | const layout = self.unionLayout(ty, extra.field_index); |
| 3472 | 3451 | |
| 3473 | | const payload = if (layout.active_field_size != 0) |
| 3452 | const union_obj = mod.typeToUnion(ty).?; |
| 3453 | const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType(); |
| 3454 | const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 3474 | 3455 | try self.resolve(extra.init) |
| 3475 | 3456 | else |
| 3476 | 3457 | null; |
| ... | ... | @@ -3499,30 +3480,24 @@ const DeclGen = struct { |
| 3499 | 3480 | .Union => switch (object_ty.containerLayout(mod)) { |
| 3500 | 3481 | .Packed => unreachable, // TODO |
| 3501 | 3482 | else => { |
| 3502 | | // Store, pointer-cast, load |
| 3503 | | const un_general_ty_ref = try self.resolveType(object_ty, .indirect); |
| 3504 | | const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function); |
| 3505 | | const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index); |
| 3506 | | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); |
| 3507 | | const field_ty_ref = try self.resolveType(field_ty, .indirect); |
| 3508 | | const field_ptr_ty_ref = try self.spv.ptrType(field_ty_ref, .Function); |
| 3509 | | |
| 3510 | | const tmp_id = self.spv.allocId(); |
| 3511 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 3512 | | .id_result_type = self.typeId(un_general_ptr_ty_ref), |
| 3513 | | .id_result = tmp_id, |
| 3514 | | .storage_class = .Function, |
| 3515 | | }); |
| 3516 | | try self.store(object_ty, tmp_id, object_id, false); |
| 3517 | | const casted_tmp_id = self.spv.allocId(); |
| 3483 | // Store, ptr-elem-ptr, pointer-cast, load |
| 3484 | const layout = self.unionLayout(object_ty); |
| 3485 | assert(layout.has_payload); |
| 3486 | |
| 3487 | const tmp_id = try self.alloc(object_ty, .{ .storage_class = .Function }); |
| 3488 | try self.store(object_ty, tmp_id, object_id, .{}); |
| 3489 | |
| 3490 | const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, .Function); |
| 3491 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, tmp_id, &.{layout.payload_index}); |
| 3492 | |
| 3493 | const active_pl_ptr_ty_ref = try self.ptrType(field_ty, .Function); |
| 3494 | const active_pl_ptr_id = self.spv.allocId(); |
| 3518 | 3495 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 3519 | | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 3520 | | .id_result = casted_tmp_id, |
| 3521 | | .operand = tmp_id, |
| 3496 | .id_result_type = self.typeId(active_pl_ptr_ty_ref), |
| 3497 | .id_result = active_pl_ptr_id, |
| 3498 | .operand = pl_ptr_id, |
| 3522 | 3499 | }); |
| 3523 | | const layout = self.unionLayout(object_ty, field_index); |
| 3524 | | const field_ptr_id = try self.accessChain(field_ptr_ty_ref, casted_tmp_id, &.{layout.active_field_index}); |
| 3525 | | return try self.load(field_ty, field_ptr_id, false); |
| 3500 | return try self.load(field_ty, active_pl_ptr_id, .{}); |
| 3526 | 3501 | }, |
| 3527 | 3502 | }, |
| 3528 | 3503 | else => unreachable, |
| ... | ... | @@ -3581,18 +3556,24 @@ const DeclGen = struct { |
| 3581 | 3556 | .Union => switch (object_ty.containerLayout(mod)) { |
| 3582 | 3557 | .Packed => unreachable, // TODO |
| 3583 | 3558 | else => { |
| 3559 | const layout = self.unionLayout(object_ty); |
| 3560 | if (!layout.has_payload) { |
| 3561 | // Asked to get a pointer to a zero-sized field. Just lower this |
| 3562 | // to undefined, there is no reason to make it be a valid pointer. |
| 3563 | return try self.spv.constUndef(result_ty_ref); |
| 3564 | } |
| 3565 | |
| 3584 | 3566 | const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); |
| 3585 | | const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index); |
| 3586 | | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, storage_class); |
| 3567 | const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, storage_class); |
| 3568 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, object_ptr, &.{layout.payload_index}); |
| 3587 | 3569 | |
| 3588 | | const casted_id = self.spv.allocId(); |
| 3570 | const active_pl_ptr_id = self.spv.allocId(); |
| 3589 | 3571 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 3590 | | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 3591 | | .id_result = casted_id, |
| 3592 | | .operand = object_ptr, |
| 3572 | .id_result_type = self.typeId(result_ty_ref), |
| 3573 | .id_result = active_pl_ptr_id, |
| 3574 | .operand = pl_ptr_id, |
| 3593 | 3575 | }); |
| 3594 | | const layout = self.unionLayout(object_ty, field_index); |
| 3595 | | return try self.accessChain(result_ty_ref, casted_id, &.{layout.active_field_index}); |
| 3576 | return active_pl_ptr_id; |
| 3596 | 3577 | }, |
| 3597 | 3578 | }, |
| 3598 | 3579 | else => unreachable, |
| ... | ... | @@ -3608,23 +3589,13 @@ const DeclGen = struct { |
| 3608 | 3589 | return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); |
| 3609 | 3590 | } |
| 3610 | 3591 | |
| 3611 | | /// We cannot use an OpVariable directly in an OpSpecConstantOp, but we can |
| 3612 | | /// after we insert a dummy AccessChain... |
| 3613 | | /// TODO: Get rid of this |
| 3614 | | fn makePointerConstant( |
| 3615 | | self: *DeclGen, |
| 3616 | | section: *SpvSection, |
| 3617 | | ptr_ty_ref: CacheRef, |
| 3618 | | ptr_id: IdRef, |
| 3619 | | ) !IdRef { |
| 3620 | | const result_id = self.spv.allocId(); |
| 3621 | | try section.emitSpecConstantOp(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| 3622 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 3623 | | .id_result = result_id, |
| 3624 | | .base = ptr_id, |
| 3625 | | }); |
| 3626 | | return result_id; |
| 3627 | | } |
| 3592 | const AllocOptions = struct { |
| 3593 | initializer: ?IdRef = null, |
| 3594 | /// The final storage class of the pointer. This may be either `.Generic` or `.Function`. |
| 3595 | /// In either case, the local is allocated in the `.Function` storage class, and optionally |
| 3596 | /// cast back to `.Generic`. |
| 3597 | storage_class: StorageClass = .Generic, |
| 3598 | }; |
| 3628 | 3599 | |
| 3629 | 3600 | // Allocate a function-local variable, with possible initializer. |
| 3630 | 3601 | // This function returns a pointer to a variable of type `ty_ref`, |
| ... | ... | @@ -3632,30 +3603,36 @@ const DeclGen = struct { |
| 3632 | 3603 | // placed in the Function address space. |
| 3633 | 3604 | fn alloc( |
| 3634 | 3605 | self: *DeclGen, |
| 3635 | | ty_ref: CacheRef, |
| 3636 | | initializer: ?IdRef, |
| 3606 | ty: Type, |
| 3607 | options: AllocOptions, |
| 3637 | 3608 | ) !IdRef { |
| 3638 | | const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function); |
| 3639 | | const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic); |
| 3609 | const ptr_fn_ty_ref = try self.ptrType(ty, .Function); |
| 3640 | 3610 | |
| 3641 | 3611 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to |
| 3642 | 3612 | // directly generate them into func.prologue instead of the body. |
| 3643 | 3613 | const var_id = self.spv.allocId(); |
| 3644 | 3614 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 3645 | | .id_result_type = self.typeId(fn_ptr_ty_ref), |
| 3615 | .id_result_type = self.typeId(ptr_fn_ty_ref), |
| 3646 | 3616 | .id_result = var_id, |
| 3647 | 3617 | .storage_class = .Function, |
| 3648 | | .initializer = initializer, |
| 3618 | .initializer = options.initializer, |
| 3649 | 3619 | }); |
| 3650 | 3620 | |
| 3651 | | // Convert to a generic pointer |
| 3652 | | const result_id = self.spv.allocId(); |
| 3653 | | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 3654 | | .id_result_type = self.typeId(general_ptr_ty_ref), |
| 3655 | | .id_result = result_id, |
| 3656 | | .pointer = var_id, |
| 3657 | | }); |
| 3658 | | return result_id; |
| 3621 | switch (options.storage_class) { |
| 3622 | .Generic => { |
| 3623 | const ptr_gn_ty_ref = try self.ptrType(ty, .Generic); |
| 3624 | // Convert to a generic pointer |
| 3625 | const result_id = self.spv.allocId(); |
| 3626 | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 3627 | .id_result_type = self.typeId(ptr_gn_ty_ref), |
| 3628 | .id_result = result_id, |
| 3629 | .pointer = var_id, |
| 3630 | }); |
| 3631 | return result_id; |
| 3632 | }, |
| 3633 | .Function => return var_id, |
| 3634 | else => unreachable, |
| 3635 | } |
| 3659 | 3636 | } |
| 3660 | 3637 | |
| 3661 | 3638 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3664,8 +3641,7 @@ const DeclGen = struct { |
| 3664 | 3641 | const ptr_ty = self.typeOfIndex(inst); |
| 3665 | 3642 | assert(ptr_ty.ptrAddressSpace(mod) == .generic); |
| 3666 | 3643 | const child_ty = ptr_ty.childType(mod); |
| 3667 | | const child_ty_ref = try self.resolveType(child_ty, .indirect); |
| 3668 | | return try self.alloc(child_ty_ref, null); |
| 3644 | return try self.alloc(child_ty, .{}); |
| 3669 | 3645 | } |
| 3670 | 3646 | |
| 3671 | 3647 | fn airArg(self: *DeclGen) IdRef { |
| ... | ... | @@ -3780,7 +3756,7 @@ const DeclGen = struct { |
| 3780 | 3756 | const operand = try self.resolve(ty_op.operand); |
| 3781 | 3757 | if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null; |
| 3782 | 3758 | |
| 3783 | | return try self.load(elem_ty, operand, ptr_ty.isVolatilePtr(mod)); |
| 3759 | return try self.load(elem_ty, operand, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); |
| 3784 | 3760 | } |
| 3785 | 3761 | |
| 3786 | 3762 | fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3790,7 +3766,7 @@ const DeclGen = struct { |
| 3790 | 3766 | const ptr = try self.resolve(bin_op.lhs); |
| 3791 | 3767 | const value = try self.resolve(bin_op.rhs); |
| 3792 | 3768 | |
| 3793 | | try self.store(elem_ty, ptr, value, ptr_ty.isVolatilePtr(self.module)); |
| 3769 | try self.store(elem_ty, ptr, value, .{ .is_volatile = ptr_ty.isVolatilePtr(self.module) }); |
| 3794 | 3770 | } |
| 3795 | 3771 | |
| 3796 | 3772 | fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3854,7 +3830,7 @@ const DeclGen = struct { |
| 3854 | 3830 | } |
| 3855 | 3831 | |
| 3856 | 3832 | const ptr = try self.resolve(un_op); |
| 3857 | | const value = try self.load(ret_ty, ptr, ptr_ty.isVolatilePtr(mod)); |
| 3833 | const value = try self.load(ret_ty, ptr, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) }); |
| 3858 | 3834 | try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ |
| 3859 | 3835 | .value = value, |
| 3860 | 3836 | }); |
| ... | ... | @@ -3980,8 +3956,11 @@ const DeclGen = struct { |
| 3980 | 3956 | members[eu_layout.errorFieldIndex()] = operand_id; |
| 3981 | 3957 | members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_ref); |
| 3982 | 3958 | |
| 3983 | | const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); |
| 3984 | | return try self.constructStruct(err_union_ty_ref, &members); |
| 3959 | var types: [2]Type = undefined; |
| 3960 | types[eu_layout.errorFieldIndex()] = Type.anyerror; |
| 3961 | types[eu_layout.payloadFieldIndex()] = payload_ty; |
| 3962 | |
| 3963 | return try self.constructStruct(err_union_ty, &types, &members); |
| 3985 | 3964 | } |
| 3986 | 3965 | |
| 3987 | 3966 | fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -4002,8 +3981,11 @@ const DeclGen = struct { |
| 4002 | 3981 | members[eu_layout.errorFieldIndex()] = try self.constInt(err_ty_ref, 0); |
| 4003 | 3982 | members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); |
| 4004 | 3983 | |
| 4005 | | const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); |
| 4006 | | return try self.constructStruct(err_union_ty_ref, &members); |
| 3984 | var types: [2]Type = undefined; |
| 3985 | types[eu_layout.errorFieldIndex()] = Type.anyerror; |
| 3986 | types[eu_layout.payloadFieldIndex()] = payload_ty; |
| 3987 | |
| 3988 | return try self.constructStruct(err_union_ty, &types, &members); |
| 4007 | 3989 | } |
| 4008 | 3990 | |
| 4009 | 3991 | fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef { |
| ... | ... | @@ -4037,7 +4019,7 @@ const DeclGen = struct { |
| 4037 | 4019 | .is_null => .eq, |
| 4038 | 4020 | .is_non_null => .neq, |
| 4039 | 4021 | }; |
| 4040 | | return try self.cmp(op, ptr_ty, ptr_id, null_id); |
| 4022 | return try self.cmp(op, Type.bool, ptr_ty, ptr_id, null_id); |
| 4041 | 4023 | } |
| 4042 | 4024 | |
| 4043 | 4025 | const is_non_null_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| ... | ... | @@ -4135,10 +4117,10 @@ const DeclGen = struct { |
| 4135 | 4117 | return operand_id; |
| 4136 | 4118 | } |
| 4137 | 4119 | |
| 4138 | | const optional_ty_ref = try self.resolveType(optional_ty, .direct); |
| 4139 | 4120 | const payload_id = try self.convertToIndirect(payload_ty, operand_id); |
| 4140 | 4121 | const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) }; |
| 4141 | | return try self.constructStruct(optional_ty_ref, &members); |
| 4122 | const types = [_]Type{ payload_ty, Type.bool }; |
| 4123 | return try self.constructStruct(optional_ty, &types, &members); |
| 4142 | 4124 | } |
| 4143 | 4125 | |
| 4144 | 4126 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -4420,6 +4402,7 @@ const DeclGen = struct { |
| 4420 | 4402 | } |
| 4421 | 4403 | |
| 4422 | 4404 | // TODO: Multiple results |
| 4405 | // TODO: Check that the output type from assembly is the same as the type actually expected by Zig. |
| 4423 | 4406 | } |
| 4424 | 4407 | |
| 4425 | 4408 | return null; |