authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-07 22:47:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-07 22:47:08-07:00
log53c86febcbeee858e9c6536c54adf99ee644147e
tree4d2ab07845f46e98dce59c6fea920f02444d339b
parent3e30ba3f20dce2d406253de3fc0eb86934a3eaa7

stage2: packed struct fixes for big-endian targets


3 files changed, 58 insertions(+), 17 deletions(-)

src/Sema.zig+9-2
...@@ -18708,11 +18708,18 @@ fn structFieldPtrByIndex(...@@ -18708,11 +18708,18 @@ fn structFieldPtrByIndex(
18708 // If the field happens to be byte-aligned, simplify the pointer type.18708 // If the field happens to be byte-aligned, simplify the pointer type.
18709 // The pointee type bit size must match its ABI byte size so that loads and stores18709 // The pointee type bit size must match its ABI byte size so that loads and stores
18710 // do not interfere with the surrounding packed bits.18710 // do not interfere with the surrounding packed bits.
18711 if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0) {18711 // We do not attempt this with big-endian targets yet because of nested
18712 const byte_offset = ptr_ty_data.bit_offset / 8;18712 // structs and floats. I need to double-check the desired behavior for big endian
18713 // targets before adding the necessary complications to this code. This will not
18714 // cause miscompilations; it only means the field pointer uses bit masking when it
18715 // might not be strictly necessary.
18716 if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0 and
18717 target.cpu.arch.endian() == .Little)
18718 {
18713 const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target);18719 const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target);
18714 const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target);18720 const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target);
18715 if (elem_size_bytes * 8 == elem_size_bits) {18721 if (elem_size_bytes * 8 == elem_size_bits) {
18722 const byte_offset = ptr_ty_data.bit_offset / 8;
18716 const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, byte_offset | parent_align));18723 const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, byte_offset | parent_align));
18717 ptr_ty_data.bit_offset = 0;18724 ptr_ty_data.bit_offset = 0;
18718 ptr_ty_data.host_size = 0;18725 ptr_ty_data.host_size = 0;
src/type.zig+4-3
...@@ -5597,17 +5597,18 @@ pub const Type = extern union {...@@ -5597,17 +5597,18 @@ pub const Type = extern union {
5597 comptime assert(Type.packed_struct_layout_version == 2);5597 comptime assert(Type.packed_struct_layout_version == 2);
55985598
5599 var bit_offset: u16 = undefined;5599 var bit_offset: u16 = undefined;
5600 var elem_size_bits: u16 = undefined;
5600 var running_bits: u16 = 0;5601 var running_bits: u16 = 0;
5601 for (struct_obj.fields.values()) |f, i| {5602 for (struct_obj.fields.values()) |f, i| {
5602 if (!f.ty.hasRuntimeBits()) continue;5603 if (!f.ty.hasRuntimeBits()) continue;
56035604
5605 const field_bits = @intCast(u16, f.ty.bitSize(target));
5604 if (i == field_index) {5606 if (i == field_index) {
5605 bit_offset = running_bits;5607 bit_offset = running_bits;
5608 elem_size_bits = field_bits;
5606 }5609 }
5607 running_bits += @intCast(u16, f.ty.bitSize(target));5610 running_bits += field_bits;
5608 }5611 }
5609 const host_size = (running_bits + 7) / 8;
5610 _ = host_size; // TODO big-endian
5611 const byte_offset = bit_offset / 8;5612 const byte_offset = bit_offset / 8;
5612 return byte_offset;5613 return byte_offset;
5613 }5614 }
test/behavior/packed-struct.zig+45-12
...@@ -289,13 +289,7 @@ test "regular in irregular packed struct" {...@@ -289,13 +289,7 @@ test "regular in irregular packed struct" {
289289
290 const Irregular = packed struct {290 const Irregular = packed struct {
291 bar: Regular = Regular{},291 bar: Regular = Regular{},
292
293 // This field forces the regular packed struct to be a part of single u48
294 // and thus it all gets represented as an array of 6 bytes in LLVM
295 _: u24 = 0,292 _: u24 = 0,
296
297 // This struct on its own can represent its fields directly in LLVM
298 // with no need to use array of bytes as underlaying representation.
299 pub const Regular = packed struct { a: u16 = 0, b: u8 = 0 };293 pub const Regular = packed struct { a: u16 = 0, b: u8 = 0 };
300 };294 };
301295
...@@ -335,17 +329,44 @@ test "byte-aligned field pointer offsets" {...@@ -335,17 +329,44 @@ test "byte-aligned field pointer offsets" {
335 .c = 3,329 .c = 3,
336 .d = 4,330 .d = 4,
337 };331 };
338 comptime assert(@TypeOf(&a.a) == *align(4) u8);332 switch (comptime builtin.cpu.arch.endian()) {
339 comptime assert(@TypeOf(&a.b) == *u8);333 .Little => {
340 comptime assert(@TypeOf(&a.c) == *align(2) u8);334 comptime assert(@TypeOf(&a.a) == *align(4) u8);
341 comptime assert(@TypeOf(&a.d) == *u8);335 comptime assert(@TypeOf(&a.b) == *u8);
336 comptime assert(@TypeOf(&a.c) == *align(2) u8);
337 comptime assert(@TypeOf(&a.d) == *u8);
338 },
339 .Big => {
340 // TODO re-evaluate packed struct endianness
341 comptime assert(@TypeOf(&a.a) == *align(4:0:4) u8);
342 comptime assert(@TypeOf(&a.b) == *align(4:8:4) u8);
343 comptime assert(@TypeOf(&a.c) == *align(4:16:4) u8);
344 comptime assert(@TypeOf(&a.d) == *align(4:24:4) u8);
345 },
346 }
342 try expect(a.a == 1);347 try expect(a.a == 1);
343 try expect(a.b == 2);348 try expect(a.b == 2);
344 try expect(a.c == 3);349 try expect(a.c == 3);
345 try expect(a.d == 4);350 try expect(a.d == 4);
351
346 a.a += 1;352 a.a += 1;
353 try expect(a.a == 2);
354 try expect(a.b == 2);
355 try expect(a.c == 3);
356 try expect(a.d == 4);
357
347 a.b += 1;358 a.b += 1;
359 try expect(a.a == 2);
360 try expect(a.b == 3);
361 try expect(a.c == 3);
362 try expect(a.d == 4);
363
348 a.c += 1;364 a.c += 1;
365 try expect(a.a == 2);
366 try expect(a.b == 3);
367 try expect(a.c == 4);
368 try expect(a.d == 4);
369
349 a.d += 1;370 a.d += 1;
350 try expect(a.a == 2);371 try expect(a.a == 2);
351 try expect(a.b == 3);372 try expect(a.b == 3);
...@@ -356,11 +377,23 @@ test "byte-aligned field pointer offsets" {...@@ -356,11 +377,23 @@ test "byte-aligned field pointer offsets" {
356 .a = 1,377 .a = 1,
357 .b = 2,378 .b = 2,
358 };379 };
359 comptime assert(@TypeOf(&b.a) == *align(4) u16);380 switch (comptime builtin.cpu.arch.endian()) {
360 comptime assert(@TypeOf(&b.b) == *u16);381 .Little => {
382 comptime assert(@TypeOf(&b.a) == *align(4) u16);
383 comptime assert(@TypeOf(&b.b) == *u16);
384 },
385 .Big => {
386 comptime assert(@TypeOf(&b.a) == *align(4:0:4) u16);
387 comptime assert(@TypeOf(&b.b) == *align(4:16:4) u16);
388 },
389 }
361 try expect(b.a == 1);390 try expect(b.a == 1);
362 try expect(b.b == 2);391 try expect(b.b == 2);
392
363 b.a += 1;393 b.a += 1;
394 try expect(b.a == 2);
395 try expect(b.b == 2);
396
364 b.b += 1;397 b.b += 1;
365 try expect(b.a == 2);398 try expect(b.a == 2);
366 try expect(b.b == 3);399 try expect(b.b == 3);