authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-11 14:23:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-14 21:42:43-07:00
log1f76b4c6b84c1fd7d370bde75b8e37822bd85086
tree09d06339aba8246977d7c9cc3e79b60c14e2f9c2
parent5fa057053ce32274b878077e5abff82335530fc8

stage2 llvm: Respect container type when lowering parent pointers

We need to make sure that we bitcast our pointers correctly before we use get_element_ptr to compute the offset for the parent pointer. This also includes a small fix-up for a problem where ptrs to const i64/u64 were not using the correct type in >1-level decl chains (where we call lowerParentPtr recursively)

3 files changed, 155 insertions(+), 101 deletions(-)

src/codegen/llvm.zig+61-99
......@@ -1518,26 +1518,8 @@ pub const DeclGen = struct {
15181518 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
15191519 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));
15201520 },
1521 .field_ptr, .opt_payload_ptr, .eu_payload_ptr => {
1522 const parent = try dg.lowerParentPtr(tv.val, tv.ty);
1523 return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
1524 },
1525 .elem_ptr => {
1526 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
1527 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, tv.ty);
1528 const llvm_usize = try dg.llvmType(Type.usize);
1529 if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) {
1530 const indices: [2]*const llvm.Value = .{
1531 llvm_usize.constInt(0, .False),
1532 llvm_usize.constInt(elem_ptr.index, .False),
1533 };
1534 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1535 } else {
1536 const indices: [1]*const llvm.Value = .{
1537 llvm_usize.constInt(elem_ptr.index, .False),
1538 };
1539 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1540 }
1521 .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => {
1522 return dg.lowerParentPtr(tv.val, tv.ty.childType());
15411523 },
15421524 .null_value, .zero => {
15431525 const llvm_type = try dg.llvmType(tv.ty);
......@@ -2786,7 +2768,7 @@ pub const DeclGen = struct {
27862768 llvm_ptr: *const llvm.Value,
27872769 };
27882770
2789 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr {
2771 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl, ptr_child_ty: Type) Error!*const llvm.Value {
27902772 decl.markAlive();
27912773 var ptr_ty_payload: Type.Payload.ElemType = .{
27922774 .base = .{ .tag = .single_mut_pointer },
......@@ -2794,123 +2776,104 @@ pub const DeclGen = struct {
27942776 };
27952777 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
27962778 const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
2797 return ParentPtr{
2798 .llvm_ptr = llvm_ptr,
2799 .ty = decl.ty,
2800 };
2779
2780 if (ptr_child_ty.eql(decl.ty)) {
2781 return llvm_ptr;
2782 } else {
2783 return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
2784 }
28012785 }
28022786
2803 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, base_ty: Type) Error!ParentPtr {
2804 switch (ptr_val.tag()) {
2787 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, ptr_child_ty: Type) Error!*const llvm.Value {
2788 var bitcast_needed: bool = undefined;
2789 const llvm_ptr = switch (ptr_val.tag()) {
28052790 .decl_ref_mut => {
28062791 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
2807 return dg.lowerParentPtrDecl(ptr_val, decl);
2792 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
28082793 },
28092794 .decl_ref => {
28102795 const decl = ptr_val.castTag(.decl_ref).?.data;
2811 return dg.lowerParentPtrDecl(ptr_val, decl);
2796 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
28122797 },
28132798 .variable => {
28142799 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
2815 return dg.lowerParentPtrDecl(ptr_val, decl);
2800 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
28162801 },
28172802 .int_i64 => {
28182803 const int = ptr_val.castTag(.int_i64).?.data;
28192804 const llvm_usize = try dg.llvmType(Type.usize);
28202805 const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False);
2821 return ParentPtr{
2822 .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
2823 .ty = base_ty,
2824 };
2806 return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
28252807 },
28262808 .int_u64 => {
28272809 const int = ptr_val.castTag(.int_u64).?.data;
28282810 const llvm_usize = try dg.llvmType(Type.usize);
28292811 const llvm_int = llvm_usize.constInt(int, .False);
2830 return ParentPtr{
2831 .llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
2832 .ty = base_ty,
2833 };
2812 return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
28342813 },
2835 .field_ptr => {
2814 .field_ptr => blk: {
28362815 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
2837 const parent = try dg.lowerParentPtr(field_ptr.container_ptr, base_ty);
2816 const parent_llvm_ptr = try dg.lowerParentPtr(field_ptr.container_ptr, field_ptr.container_ty);
2817 const parent_ty = field_ptr.container_ty;
2818
28382819 const field_index = @intCast(u32, field_ptr.field_index);
28392820 const llvm_u32 = dg.context.intType(32);
28402821 const target = dg.module.getTarget();
2841 switch (parent.ty.zigTypeTag()) {
2822 switch (parent_ty.zigTypeTag()) {
28422823 .Union => {
2843 const fields = parent.ty.unionFields();
2844 const layout = parent.ty.unionGetLayout(target);
2845 const field_ty = fields.values()[field_index].ty;
2824 bitcast_needed = true;
2825
2826 const layout = parent_ty.unionGetLayout(target);
28462827 if (layout.payload_size == 0) {
28472828 // In this case a pointer to the union and a pointer to any
28482829 // (void) payload is the same.
2849 return ParentPtr{
2850 .llvm_ptr = parent.llvm_ptr,
2851 .ty = field_ty,
2852 };
2853 }
2854 if (layout.tag_size == 0) {
2855 const indices: [2]*const llvm.Value = .{
2856 llvm_u32.constInt(0, .False),
2857 llvm_u32.constInt(0, .False),
2858 };
2859 return ParentPtr{
2860 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2861 .ty = field_ty,
2862 };
2830 break :blk parent_llvm_ptr;
28632831 }
2864 const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align);
2832 const llvm_pl_index = if (layout.tag_size == 0) 0 else @boolToInt(layout.tag_align >= layout.payload_align);
28652833 const indices: [2]*const llvm.Value = .{
28662834 llvm_u32.constInt(0, .False),
28672835 llvm_u32.constInt(llvm_pl_index, .False),
28682836 };
2869 return ParentPtr{
2870 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2871 .ty = field_ty,
2872 };
2837 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
28732838 },
28742839 .Struct => {
2840 const field_ty = parent_ty.structFieldType(field_index);
2841 bitcast_needed = !field_ty.eql(ptr_child_ty);
2842
28752843 var ty_buf: Type.Payload.Pointer = undefined;
2876 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
2844 const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?;
28772845 const indices: [2]*const llvm.Value = .{
28782846 llvm_u32.constInt(0, .False),
28792847 llvm_u32.constInt(llvm_field_index, .False),
28802848 };
2881 return ParentPtr{
2882 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2883 .ty = parent.ty.structFieldType(field_index),
2884 };
2849 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
28852850 },
28862851 else => unreachable,
28872852 }
28882853 },
2889 .elem_ptr => {
2854 .elem_ptr => blk: {
28902855 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2891 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, base_ty);
2856 const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
2857 bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty);
2858
28922859 const llvm_usize = try dg.llvmType(Type.usize);
2893 const indices: [2]*const llvm.Value = .{
2894 llvm_usize.constInt(0, .False),
2860 const indices: [1]*const llvm.Value = .{
28952861 llvm_usize.constInt(elem_ptr.index, .False),
28962862 };
2897 return ParentPtr{
2898 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2899 .ty = parent.ty.childType(),
2900 };
2863 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
29012864 },
2902 .opt_payload_ptr => {
2865 .opt_payload_ptr => blk: {
29032866 const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2904 const parent = try dg.lowerParentPtr(opt_payload_ptr.container_ptr, base_ty);
2867 const parent_llvm_ptr = try dg.lowerParentPtr(opt_payload_ptr.container_ptr, opt_payload_ptr.container_ty);
29052868 var buf: Type.Payload.ElemType = undefined;
2906 const payload_ty = parent.ty.optionalChild(&buf);
2907 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or parent.ty.isPtrLikeOptional()) {
2869
2870 const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf);
2871 bitcast_needed = !payload_ty.eql(ptr_child_ty);
2872
2873 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
29082874 // In this case, we represent pointer to optional the same as pointer
29092875 // to the payload.
2910 return ParentPtr{
2911 .llvm_ptr = parent.llvm_ptr,
2912 .ty = payload_ty,
2913 };
2876 break :blk parent_llvm_ptr;
29142877 }
29152878
29162879 const llvm_u32 = dg.context.intType(32);
......@@ -2918,22 +2881,19 @@ pub const DeclGen = struct {
29182881 llvm_u32.constInt(0, .False),
29192882 llvm_u32.constInt(0, .False),
29202883 };
2921 return ParentPtr{
2922 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2923 .ty = payload_ty,
2924 };
2884 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
29252885 },
2926 .eu_payload_ptr => {
2886 .eu_payload_ptr => blk: {
29272887 const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
2928 const parent = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, base_ty);
2929 const payload_ty = parent.ty.errorUnionPayload();
2888 const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty);
2889
2890 const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload();
2891 bitcast_needed = !payload_ty.eql(ptr_child_ty);
2892
29302893 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
29312894 // In this case, we represent pointer to error union the same as pointer
29322895 // to the payload.
2933 return ParentPtr{
2934 .llvm_ptr = parent.llvm_ptr,
2935 .ty = payload_ty,
2936 };
2896 break :blk parent_llvm_ptr;
29372897 }
29382898
29392899 const llvm_u32 = dg.context.intType(32);
......@@ -2941,12 +2901,14 @@ pub const DeclGen = struct {
29412901 llvm_u32.constInt(0, .False),
29422902 llvm_u32.constInt(1, .False),
29432903 };
2944 return ParentPtr{
2945 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
2946 .ty = payload_ty,
2947 };
2904 break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
29482905 },
29492906 else => unreachable,
2907 };
2908 if (bitcast_needed) {
2909 return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
2910 } else {
2911 return llvm_ptr;
29502912 }
29512913 }
29522914
test/behavior/cast.zig+3-1
......@@ -887,7 +887,9 @@ test "peer cast *[N:x]T to *[N]T" {
887887}
888888
889889test "peer cast [*:x]T to [*]T" {
890 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
890 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
891 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
892 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
891893
892894 const S = struct {
893895 fn doTheTest() !void {
test/behavior/ptrcast.zig+91-1
......@@ -21,8 +21,47 @@ fn testReinterpretBytesAsInteger() !void {
2121 try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
2222}
2323
24test "reinterpret an array over multiple elements, with no well-defined layout" {
25 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
26 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
27 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
28 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
29
30 try testReinterpretWithOffsetAndNoWellDefinedLayout();
31 comptime try testReinterpretWithOffsetAndNoWellDefinedLayout();
32}
33
34fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
35 const bytes: ?[5]?u8 = [5]?u8{ 0x12, 0x34, 0x56, 0x78, 0x9a };
36 const ptr = &bytes.?[1];
37 const copy: [4]?u8 = @ptrCast(*const [4]?u8, ptr).*;
38 _ = copy;
39 //try expect(@ptrCast(*align(1)?u8, bytes[1..5]).* == );
40}
41
42test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
43 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
44 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
45 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
46
47 try testReinterpretStructWrappedBytesAsInteger();
48 comptime try testReinterpretStructWrappedBytesAsInteger();
49}
50
51fn testReinterpretStructWrappedBytesAsInteger() !void {
52 const S = struct { bytes: [5:0]u8 };
53 const obj = S{ .bytes = "\x12\x34\x56\x78\xab".* };
54 const expected = switch (native_endian) {
55 .Little => 0xab785634,
56 .Big => 0x345678ab,
57 };
58 try expect(@ptrCast(*align(1) const u32, obj.bytes[1..5]).* == expected);
59}
60
2461test "reinterpret bytes of an array into an extern struct" {
25 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
62 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
64 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
2665
2766 try testReinterpretBytesAsExternStruct();
2867 comptime try testReinterpretBytesAsExternStruct();
......@@ -42,6 +81,57 @@ fn testReinterpretBytesAsExternStruct() !void {
4281 try expect(val == 5);
4382}
4483
84test "reinterpret bytes of an extern struct into another" {
85 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
88
89 try testReinterpretExternStructAsExternStruct();
90 comptime try testReinterpretExternStructAsExternStruct();
91}
92
93fn testReinterpretExternStructAsExternStruct() !void {
94 const S1 = extern struct {
95 a: u8,
96 b: u16,
97 c: u8,
98 };
99 comptime var bytes align(2) = S1{ .a = 0, .b = 0, .c = 5 };
100
101 const S2 = extern struct {
102 a: u32 align(2),
103 c: u8,
104 };
105 var ptr = @ptrCast(*const S2, &bytes);
106 var val = ptr.c;
107 try expect(val == 5);
108}
109
110test "lower reinterpreted comptime field ptr" {
111 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
112 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
113 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
114 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
115 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
117
118 // Test lowering a field ptr
119 comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
120 const S = extern struct {
121 a: u32 align(2),
122 c: u8,
123 };
124 comptime var ptr = @ptrCast(*const S, &bytes);
125 var val = &ptr.c;
126 try expect(val.* == 5);
127
128 // Test lowering an elem ptr
129 comptime var src_value = S{ .a = 15, .c = 5 };
130 comptime var ptr2 = @ptrCast(*[@sizeOf(S)]u8, &src_value);
131 var val2 = &ptr2[4];
132 try expect(val2.* == 5);
133}
134
45135test "reinterpret struct field at comptime" {
46136 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
47137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO