authorgravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-08 13:57:22+01:00
committergravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-08 14:01:28+01:00
log5cbb35abd06f7975c17b0a188b7a1b88e08dbd1e
tree8d1333ace9bceffc955f651c54dead2d2c15b5c6
parentd8d5e2d4b959f9627853a9f1248609929f0f0de4

Implement bitOffsetOf

This also refactors getting struct field offsets into two iterators. This will be useful when implementing bitCast at comptime on structs.

5 files changed, 277 insertions(+), 174 deletions(-)

src/Sema.zig+25-5
......@@ -10993,12 +10993,16 @@ fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1099310993}
1099410994
1099510995fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10996 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
10997 const src = inst_data.src();
10998 return sema.fail(block, src, "TODO: Sema.zirBitOffsetOf", .{});
10996 const offset = try bitOffsetOf(sema, block, inst);
10997 return sema.addIntUnsigned(Type.comptime_int, offset);
1099910998}
1100010999
1100111000fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11001 const offset = try bitOffsetOf(sema, block, inst);
11002 return sema.addIntUnsigned(Type.comptime_int, offset / 8);
11003}
11004
11005fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
1100211006 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1100311007 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
1100411008 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
1102811032 };
1102911033
1103011034 const target = sema.mod.getTarget();
11031 const offset = ty.structFieldOffset(index, target);
11032 return sema.addIntUnsigned(Type.comptime_int, offset);
11035 const layout = ty.containerLayout();
11036 if (layout == .Packed) {
11037 var it = ty.iteratePackedStructOffsets(target);
11038 while (it.next()) |field_offset| {
11039 if (field_offset.field == index) {
11040 return (field_offset.offset * 8) + field_offset.running_bits;
11041 }
11042 }
11043 } else {
11044 var it = ty.iterateStructOffsets(target);
11045 while (it.next()) |field_offset| {
11046 if (field_offset.field == index) {
11047 return field_offset.offset * 8;
11048 }
11049 }
11050 }
11051
11052 unreachable;
1103311053}
1103411054
1103511055/// Returns `true` if the type was a comptime_int.
src/type.zig+137-55
......@@ -2922,6 +2922,15 @@ pub const Type = extern union {
29222922 }
29232923 }
29242924
2925 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
2926 return switch (ty.tag()) {
2927 .@"struct" => ty.castTag(.@"struct").?.data.layout,
2928 .@"union" => ty.castTag(.@"union").?.data.layout,
2929 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
2930 else => unreachable,
2931 };
2932 }
2933
29252934 /// Asserts that the type is an error union.
29262935 pub fn errorUnionPayload(self: Type) Type {
29272936 return switch (self.tag()) {
......@@ -3765,6 +3774,116 @@ pub const Type = extern union {
37653774 }
37663775 }
37673776
3777 pub const PackedFieldOffset = struct {
3778 field: usize,
3779 offset: u64,
3780 running_bits: u16,
3781 };
3782
3783 pub const PackedStructOffsetIterator = struct {
3784 field: usize = 0,
3785 offset: u64 = 0,
3786 big_align: u32 = 0,
3787 running_bits: u16 = 0,
3788 struct_obj: *Module.Struct,
3789 target: Target,
3790
3791 pub fn next(it: *PackedStructOffsetIterator) ?PackedFieldOffset {
3792 comptime assert(Type.packed_struct_layout_version == 1);
3793 if (it.struct_obj.fields.count() <= it.field)
3794 return null;
3795
3796 const field = it.struct_obj.fields.values()[it.field];
3797 defer it.field += 1;
3798 if (!field.ty.hasCodeGenBits()) {
3799 return PackedFieldOffset{
3800 .field = it.field,
3801 .offset = it.offset,
3802 .running_bits = it.running_bits,
3803 };
3804 }
3805
3806 const field_align = field.packedAlignment();
3807 if (field_align == 0) {
3808 defer it.running_bits += @intCast(u16, field.ty.bitSize(it.target));
3809 return PackedFieldOffset{
3810 .field = it.field,
3811 .offset = it.offset,
3812 .running_bits = it.running_bits,
3813 };
3814 } else {
3815 it.big_align = @maximum(it.big_align, field_align);
3816
3817 if (it.running_bits != 0) {
3818 var int_payload: Payload.Bits = .{
3819 .base = .{ .tag = .int_unsigned },
3820 .data = it.running_bits,
3821 };
3822 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
3823 const int_align = int_ty.abiAlignment(it.target);
3824 it.big_align = @maximum(it.big_align, int_align);
3825 it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
3826 it.offset += int_ty.abiSize(it.target);
3827 it.running_bits = 0;
3828 }
3829 it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
3830 defer it.offset += field.ty.abiSize(it.target);
3831 return PackedFieldOffset{
3832 .field = it.field,
3833 .offset = it.offset,
3834 .running_bits = it.running_bits,
3835 };
3836 }
3837 }
3838 };
3839
3840 /// Get an iterator that iterates over all the struct field, returning the field and
3841 /// offset of that field. Asserts that the type is a none packed struct.
3842 pub fn iteratePackedStructOffsets(ty: Type, target: Target) PackedStructOffsetIterator {
3843 const struct_obj = ty.castTag(.@"struct").?.data;
3844 assert(struct_obj.haveLayout());
3845 assert(struct_obj.layout == .Packed);
3846 return .{ .struct_obj = struct_obj, .target = target };
3847 }
3848
3849 pub const FieldOffset = struct {
3850 field: usize,
3851 offset: u64,
3852 };
3853
3854 pub const StructOffsetIterator = struct {
3855 field: usize = 0,
3856 offset: u64 = 0,
3857 big_align: u32 = 0,
3858 struct_obj: *Module.Struct,
3859 target: Target,
3860
3861 pub fn next(it: *StructOffsetIterator) ?FieldOffset {
3862 if (it.struct_obj.fields.count() <= it.field)
3863 return null;
3864
3865 const field = it.struct_obj.fields.values()[it.field];
3866 defer it.field += 1;
3867 if (!field.ty.hasCodeGenBits())
3868 return FieldOffset{ .field = it.field, .offset = it.offset };
3869
3870 const field_align = field.normalAlignment(it.target);
3871 it.big_align = @maximum(it.big_align, field_align);
3872 it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
3873 defer it.offset += field.ty.abiSize(it.target);
3874 return FieldOffset{ .field = it.field, .offset = it.offset };
3875 }
3876 };
3877
3878 /// Get an iterator that iterates over all the struct field, returning the field and
3879 /// offset of that field. Asserts that the type is a none packed struct.
3880 pub fn iterateStructOffsets(ty: Type, target: Target) StructOffsetIterator {
3881 const struct_obj = ty.castTag(.@"struct").?.data;
3882 assert(struct_obj.haveLayout());
3883 assert(struct_obj.layout != .Packed);
3884 return .{ .struct_obj = struct_obj, .target = target };
3885 }
3886
37683887 /// Supports structs and unions.
37693888 /// For packed structs, it returns the byte offset of the containing integer.
37703889 pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 {
......@@ -3774,71 +3893,34 @@ pub const Type = extern union {
37743893 assert(struct_obj.haveLayout());
37753894 const is_packed = struct_obj.layout == .Packed;
37763895 if (!is_packed) {
3777 var offset: u64 = 0;
3778 var big_align: u32 = 0;
3779 for (struct_obj.fields.values()) |field, i| {
3780 if (!field.ty.hasCodeGenBits()) {
3781 if (i == index) return offset;
3782 continue;
3783 }
3784
3785 const field_align = field.normalAlignment(target);
3786 big_align = @maximum(big_align, field_align);
3787 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
3788 if (i == index) return offset;
3789 offset += field.ty.abiSize(target);
3790 }
3791 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
3792 return offset;
3793 }
3794
3795 comptime assert(Type.packed_struct_layout_version == 1);
3796 var offset: u64 = 0;
3797 var big_align: u32 = 0;
3798 var running_bits: u16 = 0;
3799 for (struct_obj.fields.values()) |field, i| {
3800 if (!field.ty.hasCodeGenBits()) {
3801 if (i == index) return offset;
3802 continue;
3896 var it = ty.iterateStructOffsets(target);
3897 while (it.next()) |field_offset| {
3898 if (index == field_offset.field)
3899 return field_offset.offset;
38033900 }
38043901
3805 const field_align = field.packedAlignment();
3806 if (field_align == 0) {
3807 if (i == index) return offset;
3808 running_bits += @intCast(u16, field.ty.bitSize(target));
3809 } else {
3810 big_align = @maximum(big_align, field_align);
3902 return std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3903 }
38113904
3812 if (running_bits != 0) {
3813 var int_payload: Payload.Bits = .{
3814 .base = .{ .tag = .int_unsigned },
3815 .data = running_bits,
3816 };
3817 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
3818 const int_align = int_ty.abiAlignment(target);
3819 big_align = @maximum(big_align, int_align);
3820 offset = std.mem.alignForwardGeneric(u64, offset, int_align);
3821 offset += int_ty.abiSize(target);
3822 running_bits = 0;
3823 }
3824 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
3825 if (i == index) return offset;
3826 offset += field.ty.abiSize(target);
3827 }
3905 var it = ty.iteratePackedStructOffsets(target);
3906 while (it.next()) |field_offset| {
3907 if (index == field_offset.field)
3908 return field_offset.offset;
38283909 }
3829 if (running_bits != 0) {
3910
3911 if (it.running_bits != 0) {
38303912 var int_payload: Payload.Bits = .{
38313913 .base = .{ .tag = .int_unsigned },
3832 .data = running_bits,
3914 .data = it.running_bits,
38333915 };
38343916 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
38353917 const int_align = int_ty.abiAlignment(target);
3836 big_align = @maximum(big_align, int_align);
3837 offset = std.mem.alignForwardGeneric(u64, offset, int_align);
3838 offset += int_ty.abiSize(target);
3918 it.big_align = @maximum(it.big_align, int_align);
3919 it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
3920 it.offset += int_ty.abiSize(target);
38393921 }
3840 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
3841 return offset;
3922 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3923 return it.offset;
38423924 },
38433925 .@"union" => return 0,
38443926 .union_tagged => {
test/behavior.zig+2-2
......@@ -80,6 +80,7 @@ test {
8080 _ = @import("behavior/bugs/1310.zig");
8181 _ = @import("behavior/bugs/1381.zig");
8282 _ = @import("behavior/bugs/1500.zig");
83 _ = @import("behavior/bugs/1735.zig");
8384 _ = @import("behavior/bugs/1741.zig");
8485 _ = @import("behavior/bugs/2006.zig");
8586 _ = @import("behavior/bugs/2578.zig");
......@@ -96,6 +97,7 @@ test {
9697 _ = @import("behavior/generics_llvm.zig");
9798 _ = @import("behavior/math.zig");
9899 _ = @import("behavior/maximum_minimum.zig");
100 _ = @import("behavior/merge_error_sets.zig");
99101 _ = @import("behavior/namespace_depends_on_compile_var.zig");
100102 _ = @import("behavior/null_llvm.zig");
101103 _ = @import("behavior/optional_llvm.zig");
......@@ -135,7 +137,6 @@ test {
135137 _ = @import("behavior/bugs/1421.zig");
136138 _ = @import("behavior/bugs/1442.zig");
137139 _ = @import("behavior/bugs/1607.zig");
138 _ = @import("behavior/bugs/1735.zig");
139140 _ = @import("behavior/bugs/1851.zig");
140141 _ = @import("behavior/bugs/1914.zig");
141142 _ = @import("behavior/bugs/2114.zig");
......@@ -169,7 +170,6 @@ test {
169170 _ = @import("behavior/if_stage1.zig");
170171 _ = @import("behavior/ir_block_deps.zig");
171172 _ = @import("behavior/math_stage1.zig");
172 _ = @import("behavior/merge_error_sets.zig");
173173 _ = @import("behavior/misc.zig");
174174 _ = @import("behavior/muladd.zig");
175175 _ = @import("behavior/null_stage1.zig");
test/behavior/sizeof_and_typeof.zig+113
......@@ -47,3 +47,116 @@ fn fn1(alpha: bool) void {
4747test "lazy @sizeOf result is checked for definedness" {
4848 _ = fn1;
4949}
50
51const A = struct {
52 a: u8,
53 b: u32,
54 c: u8,
55 d: u3,
56 e: u5,
57 f: u16,
58 g: u16,
59 h: u9,
60 i: u7,
61};
62
63const P = packed struct {
64 a: u8,
65 b: u32,
66 c: u8,
67 d: u3,
68 e: u5,
69 f: u16,
70 g: u16,
71 h: u9,
72 i: u7,
73};
74
75test "@offsetOf" {
76
77 // Packed structs have fixed memory layout
78 try expect(@offsetOf(P, "a") == 0);
79 try expect(@offsetOf(P, "b") == 1);
80 try expect(@offsetOf(P, "c") == 5);
81 try expect(@offsetOf(P, "d") == 6);
82 try expect(@offsetOf(P, "e") == 6);
83 try expect(@offsetOf(P, "f") == 7);
84 try expect(@offsetOf(P, "g") == 9);
85 try expect(@offsetOf(P, "h") == 11);
86 try expect(@offsetOf(P, "i") == 12);
87
88 // // Normal struct fields can be moved/padded
89 var a: A = undefined;
90 try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
91 try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
92 try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
93 try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
94 try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
95 try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
96 try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
97 try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
98 try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
99}
100
101test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
102 const p3a_len = 3;
103 const P3 = packed struct {
104 a: [p3a_len]u8,
105 b: usize,
106 };
107 try std.testing.expect(0 == @offsetOf(P3, "a"));
108 try std.testing.expect(p3a_len == @offsetOf(P3, "b"));
109
110 const p5a_len = 5;
111 const P5 = packed struct {
112 a: [p5a_len]u8,
113 b: usize,
114 };
115 try std.testing.expect(0 == @offsetOf(P5, "a"));
116 try std.testing.expect(p5a_len == @offsetOf(P5, "b"));
117
118 const p6a_len = 6;
119 const P6 = packed struct {
120 a: [p6a_len]u8,
121 b: usize,
122 };
123 try std.testing.expect(0 == @offsetOf(P6, "a"));
124 try std.testing.expect(p6a_len == @offsetOf(P6, "b"));
125
126 const p7a_len = 7;
127 const P7 = packed struct {
128 a: [p7a_len]u8,
129 b: usize,
130 };
131 try std.testing.expect(0 == @offsetOf(P7, "a"));
132 try std.testing.expect(p7a_len == @offsetOf(P7, "b"));
133
134 const p9a_len = 9;
135 const P9 = packed struct {
136 a: [p9a_len]u8,
137 b: usize,
138 };
139 try std.testing.expect(0 == @offsetOf(P9, "a"));
140 try std.testing.expect(p9a_len == @offsetOf(P9, "b"));
141
142 // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
143}
144
145test "@bitOffsetOf" {
146 // Packed structs have fixed memory layout
147 try expect(@bitOffsetOf(P, "a") == 0);
148 try expect(@bitOffsetOf(P, "b") == 8);
149 try expect(@bitOffsetOf(P, "c") == 40);
150 try expect(@bitOffsetOf(P, "d") == 48);
151 try expect(@bitOffsetOf(P, "e") == 51);
152 try expect(@bitOffsetOf(P, "f") == 56);
153 try expect(@bitOffsetOf(P, "g") == 72);
154
155 try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
156 try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
157 try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
158 try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
159 try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
160 try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
161 try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
162}
test/behavior/sizeof_and_typeof_stage1.zig-112
......@@ -2,118 +2,6 @@ const std = @import("std");
22const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44
5const A = struct {
6 a: u8,
7 b: u32,
8 c: u8,
9 d: u3,
10 e: u5,
11 f: u16,
12 g: u16,
13 h: u9,
14 i: u7,
15};
16
17const P = packed struct {
18 a: u8,
19 b: u32,
20 c: u8,
21 d: u3,
22 e: u5,
23 f: u16,
24 g: u16,
25 h: u9,
26 i: u7,
27};
28
29test "@offsetOf" {
30 // Packed structs have fixed memory layout
31 try expect(@offsetOf(P, "a") == 0);
32 try expect(@offsetOf(P, "b") == 1);
33 try expect(@offsetOf(P, "c") == 5);
34 try expect(@offsetOf(P, "d") == 6);
35 try expect(@offsetOf(P, "e") == 6);
36 try expect(@offsetOf(P, "f") == 7);
37 try expect(@offsetOf(P, "g") == 9);
38 try expect(@offsetOf(P, "h") == 11);
39 try expect(@offsetOf(P, "i") == 12);
40
41 // Normal struct fields can be moved/padded
42 var a: A = undefined;
43 try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
44 try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
45 try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
46 try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
47 try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
48 try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
49 try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
50 try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
51 try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
52}
53
54test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
55 const p3a_len = 3;
56 const P3 = packed struct {
57 a: [p3a_len]u8,
58 b: usize,
59 };
60 try std.testing.expectEqual(0, @offsetOf(P3, "a"));
61 try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b"));
62
63 const p5a_len = 5;
64 const P5 = packed struct {
65 a: [p5a_len]u8,
66 b: usize,
67 };
68 try std.testing.expectEqual(0, @offsetOf(P5, "a"));
69 try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b"));
70
71 const p6a_len = 6;
72 const P6 = packed struct {
73 a: [p6a_len]u8,
74 b: usize,
75 };
76 try std.testing.expectEqual(0, @offsetOf(P6, "a"));
77 try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b"));
78
79 const p7a_len = 7;
80 const P7 = packed struct {
81 a: [p7a_len]u8,
82 b: usize,
83 };
84 try std.testing.expectEqual(0, @offsetOf(P7, "a"));
85 try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b"));
86
87 const p9a_len = 9;
88 const P9 = packed struct {
89 a: [p9a_len]u8,
90 b: usize,
91 };
92 try std.testing.expectEqual(0, @offsetOf(P9, "a"));
93 try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b"));
94
95 // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
96}
97
98test "@bitOffsetOf" {
99 // Packed structs have fixed memory layout
100 try expect(@bitOffsetOf(P, "a") == 0);
101 try expect(@bitOffsetOf(P, "b") == 8);
102 try expect(@bitOffsetOf(P, "c") == 40);
103 try expect(@bitOffsetOf(P, "d") == 48);
104 try expect(@bitOffsetOf(P, "e") == 51);
105 try expect(@bitOffsetOf(P, "f") == 56);
106 try expect(@bitOffsetOf(P, "g") == 72);
107
108 try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
109 try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
110 try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
111 try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
112 try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
113 try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
114 try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
115}
116
1175test "@sizeOf(T) == 0 doesn't force resolving struct size" {
1186 const S = struct {
1197 const Foo = struct {