| ... | @@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig"); | ... | @@ -30,6 +30,7 @@ const SpvAssembler = @import("spirv/Assembler.zig"); |
| 30 | const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); | 30 | const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); |
| 31 | | 31 | |
| 32 | pub const zig_call_abi_ver = 3; | 32 | pub const zig_call_abi_ver = 3; |
| | 33 | pub const big_int_bits = 32; |
| 33 | | 34 | |
| 34 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult); | 35 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, NavGen.Repr }, IdResult); |
| 35 | const PtrTypeMap = std.AutoHashMapUnmanaged( | 36 | const PtrTypeMap = std.AutoHashMapUnmanaged( |
| ... | @@ -169,12 +170,10 @@ pub const Object = struct { | ... | @@ -169,12 +170,10 @@ pub const Object = struct { |
| 169 | /// via the usual `intern_map` mechanism. | 170 | /// via the usual `intern_map` mechanism. |
| 170 | ptr_types: PtrTypeMap = .{}, | 171 | ptr_types: PtrTypeMap = .{}, |
| 171 | | 172 | |
| 172 | /// For test declarations for Vulkan, we have to add a push constant with a pointer to a | 173 | /// For test declarations for Vulkan, we have to add a buffer. |
| 173 | /// buffer that we can use. We only need to generate this once, this holds the link information | 174 | /// We only need to generate this once, this holds the link information |
| 174 | /// related to that. | 175 | /// related to that. |
| 175 | error_push_constant: ?struct { | 176 | error_buffer: ?SpvModule.Decl.Index = null, |
| 176 | push_constant_ptr: SpvModule.Decl.Index, | | |
| 177 | } = null, | | |
| 178 | | 177 | |
| 179 | pub fn init(gpa: Allocator, target: std.Target) Object { | 178 | pub fn init(gpa: Allocator, target: std.Target) Object { |
| 180 | return .{ | 179 | return .{ |
| ... | @@ -344,8 +343,7 @@ const NavGen = struct { | ... | @@ -344,8 +343,7 @@ const NavGen = struct { |
| 344 | | 343 | |
| 345 | /// This structure is used to return information about a type typically used for | 344 | /// This structure is used to return information about a type typically used for |
| 346 | /// arithmetic operations. These types may either be integers, floats, or a vector | 345 | /// arithmetic operations. These types may either be integers, floats, or a vector |
| 347 | /// of these. Most scalar operations also work on vectors, so we can easily represent | 346 | /// of these. If the type is a scalar, 'inner type' refers to the |
| 348 | /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the | | |
| 349 | /// scalar type. Otherwise, if its a vector, it refers to the vector's element type. | 347 | /// scalar type. Otherwise, if its a vector, it refers to the vector's element type. |
| 350 | const ArithmeticTypeInfo = struct { | 348 | const ArithmeticTypeInfo = struct { |
| 351 | /// A classification of the inner type. | 349 | /// A classification of the inner type. |
| ... | @@ -379,7 +377,7 @@ const NavGen = struct { | ... | @@ -379,7 +377,7 @@ const NavGen = struct { |
| 379 | /// The number of bits required to store the type. | 377 | /// The number of bits required to store the type. |
| 380 | /// For `integer` and `float`, this is equal to `bits`. | 378 | /// For `integer` and `float`, this is equal to `bits`. |
| 381 | /// For `strange_integer` and `bool` this is the size of the backing integer. | 379 | /// For `strange_integer` and `bool` this is the size of the backing integer. |
| 382 | /// For `composite_integer` this is 0 (TODO) | 380 | /// For `composite_integer` this is the elements count. |
| 383 | backing_bits: u16, | 381 | backing_bits: u16, |
| 384 | | 382 | |
| 385 | /// Null if this type is a scalar, or the length | 383 | /// Null if this type is a scalar, or the length |
| ... | @@ -582,11 +580,13 @@ const NavGen = struct { | ... | @@ -582,11 +580,13 @@ const NavGen = struct { |
| 582 | /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. | 580 | /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. |
| 583 | /// The result is valid to be used with OpTypeInt. | 581 | /// The result is valid to be used with OpTypeInt. |
| 584 | /// TODO: Should the result of this function be cached? | 582 | /// TODO: Should the result of this function be cached? |
| 585 | fn backingIntBits(self: *NavGen, bits: u16) ?u16 { | 583 | fn backingIntBits(self: *NavGen, bits: u16) struct { u16, bool } { |
| 586 | // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. | 584 | // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. |
| 587 | assert(bits != 0); | 585 | assert(bits != 0); |
| 588 | | 586 | |
| 589 | 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 | } |
| 590 | | 590 | |
| 591 | // We require Int8 and Int16 capabilities and benefit Int64 when available. | 591 | // We require Int8 and Int16 capabilities and benefit Int64 when available. |
| 592 | // 32-bit integers are always supported (see spec, 2.16.1, Data rules). | 592 | // 32-bit integers are always supported (see spec, 2.16.1, Data rules). |
| ... | @@ -599,10 +599,11 @@ const NavGen = struct { | ... | @@ -599,10 +599,11 @@ const NavGen = struct { |
| 599 | | 599 | |
| 600 | for (ints) |int| { | 600 | for (ints) |int| { |
| 601 | const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true; | 601 | const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true; |
| 602 | if (bits <= int.bits and has_feature) return int.bits; | 602 | if (bits <= int.bits and has_feature) return .{ int.bits, false }; |
| 603 | } | 603 | } |
| 604 | | 604 | |
| 605 | return null; | 605 | // Big int |
| | 606 | return .{ std.mem.alignForward(u16, bits, big_int_bits), true }; |
| 606 | } | 607 | } |
| 607 | | 608 | |
| 608 | /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if | 609 | /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if |
| ... | @@ -615,41 +616,6 @@ const NavGen = struct { | ... | @@ -615,41 +616,6 @@ const NavGen = struct { |
| 615 | return if (self.spv.hasFeature(.int64)) 64 else 32; | 616 | return if (self.spv.hasFeature(.int64)) 64 else 32; |
| 616 | } | 617 | } |
| 617 | | 618 | |
| 618 | /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by | | |
| 619 | /// arrays of largestSupportedIntBits(). | | |
| 620 | /// Asserts `ty` is an integer. | | |
| 621 | fn isCompositeInt(self: *NavGen, ty: Type) bool { | | |
| 622 | return self.backingIntBits(ty) == null; | | |
| 623 | } | | |
| 624 | | | |
| 625 | /// Checks whether the type can be directly translated to SPIR-V vectors | | |
| 626 | fn isSpvVector(self: *NavGen, ty: Type) bool { | | |
| 627 | const zcu = self.pt.zcu; | | |
| 628 | if (ty.zigTypeTag(zcu) != .vector) return false; | | |
| 629 | | | |
| 630 | // TODO: This check must be expanded for types that can be represented | | |
| 631 | // as integers (enums / packed structs?) and types that are represented | | |
| 632 | // by multiple SPIR-V values. | | |
| 633 | const scalar_ty = ty.scalarType(zcu); | | |
| 634 | switch (scalar_ty.zigTypeTag(zcu)) { | | |
| 635 | .bool, | | |
| 636 | .int, | | |
| 637 | .float, | | |
| 638 | => {}, | | |
| 639 | else => return false, | | |
| 640 | } | | |
| 641 | | | |
| 642 | const elem_ty = ty.childType(zcu); | | |
| 643 | const len = ty.vectorLen(zcu); | | |
| 644 | | | |
| 645 | if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) { | | |
| 646 | if (len > 1 and len <= 4) return true; | | |
| 647 | if (self.spv.hasFeature(.vector16)) return (len == 8 or len == 16); | | |
| 648 | } | | |
| 649 | | | |
| 650 | return false; | | |
| 651 | } | | |
| 652 | | | |
| 653 | fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo { | 619 | fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo { |
| 654 | const zcu = self.pt.zcu; | 620 | const zcu = self.pt.zcu; |
| 655 | const target = self.spv.target; | 621 | const target = self.spv.target; |
| ... | @@ -659,14 +625,14 @@ const NavGen = struct { | ... | @@ -659,14 +625,14 @@ const NavGen = struct { |
| 659 | } | 625 | } |
| 660 | const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null; | 626 | const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null; |
| 661 | return switch (scalar_ty.zigTypeTag(zcu)) { | 627 | return switch (scalar_ty.zigTypeTag(zcu)) { |
| 662 | .bool => ArithmeticTypeInfo{ | 628 | .bool => .{ |
| 663 | .bits = 1, // Doesn't matter for this class. | 629 | .bits = 1, // Doesn't matter for this class. |
| 664 | .backing_bits = self.backingIntBits(1).?, | 630 | .backing_bits = self.backingIntBits(1).@"0", |
| 665 | .vector_len = vector_len, | 631 | .vector_len = vector_len, |
| 666 | .signedness = .unsigned, // Technically, but doesn't matter for this class. | 632 | .signedness = .unsigned, // Technically, but doesn't matter for this class. |
| 667 | .class = .bool, | 633 | .class = .bool, |
| 668 | }, | 634 | }, |
| 669 | .float => ArithmeticTypeInfo{ | 635 | .float => .{ |
| 670 | .bits = scalar_ty.floatBits(target), | 636 | .bits = scalar_ty.floatBits(target), |
| 671 | .backing_bits = scalar_ty.floatBits(target), // TODO: F80? | 637 | .backing_bits = scalar_ty.floatBits(target), // TODO: F80? |
| 672 | .vector_len = vector_len, | 638 | .vector_len = vector_len, |
| ... | @@ -676,19 +642,16 @@ const NavGen = struct { | ... | @@ -676,19 +642,16 @@ const NavGen = struct { |
| 676 | .int => blk: { | 642 | .int => blk: { |
| 677 | const int_info = scalar_ty.intInfo(zcu); | 643 | const int_info = scalar_ty.intInfo(zcu); |
| 678 | // TODO: Maybe it's useful to also return this value. | 644 | // TODO: Maybe it's useful to also return this value. |
| 679 | const maybe_backing_bits = self.backingIntBits(int_info.bits); | 645 | const backing_bits, const big_int = self.backingIntBits(int_info.bits); |
| 680 | break :blk ArithmeticTypeInfo{ | 646 | break :blk .{ |
| 681 | .bits = int_info.bits, | 647 | .bits = int_info.bits, |
| 682 | .backing_bits = maybe_backing_bits orelse 0, | 648 | .backing_bits = backing_bits, |
| 683 | .vector_len = vector_len, | 649 | .vector_len = vector_len, |
| 684 | .signedness = int_info.signedness, | 650 | .signedness = int_info.signedness, |
| 685 | .class = if (maybe_backing_bits) |backing_bits| | 651 | .class = class: { |
| 686 | if (backing_bits == int_info.bits) | 652 | if (big_int) break :class .composite_integer; |
| 687 | ArithmeticTypeInfo.Class.integer | 653 | break :class if (backing_bits == int_info.bits) .integer else .strange_integer; |
| 688 | else | 654 | }, |
| 689 | ArithmeticTypeInfo.Class.strange_integer | | |
| 690 | else | | |
| 691 | .composite_integer, | | |
| 692 | }; | 655 | }; |
| 693 | }, | 656 | }, |
| 694 | .@"enum" => unreachable, | 657 | .@"enum" => unreachable, |
| ... | @@ -697,6 +660,34 @@ const NavGen = struct { | ... | @@ -697,6 +660,34 @@ const NavGen = struct { |
| 697 | }; | 660 | }; |
| 698 | } | 661 | } |
| 699 | | 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 | |
| 700 | /// Emits a bool constant in a particular representation. | 691 | /// Emits a bool constant in a particular representation. |
| 701 | fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef { | 692 | fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef { |
| 702 | return switch (repr) { | 693 | return switch (repr) { |
| ... | @@ -713,14 +704,26 @@ const NavGen = struct { | ... | @@ -713,14 +704,26 @@ const NavGen = struct { |
| 713 | const scalar_ty = ty.scalarType(zcu); | 704 | const scalar_ty = ty.scalarType(zcu); |
| 714 | const int_info = scalar_ty.intInfo(zcu); | 705 | const int_info = scalar_ty.intInfo(zcu); |
| 715 | // Use backing bits so that negatives are sign extended | 706 | // Use backing bits so that negatives are sign extended |
| 716 | 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); |
| 717 | assert(backing_bits != 0); // u0 is comptime | 708 | assert(backing_bits != 0); // u0 is comptime |
| 718 | | 709 | |
| | 710 | const result_ty_id = try self.resolveType(scalar_ty, .indirect); |
| 719 | const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) { | 711 | const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) { |
| 720 | .int => |int| int.signedness, | 712 | .int => |int| int.signedness, |
| 721 | .comptime_int => if (value < 0) .signed else .unsigned, | 713 | .comptime_int => if (value < 0) .signed else .unsigned, |
| 722 | else => unreachable, | 714 | else => unreachable, |
| 723 | }; | 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 | } |
| 724 | | 727 | |
| 725 | const final_value: spec.LiteralContextDependentNumber = blk: { | 728 | const final_value: spec.LiteralContextDependentNumber = blk: { |
| 726 | if (self.spv.hasFeature(.kernel)) { | 729 | if (self.spv.hasFeature(.kernel)) { |
| ... | @@ -738,18 +741,17 @@ const NavGen = struct { | ... | @@ -738,18 +741,17 @@ const NavGen = struct { |
| 738 | break :blk switch (backing_bits) { | 741 | break :blk switch (backing_bits) { |
| 739 | 1...32 => .{ .uint32 = @truncate(truncated_value) }, | 742 | 1...32 => .{ .uint32 = @truncate(truncated_value) }, |
| 740 | 33...64 => .{ .uint64 = truncated_value }, | 743 | 33...64 => .{ .uint64 = truncated_value }, |
| 741 | else => unreachable, // TODO: Large integer constants | 744 | else => unreachable, |
| 742 | }; | 745 | }; |
| 743 | } | 746 | } |
| 744 | | 747 | |
| 745 | break :blk switch (backing_bits) { | 748 | break :blk switch (backing_bits) { |
| 746 | 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) }, | 749 | 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) }, |
| 747 | 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value }, | 750 | 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value }, |
| 748 | else => unreachable, // TODO: Large integer constants | 751 | else => unreachable, |
| 749 | }; | 752 | }; |
| 750 | }; | 753 | }; |
| 751 | | 754 | |
| 752 | const result_ty_id = try self.resolveType(scalar_ty, .indirect); | | |
| 753 | const result_id = try self.spv.constant(result_ty_id, final_value); | 755 | const result_id = try self.spv.constant(result_ty_id, final_value); |
| 754 | | 756 | |
| 755 | if (!ty.isVector(zcu)) return result_id; | 757 | if (!ty.isVector(zcu)) return result_id; |
| ... | @@ -987,7 +989,7 @@ const NavGen = struct { | ... | @@ -987,7 +989,7 @@ const NavGen = struct { |
| 987 | // TODO: composite int | 989 | // TODO: composite int |
| 988 | // TODO: endianness | 990 | // TODO: endianness |
| 989 | const bits: u16 = @intCast(ty.bitSize(zcu)); | 991 | const bits: u16 = @intCast(ty.bitSize(zcu)); |
| 990 | const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).?, 8) / 8; | 992 | const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).@"0", 8) / 8; |
| 991 | var limbs: [8]u8 = undefined; | 993 | var limbs: [8]u8 = undefined; |
| 992 | @memset(&limbs, 0); | 994 | @memset(&limbs, 0); |
| 993 | val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable; | 995 | val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable; |
| ... | @@ -1106,19 +1108,11 @@ const NavGen = struct { | ... | @@ -1106,19 +1108,11 @@ const NavGen = struct { |
| 1106 | const parent_ptr_id = try self.derivePtr(oac.parent.*); | 1108 | const parent_ptr_id = try self.derivePtr(oac.parent.*); |
| 1107 | const parent_ptr_ty = try oac.parent.ptrType(pt); | 1109 | const parent_ptr_ty = try oac.parent.ptrType(pt); |
| 1108 | const result_ty_id = try self.resolveType(oac.new_ptr_ty, .direct); | 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); |
| 1109 | | 1112 | |
| 1110 | if (oac.byte_offset != 0) { | 1113 | if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 0) { |
| 1111 | const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu); | | |
| 1112 | if (oac.byte_offset % child_size != 0) { | | |
| 1113 | return self.fail("cannot perform pointer cast: '{}' to '{}'", .{ | | |
| 1114 | parent_ptr_ty.fmt(pt), | | |
| 1115 | oac.new_ptr_ty.fmt(pt), | | |
| 1116 | }); | | |
| 1117 | } | | |
| 1118 | | | |
| 1119 | // Vector element ptr accesses are derived as offset_and_cast. | 1114 | // Vector element ptr accesses are derived as offset_and_cast. |
| 1120 | // We can just use OpAccessChain. | 1115 | // We can just use OpAccessChain. |
| 1121 | assert(parent_ptr_ty.childType(zcu).zigTypeTag(zcu) == .vector); | | |
| 1122 | return self.accessChain( | 1116 | return self.accessChain( |
| 1123 | result_ty_id, | 1117 | result_ty_id, |
| 1124 | parent_ptr_id, | 1118 | parent_ptr_id, |
| ... | @@ -1126,15 +1120,22 @@ const NavGen = struct { | ... | @@ -1126,15 +1120,22 @@ const NavGen = struct { |
| 1126 | ); | 1120 | ); |
| 1127 | } | 1121 | } |
| 1128 | | 1122 | |
| 1129 | // Allow changing the pointer type child only to restructure arrays. | 1123 | if (oac.byte_offset == 0) { |
| 1130 | // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. | 1124 | // Allow changing the pointer type child only to restructure arrays. |
| 1131 | const result_ptr_id = self.spv.allocId(); | 1125 | // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. |
| 1132 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ | 1126 | const result_ptr_id = self.spv.allocId(); |
| 1133 | .id_result_type = result_ty_id, | 1127 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 1134 | .id_result = result_ptr_id, | 1128 | .id_result_type = result_ty_id, |
| 1135 | .operand = parent_ptr_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), |
| 1136 | }); | 1138 | }); |
| 1137 | return result_ptr_id; | | |
| 1138 | }, | 1139 | }, |
| 1139 | } | 1140 | } |
| 1140 | } | 1141 | } |
| ... | @@ -1255,11 +1256,14 @@ const NavGen = struct { | ... | @@ -1255,11 +1256,14 @@ const NavGen = struct { |
| 1255 | /// actual operations (as well as store) a Zig type of a particular number of bits. To create | 1256 | /// actual operations (as well as store) a Zig type of a particular number of bits. To create |
| 1256 | /// a type with an exact size, use SpvModule.intType. | 1257 | /// a type with an exact size, use SpvModule.intType. |
| 1257 | fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef { | 1258 | fn intType(self: *NavGen, signedness: std.builtin.Signedness, bits: u16) !IdRef { |
| 1258 | const backing_bits = self.backingIntBits(bits) orelse { | 1259 | const backing_bits, const big_int = self.backingIntBits(bits); |
| 1259 | // TODO: Integers too big for any native type are represented as "composite integers": | 1260 | if (big_int) { |
| 1260 | // An array of largestSupportedIntBits. | 1261 | if (backing_bits > 64) { |
| 1261 | return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); | 1262 | return self.fail("composite integers larger than 64bit aren't supported", .{}); |
| 1262 | }; | 1263 | } |
| | 1264 | const int_ty = try self.resolveType(.u32, .direct); |
| | 1265 | return self.arrayType(backing_bits / big_int_bits, int_ty); |
| | 1266 | } |
| 1263 | | 1267 | |
| 1264 | // Kernel only supports unsigned ints. | 1268 | // Kernel only supports unsigned ints. |
| 1265 | if (self.spv.hasFeature(.kernel)) { | 1269 | if (self.spv.hasFeature(.kernel)) { |
| ... | @@ -1338,19 +1342,6 @@ const NavGen = struct { | ... | @@ -1338,19 +1342,6 @@ const NavGen = struct { |
| 1338 | return self.spv.functionType(return_ty_id, param_ids); | 1342 | return self.spv.functionType(return_ty_id, param_ids); |
| 1339 | } | 1343 | } |
| 1340 | | 1344 | |
| 1341 | fn zigScalarOrVectorTypeLike(self: *NavGen, new_ty: Type, base_ty: Type) !Type { | | |
| 1342 | const pt = self.pt; | | |
| 1343 | const new_scalar_ty = new_ty.scalarType(pt.zcu); | | |
| 1344 | if (!base_ty.isVector(pt.zcu)) { | | |
| 1345 | return new_scalar_ty; | | |
| 1346 | } | | |
| 1347 | | | |
| 1348 | return try pt.vectorType(.{ | | |
| 1349 | .len = base_ty.vectorLen(pt.zcu), | | |
| 1350 | .child = new_scalar_ty.toIntern(), | | |
| 1351 | }); | | |
| 1352 | } | | |
| 1353 | | | |
| 1354 | /// Generate a union type. Union types are always generated with the | 1345 | /// Generate a union type. Union types are always generated with the |
| 1355 | /// most aligned field active. If the tag alignment is greater | 1346 | /// most aligned field active. If the tag alignment is greater |
| 1356 | /// than that of the payload, a regular union (non-packed, with both tag and | 1347 | /// than that of the payload, a regular union (non-packed, with both tag and |
| ... | @@ -1560,6 +1551,17 @@ const NavGen = struct { | ... | @@ -1560,6 +1551,17 @@ const NavGen = struct { |
| 1560 | return result_id; | 1551 | return result_id; |
| 1561 | } | 1552 | } |
| 1562 | }, | 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 | }, |
| 1563 | .@"fn" => switch (repr) { | 1565 | .@"fn" => switch (repr) { |
| 1564 | .direct => { | 1566 | .direct => { |
| 1565 | const fn_info = zcu.typeToFunc(ty).?; | 1567 | const fn_info = zcu.typeToFunc(ty).?; |
| ... | @@ -1628,17 +1630,6 @@ const NavGen = struct { | ... | @@ -1628,17 +1630,6 @@ const NavGen = struct { |
| 1628 | ); | 1630 | ); |
| 1629 | return result_id; | 1631 | return result_id; |
| 1630 | }, | 1632 | }, |
| 1631 | .vector => { | | |
| 1632 | const elem_ty = ty.childType(zcu); | | |
| 1633 | const elem_ty_id = try self.resolveType(elem_ty, repr); | | |
| 1634 | const len = ty.vectorLen(zcu); | | |
| 1635 | | | |
| 1636 | if (self.isSpvVector(ty)) { | | |
| 1637 | return try self.spv.vectorType(len, elem_ty_id); | | |
| 1638 | } else { | | |
| 1639 | return try self.arrayType(len, elem_ty_id); | | |
| 1640 | } | | |
| 1641 | }, | | |
| 1642 | .@"struct" => { | 1633 | .@"struct" => { |
| 1643 | const struct_type = switch (ip.indexToKey(ty.toIntern())) { | 1634 | const struct_type = switch (ip.indexToKey(ty.toIntern())) { |
| 1644 | .tuple_type => |tuple| { | 1635 | .tuple_type => |tuple| { |
| ... | @@ -1793,15 +1784,34 @@ const NavGen = struct { | ... | @@ -1793,15 +1784,34 @@ const NavGen = struct { |
| 1793 | fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass { | 1784 | fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass { |
| 1794 | return switch (as) { | 1785 | return switch (as) { |
| 1795 | .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function, | 1786 | .generic => if (self.spv.hasFeature(.generic_pointer)) .Generic else .Function, |
| | 1787 | .global => { |
| | 1788 | if (self.spv.hasFeature(.kernel)) return .CrossWorkgroup; |
| | 1789 | return .StorageBuffer; |
| | 1790 | }, |
| | 1791 | .push_constant => { |
| | 1792 | assert(self.spv.hasFeature(.shader)); |
| | 1793 | return .PushConstant; |
| | 1794 | }, |
| | 1795 | .output => { |
| | 1796 | assert(self.spv.hasFeature(.shader)); |
| | 1797 | return .Output; |
| | 1798 | }, |
| | 1799 | .uniform => { |
| | 1800 | assert(self.spv.hasFeature(.shader)); |
| | 1801 | return .Uniform; |
| | 1802 | }, |
| | 1803 | .storage_buffer => { |
| | 1804 | assert(self.spv.hasFeature(.shader)); |
| | 1805 | return .StorageBuffer; |
| | 1806 | }, |
| | 1807 | .physical_storage_buffer => { |
| | 1808 | assert(self.spv.hasFeature(.physical_storage_buffer)); |
| | 1809 | return .PhysicalStorageBuffer; |
| | 1810 | }, |
| | 1811 | .constant => .UniformConstant, |
| 1796 | .shared => .Workgroup, | 1812 | .shared => .Workgroup, |
| 1797 | .local => .Function, | 1813 | .local => .Function, |
| 1798 | .global => if (self.spv.hasFeature(.shader)) .PhysicalStorageBuffer else .CrossWorkgroup, | | |
| 1799 | .constant => .UniformConstant, | | |
| 1800 | .push_constant => .PushConstant, | | |
| 1801 | .input => .Input, | 1814 | .input => .Input, |
| 1802 | .output => .Output, | | |
| 1803 | .uniform => .Uniform, | | |
| 1804 | .storage_buffer => .StorageBuffer, | | |
| 1805 | .gs, | 1815 | .gs, |
| 1806 | .fs, | 1816 | .fs, |
| 1807 | .ss, | 1817 | .ss, |
| ... | @@ -2035,69 +2045,32 @@ const NavGen = struct { | ... | @@ -2035,69 +2045,32 @@ const NavGen = struct { |
| 2035 | const Vectorization = union(enum) { | 2045 | const Vectorization = union(enum) { |
| 2036 | /// This is an operation between scalars. | 2046 | /// This is an operation between scalars. |
| 2037 | scalar, | 2047 | scalar, |
| 2038 | /// This is an operation between SPIR-V vectors. | | |
| 2039 | /// Value is number of components. | | |
| 2040 | spv_vectorized: u32, | | |
| 2041 | /// This operation is unrolled into separate operations. | 2048 | /// This operation is unrolled into separate operations. |
| 2042 | /// Inputs may still be SPIR-V vectors, for example, | 2049 | /// Inputs may still be SPIR-V vectors, for example, |
| 2043 | /// when the operation can't be vectorized in SPIR-V. | 2050 | /// when the operation can't be vectorized in SPIR-V. |
| 2044 | /// Value is number of components. | 2051 | /// Value is number of components. |
| 2045 | unrolled: u32, | 2052 | unrolled: u32, |
| 2046 | | 2053 | |
| 2047 | /// Derive a vectorization from a particular type. This usually | 2054 | /// Derive a vectorization from a particular type |
| 2048 | /// only checks the size, but the source-of-truth is implemented | | |
| 2049 | /// by `isSpvVector()`. | | |
| 2050 | fn fromType(ty: Type, ng: *NavGen) Vectorization { | 2055 | fn fromType(ty: Type, ng: *NavGen) Vectorization { |
| 2051 | const zcu = ng.pt.zcu; | 2056 | const zcu = ng.pt.zcu; |
| 2052 | if (!ty.isVector(zcu)) { | 2057 | if (!ty.isVector(zcu)) return .scalar; |
| 2053 | return .scalar; | 2058 | return .{ .unrolled = ty.vectorLen(zcu) }; |
| 2054 | } else if (ng.isSpvVector(ty)) { | | |
| 2055 | return .{ .spv_vectorized = ty.vectorLen(zcu) }; | | |
| 2056 | } else { | | |
| 2057 | return .{ .unrolled = ty.vectorLen(zcu) }; | | |
| 2058 | } | | |
| 2059 | } | 2059 | } |
| 2060 | | 2060 | |
| 2061 | /// Given two vectorization methods, compute a "unification": a fallback | 2061 | /// Given two vectorization methods, compute a "unification": a fallback |
| 2062 | /// that works for both, according to the following rules: | 2062 | /// that works for both, according to the following rules: |
| 2063 | /// - Scalars may broadcast | 2063 | /// - Scalars may broadcast |
| 2064 | /// - SPIR-V vectorized operations may unroll | 2064 | /// - SPIR-V vectorized operations will unroll |
| 2065 | /// - Prefer scalar > SPIR-V vectorized > unrolled | 2065 | /// - Prefer scalar > unrolled |
| 2066 | fn unify(a: Vectorization, b: Vectorization) Vectorization { | 2066 | fn unify(a: Vectorization, b: Vectorization) Vectorization { |
| 2067 | if (a == .scalar and b == .scalar) { | 2067 | if (a == .scalar and b == .scalar) return .scalar; |
| 2068 | return .scalar; | 2068 | if (a == .unrolled or b == .unrolled) { |
| 2069 | } else if (a == .spv_vectorized and b == .spv_vectorized) { | 2069 | if (a == .unrolled and b == .unrolled) assert(a.components() == b.components()); |
| 2070 | assert(a.components() == b.components()); | 2070 | if (a == .unrolled) return .{ .unrolled = a.components() }; |
| 2071 | return .{ .spv_vectorized = a.components() }; | 2071 | return .{ .unrolled = b.components() }; |
| 2072 | } else if (a == .unrolled or b == .unrolled) { | | |
| 2073 | if (a == .unrolled and b == .unrolled) { | | |
| 2074 | assert(a.components() == b.components()); | | |
| 2075 | return .{ .unrolled = a.components() }; | | |
| 2076 | } else if (a == .unrolled) { | | |
| 2077 | return .{ .unrolled = a.components() }; | | |
| 2078 | } else if (b == .unrolled) { | | |
| 2079 | return .{ .unrolled = b.components() }; | | |
| 2080 | } else { | | |
| 2081 | unreachable; | | |
| 2082 | } | | |
| 2083 | } else { | | |
| 2084 | if (a == .spv_vectorized) { | | |
| 2085 | return .{ .spv_vectorized = a.components() }; | | |
| 2086 | } else if (b == .spv_vectorized) { | | |
| 2087 | return .{ .spv_vectorized = b.components() }; | | |
| 2088 | } else { | | |
| 2089 | unreachable; | | |
| 2090 | } | | |
| 2091 | } | 2072 | } |
| 2092 | } | 2073 | unreachable; |
| 2093 | | | |
| 2094 | /// Force this vectorization to be unrolled, if its | | |
| 2095 | /// an operation involving vectors. | | |
| 2096 | fn unroll(self: Vectorization) Vectorization { | | |
| 2097 | return switch (self) { | | |
| 2098 | .scalar, .unrolled => self, | | |
| 2099 | .spv_vectorized => |n| .{ .unrolled = n }, | | |
| 2100 | }; | | |
| 2101 | } | 2074 | } |
| 2102 | | 2075 | |
| 2103 | /// Query the number of components that inputs of this operation have. | 2076 | /// Query the number of components that inputs of this operation have. |
| ... | @@ -2106,35 +2079,10 @@ const NavGen = struct { | ... | @@ -2106,35 +2079,10 @@ const NavGen = struct { |
| 2106 | fn components(self: Vectorization) u32 { | 2079 | fn components(self: Vectorization) u32 { |
| 2107 | return switch (self) { | 2080 | return switch (self) { |
| 2108 | .scalar => 1, | 2081 | .scalar => 1, |
| 2109 | .spv_vectorized => |n| n, | | |
| 2110 | .unrolled => |n| n, | | |
| 2111 | }; | | |
| 2112 | } | | |
| 2113 | | | |
| 2114 | /// Query the number of operations involving this vectorization. | | |
| 2115 | /// This is basically the number of components, except that SPIR-V vectorized | | |
| 2116 | /// operations only need a single SPIR-V instruction. | | |
| 2117 | fn operations(self: Vectorization) u32 { | | |
| 2118 | return switch (self) { | | |
| 2119 | .scalar, .spv_vectorized => 1, | | |
| 2120 | .unrolled => |n| n, | 2082 | .unrolled => |n| n, |
| 2121 | }; | 2083 | }; |
| 2122 | } | 2084 | } |
| 2123 | | 2085 | |
| 2124 | /// Turns `ty` into the result-type of an individual vector operation. | | |
| 2125 | /// `ty` may be a scalar or vector, it doesn't matter. | | |
| 2126 | fn operationType(self: Vectorization, ng: *NavGen, ty: Type) !Type { | | |
| 2127 | const pt = ng.pt; | | |
| 2128 | const scalar_ty = ty.scalarType(pt.zcu); | | |
| 2129 | return switch (self) { | | |
| 2130 | .scalar, .unrolled => scalar_ty, | | |
| 2131 | .spv_vectorized => |n| try pt.vectorType(.{ | | |
| 2132 | .len = n, | | |
| 2133 | .child = scalar_ty.toIntern(), | | |
| 2134 | }), | | |
| 2135 | }; | | |
| 2136 | } | | |
| 2137 | | | |
| 2138 | /// Turns `ty` into the result-type of the entire operation. | 2086 | /// Turns `ty` into the result-type of the entire operation. |
| 2139 | /// `ty` may be a scalar or vector, it doesn't matter. | 2087 | /// `ty` may be a scalar or vector, it doesn't matter. |
| 2140 | fn resultType(self: Vectorization, ng: *NavGen, ty: Type) !Type { | 2088 | fn resultType(self: Vectorization, ng: *NavGen, ty: Type) !Type { |
| ... | @@ -2142,10 +2090,7 @@ const NavGen = struct { | ... | @@ -2142,10 +2090,7 @@ const NavGen = struct { |
| 2142 | const scalar_ty = ty.scalarType(pt.zcu); | 2090 | const scalar_ty = ty.scalarType(pt.zcu); |
| 2143 | return switch (self) { | 2091 | return switch (self) { |
| 2144 | .scalar => scalar_ty, | 2092 | .scalar => scalar_ty, |
| 2145 | .unrolled, .spv_vectorized => |n| try pt.vectorType(.{ | 2093 | .unrolled => |n| try pt.vectorType(.{ .len = n, .child = scalar_ty.toIntern() }), |
| 2146 | .len = n, | | |
| 2147 | .child = scalar_ty.toIntern(), | | |
| 2148 | }), | | |
| 2149 | }; | 2094 | }; |
| 2150 | } | 2095 | } |
| 2151 | | 2096 | |
| ... | @@ -2155,51 +2100,19 @@ const NavGen = struct { | ... | @@ -2155,51 +2100,19 @@ const NavGen = struct { |
| 2155 | fn prepare(self: Vectorization, ng: *NavGen, tmp: Temporary) !PreparedOperand { | 2100 | fn prepare(self: Vectorization, ng: *NavGen, tmp: Temporary) !PreparedOperand { |
| 2156 | const pt = ng.pt; | 2101 | const pt = ng.pt; |
| 2157 | const is_vector = tmp.ty.isVector(pt.zcu); | 2102 | const is_vector = tmp.ty.isVector(pt.zcu); |
| 2158 | const is_spv_vector = ng.isSpvVector(tmp.ty); | | |
| 2159 | const value: PreparedOperand.Value = switch (tmp.value) { | 2103 | const value: PreparedOperand.Value = switch (tmp.value) { |
| 2160 | .singleton => |id| switch (self) { | 2104 | .singleton => |id| switch (self) { |
| 2161 | .scalar => blk: { | 2105 | .scalar => blk: { |
| 2162 | assert(!is_vector); | 2106 | assert(!is_vector); |
| 2163 | break :blk .{ .scalar = id }; | 2107 | break :blk .{ .scalar = id }; |
| 2164 | }, | 2108 | }, |
| 2165 | .spv_vectorized => blk: { | | |
| 2166 | if (is_vector) { | | |
| 2167 | assert(is_spv_vector); | | |
| 2168 | break :blk .{ .spv_vectorwise = id }; | | |
| 2169 | } | | |
| 2170 | | | |
| 2171 | // Broadcast scalar into vector. | | |
| 2172 | const vector_ty = try pt.vectorType(.{ | | |
| 2173 | .len = self.components(), | | |
| 2174 | .child = tmp.ty.toIntern(), | | |
| 2175 | }); | | |
| 2176 | | | |
| 2177 | const vector = try ng.constructCompositeSplat(vector_ty, id); | | |
| 2178 | return .{ | | |
| 2179 | .ty = vector_ty, | | |
| 2180 | .value = .{ .spv_vectorwise = vector }, | | |
| 2181 | }; | | |
| 2182 | }, | | |
| 2183 | .unrolled => blk: { | 2109 | .unrolled => blk: { |
| 2184 | if (is_vector) { | 2110 | if (is_vector) break :blk .{ .vector_exploded = try tmp.explode(ng) }; |
| 2185 | break :blk .{ .vector_exploded = try tmp.explode(ng) }; | 2111 | break :blk .{ .scalar_broadcast = id }; |
| 2186 | } else { | | |
| 2187 | break :blk .{ .scalar_broadcast = id }; | | |
| 2188 | } | | |
| 2189 | }, | 2112 | }, |
| 2190 | }, | 2113 | }, |
| 2191 | .exploded_vector => |range| switch (self) { | 2114 | .exploded_vector => |range| switch (self) { |
| 2192 | .scalar => unreachable, | 2115 | .scalar => unreachable, |
| 2193 | .spv_vectorized => |n| blk: { | | |
| 2194 | // We can vectorize this operation, but we have an exploded vector. This can happen | | |
| 2195 | // when a vectorizable operation succeeds a non-vectorizable operation. In this case, | | |
| 2196 | // pack up the IDs into a SPIR-V vector. This path should not be able to be hit with | | |
| 2197 | // a type that cannot do that. | | |
| 2198 | assert(is_spv_vector); | | |
| 2199 | assert(range.len == n); | | |
| 2200 | const vec = try tmp.materialize(ng); | | |
| 2201 | break :blk .{ .spv_vectorwise = vec }; | | |
| 2202 | }, | | |
| 2203 | .unrolled => |n| blk: { | 2116 | .unrolled => |n| blk: { |
| 2204 | assert(range.len == n); | 2117 | assert(range.len == n); |
| 2205 | break :blk .{ .vector_exploded = range }; | 2118 | break :blk .{ .vector_exploded = range }; |
| ... | @@ -2216,17 +2129,14 @@ const NavGen = struct { | ... | @@ -2216,17 +2129,14 @@ const NavGen = struct { |
| 2216 | /// Finalize the results of an operation back into a temporary. `results` is | 2129 | /// Finalize the results of an operation back into a temporary. `results` is |
| 2217 | /// a list of result-ids of the operation. | 2130 | /// a list of result-ids of the operation. |
| 2218 | fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary { | 2131 | fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary { |
| 2219 | assert(self.operations() == results.len); | 2132 | assert(self.components() == results.len); |
| 2220 | const value: Temporary.Value = switch (self) { | 2133 | return .{ |
| 2221 | .scalar, .spv_vectorized => blk: { | 2134 | .ty = ty, |
| 2222 | break :blk .{ .singleton = results.at(0) }; | 2135 | .value = switch (self) { |
| 2223 | }, | 2136 | .scalar => .{ .singleton = results.at(0) }, |
| 2224 | .unrolled => blk: { | 2137 | .unrolled => .{ .exploded_vector = results }, |
| 2225 | break :blk .{ .exploded_vector = results }; | | |
| 2226 | }, | 2138 | }, |
| 2227 | }; | 2139 | }; |
| 2228 | | | |
| 2229 | return .{ .ty = ty, .value = value }; | | |
| 2230 | } | 2140 | } |
| 2231 | | 2141 | |
| 2232 | /// This struct represents an operand that has gone through some setup, and is | 2142 | /// This struct represents an operand that has gone through some setup, and is |
| ... | @@ -2242,32 +2152,20 @@ const NavGen = struct { | ... | @@ -2242,32 +2152,20 @@ const NavGen = struct { |
| 2242 | scalar: IdResult, | 2152 | scalar: IdResult, |
| 2243 | /// A single scalar that is broadcasted in an unrolled operation. | 2153 | /// A single scalar that is broadcasted in an unrolled operation. |
| 2244 | scalar_broadcast: IdResult, | 2154 | scalar_broadcast: IdResult, |
| 2245 | /// A SPIR-V vector that is used in SPIR-V vectorize operation. | | |
| 2246 | spv_vectorwise: IdResult, | | |
| 2247 | /// A vector represented by a consecutive list of IDs that is used in an unrolled operation. | 2155 | /// A vector represented by a consecutive list of IDs that is used in an unrolled operation. |
| 2248 | vector_exploded: IdRange, | 2156 | vector_exploded: IdRange, |
| 2249 | }; | 2157 | }; |
| 2250 | | 2158 | |
| 2251 | /// Query the value at a particular index of the operation. Note that | 2159 | /// Query the value at a particular index of the operation. Note that |
| 2252 | /// the index is *not* the component/lane, but the index of the *operation*. When | 2160 | /// the index is *not* the component/lane, but the index of the *operation*. |
| 2253 | /// this operation is vectorized, the return value of this function is a SPIR-V vector. | | |
| 2254 | /// See also `Vectorization.operations()`. | | |
| 2255 | fn at(self: PreparedOperand, i: usize) IdResult { | 2161 | fn at(self: PreparedOperand, i: usize) IdResult { |
| 2256 | switch (self.value) { | 2162 | switch (self.value) { |
| 2257 | .scalar => |id| { | 2163 | .scalar => |id| { |
| 2258 | assert(i == 0); | 2164 | assert(i == 0); |
| 2259 | return id; | 2165 | return id; |
| 2260 | }, | 2166 | }, |
| 2261 | .scalar_broadcast => |id| { | 2167 | .scalar_broadcast => |id| return id, |
| 2262 | return id; | 2168 | .vector_exploded => |range| return range.at(i), |
| 2263 | }, | | |
| 2264 | .spv_vectorwise => |id| { | | |
| 2265 | assert(i == 0); | | |
| 2266 | return id; | | |
| 2267 | }, | | |
| 2268 | .vector_exploded => |range| { | | |
| 2269 | return range.at(i); | | |
| 2270 | }, | | |
| 2271 | } | 2169 | } |
| 2272 | } | 2170 | } |
| 2273 | }; | 2171 | }; |
| ... | @@ -2299,7 +2197,7 @@ const NavGen = struct { | ... | @@ -2299,7 +2197,7 @@ const NavGen = struct { |
| 2299 | | 2197 | |
| 2300 | /// This function builds an OpSConvert of OpUConvert depending on the | 2198 | /// This function builds an OpSConvert of OpUConvert depending on the |
| 2301 | /// signedness of the types. | 2199 | /// signedness of the types. |
| 2302 | fn buildIntConvert(self: *NavGen, dst_ty: Type, src: Temporary) !Temporary { | 2200 | fn buildConvert(self: *NavGen, dst_ty: Type, src: Temporary) !Temporary { |
| 2303 | const zcu = self.pt.zcu; | 2201 | const zcu = self.pt.zcu; |
| 2304 | | 2202 | |
| 2305 | const dst_ty_id = try self.resolveType(dst_ty.scalarType(zcu), .direct); | 2203 | const dst_ty_id = try self.resolveType(dst_ty.scalarType(zcu), .direct); |
| ... | @@ -2318,13 +2216,17 @@ const NavGen = struct { | ... | @@ -2318,13 +2216,17 @@ const NavGen = struct { |
| 2318 | return src.pun(result_ty); | 2216 | return src.pun(result_ty); |
| 2319 | } | 2217 | } |
| 2320 | | 2218 | |
| 2321 | const ops = v.operations(); | 2219 | const ops = v.components(); |
| 2322 | const results = self.spv.allocIds(ops); | 2220 | const results = self.spv.allocIds(ops); |
| 2323 | | 2221 | |
| 2324 | const op_result_ty = try v.operationType(self, dst_ty); | 2222 | const op_result_ty = dst_ty.scalarType(zcu); |
| 2325 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2223 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2326 | | 2224 | |
| 2327 | const opcode: Opcode = if (dst_ty.isSignedInt(zcu)) .OpSConvert else .OpUConvert; | 2225 | const opcode: Opcode = blk: { |
| | 2226 | if (dst_ty.scalarType(zcu).isAnyFloat()) break :blk .OpFConvert; |
| | 2227 | if (dst_ty.scalarType(zcu).isSignedInt(zcu)) break :blk .OpSConvert; |
| | 2228 | break :blk .OpUConvert; |
| | 2229 | }; |
| 2328 | | 2230 | |
| 2329 | const op_src = try v.prepare(self, src); | 2231 | const op_src = try v.prepare(self, src); |
| 2330 | | 2232 | |
| ... | @@ -2339,13 +2241,14 @@ const NavGen = struct { | ... | @@ -2339,13 +2241,14 @@ const NavGen = struct { |
| 2339 | } | 2241 | } |
| 2340 | | 2242 | |
| 2341 | fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary { | 2243 | fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary { |
| | 2244 | const zcu = self.pt.zcu; |
| 2342 | const target = self.spv.target; | 2245 | const target = self.spv.target; |
| 2343 | | 2246 | |
| 2344 | const v = self.vectorization(.{ a, b, c }); | 2247 | const v = self.vectorization(.{ a, b, c }); |
| 2345 | const ops = v.operations(); | 2248 | const ops = v.components(); |
| 2346 | const results = self.spv.allocIds(ops); | 2249 | const results = self.spv.allocIds(ops); |
| 2347 | | 2250 | |
| 2348 | const op_result_ty = try v.operationType(self, a.ty); | 2251 | const op_result_ty = a.ty.scalarType(zcu); |
| 2349 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2252 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2350 | const result_ty = try v.resultType(self, a.ty); | 2253 | const result_ty = try v.resultType(self, a.ty); |
| 2351 | | 2254 | |
| ... | @@ -2382,10 +2285,10 @@ const NavGen = struct { | ... | @@ -2382,10 +2285,10 @@ const NavGen = struct { |
| 2382 | const zcu = self.pt.zcu; | 2285 | const zcu = self.pt.zcu; |
| 2383 | | 2286 | |
| 2384 | const v = self.vectorization(.{ condition, lhs, rhs }); | 2287 | const v = self.vectorization(.{ condition, lhs, rhs }); |
| 2385 | const ops = v.operations(); | 2288 | const ops = v.components(); |
| 2386 | const results = self.spv.allocIds(ops); | 2289 | const results = self.spv.allocIds(ops); |
| 2387 | | 2290 | |
| 2388 | const op_result_ty = try v.operationType(self, lhs.ty); | 2291 | const op_result_ty = lhs.ty.scalarType(zcu); |
| 2389 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2292 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2390 | const result_ty = try v.resultType(self, lhs.ty); | 2293 | const result_ty = try v.resultType(self, lhs.ty); |
| 2391 | | 2294 | |
| ... | @@ -2431,10 +2334,10 @@ const NavGen = struct { | ... | @@ -2431,10 +2334,10 @@ const NavGen = struct { |
| 2431 | | 2334 | |
| 2432 | fn buildCmp(self: *NavGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary { | 2335 | fn buildCmp(self: *NavGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary { |
| 2433 | const v = self.vectorization(.{ lhs, rhs }); | 2336 | const v = self.vectorization(.{ lhs, rhs }); |
| 2434 | const ops = v.operations(); | 2337 | const ops = v.components(); |
| 2435 | const results = self.spv.allocIds(ops); | 2338 | const results = self.spv.allocIds(ops); |
| 2436 | | 2339 | |
| 2437 | const op_result_ty = try v.operationType(self, Type.bool); | 2340 | const op_result_ty: Type = .bool; |
| 2438 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2341 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2439 | const result_ty = try v.resultType(self, Type.bool); | 2342 | const result_ty = try v.resultType(self, Type.bool); |
| 2440 | | 2343 | |
| ... | @@ -2498,22 +2401,12 @@ const NavGen = struct { | ... | @@ -2498,22 +2401,12 @@ const NavGen = struct { |
| 2498 | }; | 2401 | }; |
| 2499 | | 2402 | |
| 2500 | fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary { | 2403 | fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary { |
| | 2404 | const zcu = self.pt.zcu; |
| 2501 | const target = self.spv.target; | 2405 | const target = self.spv.target; |
| 2502 | const v = blk: { | 2406 | const v = self.vectorization(.{operand}); |
| 2503 | const v = self.vectorization(.{operand}); | 2407 | const ops = v.components(); |
| 2504 | break :blk switch (op) { | | |
| 2505 | // TODO: These instructions don't seem to be working | | |
| 2506 | // properly for LLVM-based backends on OpenCL for 8- and | | |
| 2507 | // 16-component vectors. | | |
| 2508 | .i_abs => if (self.spv.hasFeature(.vector16) and v.components() >= 8) v.unroll() else v, | | |
| 2509 | else => v, | | |
| 2510 | }; | | |
| 2511 | }; | | |
| 2512 | | | |
| 2513 | const ops = v.operations(); | | |
| 2514 | const results = self.spv.allocIds(ops); | 2408 | const results = self.spv.allocIds(ops); |
| 2515 | | 2409 | const op_result_ty = operand.ty.scalarType(zcu); |
| 2516 | const op_result_ty = try v.operationType(self, operand.ty); | | |
| 2517 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2410 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2518 | const result_ty = try v.resultType(self, operand.ty); | 2411 | const result_ty = try v.resultType(self, operand.ty); |
| 2519 | | 2412 | |
| ... | @@ -2628,13 +2521,14 @@ const NavGen = struct { | ... | @@ -2628,13 +2521,14 @@ const NavGen = struct { |
| 2628 | }; | 2521 | }; |
| 2629 | | 2522 | |
| 2630 | fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary { | 2523 | fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary { |
| | 2524 | const zcu = self.pt.zcu; |
| 2631 | const target = self.spv.target; | 2525 | const target = self.spv.target; |
| 2632 | | 2526 | |
| 2633 | const v = self.vectorization(.{ lhs, rhs }); | 2527 | const v = self.vectorization(.{ lhs, rhs }); |
| 2634 | const ops = v.operations(); | 2528 | const ops = v.components(); |
| 2635 | const results = self.spv.allocIds(ops); | 2529 | const results = self.spv.allocIds(ops); |
| 2636 | | 2530 | |
| 2637 | const op_result_ty = try v.operationType(self, lhs.ty); | 2531 | const op_result_ty = lhs.ty.scalarType(zcu); |
| 2638 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); | 2532 | const op_result_ty_id = try self.resolveType(op_result_ty, .direct); |
| 2639 | const result_ty = try v.resultType(self, lhs.ty); | 2533 | const result_ty = try v.resultType(self, lhs.ty); |
| 2640 | | 2534 | |
| ... | @@ -2730,9 +2624,9 @@ const NavGen = struct { | ... | @@ -2730,9 +2624,9 @@ const NavGen = struct { |
| 2730 | const ip = &zcu.intern_pool; | 2624 | const ip = &zcu.intern_pool; |
| 2731 | | 2625 | |
| 2732 | const v = lhs.vectorization(self).unify(rhs.vectorization(self)); | 2626 | const v = lhs.vectorization(self).unify(rhs.vectorization(self)); |
| 2733 | const ops = v.operations(); | 2627 | const ops = v.components(); |
| 2734 | | 2628 | |
| 2735 | const arith_op_ty = try v.operationType(self, lhs.ty); | 2629 | const arith_op_ty = lhs.ty.scalarType(zcu); |
| 2736 | const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct); | 2630 | const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct); |
| 2737 | | 2631 | |
| 2738 | const lhs_op = try v.prepare(self, lhs); | 2632 | const lhs_op = try v.prepare(self, lhs); |
| ... | @@ -2883,38 +2777,32 @@ const NavGen = struct { | ... | @@ -2883,38 +2777,32 @@ const NavGen = struct { |
| 2883 | }); | 2777 | }); |
| 2884 | }, | 2778 | }, |
| 2885 | .vulkan, .opengl => { | 2779 | .vulkan, .opengl => { |
| 2886 | const ptr_ptr_anyerror_ty_id = self.spv.allocId(); | 2780 | if (self.object.error_buffer == null) { |
| 2887 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ | | |
| 2888 | .id_result = ptr_ptr_anyerror_ty_id, | | |
| 2889 | .storage_class = .PushConstant, | | |
| 2890 | .type = ptr_anyerror_ty_id, | | |
| 2891 | }); | | |
| 2892 | | | |
| 2893 | if (self.object.error_push_constant == null) { | | |
| 2894 | const spv_err_decl_index = try self.spv.allocDecl(.global); | 2781 | const spv_err_decl_index = try self.spv.allocDecl(.global); |
| 2895 | try self.spv.declareDeclDeps(spv_err_decl_index, &.{}); | 2782 | try self.spv.declareDeclDeps(spv_err_decl_index, &.{}); |
| 2896 | | 2783 | |
| 2897 | const push_constant_struct_ty_id = self.spv.allocId(); | 2784 | const buffer_struct_ty_id = self.spv.allocId(); |
| 2898 | try self.spv.structType(push_constant_struct_ty_id, &.{ptr_anyerror_ty_id}, &.{"error_out_ptr"}); | 2785 | try self.spv.structType(buffer_struct_ty_id, &.{anyerror_ty_id}, &.{"error_out"}); |
| 2899 | try self.spv.decorate(push_constant_struct_ty_id, .Block); | 2786 | try self.spv.decorate(buffer_struct_ty_id, .Block); |
| 2900 | try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } }); | 2787 | try self.spv.decorateMember(buffer_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } }); |
| 2901 | | 2788 | |
| 2902 | const ptr_push_constant_struct_ty_id = self.spv.allocId(); | 2789 | const ptr_buffer_struct_ty_id = self.spv.allocId(); |
| 2903 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ | 2790 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ |
| 2904 | .id_result = ptr_push_constant_struct_ty_id, | 2791 | .id_result = ptr_buffer_struct_ty_id, |
| 2905 | .storage_class = .PushConstant, | 2792 | .storage_class = self.spvStorageClass(.global), |
| 2906 | .type = push_constant_struct_ty_id, | 2793 | .type = buffer_struct_ty_id, |
| 2907 | }); | 2794 | }); |
| 2908 | | 2795 | |
| | 2796 | const buffer_struct_id = self.spv.declPtr(spv_err_decl_index).result_id; |
| 2909 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{ | 2797 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{ |
| 2910 | .id_result_type = ptr_push_constant_struct_ty_id, | 2798 | .id_result_type = ptr_buffer_struct_ty_id, |
| 2911 | .id_result = self.spv.declPtr(spv_err_decl_index).result_id, | 2799 | .id_result = buffer_struct_id, |
| 2912 | .storage_class = .PushConstant, | 2800 | .storage_class = self.spvStorageClass(.global), |
| 2913 | }); | 2801 | }); |
| | 2802 | try self.spv.decorate(buffer_struct_id, .{ .DescriptorSet = .{ .descriptor_set = 0 } }); |
| | 2803 | try self.spv.decorate(buffer_struct_id, .{ .Binding = .{ .binding_point = 0 } }); |
| 2914 | | 2804 | |
| 2915 | self.object.error_push_constant = .{ | 2805 | self.object.error_buffer = spv_err_decl_index; |
| 2916 | .push_constant_ptr = spv_err_decl_index, | | |
| 2917 | }; | | |
| 2918 | } | 2806 | } |
| 2919 | | 2807 | |
| 2920 | try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{ | 2808 | try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{ |
| ... | @@ -2937,24 +2825,16 @@ const NavGen = struct { | ... | @@ -2937,24 +2825,16 @@ const NavGen = struct { |
| 2937 | .id_result = self.spv.allocId(), | 2825 | .id_result = self.spv.allocId(), |
| 2938 | }); | 2826 | }); |
| 2939 | | 2827 | |
| 2940 | const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr; | 2828 | const spv_err_decl_index = self.object.error_buffer.?; |
| 2941 | const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id; | 2829 | const buffer_id = self.spv.declPtr(spv_err_decl_index).result_id; |
| 2942 | try decl_deps.append(spv_err_decl_index); | 2830 | try decl_deps.append(spv_err_decl_index); |
| 2943 | | 2831 | |
| 2944 | const zero_id = try self.constInt(Type.u32, 0); | 2832 | const zero_id = try self.constInt(Type.u32, 0); |
| 2945 | // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use | | |
| 2946 | // a load. | | |
| 2947 | const tmp = self.spv.allocId(); | | |
| 2948 | try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ | 2833 | try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| 2949 | .id_result_type = ptr_ptr_anyerror_ty_id, | | |
| 2950 | .id_result = tmp, | | |
| 2951 | .base = push_constant_id, | | |
| 2952 | .indexes = &.{zero_id}, | | |
| 2953 | }); | | |
| 2954 | try section.emit(self.spv.gpa, .OpLoad, .{ | | |
| 2955 | .id_result_type = ptr_anyerror_ty_id, | 2834 | .id_result_type = ptr_anyerror_ty_id, |
| 2956 | .id_result = p_error_id, | 2835 | .id_result = p_error_id, |
| 2957 | .pointer = tmp, | 2836 | .base = buffer_id, |
| | 2837 | .indexes = &.{zero_id}, |
| 2958 | }); | 2838 | }); |
| 2959 | }, | 2839 | }, |
| 2960 | else => unreachable, | 2840 | else => unreachable, |
| ... | @@ -2990,7 +2870,7 @@ const NavGen = struct { | ... | @@ -2990,7 +2870,7 @@ const NavGen = struct { |
| 2990 | }; | 2870 | }; |
| 2991 | | 2871 | |
| 2992 | try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items); | 2872 | try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items); |
| 2993 | try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode); | 2873 | try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode, null); |
| 2994 | } | 2874 | } |
| 2995 | | 2875 | |
| 2996 | fn genNav(self: *NavGen, do_codegen: bool) !void { | 2876 | fn genNav(self: *NavGen, do_codegen: bool) !void { |
| ... | @@ -3092,6 +2972,40 @@ const NavGen = struct { | ... | @@ -3092,6 +2972,40 @@ const NavGen = struct { |
| 3092 | .storage_class = storage_class, | 2972 | .storage_class = storage_class, |
| 3093 | }); | 2973 | }); |
| 3094 | | 2974 | |
| | 2975 | if (nav.fqn.eqlSlice("position", ip)) { |
| | 2976 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .Position } }); |
| | 2977 | } else if (nav.fqn.eqlSlice("point_size", ip)) { |
| | 2978 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .PointSize } }); |
| | 2979 | } else if (nav.fqn.eqlSlice("invocation_id", ip)) { |
| | 2980 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InvocationId } }); |
| | 2981 | } else if (nav.fqn.eqlSlice("frag_coord", ip)) { |
| | 2982 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FragCoord } }); |
| | 2983 | } else if (nav.fqn.eqlSlice("point_coord", ip)) { |
| | 2984 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .PointCoord } }); |
| | 2985 | } else if (nav.fqn.eqlSlice("front_facing", ip)) { |
| | 2986 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FrontFacing } }); |
| | 2987 | } else if (nav.fqn.eqlSlice("sample_mask", ip)) { |
| | 2988 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .SampleMask } }); |
| | 2989 | } else if (nav.fqn.eqlSlice("frag_depth", ip)) { |
| | 2990 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FragDepth } }); |
| | 2991 | } else if (nav.fqn.eqlSlice("num_workgroups", ip)) { |
| | 2992 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .NumWorkgroups } }); |
| | 2993 | } else if (nav.fqn.eqlSlice("workgroup_size", ip)) { |
| | 2994 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .WorkgroupSize } }); |
| | 2995 | } else if (nav.fqn.eqlSlice("workgroup_id", ip)) { |
| | 2996 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .WorkgroupId } }); |
| | 2997 | } else if (nav.fqn.eqlSlice("local_invocation_id", ip)) { |
| | 2998 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .LocalInvocationId } }); |
| | 2999 | } else if (nav.fqn.eqlSlice("global_invocation_id", ip)) { |
| | 3000 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .GlobalInvocationId } }); |
| | 3001 | } else if (nav.fqn.eqlSlice("local_invocation_index", ip)) { |
| | 3002 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .LocalInvocationIndex } }); |
| | 3003 | } else if (nav.fqn.eqlSlice("vertex_index", ip)) { |
| | 3004 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .VertexIndex } }); |
| | 3005 | } else if (nav.fqn.eqlSlice("instance_index", ip)) { |
| | 3006 | try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InstanceIndex } }); |
| | 3007 | } |
| | 3008 | |
| 3095 | try self.spv.debugName(result_id, nav.fqn.toSlice(ip)); | 3009 | try self.spv.debugName(result_id, nav.fqn.toSlice(ip)); |
| 3096 | try self.spv.declareDeclDeps(spv_decl_index, &.{}); | 3010 | try self.spv.declareDeclDeps(spv_decl_index, &.{}); |
| 3097 | }, | 3011 | }, |
| ... | @@ -3175,17 +3089,18 @@ const NavGen = struct { | ... | @@ -3175,17 +3089,18 @@ const NavGen = struct { |
| 3175 | /// Convert representation from indirect (in memory) to direct (in 'register') | 3089 | /// Convert representation from indirect (in memory) to direct (in 'register') |
| 3176 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). | 3090 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). |
| 3177 | fn convertToDirect(self: *NavGen, ty: Type, operand_id: IdRef) !IdRef { | 3091 | fn convertToDirect(self: *NavGen, ty: Type, operand_id: IdRef) !IdRef { |
| 3178 | const zcu = self.pt.zcu; | 3092 | const pt = self.pt; |
| | 3093 | const zcu = pt.zcu; |
| 3179 | switch (ty.scalarType(zcu).zigTypeTag(zcu)) { | 3094 | switch (ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 3180 | .bool => { | 3095 | .bool => { |
| 3181 | const false_id = try self.constBool(false, .indirect); | 3096 | const false_id = try self.constBool(false, .indirect); |
| 3182 | // The operation below requires inputs in direct representation, but the operand | 3097 | const operand_ty = blk: { |
| 3183 | // is actually in indirect representation. | 3098 | if (!ty.isVector(pt.zcu)) break :blk Type.u1; |
| 3184 | // Cheekily swap out the type to the direct equivalent of the indirect type here, they have the | 3099 | break :blk try pt.vectorType(.{ |
| 3185 | // same representation when converted to SPIR-V. | 3100 | .len = ty.vectorLen(pt.zcu), |
| 3186 | const operand_ty = try self.zigScalarOrVectorTypeLike(Type.u1, ty); | 3101 | .child = Type.u1.toIntern(), |
| 3187 | // Note: We can guarantee that these are the same ID due to the SPIR-V Module's `vector_types` cache! | 3102 | }); |
| 3188 | assert(try self.resolveType(operand_ty, .direct) == try self.resolveType(ty, .indirect)); | 3103 | }; |
| 3189 | | 3104 | |
| 3190 | const result = try self.buildCmp( | 3105 | const result = try self.buildCmp( |
| 3191 | .i_ne, | 3106 | .i_ne, |
| ... | @@ -3226,7 +3141,6 @@ const NavGen = struct { | ... | @@ -3226,7 +3141,6 @@ const NavGen = struct { |
| 3226 | } | 3141 | } |
| 3227 | | 3142 | |
| 3228 | fn extractVectorComponent(self: *NavGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef { | 3143 | fn extractVectorComponent(self: *NavGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef { |
| 3229 | // Whether this is an OpTypeVector or OpTypeArray, we need to emit the same instruction regardless. | | |
| 3230 | const result_ty_id = try self.resolveType(result_ty, .direct); | 3144 | const result_ty_id = try self.resolveType(result_ty, .direct); |
| 3231 | const result_id = self.spv.allocId(); | 3145 | const result_id = self.spv.allocId(); |
| 3232 | const indexes = [_]u32{field}; | 3146 | const indexes = [_]u32{field}; |
| ... | @@ -3485,7 +3399,7 @@ const NavGen = struct { | ... | @@ -3485,7 +3399,7 @@ const NavGen = struct { |
| 3485 | // Note: The sign may differ here between the shift and the base type, in case | 3399 | // Note: The sign may differ here between the shift and the base type, in case |
| 3486 | // of an arithmetic right shift. SPIR-V still expects the same type, | 3400 | // of an arithmetic right shift. SPIR-V still expects the same type, |
| 3487 | // so in that case we have to cast convert to signed. | 3401 | // so in that case we have to cast convert to signed. |
| 3488 | const casted_shift = try self.buildIntConvert(base.ty.scalarType(zcu), shift); | 3402 | const casted_shift = try self.buildConvert(base.ty.scalarType(zcu), shift); |
| 3489 | | 3403 | |
| 3490 | const shifted = switch (info.signedness) { | 3404 | const shifted = switch (info.signedness) { |
| 3491 | .unsigned => try self.buildBinary(unsigned, base, casted_shift), | 3405 | .unsigned => try self.buildBinary(unsigned, base, casted_shift), |
| ... | @@ -3545,8 +3459,7 @@ const NavGen = struct { | ... | @@ -3545,8 +3459,7 @@ const NavGen = struct { |
| 3545 | const zcu = self.pt.zcu; | 3459 | const zcu = self.pt.zcu; |
| 3546 | const ty = value.ty; | 3460 | const ty = value.ty; |
| 3547 | switch (info.class) { | 3461 | switch (info.class) { |
| 3548 | .integer, .bool, .float => return value, | 3462 | .composite_integer, .integer, .bool, .float => return value, |
| 3549 | .composite_integer => unreachable, // TODO | | |
| 3550 | .strange_integer => switch (info.signedness) { | 3463 | .strange_integer => switch (info.signedness) { |
| 3551 | .unsigned => { | 3464 | .unsigned => { |
| 3552 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; | 3465 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; |
| ... | @@ -3815,12 +3728,12 @@ const NavGen = struct { | ... | @@ -3815,12 +3728,12 @@ const NavGen = struct { |
| 3815 | .unsigned => blk: { | 3728 | .unsigned => blk: { |
| 3816 | if (maybe_op_ty_bits) |op_ty_bits| { | 3729 | if (maybe_op_ty_bits) |op_ty_bits| { |
| 3817 | const op_ty = try pt.intType(.unsigned, op_ty_bits); | 3730 | const op_ty = try pt.intType(.unsigned, op_ty_bits); |
| 3818 | const casted_lhs = try self.buildIntConvert(op_ty, lhs); | 3731 | const casted_lhs = try self.buildConvert(op_ty, lhs); |
| 3819 | const casted_rhs = try self.buildIntConvert(op_ty, rhs); | 3732 | const casted_rhs = try self.buildConvert(op_ty, rhs); |
| 3820 | | 3733 | |
| 3821 | const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs); | 3734 | const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs); |
| 3822 | | 3735 | |
| 3823 | const low_bits = try self.buildIntConvert(lhs.ty, full_result); | 3736 | const low_bits = try self.buildConvert(lhs.ty, full_result); |
| 3824 | const result = try self.normalize(low_bits, info); | 3737 | const result = try self.normalize(low_bits, info); |
| 3825 | | 3738 | |
| 3826 | // Shift the result bits away to get the overflow bits. | 3739 | // Shift the result bits away to get the overflow bits. |
| ... | @@ -3846,9 +3759,7 @@ const NavGen = struct { | ... | @@ -3846,9 +3759,7 @@ const NavGen = struct { |
| 3846 | const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits); | 3759 | const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits); |
| 3847 | | 3760 | |
| 3848 | // If no overflow bits in low_bits, no extra work needs to be done. | 3761 | // If no overflow bits in low_bits, no extra work needs to be done. |
| 3849 | if (info.backing_bits == info.bits) { | 3762 | if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed }; |
| 3850 | break :blk .{ result, high_overflowed }; | | |
| 3851 | } | | |
| 3852 | | 3763 | |
| 3853 | // Shift the result bits away to get the overflow bits. | 3764 | // Shift the result bits away to get the overflow bits. |
| 3854 | const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits)); | 3765 | const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits)); |
| ... | @@ -3886,13 +3797,13 @@ const NavGen = struct { | ... | @@ -3886,13 +3797,13 @@ const NavGen = struct { |
| 3886 | if (maybe_op_ty_bits) |op_ty_bits| { | 3797 | if (maybe_op_ty_bits) |op_ty_bits| { |
| 3887 | const op_ty = try pt.intType(.signed, op_ty_bits); | 3798 | const op_ty = try pt.intType(.signed, op_ty_bits); |
| 3888 | // Assume normalized; sign bit is set. We want a sign extend. | 3799 | // Assume normalized; sign bit is set. We want a sign extend. |
| 3889 | const casted_lhs = try self.buildIntConvert(op_ty, lhs); | 3800 | const casted_lhs = try self.buildConvert(op_ty, lhs); |
| 3890 | const casted_rhs = try self.buildIntConvert(op_ty, rhs); | 3801 | const casted_rhs = try self.buildConvert(op_ty, rhs); |
| 3891 | | 3802 | |
| 3892 | const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs); | 3803 | const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs); |
| 3893 | | 3804 | |
| 3894 | // Truncate to the result type. | 3805 | // Truncate to the result type. |
| 3895 | const low_bits = try self.buildIntConvert(lhs.ty, full_result); | 3806 | const low_bits = try self.buildConvert(lhs.ty, full_result); |
| 3896 | const result = try self.normalize(low_bits, info); | 3807 | const result = try self.normalize(low_bits, info); |
| 3897 | | 3808 | |
| 3898 | // Now, we need to check the overflow bits AND the sign | 3809 | // Now, we need to check the overflow bits AND the sign |
| ... | @@ -3929,9 +3840,7 @@ const NavGen = struct { | ... | @@ -3929,9 +3840,7 @@ const NavGen = struct { |
| 3929 | // If no overflow bits in low_bits, no extra work needs to be done. | 3840 | // If no overflow bits in low_bits, no extra work needs to be done. |
| 3930 | // Careful, we still have to check the sign bit, so this branch | 3841 | // Careful, we still have to check the sign bit, so this branch |
| 3931 | // only goes for i33 and such. | 3842 | // only goes for i33 and such. |
| 3932 | if (info.backing_bits == info.bits + 1) { | 3843 | if (info.backing_bits == info.bits + 1) break :blk .{ result, high_overflowed }; |
| 3933 | break :blk .{ result, high_overflowed }; | | |
| 3934 | } | | |
| 3935 | | 3844 | |
| 3936 | // Shift the result bits away to get the overflow bits. | 3845 | // Shift the result bits away to get the overflow bits. |
| 3937 | const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1)); | 3846 | const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1)); |
| ... | @@ -3972,7 +3881,7 @@ const NavGen = struct { | ... | @@ -3972,7 +3881,7 @@ const NavGen = struct { |
| 3972 | | 3881 | |
| 3973 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, | 3882 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 3974 | // so just manually upcast it if required. | 3883 | // so just manually upcast it if required. |
| 3975 | const casted_shift = try self.buildIntConvert(base.ty.scalarType(zcu), shift); | 3884 | const casted_shift = try self.buildConvert(base.ty.scalarType(zcu), shift); |
| 3976 | | 3885 | |
| 3977 | const left = try self.buildBinary(.sll, base, casted_shift); | 3886 | const left = try self.buildBinary(.sll, base, casted_shift); |
| 3978 | const result = try self.normalize(left, info); | 3887 | const result = try self.normalize(left, info); |
| ... | @@ -4026,7 +3935,7 @@ const NavGen = struct { | ... | @@ -4026,7 +3935,7 @@ const NavGen = struct { |
| 4026 | // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty. | 3935 | // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty. |
| 4027 | // result_ty is always large enough to hold the result, so we might have to down | 3936 | // result_ty is always large enough to hold the result, so we might have to down |
| 4028 | // cast it. | 3937 | // cast it. |
| 4029 | const result = try self.buildIntConvert(scalar_result_ty, count); | 3938 | const result = try self.buildConvert(scalar_result_ty, count); |
| 4030 | return try result.materialize(self); | 3939 | return try result.materialize(self); |
| 4031 | } | 3940 | } |
| 4032 | | 3941 | |
| ... | @@ -4057,11 +3966,8 @@ const NavGen = struct { | ... | @@ -4057,11 +3966,8 @@ const NavGen = struct { |
| 4057 | const operand_ty = self.typeOf(reduce.operand); | 3966 | const operand_ty = self.typeOf(reduce.operand); |
| 4058 | const scalar_ty = operand_ty.scalarType(zcu); | 3967 | const scalar_ty = operand_ty.scalarType(zcu); |
| 4059 | const scalar_ty_id = try self.resolveType(scalar_ty, .direct); | 3968 | const scalar_ty_id = try self.resolveType(scalar_ty, .direct); |
| 4060 | | | |
| 4061 | const info = self.arithmeticTypeInfo(operand_ty); | 3969 | const info = self.arithmeticTypeInfo(operand_ty); |
| 4062 | | | |
| 4063 | const len = operand_ty.vectorLen(zcu); | 3970 | const len = operand_ty.vectorLen(zcu); |
| 4064 | | | |
| 4065 | const first = try self.extractVectorComponent(scalar_ty, operand, 0); | 3971 | const first = try self.extractVectorComponent(scalar_ty, operand, 0); |
| 4066 | | 3972 | |
| 4067 | switch (reduce.operation) { | 3973 | switch (reduce.operation) { |
| ... | @@ -4136,51 +4042,9 @@ const NavGen = struct { | ... | @@ -4136,51 +4042,9 @@ const NavGen = struct { |
| 4136 | | 4042 | |
| 4137 | // Note: number of components in the result, a, and b may differ. | 4043 | // Note: number of components in the result, a, and b may differ. |
| 4138 | const result_ty = self.typeOfIndex(inst); | 4044 | const result_ty = self.typeOfIndex(inst); |
| 4139 | const a_ty = self.typeOf(extra.a); | | |
| 4140 | const b_ty = self.typeOf(extra.b); | | |
| 4141 | | | |
| 4142 | const scalar_ty = result_ty.scalarType(zcu); | 4045 | const scalar_ty = result_ty.scalarType(zcu); |
| 4143 | const scalar_ty_id = try self.resolveType(scalar_ty, .direct); | 4046 | const scalar_ty_id = try self.resolveType(scalar_ty, .direct); |
| 4144 | | 4047 | |
| 4145 | // If all of the types are SPIR-V vectors, we can use OpVectorShuffle. | | |
| 4146 | if (self.isSpvVector(result_ty) and self.isSpvVector(a_ty) and self.isSpvVector(b_ty)) { | | |
| 4147 | // The SPIR-V shuffle instruction is similar to the Air instruction, except that the elements are | | |
| 4148 | // numbered consecutively instead of using negatives. | | |
| 4149 | | | |
| 4150 | const components = try self.gpa.alloc(Word, result_ty.vectorLen(zcu)); | | |
| 4151 | defer self.gpa.free(components); | | |
| 4152 | | | |
| 4153 | const a_len = a_ty.vectorLen(zcu); | | |
| 4154 | | | |
| 4155 | for (components, 0..) |*component, i| { | | |
| 4156 | const elem = try mask.elemValue(pt, i); | | |
| 4157 | if (elem.isUndef(zcu)) { | | |
| 4158 | // This is explicitly valid for OpVectorShuffle, it indicates undefined. | | |
| 4159 | component.* = 0xFFFF_FFFF; | | |
| 4160 | continue; | | |
| 4161 | } | | |
| 4162 | | | |
| 4163 | const index = elem.toSignedInt(zcu); | | |
| 4164 | if (index >= 0) { | | |
| 4165 | component.* = @intCast(index); | | |
| 4166 | } else { | | |
| 4167 | component.* = @intCast(~index + a_len); | | |
| 4168 | } | | |
| 4169 | } | | |
| 4170 | | | |
| 4171 | const result_id = self.spv.allocId(); | | |
| 4172 | try self.func.body.emit(self.spv.gpa, .OpVectorShuffle, .{ | | |
| 4173 | .id_result_type = try self.resolveType(result_ty, .direct), | | |
| 4174 | .id_result = result_id, | | |
| 4175 | .vector_1 = a, | | |
| 4176 | .vector_2 = b, | | |
| 4177 | .components = components, | | |
| 4178 | }); | | |
| 4179 | return result_id; | | |
| 4180 | } | | |
| 4181 | | | |
| 4182 | // Fall back to manually extracting and inserting components. | | |
| 4183 | | | |
| 4184 | const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu)); | 4048 | const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu)); |
| 4185 | defer self.gpa.free(constituents); | 4049 | defer self.gpa.free(constituents); |
| 4186 | | 4050 | |
| ... | @@ -4535,9 +4399,7 @@ const NavGen = struct { | ... | @@ -4535,9 +4399,7 @@ const NavGen = struct { |
| 4535 | const dst_ty_id = try self.resolveType(dst_ty, .direct); | 4399 | const dst_ty_id = try self.resolveType(dst_ty, .direct); |
| 4536 | | 4400 | |
| 4537 | const result_id = blk: { | 4401 | const result_id = blk: { |
| 4538 | if (src_ty_id == dst_ty_id) { | 4402 | if (src_ty_id == dst_ty_id) break :blk src_id; |
| 4539 | break :blk src_id; | | |
| 4540 | } | | |
| 4541 | | 4403 | |
| 4542 | // TODO: Some more cases are missing here | 4404 | // TODO: Some more cases are missing here |
| 4543 | // See fn bitCast in llvm.zig | 4405 | // See fn bitCast in llvm.zig |
| ... | @@ -4618,7 +4480,7 @@ const NavGen = struct { | ... | @@ -4618,7 +4480,7 @@ const NavGen = struct { |
| 4618 | return try src.materialize(self); | 4480 | return try src.materialize(self); |
| 4619 | } | 4481 | } |
| 4620 | | 4482 | |
| 4621 | const converted = try self.buildIntConvert(dst_ty, src); | 4483 | const converted = try self.buildConvert(dst_ty, src); |
| 4622 | | 4484 | |
| 4623 | // Make sure to normalize the result if shrinking. | 4485 | // Make sure to normalize the result if shrinking. |
| 4624 | // Because strange ints are sign extended in their backing | 4486 | // Because strange ints are sign extended in their backing |
| ... | @@ -4698,17 +4560,10 @@ const NavGen = struct { | ... | @@ -4698,17 +4560,10 @@ const NavGen = struct { |
| 4698 | | 4560 | |
| 4699 | fn airFloatCast(self: *NavGen, inst: Air.Inst.Index) !?IdRef { | 4561 | fn airFloatCast(self: *NavGen, inst: Air.Inst.Index) !?IdRef { |
| 4700 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4562 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4701 | const operand_id = try self.resolve(ty_op.operand); | 4563 | const operand = try self.temporary(ty_op.operand); |
| 4702 | const dest_ty = self.typeOfIndex(inst); | 4564 | const dest_ty = self.typeOfIndex(inst); |
| 4703 | const dest_ty_id = try self.resolveType(dest_ty, .direct); | 4565 | const result = try self.buildConvert(dest_ty, operand); |
| 4704 | | 4566 | return try result.materialize(self); |
| 4705 | const result_id = self.spv.allocId(); | | |
| 4706 | try self.func.body.emit(self.spv.gpa, .OpFConvert, .{ | | |
| 4707 | .id_result_type = dest_ty_id, | | |
| 4708 | .id_result = result_id, | | |
| 4709 | .float_value = operand_id, | | |
| 4710 | }); | | |
| 4711 | return result_id; | | |
| 4712 | } | 4567 | } |
| 4713 | | 4568 | |
| 4714 | fn airNot(self: *NavGen, inst: Air.Inst.Index) !?IdRef { | 4569 | fn airNot(self: *NavGen, inst: Air.Inst.Index) !?IdRef { |
| ... | @@ -4790,13 +4645,14 @@ const NavGen = struct { | ... | @@ -4790,13 +4645,14 @@ const NavGen = struct { |
| 4790 | const field_int_id = blk: { | 4645 | const field_int_id = blk: { |
| 4791 | if (field_ty.isPtrAtRuntime(zcu)) { | 4646 | if (field_ty.isPtrAtRuntime(zcu)) { |
| 4792 | assert(self.spv.hasFeature(.addresses) or | 4647 | assert(self.spv.hasFeature(.addresses) or |
| 4793 | (self.spv.hasFeature(.physical_storage_buffer) and field_ty.ptrAddressSpace(zcu) == .storage_buffer)); | 4648 | (self.spv.hasFeature(.physical_storage_buffer) and |
| | 4649 | field_ty.ptrAddressSpace(zcu) == .storage_buffer)); |
| 4794 | break :blk try self.intFromPtr(field_id); | 4650 | break :blk try self.intFromPtr(field_id); |
| 4795 | } | 4651 | } |
| 4796 | break :blk try self.bitCast(field_int_ty, field_ty, field_id); | 4652 | break :blk try self.bitCast(field_int_ty, field_ty, field_id); |
| 4797 | }; | 4653 | }; |
| 4798 | const shift_rhs = try self.constInt(backing_int_ty, running_bits); | 4654 | const shift_rhs = try self.constInt(backing_int_ty, running_bits); |
| 4799 | const extended_int_conv = try self.buildIntConvert(backing_int_ty, .{ | 4655 | const extended_int_conv = try self.buildConvert(backing_int_ty, .{ |
| 4800 | .ty = field_int_ty, | 4656 | .ty = field_int_ty, |
| 4801 | .value = .{ .singleton = field_int_id }, | 4657 | .value = .{ .singleton = field_int_id }, |
| 4802 | }); | 4658 | }); |
| ... | @@ -5016,17 +4872,6 @@ const NavGen = struct { | ... | @@ -5016,17 +4872,6 @@ const NavGen = struct { |
| 5016 | const array_id = try self.resolve(bin_op.lhs); | 4872 | const array_id = try self.resolve(bin_op.lhs); |
| 5017 | const index_id = try self.resolve(bin_op.rhs); | 4873 | const index_id = try self.resolve(bin_op.rhs); |
| 5018 | | 4874 | |
| 5019 | if (self.isSpvVector(array_ty)) { | | |
| 5020 | const result_id = self.spv.allocId(); | | |
| 5021 | try self.func.body.emit(self.spv.gpa, .OpVectorExtractDynamic, .{ | | |
| 5022 | .id_result_type = try self.resolveType(elem_ty, .direct), | | |
| 5023 | .id_result = result_id, | | |
| 5024 | .vector = array_id, | | |
| 5025 | .index = index_id, | | |
| 5026 | }); | | |
| 5027 | return result_id; | | |
| 5028 | } | | |
| 5029 | | | |
| 5030 | // SPIR-V doesn't have an array indexing function for some damn reason. | 4875 | // SPIR-V doesn't have an array indexing function for some damn reason. |
| 5031 | // For now, just generate a temporary and use that. | 4876 | // For now, just generate a temporary and use that. |
| 5032 | // TODO: This backend probably also should use isByRef from llvm... | 4877 | // TODO: This backend probably also should use isByRef from llvm... |
| ... | @@ -5173,7 +5018,7 @@ const NavGen = struct { | ... | @@ -5173,7 +5018,7 @@ const NavGen = struct { |
| 5173 | return self.bitCast(ty, payload_ty, payload.?); | 5018 | return self.bitCast(ty, payload_ty, payload.?); |
| 5174 | } | 5019 | } |
| 5175 | | 5020 | |
| 5176 | const trunc = try self.buildIntConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } }); | 5021 | const trunc = try self.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } }); |
| 5177 | return try trunc.materialize(self); | 5022 | return try trunc.materialize(self); |
| 5178 | } | 5023 | } |
| 5179 | | 5024 | |
| ... | @@ -5182,7 +5027,7 @@ const NavGen = struct { | ... | @@ -5182,7 +5027,7 @@ const NavGen = struct { |
| 5182 | try self.convertToIndirect(payload_ty, payload.?) | 5027 | try self.convertToIndirect(payload_ty, payload.?) |
| 5183 | else | 5028 | else |
| 5184 | try self.bitCast(payload_int_ty, payload_ty, payload.?); | 5029 | try self.bitCast(payload_int_ty, payload_ty, payload.?); |
| 5185 | const trunc = try self.buildIntConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } }); | 5030 | const trunc = try self.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } }); |
| 5186 | return try trunc.materialize(self); | 5031 | return try trunc.materialize(self); |
| 5187 | } | 5032 | } |
| 5188 | | 5033 | |
| ... | @@ -5208,13 +5053,16 @@ const NavGen = struct { | ... | @@ -5208,13 +5053,16 @@ const NavGen = struct { |
| 5208 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 5053 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 5209 | const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect); | 5054 | const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect); |
| 5210 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index}); | 5055 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index}); |
| 5211 | const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect); | 5056 | const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: { |
| 5212 | const active_pl_ptr_id = self.spv.allocId(); | 5057 | const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect); |
| 5213 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ | 5058 | const active_pl_ptr_id = self.spv.allocId(); |
| 5214 | .id_result_type = active_pl_ptr_ty_id, | 5059 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 5215 | .id_result = active_pl_ptr_id, | 5060 | .id_result_type = active_pl_ptr_ty_id, |
| 5216 | .operand = pl_ptr_id, | 5061 | .id_result = active_pl_ptr_id, |
| 5217 | }); | 5062 | .operand = pl_ptr_id, |
| | 5063 | }); |
| | 5064 | break :blk active_pl_ptr_id; |
| | 5065 | } else pl_ptr_id; |
| 5218 | | 5066 | |
| 5219 | try self.store(payload_ty, active_pl_ptr_id, payload.?, .{}); | 5067 | try self.store(payload_ty, active_pl_ptr_id, payload.?, .{}); |
| 5220 | } else { | 5068 | } else { |
| ... | @@ -5271,9 +5119,9 @@ const NavGen = struct { | ... | @@ -5271,9 +5119,9 @@ const NavGen = struct { |
| 5271 | const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1); | 5119 | const mask_id = try self.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1); |
| 5272 | const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } }); | 5120 | const masked = try self.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } }); |
| 5273 | const result_id = blk: { | 5121 | const result_id = blk: { |
| 5274 | if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).?) | 5122 | if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0") |
| 5275 | break :blk try self.bitCast(field_int_ty, object_ty, try masked.materialize(self)); | 5123 | break :blk try self.bitCast(field_int_ty, object_ty, try masked.materialize(self)); |
| 5276 | const trunc = try self.buildIntConvert(field_int_ty, masked); | 5124 | const trunc = try self.buildConvert(field_int_ty, masked); |
| 5277 | break :blk try trunc.materialize(self); | 5125 | break :blk try trunc.materialize(self); |
| 5278 | }; | 5126 | }; |
| 5279 | if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id); | 5127 | if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id); |
| ... | @@ -5295,9 +5143,9 @@ const NavGen = struct { | ... | @@ -5295,9 +5143,9 @@ const NavGen = struct { |
| 5295 | .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } }, | 5143 | .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } }, |
| 5296 | ); | 5144 | ); |
| 5297 | const result_id = blk: { | 5145 | const result_id = blk: { |
| 5298 | if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).?) | 5146 | if (self.backingIntBits(field_bit_size).@"0" == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).@"0") |
| 5299 | break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self)); | 5147 | break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self)); |
| 5300 | const trunc = try self.buildIntConvert(int_ty, masked); | 5148 | const trunc = try self.buildConvert(int_ty, masked); |
| 5301 | break :blk try trunc.materialize(self); | 5149 | break :blk try trunc.materialize(self); |
| 5302 | }; | 5150 | }; |
| 5303 | if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id); | 5151 | if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id); |
| ... | @@ -6332,17 +6180,15 @@ const NavGen = struct { | ... | @@ -6332,17 +6180,15 @@ const NavGen = struct { |
| 6332 | .bool, .error_set => 1, | 6180 | .bool, .error_set => 1, |
| 6333 | .int => blk: { | 6181 | .int => blk: { |
| 6334 | const bits = cond_ty.intInfo(zcu).bits; | 6182 | const bits = cond_ty.intInfo(zcu).bits; |
| 6335 | const backing_bits = self.backingIntBits(bits) orelse { | 6183 | const backing_bits, const big_int = self.backingIntBits(bits); |
| 6336 | return self.todo("implement composite int switch", .{}); | 6184 | if (big_int) return self.todo("implement composite int switch", .{}); |
| 6337 | }; | | |
| 6338 | break :blk if (backing_bits <= 32) 1 else 2; | 6185 | break :blk if (backing_bits <= 32) 1 else 2; |
| 6339 | }, | 6186 | }, |
| 6340 | .@"enum" => blk: { | 6187 | .@"enum" => blk: { |
| 6341 | const int_ty = cond_ty.intTagType(zcu); | 6188 | const int_ty = cond_ty.intTagType(zcu); |
| 6342 | const int_info = int_ty.intInfo(zcu); | 6189 | const int_info = int_ty.intInfo(zcu); |
| 6343 | const backing_bits = self.backingIntBits(int_info.bits) orelse { | 6190 | const backing_bits, const big_int = self.backingIntBits(int_info.bits); |
| 6344 | return self.todo("implement composite int switch", .{}); | 6191 | if (big_int) return self.todo("implement composite int switch", .{}); |
| 6345 | }; | | |
| 6346 | break :blk if (backing_bits <= 32) 1 else 2; | 6192 | break :blk if (backing_bits <= 32) 1 else 2; |
| 6347 | }, | 6193 | }, |
| 6348 | .pointer => blk: { | 6194 | .pointer => blk: { |
| ... | @@ -6752,7 +6598,7 @@ const NavGen = struct { | ... | @@ -6752,7 +6598,7 @@ const NavGen = struct { |
| 6752 | // TODO: Should we make these builtins return usize? | 6598 | // TODO: Should we make these builtins return usize? |
| 6753 | const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0); | 6599 | const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0); |
| 6754 | const tmp = Temporary.init(Type.u64, result_id); | 6600 | const tmp = Temporary.init(Type.u64, result_id); |
| 6755 | const result = try self.buildIntConvert(Type.u32, tmp); | 6601 | const result = try self.buildConvert(Type.u32, tmp); |
| 6756 | return try result.materialize(self); | 6602 | return try result.materialize(self); |
| 6757 | } | 6603 | } |
| 6758 | | 6604 | |
| ... | @@ -6763,7 +6609,7 @@ const NavGen = struct { | ... | @@ -6763,7 +6609,7 @@ const NavGen = struct { |
| 6763 | // TODO: Should we make these builtins return usize? | 6609 | // TODO: Should we make these builtins return usize? |
| 6764 | const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0); | 6610 | const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0); |
| 6765 | const tmp = Temporary.init(Type.u64, result_id); | 6611 | const tmp = Temporary.init(Type.u64, result_id); |
| 6766 | const result = try self.buildIntConvert(Type.u32, tmp); | 6612 | const result = try self.buildConvert(Type.u32, tmp); |
| 6767 | return try result.materialize(self); | 6613 | return try result.materialize(self); |
| 6768 | } | 6614 | } |
| 6769 | | 6615 | |
| ... | @@ -6774,7 +6620,7 @@ const NavGen = struct { | ... | @@ -6774,7 +6620,7 @@ const NavGen = struct { |
| 6774 | // TODO: Should we make these builtins return usize? | 6620 | // TODO: Should we make these builtins return usize? |
| 6775 | const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0); | 6621 | const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0); |
| 6776 | const tmp = Temporary.init(Type.u64, result_id); | 6622 | const tmp = Temporary.init(Type.u64, result_id); |
| 6777 | const result = try self.buildIntConvert(Type.u32, tmp); | 6623 | const result = try self.buildConvert(Type.u32, tmp); |
| 6778 | return try result.materialize(self); | 6624 | return try result.materialize(self); |
| 6779 | } | 6625 | } |
| 6780 | | 6626 | |