| ... | ... | @@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig"); |
| 30 | 30 | const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); |
| 31 | 31 | |
| 32 | 32 | pub const zig_call_abi_ver = 3; |
| 33 | pub const big_int_bits = 32; |
| 33 | 34 | |
| 34 | 35 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult); |
| 35 | 36 | const PtrTypeMap = std.AutoHashMapUnmanaged( |
| ... | ... | @@ -376,7 +377,7 @@ const NavGen = struct { |
| 376 | 377 | /// The number of bits required to store the type. |
| 377 | 378 | /// For `integer` and `float`, this is equal to `bits`. |
| 378 | 379 | /// For `strange_integer` and `bool` this is the size of the backing integer. |
| 379 | | /// For `composite_integer` this is 0 (TODO) |
| 380 | /// For `composite_integer` this is the elements count. |
| 380 | 381 | backing_bits: u16, |
| 381 | 382 | |
| 382 | 383 | /// Null if this type is a scalar, or the length |
| ... | ... | @@ -579,11 +580,13 @@ const NavGen = struct { |
| 579 | 580 | /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. |
| 580 | 581 | /// The result is valid to be used with OpTypeInt. |
| 581 | 582 | /// TODO: Should the result of this function be cached? |
| 582 | | fn backingIntBits(self: *NavGen, bits: u16) ?u16 { |
| 583 | fn backingIntBits(self: *NavGen, bits: u16) struct { u16, bool } { |
| 583 | 584 | // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. |
| 584 | 585 | assert(bits != 0); |
| 585 | 586 | |
| 586 | | if (self.spv.hasFeature(.arbitrary_precision_integers) and bits <= 32) return bits; |
| 587 | if (self.spv.hasFeature(.arbitrary_precision_integers) and bits <= 32) { |
| 588 | return .{ bits, false }; |
| 589 | } |
| 587 | 590 | |
| 588 | 591 | // We require Int8 and Int16 capabilities and benefit Int64 when available. |
| 589 | 592 | // 32-bit integers are always supported (see spec, 2.16.1, Data rules). |
| ... | ... | @@ -596,10 +599,11 @@ const NavGen = struct { |
| 596 | 599 | |
| 597 | 600 | for (ints) |int| { |
| 598 | 601 | const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true; |
| 599 | | if (bits <= int.bits and has_feature) return int.bits; |
| 602 | if (bits <= int.bits and has_feature) return .{ int.bits, false }; |
| 600 | 603 | } |
| 601 | 604 | |
| 602 | | return null; |
| 605 | // Big int |
| 606 | return .{ std.mem.alignForward(u16, bits, big_int_bits), true }; |
| 603 | 607 | } |
| 604 | 608 | |
| 605 | 609 | /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if |
| ... | ... | @@ -623,7 +627,7 @@ const NavGen = struct { |
| 623 | 627 | return switch (scalar_ty.zigTypeTag(zcu)) { |
| 624 | 628 | .bool => .{ |
| 625 | 629 | .bits = 1, // Doesn't matter for this class. |
| 626 | | .backing_bits = self.backingIntBits(1).?, |
| 630 | .backing_bits = self.backingIntBits(1).@"0", |
| 627 | 631 | .vector_len = vector_len, |
| 628 | 632 | .signedness = .unsigned, // Technically, but doesn't matter for this class. |
| 629 | 633 | .class = .bool, |
| ... | ... | @@ -638,19 +642,16 @@ const NavGen = struct { |
| 638 | 642 | .int => blk: { |
| 639 | 643 | const int_info = scalar_ty.intInfo(zcu); |
| 640 | 644 | // TODO: Maybe it's useful to also return this value. |
| 641 | | const maybe_backing_bits = self.backingIntBits(int_info.bits); |
| 645 | const backing_bits, const big_int = self.backingIntBits(int_info.bits); |
| 642 | 646 | break :blk .{ |
| 643 | 647 | .bits = int_info.bits, |
| 644 | | .backing_bits = maybe_backing_bits orelse 0, |
| 648 | .backing_bits = backing_bits, |
| 645 | 649 | .vector_len = vector_len, |
| 646 | 650 | .signedness = int_info.signedness, |
| 647 | | .class = if (maybe_backing_bits) |backing_bits| |
| 648 | | if (backing_bits == int_info.bits) |
| 649 | | .integer |
| 650 | | else |
| 651 | | .strange_integer |
| 652 | | else |
| 653 | | .composite_integer, |
| 651 | .class = class: { |
| 652 | if (big_int) break :class .composite_integer; |
| 653 | break :class if (backing_bits == int_info.bits) .integer else .strange_integer; |
| 654 | }, |
| 654 | 655 | }; |
| 655 | 656 | }, |
| 656 | 657 | .@"enum" => unreachable, |
| ... | ... | @@ -659,6 +660,34 @@ const NavGen = struct { |
| 659 | 660 | }; |
| 660 | 661 | } |
| 661 | 662 | |
| 663 | /// Checks whether the type can be directly translated to SPIR-V vectors |
| 664 | fn isSpvVector(self: *NavGen, ty: Type) bool { |
| 665 | const zcu = self.pt.zcu; |
| 666 | if (ty.zigTypeTag(zcu) != .vector) return false; |
| 667 | |
| 668 | // TODO: This check must be expanded for types that can be represented |
| 669 | // as integers (enums / packed structs?) and types that are represented |
| 670 | // by multiple SPIR-V values. |
| 671 | const scalar_ty = ty.scalarType(zcu); |
| 672 | switch (scalar_ty.zigTypeTag(zcu)) { |
| 673 | .bool, |
| 674 | .int, |
| 675 | .float, |
| 676 | => {}, |
| 677 | else => return false, |
| 678 | } |
| 679 | |
| 680 | const elem_ty = ty.childType(zcu); |
| 681 | const len = ty.vectorLen(zcu); |
| 682 | |
| 683 | if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) { |
| 684 | if (len > 1 and len <= 4) return true; |
| 685 | if (self.spv.hasFeature(.vector16)) return (len == 8 or len == 16); |
| 686 | } |
| 687 | |
| 688 | return false; |
| 689 | } |
| 690 | |
| 662 | 691 | /// Emits a bool constant in a particular representation. |
| 663 | 692 | fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef { |
| 664 | 693 | return switch (repr) { |
| ... | ... | @@ -675,14 +704,26 @@ const NavGen = struct { |
| 675 | 704 | const scalar_ty = ty.scalarType(zcu); |
| 676 | 705 | const int_info = scalar_ty.intInfo(zcu); |
| 677 | 706 | // Use backing bits so that negatives are sign extended |
| 678 | | const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int |
| 707 | const backing_bits, const big_int = self.backingIntBits(int_info.bits); |
| 679 | 708 | assert(backing_bits != 0); // u0 is comptime |
| 680 | 709 | |
| 710 | const result_ty_id = try self.resolveType(scalar_ty, .indirect); |
| 681 | 711 | const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) { |
| 682 | 712 | .int => |int| int.signedness, |
| 683 | 713 | .comptime_int => if (value < 0) .signed else .unsigned, |
| 684 | 714 | else => unreachable, |
| 685 | 715 | }; |
| 716 | if (@sizeOf(@TypeOf(value)) >= 4 and big_int) { |
| 717 | const value64: u64 = switch (signedness) { |
| 718 | .signed => @bitCast(@as(i64, @intCast(value))), |
| 719 | .unsigned => @as(u64, @intCast(value)), |
| 720 | }; |
| 721 | assert(backing_bits == 64); |
| 722 | return self.constructComposite(result_ty_id, &.{ |
| 723 | try self.constInt(.u32, @as(u32, @truncate(value64))), |
| 724 | try self.constInt(.u32, @as(u32, @truncate(value64 << 32))), |
| 725 | }); |
| 726 | } |
| 686 | 727 | |
| 687 | 728 | const final_value: spec.LiteralContextDependentNumber = blk: { |
| 688 | 729 | if (self.spv.hasFeature(.kernel)) { |
| ... | ... | @@ -700,18 +741,17 @@ const NavGen = struct { |
| 700 | 741 | break :blk switch (backing_bits) { |
| 701 | 742 | 1...32 => .{ .uint32 = @truncate(truncated_value) }, |
| 702 | 743 | 33...64 => .{ .uint64 = truncated_value }, |
| 703 | | else => unreachable, // TODO: Large integer constants |
| 744 | else => unreachable, |
| 704 | 745 | }; |
| 705 | 746 | } |
| 706 | 747 | |
| 707 | 748 | break :blk switch (backing_bits) { |
| 708 | 749 | 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) }, |
| 709 | 750 | 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value }, |
| 710 | | else => unreachable, // TODO: Large integer constants |
| 751 | else => unreachable, |
| 711 | 752 | }; |
| 712 | 753 | }; |
| 713 | 754 | |
| 714 | | const result_ty_id = try self.resolveType(scalar_ty, .indirect); |
| 715 | 755 | const result_id = try self.spv.constant(result_ty_id, final_value); |
| 716 | 756 | |
| 717 | 757 | if (!ty.isVector(zcu)) return result_id; |
| ... | ... | @@ -949,7 +989,7 @@ const NavGen = struct { |
| 949 | 989 | // TODO: composite int |
| 950 | 990 | // TODO: endianness |
| 951 | 991 | const bits: u16 = @intCast(ty.bitSize(zcu)); |
| 952 | | const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).?, 8) / 8; |
| 992 | const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).@"0", 8) / 8; |
| 953 | 993 | var limbs: [8]u8 = undefined; |
| 954 | 994 | @memset(&limbs, 0); |
| 955 | 995 | val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable; |
| ... | ... | @@ -1068,19 +1108,11 @@ const NavGen = struct { |
| 1068 | 1108 | const parent_ptr_id = try self.derivePtr(oac.parent.*); |
| 1069 | 1109 | const parent_ptr_ty = try oac.parent.ptrType(pt); |
| 1070 | 1110 | const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct); |
| 1111 | const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu); |
| 1071 | 1112 | |
| 1072 | | if (oac.byte_offset != 0) { |
| 1073 | | const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu); |
| 1074 | | if (oac.byte_offset % child_size != 0) { |
| 1075 | | return self.fail("cannot perform pointer cast: '{}' to '{}'", .{ |
| 1076 | | parent_ptr_ty.fmt(pt), |
| 1077 | | oac.new_ptr_ty.fmt(pt), |
| 1078 | | }); |
| 1079 | | } |
| 1080 | | |
| 1113 | if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 0) { |
| 1081 | 1114 | // Vector element ptr accesses are derived as offset_and_cast. |
| 1082 | 1115 | // We can just use OpAccessChain. |
| 1083 | | assert(parent_ptr_ty.childType(zcu).zigTypeTag(zcu) == .vector); |
| 1084 | 1116 | return self.accessChain( |
| 1085 | 1117 | result_ty_id, |
| 1086 | 1118 | parent_ptr_id, |
| ... | ... | @@ -1088,15 +1120,22 @@ const NavGen = struct { |
| 1088 | 1120 | ); |
| 1089 | 1121 | } |
| 1090 | 1122 | |
| 1091 | | // Allow changing the pointer type child only to restructure arrays. |
| 1092 | | // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. |
| 1093 | | const result_ptr_id = self.spv.allocId(); |
| 1094 | | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 1095 | | .id_result_type = result_ty_id, |
| 1096 | | .id_result = result_ptr_id, |
| 1097 | | .operand = parent_ptr_id, |
| 1123 | if (oac.byte_offset == 0) { |
| 1124 | // Allow changing the pointer type child only to restructure arrays. |
| 1125 | // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. |
| 1126 | const result_ptr_id = self.spv.allocId(); |
| 1127 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 1128 | .id_result_type = result_ty_id, |
| 1129 | .id_result = result_ptr_id, |
| 1130 | .operand = parent_ptr_id, |
| 1131 | }); |
| 1132 | return result_ptr_id; |
| 1133 | } |
| 1134 | |
| 1135 | return self.fail("cannot perform pointer cast: '{}' to '{}'", .{ |
| 1136 | parent_ptr_ty.fmt(pt), |
| 1137 | oac.new_ptr_ty.fmt(pt), |
| 1098 | 1138 | }); |
| 1099 | | return result_ptr_id; |
| 1100 | 1139 | }, |
| 1101 | 1140 | } |
| 1102 | 1141 | } |
| ... | ... | @@ -1217,11 +1256,14 @@ const NavGen = struct { |
| 1217 | 1256 | /// actual operations (as well as store) a Zig type of a particular number of bits. To create |
| 1218 | 1257 | /// a type with an exact size, use SpvModule.intType. |
| 1219 | 1258 | fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef { |
| 1220 | | const backing_bits = self.backingIntBits(bits) orelse { |
| 1221 | | // TODO: Integers too big for any native type are represented as "composite integers": |
| 1222 | | // An array of largestSupportedIntBits. |
| 1223 | | return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); |
| 1224 | | }; |
| 1259 | const backing_bits, const big_int = self.backingIntBits(bits); |
| 1260 | if (big_int) { |
| 1261 | if (backing_bits > 64) { |
| 1262 | return self.fail("composite integers larger than 64bit aren't supported", .{}); |
| 1263 | } |
| 1264 | const int_ty = try self.resolveType(.u32, .direct); |
| 1265 | return self.arrayType(backing_bits / big_int_bits, int_ty); |
| 1266 | } |
| 1225 | 1267 | |
| 1226 | 1268 | // Kernel only supports unsigned ints. |
| 1227 | 1269 | if (self.spv.hasFeature(.kernel)) { |
| ... | ... | @@ -1509,6 +1551,17 @@ const NavGen = struct { |
| 1509 | 1551 | return result_id; |
| 1510 | 1552 | } |
| 1511 | 1553 | }, |
| 1554 | .vector => { |
| 1555 | const elem_ty = ty.childType(zcu); |
| 1556 | const elem_ty_id = try self.resolveType(elem_ty, repr); |
| 1557 | const len = ty.vectorLen(zcu); |
| 1558 | |
| 1559 | if (self.isSpvVector(ty)) { |
| 1560 | return try self.spv.vectorType(len, elem_ty_id); |
| 1561 | } else { |
| 1562 | return try self.arrayType(len, elem_ty_id); |
| 1563 | } |
| 1564 | }, |
| 1512 | 1565 | .@"fn" => switch (repr) { |
| 1513 | 1566 | .direct => { |
| 1514 | 1567 | const fn_info = zcu.typeToFunc(ty).?; |
| ... | ... | @@ -1577,12 +1630,6 @@ const NavGen = struct { |
| 1577 | 1630 | ); |
| 1578 | 1631 | return result_id; |
| 1579 | 1632 | }, |
| 1580 | | .vector => { |
| 1581 | | const elem_ty = ty.childType(zcu); |
| 1582 | | const elem_ty_id = try self.resolveType(elem_ty, repr); |
| 1583 | | const len = ty.vectorLen(zcu); |
| 1584 | | return self.arrayType(len, elem_ty_id); |
| 1585 | | }, |
| 1586 | 1633 | .@"struct" => { |
| 1587 | 1634 | const struct_type = switch (ip.indexToKey(ty.toIntern())) { |
| 1588 | 1635 | .tuple_type => |tuple| { |
| ... | ... | @@ -3378,8 +3425,7 @@ const NavGen = struct { |
| 3378 | 3425 | const zcu = self.pt.zcu; |
| 3379 | 3426 | const ty = value.ty; |
| 3380 | 3427 | switch (info.class) { |
| 3381 | | .integer, .bool, .float => return value, |
| 3382 | | .composite_integer => unreachable, // TODO |
| 3428 | .composite_integer, .integer, .bool, .float => return value, |
| 3383 | 3429 | .strange_integer => switch (info.signedness) { |
| 3384 | 3430 | .unsigned => { |
| 3385 | 3431 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; |
| ... | ... | @@ -5039,7 +5085,7 @@ const NavGen = struct { |
| 5039 | 5085 | const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1); |
| 5040 | 5086 | const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } }); |
| 5041 | 5087 | const result_id = blk: { |
| 5042 | | if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).?) |
| 5088 | if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0") |
| 5043 | 5089 | break :blk try self.bitCast(field_int_ty, object_ty, try masked.materialize(self)); |
| 5044 | 5090 | const trunc = try self.buildConvert(field_int_ty, masked); |
| 5045 | 5091 | break :blk try trunc.materialize(self); |
| ... | ... | @@ -5063,7 +5109,7 @@ const NavGen = struct { |
| 5063 | 5109 | .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } }, |
| 5064 | 5110 | ); |
| 5065 | 5111 | const result_id = blk: { |
| 5066 | | if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).?) |
| 5112 | if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).@"0") |
| 5067 | 5113 | break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self)); |
| 5068 | 5114 | const trunc = try self.buildConvert(int_ty, masked); |
| 5069 | 5115 | break :blk try trunc.materialize(self); |
| ... | ... | @@ -6100,17 +6146,15 @@ const NavGen = struct { |
| 6100 | 6146 | .bool, .error_set => 1, |
| 6101 | 6147 | .int => blk: { |
| 6102 | 6148 | const bits = cond_ty.intInfo(zcu).bits; |
| 6103 | | const backing_bits = self.backingIntBits(bits) orelse { |
| 6104 | | return self.todo("implement composite int switch", .{}); |
| 6105 | | }; |
| 6149 | const backing_bits, const big_int = self.backingIntBits(bits); |
| 6150 | if (big_int) return self.todo("implement composite int switch", .{}); |
| 6106 | 6151 | break :blk if (backing_bits <= 32) 1 else 2; |
| 6107 | 6152 | }, |
| 6108 | 6153 | .@"enum" => blk: { |
| 6109 | 6154 | const int_ty = cond_ty.intTagType(zcu); |
| 6110 | 6155 | const int_info = int_ty.intInfo(zcu); |
| 6111 | | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 6112 | | return self.todo("implement composite int switch", .{}); |
| 6113 | | }; |
| 6156 | const backing_bits, const big_int = self.backingIntBits(int_info.bits); |
| 6157 | if (big_int) return self.todo("implement composite int switch", .{}); |
| 6114 | 6158 | break :blk if (backing_bits <= 32) 1 else 2; |
| 6115 | 6159 | }, |
| 6116 | 6160 | .pointer => blk: { |