mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
frontend: packed struct field ptr no longer finds byte borders
technically breaking, but I doubt anyone will notice.
This commit is contained in:
parent
e1a750655e
commit
0681bf06ab
5 changed files with 74 additions and 152 deletions
|
|
@ -2682,12 +2682,10 @@ const Block = struct {
|
|||
},
|
||||
.@"packed" => switch (agg_ty.zigTypeTag(zcu)) {
|
||||
else => unreachable,
|
||||
.@"struct" => switch (agg_ty.packedStructFieldPtrInfo(agg_ptr_ty, @intCast(field_index), pt)) {
|
||||
.bit_ptr => |packed_offset| {
|
||||
field_ptr_info.packed_offset = packed_offset;
|
||||
break :field_ptr_align agg_ptr_align;
|
||||
},
|
||||
.byte_ptr => |ptr_info| ptr_info.alignment,
|
||||
.@"struct" => {
|
||||
const packed_offset = agg_ty.packedStructFieldPtrInfo(agg_ptr_ty, @intCast(field_index), pt);
|
||||
field_ptr_info.packed_offset = packed_offset;
|
||||
break :field_ptr_align agg_ptr_align;
|
||||
},
|
||||
.@"union" => {
|
||||
field_ptr_info.packed_offset = .{
|
||||
|
|
|
|||
12
src/Sema.zig
12
src/Sema.zig
|
|
@ -27457,15 +27457,9 @@ fn structFieldPtrByIndex(
|
|||
|
||||
if (struct_type.layout == .@"packed") {
|
||||
assert(!field_is_comptime);
|
||||
switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {
|
||||
.bit_ptr => |packed_offset| {
|
||||
ptr_ty_data.flags.alignment = parent_align;
|
||||
ptr_ty_data.packed_offset = packed_offset;
|
||||
},
|
||||
.byte_ptr => |ptr_info| {
|
||||
ptr_ty_data.flags.alignment = ptr_info.alignment;
|
||||
},
|
||||
}
|
||||
const packed_offset = struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt);
|
||||
ptr_ty_data.flags.alignment = parent_align;
|
||||
ptr_ty_data.packed_offset = packed_offset;
|
||||
} else if (struct_type.layout == .@"extern") {
|
||||
assert(!field_is_comptime);
|
||||
// For extern structs, field alignment might be bigger than type's
|
||||
|
|
|
|||
41
src/Type.zig
41
src/Type.zig
|
|
@ -3514,22 +3514,17 @@ pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
|
|||
return .{ cur_ty, cur_len };
|
||||
}
|
||||
|
||||
pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx: u32, pt: Zcu.PerThread) union(enum) {
|
||||
/// The result is a bit-pointer with the same value and a new packed offset.
|
||||
bit_ptr: InternPool.Key.PtrType.PackedOffset,
|
||||
/// The result is a standard pointer.
|
||||
byte_ptr: struct {
|
||||
/// The byte offset of the field pointer from the parent pointer value.
|
||||
offset: u64,
|
||||
/// The alignment of the field pointer type.
|
||||
alignment: InternPool.Alignment,
|
||||
},
|
||||
} {
|
||||
/// Returns a bit-pointer with the same value and a new packed offset.
|
||||
pub fn packedStructFieldPtrInfo(
|
||||
struct_ty: Type,
|
||||
parent_ptr_ty: Type,
|
||||
field_idx: u32,
|
||||
pt: Zcu.PerThread,
|
||||
) InternPool.Key.PtrType.PackedOffset {
|
||||
comptime assert(Type.packed_struct_layout_version == 2);
|
||||
|
||||
const zcu = pt.zcu;
|
||||
const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
|
||||
const field_ty = struct_ty.fieldType(field_idx, zcu);
|
||||
|
||||
var bit_offset: u16 = 0;
|
||||
var running_bits: u16 = 0;
|
||||
|
|
@ -3552,28 +3547,10 @@ pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx:
|
|||
bit_offset,
|
||||
};
|
||||
|
||||
// If the field happens to be byte-aligned, simplify the pointer type.
|
||||
// We can only do this if the pointee's bit size matches its ABI byte size,
|
||||
// so that loads and stores do not interfere with surrounding packed bits.
|
||||
//
|
||||
// TODO: we do not attempt this with big-endian targets yet because of nested
|
||||
// structs and floats. I need to double-check the desired behavior for big endian
|
||||
// targets before adding the necessary complications to this code. This will not
|
||||
// cause miscompilations; it only means the field pointer uses bit masking when it
|
||||
// might not be strictly necessary.
|
||||
if (res_bit_offset % 8 == 0 and field_ty.bitSize(zcu) == field_ty.abiSize(zcu) * 8 and zcu.getTarget().cpu.arch.endian() == .little) {
|
||||
const byte_offset = res_bit_offset / 8;
|
||||
const new_align = Alignment.fromLog2Units(@ctz(byte_offset | parent_ptr_ty.ptrAlignment(zcu).toByteUnits().?));
|
||||
return .{ .byte_ptr = .{
|
||||
.offset = byte_offset,
|
||||
.alignment = new_align,
|
||||
} };
|
||||
}
|
||||
|
||||
return .{ .bit_ptr = .{
|
||||
return .{
|
||||
.host_size = res_host_size,
|
||||
.bit_offset = res_bit_offset,
|
||||
} };
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resolveLayout(ty: Type, pt: Zcu.PerThread) SemaError!void {
|
||||
|
|
|
|||
|
|
@ -2255,32 +2255,18 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
|
|||
});
|
||||
return parent_ptr.getOffsetPtr(byte_off, result_ty, pt);
|
||||
},
|
||||
.@"packed" => switch (aggregate_ty.packedStructFieldPtrInfo(parent_ptr_ty, field_idx, pt)) {
|
||||
.bit_ptr => |packed_offset| {
|
||||
const result_ty = try pt.ptrType(info: {
|
||||
var new = parent_ptr_info;
|
||||
new.packed_offset = packed_offset;
|
||||
new.child = field_ty.toIntern();
|
||||
if (new.flags.alignment == .none) {
|
||||
new.flags.alignment = try aggregate_ty.abiAlignmentSema(pt);
|
||||
}
|
||||
break :info new;
|
||||
});
|
||||
return pt.getCoerced(parent_ptr, result_ty);
|
||||
},
|
||||
.byte_ptr => |ptr_info| {
|
||||
const result_ty = try pt.ptrTypeSema(info: {
|
||||
var new = parent_ptr_info;
|
||||
new.child = field_ty.toIntern();
|
||||
new.packed_offset = .{
|
||||
.host_size = 0,
|
||||
.bit_offset = 0,
|
||||
};
|
||||
new.flags.alignment = ptr_info.alignment;
|
||||
break :info new;
|
||||
});
|
||||
return parent_ptr.getOffsetPtr(ptr_info.offset, result_ty, pt);
|
||||
},
|
||||
.@"packed" => {
|
||||
const packed_offset = aggregate_ty.packedStructFieldPtrInfo(parent_ptr_ty, field_idx, pt);
|
||||
const result_ty = try pt.ptrType(info: {
|
||||
var new = parent_ptr_info;
|
||||
new.packed_offset = packed_offset;
|
||||
new.child = field_ty.toIntern();
|
||||
if (new.flags.alignment == .none) {
|
||||
new.flags.alignment = try aggregate_ty.abiAlignmentSema(pt);
|
||||
}
|
||||
break :info new;
|
||||
});
|
||||
return pt.getCoerced(parent_ptr, result_ty);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ const builtin = @import("builtin");
|
|||
const assert = std.debug.assert;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
|
||||
test "flags in packed structs" {
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
|
|
@ -163,26 +162,24 @@ test "correct sizeOf and offsets in packed structs" {
|
|||
try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));
|
||||
try expectEqual(4, @sizeOf(PStruct));
|
||||
|
||||
if (native_endian == .little) {
|
||||
const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));
|
||||
try expectEqual(false, s1.bool_a);
|
||||
try expectEqual(false, s1.bool_b);
|
||||
try expectEqual(false, s1.bool_c);
|
||||
try expectEqual(true, s1.bool_d);
|
||||
try expectEqual(true, s1.bool_e);
|
||||
try expectEqual(true, s1.bool_f);
|
||||
try expectEqual(1, s1.u1_a);
|
||||
try expectEqual(false, s1.bool_g);
|
||||
try expectEqual(0, s1.u1_b);
|
||||
try expectEqual(3, s1.u3_a);
|
||||
try expectEqual(0b1101000101, s1.u10_a);
|
||||
try expectEqual(0b0001001000, s1.u10_b);
|
||||
const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));
|
||||
try expectEqual(false, s1.bool_a);
|
||||
try expectEqual(false, s1.bool_b);
|
||||
try expectEqual(false, s1.bool_c);
|
||||
try expectEqual(true, s1.bool_d);
|
||||
try expectEqual(true, s1.bool_e);
|
||||
try expectEqual(true, s1.bool_f);
|
||||
try expectEqual(1, s1.u1_a);
|
||||
try expectEqual(false, s1.bool_g);
|
||||
try expectEqual(0, s1.u1_b);
|
||||
try expectEqual(3, s1.u3_a);
|
||||
try expectEqual(0b1101000101, s1.u10_a);
|
||||
try expectEqual(0b0001001000, s1.u10_b);
|
||||
|
||||
const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
|
||||
try expectEqual(0, s2.x);
|
||||
try expectEqual(0b1111010, s2.y);
|
||||
try expectEqual(0xd5c71f, s2.z);
|
||||
}
|
||||
const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
|
||||
try expectEqual(0, s2.x);
|
||||
try expectEqual(0b1111010, s2.y);
|
||||
try expectEqual(0xd5c71f, s2.z);
|
||||
}
|
||||
|
||||
test "nested packed structs" {
|
||||
|
|
@ -202,15 +199,13 @@ test "nested packed structs" {
|
|||
try expectEqual(3, @offsetOf(S3, "y"));
|
||||
try expectEqual(24, @bitOffsetOf(S3, "y"));
|
||||
|
||||
if (native_endian == .little) {
|
||||
const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
|
||||
try expectEqual(0xf4, s3.x.a);
|
||||
try expectEqual(0x1f, s3.x.b);
|
||||
try expectEqual(0xc7, s3.x.c);
|
||||
try expectEqual(0xd5, s3.y.d);
|
||||
try expectEqual(0x52, s3.y.e);
|
||||
try expectEqual(0xe9, s3.y.f);
|
||||
}
|
||||
const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
|
||||
try expectEqual(0xf4, s3.x.a);
|
||||
try expectEqual(0x1f, s3.x.b);
|
||||
try expectEqual(0xc7, s3.x.c);
|
||||
try expectEqual(0xd5, s3.y.d);
|
||||
try expectEqual(0x52, s3.y.e);
|
||||
try expectEqual(0xe9, s3.y.f);
|
||||
|
||||
const S4 = packed struct { a: i32, b: i8 };
|
||||
const S5 = packed struct { a: i32, b: i8, c: S4 };
|
||||
|
|
@ -252,7 +247,6 @@ test "nested packed struct unaligned" {
|
|||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
|
||||
|
||||
const S1 = packed struct {
|
||||
a: u4,
|
||||
|
|
@ -344,21 +338,12 @@ test "byte-aligned field pointer offsets" {
|
|||
.c = 3,
|
||||
.d = 4,
|
||||
};
|
||||
switch (comptime builtin.cpu.arch.endian()) {
|
||||
.little => {
|
||||
comptime assert(@TypeOf(&a.a) == *align(4) u8);
|
||||
comptime assert(@TypeOf(&a.b) == *u8);
|
||||
comptime assert(@TypeOf(&a.c) == *align(2) u8);
|
||||
comptime assert(@TypeOf(&a.d) == *u8);
|
||||
},
|
||||
.big => {
|
||||
// TODO re-evaluate packed struct endianness
|
||||
comptime assert(@TypeOf(&a.a) == *align(4:0:4) u8);
|
||||
comptime assert(@TypeOf(&a.b) == *align(4:8:4) u8);
|
||||
comptime assert(@TypeOf(&a.c) == *align(4:16:4) u8);
|
||||
comptime assert(@TypeOf(&a.d) == *align(4:24:4) u8);
|
||||
},
|
||||
}
|
||||
|
||||
comptime assert(@TypeOf(&a.a) == *align(4:0:4) u8);
|
||||
comptime assert(@TypeOf(&a.b) == *align(4:8:4) u8);
|
||||
comptime assert(@TypeOf(&a.c) == *align(4:16:4) u8);
|
||||
comptime assert(@TypeOf(&a.d) == *align(4:24:4) u8);
|
||||
|
||||
try expect(a.a == 1);
|
||||
try expect(a.b == 2);
|
||||
try expect(a.c == 3);
|
||||
|
|
@ -392,16 +377,10 @@ test "byte-aligned field pointer offsets" {
|
|||
.a = 1,
|
||||
.b = 2,
|
||||
};
|
||||
switch (comptime builtin.cpu.arch.endian()) {
|
||||
.little => {
|
||||
comptime assert(@TypeOf(&b.a) == *align(4) u16);
|
||||
comptime assert(@TypeOf(&b.b) == *u16);
|
||||
},
|
||||
.big => {
|
||||
comptime assert(@TypeOf(&b.a) == *align(4:0:4) u16);
|
||||
comptime assert(@TypeOf(&b.b) == *align(4:16:4) u16);
|
||||
},
|
||||
}
|
||||
|
||||
comptime assert(@TypeOf(&b.a) == *align(4:0:4) u16);
|
||||
comptime assert(@TypeOf(&b.b) == *align(4:16:4) u16);
|
||||
|
||||
try expect(b.a == 1);
|
||||
try expect(b.b == 2);
|
||||
|
||||
|
|
@ -426,7 +405,6 @@ test "nested packed struct field pointers" {
|
|||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
|
||||
if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
|
||||
|
||||
const S2 = packed struct {
|
||||
base: u8,
|
||||
|
|
@ -483,7 +461,6 @@ test "@intFromPtr on a packed struct field" {
|
|||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
if (native_endian != .little) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
const P = packed struct {
|
||||
|
|
@ -498,14 +475,13 @@ test "@intFromPtr on a packed struct field" {
|
|||
.z = 0,
|
||||
};
|
||||
};
|
||||
try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 2);
|
||||
try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 0);
|
||||
}
|
||||
|
||||
test "@intFromPtr on a packed struct field unaligned and nested" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
|
||||
|
||||
const S1 = packed struct {
|
||||
a: u4,
|
||||
|
|
@ -565,16 +541,16 @@ test "@intFromPtr on a packed struct field unaligned and nested" {
|
|||
else => {},
|
||||
}
|
||||
try expect(@intFromPtr(&S2.s.base) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 1);
|
||||
try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 1);
|
||||
try expect(@intFromPtr(&S2.s.p0.c) - @intFromPtr(&S2.s) == 2);
|
||||
try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p0.c) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.bit0) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p1.a) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p2.a) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p2.b) - @intFromPtr(&S2.s) == 5);
|
||||
try expect(@intFromPtr(&S2.s.p3.a) - @intFromPtr(&S2.s) == 6);
|
||||
try expect(@intFromPtr(&S2.s.p3.b) - @intFromPtr(&S2.s) == 6);
|
||||
try expect(@intFromPtr(&S2.s.p3.c) - @intFromPtr(&S2.s) == 7);
|
||||
try expect(@intFromPtr(&S2.s.p2.b) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p3.a) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p3.b) - @intFromPtr(&S2.s) == 0);
|
||||
try expect(@intFromPtr(&S2.s.p3.c) - @intFromPtr(&S2.s) == 0);
|
||||
|
||||
const S3 = packed struct {
|
||||
pad: u8,
|
||||
|
|
@ -597,7 +573,7 @@ test "@intFromPtr on a packed struct field unaligned and nested" {
|
|||
comptime assert(@TypeOf(&S3.v0.s.v) == *align(4:10:4) u3);
|
||||
comptime assert(@TypeOf(&S3.v0.s.s.v) == *align(4:13:4) u2);
|
||||
comptime assert(@TypeOf(&S3.v0.s.s.s.bit0) == *align(4:15:4) u1);
|
||||
comptime assert(@TypeOf(&S3.v0.s.s.s.byte) == *align(2) u8);
|
||||
comptime assert(@TypeOf(&S3.v0.s.s.s.byte) == *align(4:16:4) u8);
|
||||
comptime assert(@TypeOf(&S3.v0.s.s.s.bit1) == *align(4:24:4) u1);
|
||||
try expect(@intFromPtr(&S3.v0.v) - @intFromPtr(&S3.v0) == 0);
|
||||
try expect(@intFromPtr(&S3.v0.s) - @intFromPtr(&S3.v0) == 0);
|
||||
|
|
@ -606,7 +582,7 @@ test "@intFromPtr on a packed struct field unaligned and nested" {
|
|||
try expect(@intFromPtr(&S3.v0.s.s.v) - @intFromPtr(&S3.v0) == 0);
|
||||
try expect(@intFromPtr(&S3.v0.s.s.s) - @intFromPtr(&S3.v0) == 0);
|
||||
try expect(@intFromPtr(&S3.v0.s.s.s.bit0) - @intFromPtr(&S3.v0) == 0);
|
||||
try expect(@intFromPtr(&S3.v0.s.s.s.byte) - @intFromPtr(&S3.v0) == 2);
|
||||
try expect(@intFromPtr(&S3.v0.s.s.s.byte) - @intFromPtr(&S3.v0) == 0);
|
||||
try expect(@intFromPtr(&S3.v0.s.s.s.bit1) - @intFromPtr(&S3.v0) == 0);
|
||||
}
|
||||
|
||||
|
|
@ -915,17 +891,8 @@ test "overaligned pointer to packed struct" {
|
|||
const S = packed struct { a: u32, b: u32 };
|
||||
var foo: S align(4) = .{ .a = 123, .b = 456 };
|
||||
const ptr: *align(4) S = &foo;
|
||||
switch (comptime builtin.cpu.arch.endian()) {
|
||||
.little => {
|
||||
const ptr_to_b: *u32 = &ptr.b;
|
||||
try expect(ptr_to_b.* == 456);
|
||||
},
|
||||
.big => {
|
||||
// Byte aligned packed struct field pointers have not been implemented yet.
|
||||
const ptr_to_a: *align(4:0:8) u32 = &ptr.a;
|
||||
try expect(ptr_to_a.* == 123);
|
||||
},
|
||||
}
|
||||
const ptr_to_a: *align(4:0:8) u32 = &ptr.a;
|
||||
try expect(ptr_to_a.* == 123);
|
||||
}
|
||||
|
||||
test "packed struct initialized in bitcast" {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue