Commit 5cbb35abd0
Changed files (5)
src/Sema.zig
@@ -10993,12 +10993,16 @@ fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
}
fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirBitOffsetOf", .{});
+ const offset = try bitOffsetOf(sema, block, inst);
+ return sema.addIntUnsigned(Type.comptime_int, offset);
}
fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const offset = try bitOffsetOf(sema, block, inst);
+ return sema.addIntUnsigned(Type.comptime_int, offset / 8);
+}
+
+fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
sema.src = .{ .node_offset_bin_op = inst_data.src_node };
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -11028,8 +11032,24 @@ fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
};
const target = sema.mod.getTarget();
- const offset = ty.structFieldOffset(index, target);
- return sema.addIntUnsigned(Type.comptime_int, offset);
+ const layout = ty.containerLayout();
+ if (layout == .Packed) {
+ var it = ty.iteratePackedStructOffsets(target);
+ while (it.next()) |field_offset| {
+ if (field_offset.field == index) {
+ return (field_offset.offset * 8) + field_offset.running_bits;
+ }
+ }
+ } else {
+ var it = ty.iterateStructOffsets(target);
+ while (it.next()) |field_offset| {
+ if (field_offset.field == index) {
+ return field_offset.offset * 8;
+ }
+ }
+ }
+
+ unreachable;
}
/// Returns `true` if the type was a comptime_int.
src/type.zig
@@ -2922,6 +2922,15 @@ pub const Type = extern union {
}
}
+ pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
+ return switch (ty.tag()) {
+ .@"struct" => ty.castTag(.@"struct").?.data.layout,
+ .@"union" => ty.castTag(.@"union").?.data.layout,
+ .union_tagged => ty.castTag(.union_tagged).?.data.layout,
+ else => unreachable,
+ };
+ }
+
/// Asserts that the type is an error union.
pub fn errorUnionPayload(self: Type) Type {
return switch (self.tag()) {
@@ -3765,6 +3774,116 @@ pub const Type = extern union {
}
}
+ pub const PackedFieldOffset = struct {
+ field: usize,
+ offset: u64,
+ running_bits: u16,
+ };
+
+ pub const PackedStructOffsetIterator = struct {
+ field: usize = 0,
+ offset: u64 = 0,
+ big_align: u32 = 0,
+ running_bits: u16 = 0,
+ struct_obj: *Module.Struct,
+ target: Target,
+
+ pub fn next(it: *PackedStructOffsetIterator) ?PackedFieldOffset {
+ comptime assert(Type.packed_struct_layout_version == 1);
+ if (it.struct_obj.fields.count() <= it.field)
+ return null;
+
+ const field = it.struct_obj.fields.values()[it.field];
+ defer it.field += 1;
+ if (!field.ty.hasCodeGenBits()) {
+ return PackedFieldOffset{
+ .field = it.field,
+ .offset = it.offset,
+ .running_bits = it.running_bits,
+ };
+ }
+
+ const field_align = field.packedAlignment();
+ if (field_align == 0) {
+ defer it.running_bits += @intCast(u16, field.ty.bitSize(it.target));
+ return PackedFieldOffset{
+ .field = it.field,
+ .offset = it.offset,
+ .running_bits = it.running_bits,
+ };
+ } else {
+ it.big_align = @maximum(it.big_align, field_align);
+
+ if (it.running_bits != 0) {
+ var int_payload: Payload.Bits = .{
+ .base = .{ .tag = .int_unsigned },
+ .data = it.running_bits,
+ };
+ const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
+ const int_align = int_ty.abiAlignment(it.target);
+ it.big_align = @maximum(it.big_align, int_align);
+ it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
+ it.offset += int_ty.abiSize(it.target);
+ it.running_bits = 0;
+ }
+ it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
+ defer it.offset += field.ty.abiSize(it.target);
+ return PackedFieldOffset{
+ .field = it.field,
+ .offset = it.offset,
+ .running_bits = it.running_bits,
+ };
+ }
+ }
+ };
+
+ /// Get an iterator that iterates over all the struct field, returning the field and
+ /// offset of that field. Asserts that the type is a none packed struct.
+ pub fn iteratePackedStructOffsets(ty: Type, target: Target) PackedStructOffsetIterator {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ assert(struct_obj.haveLayout());
+ assert(struct_obj.layout == .Packed);
+ return .{ .struct_obj = struct_obj, .target = target };
+ }
+
+ pub const FieldOffset = struct {
+ field: usize,
+ offset: u64,
+ };
+
+ pub const StructOffsetIterator = struct {
+ field: usize = 0,
+ offset: u64 = 0,
+ big_align: u32 = 0,
+ struct_obj: *Module.Struct,
+ target: Target,
+
+ pub fn next(it: *StructOffsetIterator) ?FieldOffset {
+ if (it.struct_obj.fields.count() <= it.field)
+ return null;
+
+ const field = it.struct_obj.fields.values()[it.field];
+ defer it.field += 1;
+ if (!field.ty.hasCodeGenBits())
+ return FieldOffset{ .field = it.field, .offset = it.offset };
+
+ const field_align = field.normalAlignment(it.target);
+ it.big_align = @maximum(it.big_align, field_align);
+ it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
+ defer it.offset += field.ty.abiSize(it.target);
+ return FieldOffset{ .field = it.field, .offset = it.offset };
+ }
+ };
+
+ /// Get an iterator that iterates over all the struct field, returning the field and
+ /// offset of that field. Asserts that the type is a none packed struct.
+ pub fn iterateStructOffsets(ty: Type, target: Target) StructOffsetIterator {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ assert(struct_obj.haveLayout());
+ assert(struct_obj.layout != .Packed);
+ return .{ .struct_obj = struct_obj, .target = target };
+ }
+
/// Supports structs and unions.
/// For packed structs, it returns the byte offset of the containing integer.
pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 {
@@ -3774,71 +3893,34 @@ pub const Type = extern union {
assert(struct_obj.haveLayout());
const is_packed = struct_obj.layout == .Packed;
if (!is_packed) {
- var offset: u64 = 0;
- var big_align: u32 = 0;
- for (struct_obj.fields.values()) |field, i| {
- if (!field.ty.hasCodeGenBits()) {
- if (i == index) return offset;
- continue;
- }
-
- const field_align = field.normalAlignment(target);
- big_align = @maximum(big_align, field_align);
- offset = std.mem.alignForwardGeneric(u64, offset, field_align);
- if (i == index) return offset;
- offset += field.ty.abiSize(target);
- }
- offset = std.mem.alignForwardGeneric(u64, offset, big_align);
- return offset;
- }
-
- comptime assert(Type.packed_struct_layout_version == 1);
- var offset: u64 = 0;
- var big_align: u32 = 0;
- var running_bits: u16 = 0;
- for (struct_obj.fields.values()) |field, i| {
- if (!field.ty.hasCodeGenBits()) {
- if (i == index) return offset;
- continue;
+ var it = ty.iterateStructOffsets(target);
+ while (it.next()) |field_offset| {
+ if (index == field_offset.field)
+ return field_offset.offset;
}
- const field_align = field.packedAlignment();
- if (field_align == 0) {
- if (i == index) return offset;
- running_bits += @intCast(u16, field.ty.bitSize(target));
- } else {
- big_align = @maximum(big_align, field_align);
+ return std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
+ }
- if (running_bits != 0) {
- var int_payload: Payload.Bits = .{
- .base = .{ .tag = .int_unsigned },
- .data = running_bits,
- };
- const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
- const int_align = int_ty.abiAlignment(target);
- big_align = @maximum(big_align, int_align);
- offset = std.mem.alignForwardGeneric(u64, offset, int_align);
- offset += int_ty.abiSize(target);
- running_bits = 0;
- }
- offset = std.mem.alignForwardGeneric(u64, offset, field_align);
- if (i == index) return offset;
- offset += field.ty.abiSize(target);
- }
+ var it = ty.iteratePackedStructOffsets(target);
+ while (it.next()) |field_offset| {
+ if (index == field_offset.field)
+ return field_offset.offset;
}
- if (running_bits != 0) {
+
+ if (it.running_bits != 0) {
var int_payload: Payload.Bits = .{
.base = .{ .tag = .int_unsigned },
- .data = running_bits,
+ .data = it.running_bits,
};
const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
const int_align = int_ty.abiAlignment(target);
- big_align = @maximum(big_align, int_align);
- offset = std.mem.alignForwardGeneric(u64, offset, int_align);
- offset += int_ty.abiSize(target);
+ it.big_align = @maximum(it.big_align, int_align);
+ it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
+ it.offset += int_ty.abiSize(target);
}
- offset = std.mem.alignForwardGeneric(u64, offset, big_align);
- return offset;
+ it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
+ return it.offset;
},
.@"union" => return 0,
.union_tagged => {
test/behavior/sizeof_and_typeof.zig
@@ -47,3 +47,116 @@ fn fn1(alpha: bool) void {
test "lazy @sizeOf result is checked for definedness" {
_ = fn1;
}
+
+const A = struct {
+ a: u8,
+ b: u32,
+ c: u8,
+ d: u3,
+ e: u5,
+ f: u16,
+ g: u16,
+ h: u9,
+ i: u7,
+};
+
+const P = packed struct {
+ a: u8,
+ b: u32,
+ c: u8,
+ d: u3,
+ e: u5,
+ f: u16,
+ g: u16,
+ h: u9,
+ i: u7,
+};
+
+test "@offsetOf" {
+
+ // Packed structs have fixed memory layout
+ try expect(@offsetOf(P, "a") == 0);
+ try expect(@offsetOf(P, "b") == 1);
+ try expect(@offsetOf(P, "c") == 5);
+ try expect(@offsetOf(P, "d") == 6);
+ try expect(@offsetOf(P, "e") == 6);
+ try expect(@offsetOf(P, "f") == 7);
+ try expect(@offsetOf(P, "g") == 9);
+ try expect(@offsetOf(P, "h") == 11);
+ try expect(@offsetOf(P, "i") == 12);
+
+ // // Normal struct fields can be moved/padded
+ var a: A = undefined;
+ try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
+ try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
+ try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
+ try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
+ try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
+ try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
+ try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
+ try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
+ try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
+}
+
+test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
+ const p3a_len = 3;
+ const P3 = packed struct {
+ a: [p3a_len]u8,
+ b: usize,
+ };
+ try std.testing.expect(0 == @offsetOf(P3, "a"));
+ try std.testing.expect(p3a_len == @offsetOf(P3, "b"));
+
+ const p5a_len = 5;
+ const P5 = packed struct {
+ a: [p5a_len]u8,
+ b: usize,
+ };
+ try std.testing.expect(0 == @offsetOf(P5, "a"));
+ try std.testing.expect(p5a_len == @offsetOf(P5, "b"));
+
+ const p6a_len = 6;
+ const P6 = packed struct {
+ a: [p6a_len]u8,
+ b: usize,
+ };
+ try std.testing.expect(0 == @offsetOf(P6, "a"));
+ try std.testing.expect(p6a_len == @offsetOf(P6, "b"));
+
+ const p7a_len = 7;
+ const P7 = packed struct {
+ a: [p7a_len]u8,
+ b: usize,
+ };
+ try std.testing.expect(0 == @offsetOf(P7, "a"));
+ try std.testing.expect(p7a_len == @offsetOf(P7, "b"));
+
+ const p9a_len = 9;
+ const P9 = packed struct {
+ a: [p9a_len]u8,
+ b: usize,
+ };
+ try std.testing.expect(0 == @offsetOf(P9, "a"));
+ try std.testing.expect(p9a_len == @offsetOf(P9, "b"));
+
+ // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
+}
+
+test "@bitOffsetOf" {
+ // Packed structs have fixed memory layout
+ try expect(@bitOffsetOf(P, "a") == 0);
+ try expect(@bitOffsetOf(P, "b") == 8);
+ try expect(@bitOffsetOf(P, "c") == 40);
+ try expect(@bitOffsetOf(P, "d") == 48);
+ try expect(@bitOffsetOf(P, "e") == 51);
+ try expect(@bitOffsetOf(P, "f") == 56);
+ try expect(@bitOffsetOf(P, "g") == 72);
+
+ try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
+ try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
+ try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
+ try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
+ try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
+ try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
+ try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
+}
test/behavior/sizeof_and_typeof_stage1.zig
@@ -2,118 +2,6 @@ const std = @import("std");
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
-const A = struct {
- a: u8,
- b: u32,
- c: u8,
- d: u3,
- e: u5,
- f: u16,
- g: u16,
- h: u9,
- i: u7,
-};
-
-const P = packed struct {
- a: u8,
- b: u32,
- c: u8,
- d: u3,
- e: u5,
- f: u16,
- g: u16,
- h: u9,
- i: u7,
-};
-
-test "@offsetOf" {
- // Packed structs have fixed memory layout
- try expect(@offsetOf(P, "a") == 0);
- try expect(@offsetOf(P, "b") == 1);
- try expect(@offsetOf(P, "c") == 5);
- try expect(@offsetOf(P, "d") == 6);
- try expect(@offsetOf(P, "e") == 6);
- try expect(@offsetOf(P, "f") == 7);
- try expect(@offsetOf(P, "g") == 9);
- try expect(@offsetOf(P, "h") == 11);
- try expect(@offsetOf(P, "i") == 12);
-
- // Normal struct fields can be moved/padded
- var a: A = undefined;
- try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
- try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
- try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
- try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
- try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
- try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
- try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
- try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
- try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
-}
-
-test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
- const p3a_len = 3;
- const P3 = packed struct {
- a: [p3a_len]u8,
- b: usize,
- };
- try std.testing.expectEqual(0, @offsetOf(P3, "a"));
- try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b"));
-
- const p5a_len = 5;
- const P5 = packed struct {
- a: [p5a_len]u8,
- b: usize,
- };
- try std.testing.expectEqual(0, @offsetOf(P5, "a"));
- try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b"));
-
- const p6a_len = 6;
- const P6 = packed struct {
- a: [p6a_len]u8,
- b: usize,
- };
- try std.testing.expectEqual(0, @offsetOf(P6, "a"));
- try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b"));
-
- const p7a_len = 7;
- const P7 = packed struct {
- a: [p7a_len]u8,
- b: usize,
- };
- try std.testing.expectEqual(0, @offsetOf(P7, "a"));
- try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b"));
-
- const p9a_len = 9;
- const P9 = packed struct {
- a: [p9a_len]u8,
- b: usize,
- };
- try std.testing.expectEqual(0, @offsetOf(P9, "a"));
- try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b"));
-
- // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
-}
-
-test "@bitOffsetOf" {
- // Packed structs have fixed memory layout
- try expect(@bitOffsetOf(P, "a") == 0);
- try expect(@bitOffsetOf(P, "b") == 8);
- try expect(@bitOffsetOf(P, "c") == 40);
- try expect(@bitOffsetOf(P, "d") == 48);
- try expect(@bitOffsetOf(P, "e") == 51);
- try expect(@bitOffsetOf(P, "f") == 56);
- try expect(@bitOffsetOf(P, "g") == 72);
-
- try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
- try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
- try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
- try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
- try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
- try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
- try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
-}
-
test "@sizeOf(T) == 0 doesn't force resolving struct size" {
const S = struct {
const Foo = struct {
test/behavior.zig
@@ -80,6 +80,7 @@ test {
_ = @import("behavior/bugs/1310.zig");
_ = @import("behavior/bugs/1381.zig");
_ = @import("behavior/bugs/1500.zig");
+ _ = @import("behavior/bugs/1735.zig");
_ = @import("behavior/bugs/1741.zig");
_ = @import("behavior/bugs/2006.zig");
_ = @import("behavior/bugs/2578.zig");
@@ -96,6 +97,7 @@ test {
_ = @import("behavior/generics_llvm.zig");
_ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
+ _ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/namespace_depends_on_compile_var.zig");
_ = @import("behavior/null_llvm.zig");
_ = @import("behavior/optional_llvm.zig");
@@ -135,7 +137,6 @@ test {
_ = @import("behavior/bugs/1421.zig");
_ = @import("behavior/bugs/1442.zig");
_ = @import("behavior/bugs/1607.zig");
- _ = @import("behavior/bugs/1735.zig");
_ = @import("behavior/bugs/1851.zig");
_ = @import("behavior/bugs/1914.zig");
_ = @import("behavior/bugs/2114.zig");
@@ -169,7 +170,6 @@ test {
_ = @import("behavior/if_stage1.zig");
_ = @import("behavior/ir_block_deps.zig");
_ = @import("behavior/math_stage1.zig");
- _ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/misc.zig");
_ = @import("behavior/muladd.zig");
_ = @import("behavior/null_stage1.zig");