authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-03-12 07:38:50+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-03-18 07:05:48+03:30
log54c097f50ddc794dc2b3890490379ab2f8371443
treebc08f97c3095a37deb1e81922470b2782f08a212
parent50539a2447c0720f91789063d7349bd0103de4bd

spirv: packed struct init + field val access


8 files changed, 116 insertions(+), 61 deletions(-)

src/codegen/spirv.zig+103-39
......@@ -714,6 +714,7 @@ const NavGen = struct {
714714 const int_info = scalar_ty.intInfo(zcu);
715715 // Use backing bits so that negatives are sign extended
716716 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int
717 assert(backing_bits != 0); // u0 is comptime
717718
718719 const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {
719720 .int => |int| int.signedness,
......@@ -721,35 +722,35 @@ const NavGen = struct {
721722 else => unreachable,
722723 };
723724
724 const value64: u64 = switch (signedness) {
725 .signed => @bitCast(@as(i64, @intCast(value))),
726 .unsigned => @as(u64, @intCast(value)),
727 };
725 const final_value: spec.LiteralContextDependentNumber = blk: {
726 if (self.spv.hasFeature(.kernel)) {
727 const value64: u64 = switch (signedness) {
728 .signed => @bitCast(@as(i64, @intCast(value))),
729 .unsigned => @as(u64, @intCast(value)),
730 };
728731
729 // Manually truncate the value to the right amount of bits.
730 const truncated_value = if (backing_bits == 64)
731 value64
732 else
733 value64 & (@as(u64, 1) << @intCast(backing_bits)) - 1;
732 // Manually truncate the value to the right amount of bits.
733 const truncated_value = if (backing_bits == 64)
734 value64
735 else
736 value64 & (@as(u64, 1) << @intCast(backing_bits)) - 1;
734737
735 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
736 const result_id = self.spv.allocId();
738 break :blk switch (backing_bits) {
739 1...32 => .{ .uint32 = @truncate(truncated_value) },
740 33...64 => .{ .uint64 = truncated_value },
741 else => unreachable, // TODO: Large integer constants
742 };
743 }
737744
738 const section = &self.spv.sections.types_globals_constants;
739 switch (backing_bits) {
740 0 => unreachable, // u0 is comptime
741 1...32 => try section.emit(self.spv.gpa, .OpConstant, .{
742 .id_result_type = result_ty_id,
743 .id_result = result_id,
744 .value = .{ .uint32 = @truncate(truncated_value) },
745 }),
746 33...64 => try section.emit(self.spv.gpa, .OpConstant, .{
747 .id_result_type = result_ty_id,
748 .id_result = result_id,
749 .value = .{ .uint64 = truncated_value },
750 }),
751 else => unreachable, // TODO: Large integer constants
752 }
745 break :blk switch (backing_bits) {
746 1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) },
747 33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value },
748 else => unreachable, // TODO: Large integer constants
749 };
750 };
751
752 const result_ty_id = try self.resolveType(scalar_ty, .indirect);
753 const result_id = try self.spv.constant(result_ty_id, final_value);
753754
754755 if (!ty.isVector(zcu)) return result_id;
755756 return self.constructCompositeSplat(ty, result_id);
......@@ -804,8 +805,6 @@ const NavGen = struct {
804805 return self.spv.constUndef(result_ty_id);
805806 }
806807
807 const section = &self.spv.sections.types_globals_constants;
808
809808 const cacheable_id = cache: {
810809 switch (ip.indexToKey(val.toIntern())) {
811810 .int_type,
......@@ -860,13 +859,7 @@ const NavGen = struct {
860859 80, 128 => unreachable, // TODO
861860 else => unreachable,
862861 };
863 const result_id = self.spv.allocId();
864 try section.emit(self.spv.gpa, .OpConstant, .{
865 .id_result_type = result_ty_id,
866 .id_result = result_id,
867 .value = lit,
868 });
869 break :cache result_id;
862 break :cache try self.spv.constant(result_ty_id, lit);
870863 },
871864 .err => |err| {
872865 const value = try pt.getErrorValue(err.name);
......@@ -989,8 +982,17 @@ const NavGen = struct {
989982 },
990983 .struct_type => {
991984 const struct_type = zcu.typeToStruct(ty).?;
985
992986 if (struct_type.layout == .@"packed") {
993 return self.todo("packed struct constants", .{});
987 // TODO: composite int
988 // TODO: endianness
989 const bits: u16 = @intCast(ty.bitSize(zcu));
990 const bytes = std.mem.alignForward(u16, self.backingIntBits(bits).?, 8) / 8;
991 var limbs: [8]u8 = undefined;
992 @memset(&limbs, 0);
993 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
994 const backing_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));
995 return try self.constInt(backing_ty, @as(u64, @bitCast(limbs)));
994996 }
995997
996998 var types = std.ArrayList(Type).init(self.gpa);
......@@ -4309,6 +4311,7 @@ const NavGen = struct {
43094311 ) !Temporary {
43104312 const pt = self.pt;
43114313 const zcu = pt.zcu;
4314 const ip = &zcu.intern_pool;
43124315 const scalar_ty = lhs.ty.scalarType(zcu);
43134316 const is_vector = lhs.ty.isVector(zcu);
43144317
......@@ -4319,6 +4322,11 @@ const NavGen = struct {
43194322 const ty = lhs.ty.intTagType(zcu);
43204323 return try self.cmp(op, lhs.pun(ty), rhs.pun(ty));
43214324 },
4325 .@"struct" => {
4326 const struct_ty = zcu.typeToPackedStruct(scalar_ty).?;
4327 const ty = Type.fromInterned(struct_ty.backingIntTypeUnordered(ip));
4328 return try self.cmp(op, lhs.pun(ty), rhs.pun(ty));
4329 },
43224330 .error_set => {
43234331 assert(!is_vector);
43244332 const err_int_ty = try pt.errorIntType();
......@@ -4746,8 +4754,42 @@ const NavGen = struct {
47464754 switch (result_ty.zigTypeTag(zcu)) {
47474755 .@"struct" => {
47484756 if (zcu.typeToPackedStruct(result_ty)) |struct_type| {
4749 _ = struct_type;
4750 unreachable; // TODO
4757 comptime assert(Type.packed_struct_layout_version == 2);
4758 const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));
4759 var running_int_id = try self.constInt(backing_int_ty, 0);
4760 var running_bits: u16 = 0;
4761 for (struct_type.field_types.get(ip), elements) |field_ty_ip, element| {
4762 const field_ty = Type.fromInterned(field_ty_ip);
4763 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
4764 const field_id = try self.resolve(element);
4765 const ty_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
4766 const field_int_ty = try self.pt.intType(.unsigned, ty_bit_size);
4767 const field_int_id = blk: {
4768 if (field_ty.isPtrAtRuntime(zcu)) {
4769 assert(self.spv.hasFeature(.addresses) or
4770 (self.spv.hasFeature(.physical_storage_buffer) and field_ty.ptrAddressSpace(zcu) == .storage_buffer));
4771 break :blk try self.intFromPtr(field_id);
4772 }
4773 break :blk try self.bitCast(field_int_ty, field_ty, field_id);
4774 };
4775 const shift_rhs = try self.constInt(backing_int_ty, running_bits);
4776 const extended_int_conv = try self.buildIntConvert(backing_int_ty, .{
4777 .ty = field_int_ty,
4778 .value = .{ .singleton = field_int_id },
4779 });
4780 const shifted = try self.buildBinary(.sll, extended_int_conv, .{
4781 .ty = backing_int_ty,
4782 .value = .{ .singleton = shift_rhs },
4783 });
4784 const running_int_tmp = try self.buildBinary(
4785 .bit_or,
4786 .{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } },
4787 shifted,
4788 );
4789 running_int_id = try running_int_tmp.materialize(self);
4790 running_bits += ty_bit_size;
4791 }
4792 return running_int_id;
47514793 }
47524794
47534795 const types = try self.gpa.alloc(Type, elements.len);
......@@ -5156,6 +5198,7 @@ const NavGen = struct {
51565198 fn airStructFieldVal(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
51575199 const pt = self.pt;
51585200 const zcu = pt.zcu;
5201 const ip = &zcu.intern_pool;
51595202 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
51605203 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
51615204
......@@ -5168,7 +5211,28 @@ const NavGen = struct {
51685211
51695212 switch (object_ty.zigTypeTag(zcu)) {
51705213 .@"struct" => switch (object_ty.containerLayout(zcu)) {
5171 .@"packed" => unreachable, // TODO
5214 .@"packed" => {
5215 const struct_ty = zcu.typeToPackedStruct(object_ty).?;
5216 const backing_int_ty = Type.fromInterned(struct_ty.backingIntTypeUnordered(ip));
5217 const bit_offset = pt.structPackedFieldBitOffset(struct_ty, field_index);
5218 const bit_offset_id = try self.constInt(.u16, bit_offset);
5219 const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
5220 const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
5221 const int_ty = try pt.intType(signedness, field_bit_size);
5222 const shift_lhs: Temporary = .{ .ty = backing_int_ty, .value = .{ .singleton = object_id } };
5223 const shift = try self.buildBinary(.srl, shift_lhs, .{ .ty = .u16, .value = .{ .singleton = bit_offset_id } });
5224 const mask_id = try self.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
5225 const masked = try self.buildBinary(.bit_and, shift, .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } });
5226 const result_id = blk: {
5227 if (self.backingIntBits(field_bit_size).? == self.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).?)
5228 break :blk try self.bitCast(int_ty, backing_int_ty, try masked.materialize(self));
5229 const trunc = try self.buildIntConvert(int_ty, masked);
5230 break :blk try trunc.materialize(self);
5231 };
5232 if (field_ty.ip_index == .bool_type) return try self.convertToDirect(.bool, result_id);
5233 if (field_ty.isInt(zcu)) return result_id;
5234 return try self.bitCast(field_ty, int_ty, result_id);
5235 },
51725236 else => return try self.extractField(field_ty, object_id, field_index),
51735237 },
51745238 .@"union" => switch (object_ty.containerLayout(zcu)) {
src/codegen/spirv/Module.zig+11
......@@ -613,6 +613,17 @@ pub fn functionType(self: *Module, return_ty_id: IdRef, param_type_ids: []const
613613 return result_id;
614614}
615615
616pub fn constant(self: *Module, result_ty_id: IdRef, value: spec.LiteralContextDependentNumber) !IdRef {
617 const result_id = self.allocId();
618 const section = &self.sections.types_globals_constants;
619 try section.emit(self.gpa, .OpConstant, .{
620 .id_result_type = result_ty_id,
621 .id_result = result_id,
622 .value = value,
623 });
624 return result_id;
625}
626
616627pub fn constBool(self: *Module, value: bool) !IdRef {
617628 if (self.cache.bool_const[@intFromBool(value)]) |b| return b;
618629
test/behavior/bitcast.zig-2
......@@ -165,7 +165,6 @@ test "@bitCast packed structs at runtime and comptime" {
165165 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
166166 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
167167 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
168 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
169168
170169 const Full = packed struct {
171170 number: u16,
......@@ -226,7 +225,6 @@ test "bitcast packed struct to integer and back" {
226225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
227226 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
228227 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
229 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
230228
231229 const LevelUpMove = packed struct {
232230 move_id: u9,
test/behavior/packed-struct.zig-14
......@@ -123,7 +123,6 @@ test "correct sizeOf and offsets in packed structs" {
123123 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
124124 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
125125 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
126 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
127126
128127 const PStruct = packed struct {
129128 bool_a: bool,
......@@ -191,7 +190,6 @@ test "nested packed structs" {
191190 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
192191 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
193192 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
195193
196194 const S1 = packed struct { a: u8, b: u8, c: u8 };
197195
......@@ -257,7 +255,6 @@ test "nested packed struct unaligned" {
257255 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
258256 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
259257 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
260 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
261258 if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
262259
263260 const S1 = packed struct {
......@@ -895,7 +892,6 @@ test "packed struct passed to callconv(.c) function" {
895892 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
896893 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
897894 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
898 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
899895 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
900896
901897 const S = struct {
......@@ -944,7 +940,6 @@ test "packed struct initialized in bitcast" {
944940 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
945941 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
946942 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
947 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
948943 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
949944
950945 const T = packed struct { val: u8 };
......@@ -982,7 +977,6 @@ test "pointer to container level packed struct field" {
982977test "store undefined to packed result location" {
983978 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
984979 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
985 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
986980 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
987981
988982 var x: u4 = 0;
......@@ -992,8 +986,6 @@ test "store undefined to packed result location" {
992986}
993987
994988test "bitcast back and forth" {
995 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
996
997989 // Originally reported at https://github.com/ziglang/zig/issues/9914
998990 const S = packed struct { one: u6, two: u1 };
999991 const s = S{ .one = 0b110101, .two = 0b1 };
......@@ -1290,8 +1282,6 @@ test "2-byte packed struct argument in C calling convention" {
12901282}
12911283
12921284test "packed struct contains optional pointer" {
1293 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1294
12951285 const foo: packed struct {
12961286 a: ?*@This() = null,
12971287 } = .{};
......@@ -1299,8 +1289,6 @@ test "packed struct contains optional pointer" {
12991289}
13001290
13011291test "packed struct equality" {
1302 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1303
13041292 const Foo = packed struct {
13051293 a: u4,
13061294 b: u4,
......@@ -1321,8 +1309,6 @@ test "packed struct equality" {
13211309}
13221310
13231311test "packed struct with signed field" {
1324 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1325
13261312 var s: packed struct {
13271313 a: i2,
13281314 b: u6,
test/behavior/packed_struct_explicit_backing_int.zig-1
......@@ -9,7 +9,6 @@ test "packed struct explicit backing integer" {
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1010 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1111 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1312
1413 const S1 = packed struct { a: u8, b: u8, c: u8 };
1514
test/behavior/ptrcast.zig-2
......@@ -287,8 +287,6 @@ test "@ptrCast undefined value at comptime" {
287287}
288288
289289test "comptime @ptrCast with packed struct leaves value unmodified" {
290 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
291
292290 const S = packed struct { three: u3 };
293291 const st: S = .{ .three = 6 };
294292 try expect(st.three == 6);
test/behavior/struct.zig-3
......@@ -1023,7 +1023,6 @@ test "packed struct with undefined initializers" {
10231023 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10241024 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10251025 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1026 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10271026
10281027 const S = struct {
10291028 const P = packed struct {
......@@ -1221,7 +1220,6 @@ test "packed struct aggregate init" {
12211220 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12221221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12231222 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1224 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12251223 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
12261224
12271225 const S = struct {
......@@ -1971,7 +1969,6 @@ test "struct field default value is a call" {
19711969 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
19721970 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
19731971 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1974 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
19751972
19761973 const Z = packed struct {
19771974 a: u32,
test/behavior/vector.zig+2
......@@ -11,6 +11,7 @@ test "implicit cast vector to array - bool" {
1111 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1212 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1313 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1415
1516 const S = struct {
1617 fn doTheTest() !void {
......@@ -29,6 +30,7 @@ test "vector wrap operators" {
2930 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
3132 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
33 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
3234 if (builtin.zig_backend == .stage2_x86_64 and
3335 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
3436