authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-15 17:17:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-15 17:17:59-07:00
log186126c2a4032424e1b1cdb8ac379fb2beab7429
tree32aa04aa0e19f7a9b4d51677c2051a3b7d7048c4
parent0536c25578fa15e2326eb1061f6db61d6ad3cd65

stage2: make hasCodeGenBits() always true for pointers

* LLVM backend: The `alloc` AIR instruction as well as pointer constants which point to a 0-bit element type now call a common codepath to produce a `*const llvm.Value` which is a non-zero pointer with a bogus-but-properly-aligned address. * LLVM backend: improve the lowering of optional types. * Type: `hasCodeGenBits()` now returns `true` for pointers even when it returns `false` for their element types. Effectively, #6706 is now implemented in stage2 but not stage1.

6 files changed, 83 insertions(+), 38 deletions(-)

src/codegen/llvm.zig+60-17
...@@ -757,11 +757,14 @@ pub const DeclGen = struct {...@@ -757,11 +757,14 @@ pub const DeclGen = struct {
757 try dg.llvmType(Type.usize),757 try dg.llvmType(Type.usize),
758 };758 };
759 return dg.context.structType(&fields, fields.len, .False);759 return dg.context.structType(&fields, fields.len, .False);
760 } else {
761 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
762 const llvm_elem_ty = try dg.llvmType(t.childType());
763 return llvm_elem_ty.pointerType(llvm_addrspace);
764 }760 }
761 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
762 const elem_ty = t.childType();
763 const llvm_elem_ty = if (elem_ty.hasCodeGenBits())
764 try dg.llvmType(elem_ty)
765 else
766 dg.context.intType(8);
767 return llvm_elem_ty.pointerType(llvm_addrspace);
765 },768 },
766 .Opaque => {769 .Opaque => {
767 const gop = try dg.object.type_map.getOrPut(gpa, t);770 const gop = try dg.object.type_map.getOrPut(gpa, t);
...@@ -791,10 +794,14 @@ pub const DeclGen = struct {...@@ -791,10 +794,14 @@ pub const DeclGen = struct {
791 .Optional => {794 .Optional => {
792 var buf: Type.Payload.ElemType = undefined;795 var buf: Type.Payload.ElemType = undefined;
793 const child_type = t.optionalChild(&buf);796 const child_type = t.optionalChild(&buf);
797 if (!child_type.hasCodeGenBits()) {
798 return dg.context.intType(1);
799 }
794 const payload_llvm_ty = try dg.llvmType(child_type);800 const payload_llvm_ty = try dg.llvmType(child_type);
795
796 if (t.isPtrLikeOptional()) {801 if (t.isPtrLikeOptional()) {
797 return payload_llvm_ty;802 return payload_llvm_ty;
803 } else if (!child_type.hasCodeGenBits()) {
804 return dg.context.intType(1);
798 }805 }
799806
800 const fields: [2]*const llvm.Type = .{807 const fields: [2]*const llvm.Type = .{
...@@ -826,7 +833,6 @@ pub const DeclGen = struct {...@@ -826,7 +833,6 @@ pub const DeclGen = struct {
826 gop.key_ptr.* = try t.copy(&dg.object.type_map_arena.allocator);833 gop.key_ptr.* = try t.copy(&dg.object.type_map_arena.allocator);
827834
828 const struct_obj = t.castTag(.@"struct").?.data;835 const struct_obj = t.castTag(.@"struct").?.data;
829 assert(struct_obj.haveFieldTypes());
830836
831 const name = try struct_obj.getFullyQualifiedName(gpa);837 const name = try struct_obj.getFullyQualifiedName(gpa);
832 defer gpa.free(name);838 defer gpa.free(name);
...@@ -834,6 +840,8 @@ pub const DeclGen = struct {...@@ -834,6 +840,8 @@ pub const DeclGen = struct {
834 const llvm_struct_ty = dg.context.structCreateNamed(name);840 const llvm_struct_ty = dg.context.structCreateNamed(name);
835 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls841 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
836842
843 assert(struct_obj.haveFieldTypes());
844
837 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};845 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
838 try llvm_field_types.ensureTotalCapacity(gpa, struct_obj.fields.count());846 try llvm_field_types.ensureTotalCapacity(gpa, struct_obj.fields.count());
839 defer llvm_field_types.deinit(gpa);847 defer llvm_field_types.deinit(gpa);
...@@ -1129,7 +1137,12 @@ pub const DeclGen = struct {...@@ -1129,7 +1137,12 @@ pub const DeclGen = struct {
1129 .Optional => {1137 .Optional => {
1130 var buf: Type.Payload.ElemType = undefined;1138 var buf: Type.Payload.ElemType = undefined;
1131 const payload_ty = tv.ty.optionalChild(&buf);1139 const payload_ty = tv.ty.optionalChild(&buf);
11321140 const llvm_i1 = self.context.intType(1);
1141 const is_pl = !tv.val.isNull();
1142 const non_null_bit = if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull();
1143 if (!payload_ty.hasCodeGenBits()) {
1144 return non_null_bit;
1145 }
1133 if (tv.ty.isPtrLikeOptional()) {1146 if (tv.ty.isPtrLikeOptional()) {
1134 if (tv.val.castTag(.opt_payload)) |payload| {1147 if (tv.val.castTag(.opt_payload)) |payload| {
1135 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });1148 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
...@@ -1138,15 +1151,12 @@ pub const DeclGen = struct {...@@ -1138,15 +1151,12 @@ pub const DeclGen = struct {
1138 return llvm_ty.constNull();1151 return llvm_ty.constNull();
1139 }1152 }
1140 }1153 }
1141 const is_pl = !tv.val.isNull();
1142 const llvm_i1 = self.context.intType(1);
1143
1144 const fields: [2]*const llvm.Value = .{1154 const fields: [2]*const llvm.Value = .{
1145 try self.genTypedValue(.{1155 try self.genTypedValue(.{
1146 .ty = payload_ty,1156 .ty = payload_ty,
1147 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),1157 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
1148 }),1158 }),
1149 if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(),1159 non_null_bit,
1150 };1160 };
1151 return self.context.constStruct(&fields, fields.len, .False);1161 return self.context.constStruct(&fields, fields.len, .False);
1152 },1162 },
...@@ -1307,6 +1317,10 @@ pub const DeclGen = struct {...@@ -1307,6 +1317,10 @@ pub const DeclGen = struct {
13071317
1308 decl.alive = true;1318 decl.alive = true;
1309 const llvm_type = try self.llvmType(tv.ty);1319 const llvm_type = try self.llvmType(tv.ty);
1320 if (!tv.ty.childType().hasCodeGenBits()) {
1321 return self.lowerPtrToVoid(tv.ty);
1322 }
1323
1310 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)1324 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
1311 try self.resolveLlvmFunction(decl)1325 try self.resolveLlvmFunction(decl)
1312 else1326 else
...@@ -1314,6 +1328,32 @@ pub const DeclGen = struct {...@@ -1314,6 +1328,32 @@ pub const DeclGen = struct {
1314 return llvm_val.constBitCast(llvm_type);1328 return llvm_val.constBitCast(llvm_type);
1315 }1329 }
13161330
1331 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
1332 const target = dg.module.getTarget();
1333 const alignment = ptr_ty.ptrAlignment(target);
1334 // Even though we are pointing at something which has zero bits (e.g. `void`),
1335 // Pointers are defined to have bits. So we must return something here.
1336 // The value cannot be undefined, because we use the `nonnull` annotation
1337 // for non-optional pointers. We also need to respect the alignment, even though
1338 // the address will never be dereferenced.
1339 const llvm_usize = try dg.llvmType(Type.usize);
1340 const llvm_ptr_ty = dg.context.intType(8).pointerType(0);
1341 if (alignment != 0) {
1342 return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty);
1343 }
1344 // Note that these 0xaa values are appropriate even in release-optimized builds
1345 // because we need a well-defined value that is not null, and LLVM does not
1346 // have an "undef_but_not_null" attribute. As an example, if this `alloc` AIR
1347 // instruction is followed by a `wrap_optional`, it will return this value
1348 // verbatim, and the result should test as non-null.
1349 const int = switch (target.cpu.arch.ptrBitWidth()) {
1350 32 => llvm_usize.constInt(0xaaaaaaaa, .False),
1351 64 => llvm_usize.constInt(0xaaaaaaaa_aaaaaaaa, .False),
1352 else => unreachable,
1353 };
1354 return int.constIntToPtr(llvm_ptr_ty);
1355 }
1356
1317 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {1357 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1318 return dg.addAttrInt(val, index, name, 0);1358 return dg.addAttrInt(val, index, name, 0);
1319 }1359 }
...@@ -1972,12 +2012,13 @@ pub const FuncGen = struct {...@@ -1972,12 +2012,13 @@ pub const FuncGen = struct {
19722012
1973 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2013 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1974 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2014 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1975 const lhs_ty = self.air.typeOf(bin_op.lhs);2015 const ptr_ty = self.air.typeOf(bin_op.lhs);
1976 if (!lhs_ty.hasCodeGenBits()) return null;2016 const elem_ty = ptr_ty.childType();
2017 if (!elem_ty.hasCodeGenBits()) return null;
19772018
1978 const base_ptr = try self.resolveInst(bin_op.lhs);2019 const base_ptr = try self.resolveInst(bin_op.lhs);
1979 const rhs = try self.resolveInst(bin_op.rhs);2020 const rhs = try self.resolveInst(bin_op.rhs);
1980 if (lhs_ty.isSinglePointer()) {2021 if (ptr_ty.isSinglePointer()) {
1981 // If this is a single-item pointer to an array, we need another index in the GEP.2022 // If this is a single-item pointer to an array, we need another index in the GEP.
1982 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };2023 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
1983 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");2024 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
...@@ -2832,11 +2873,13 @@ pub const FuncGen = struct {...@@ -2832,11 +2873,13 @@ pub const FuncGen = struct {
2832 if (self.liveness.isUnused(inst)) return null;2873 if (self.liveness.isUnused(inst)) return null;
2833 const ptr_ty = self.air.typeOfIndex(inst);2874 const ptr_ty = self.air.typeOfIndex(inst);
2834 const pointee_type = ptr_ty.childType();2875 const pointee_type = ptr_ty.childType();
2835 if (!pointee_type.hasCodeGenBits()) return null;2876 if (!pointee_type.hasCodeGenBits()) return self.dg.lowerPtrToVoid(ptr_ty);
2877
2836 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);2878 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);
2837 const target = self.dg.module.getTarget();
2838 const alloca_inst = self.buildAlloca(pointee_llvm_ty);2879 const alloca_inst = self.buildAlloca(pointee_llvm_ty);
2839 alloca_inst.setAlignment(ptr_ty.ptrAlignment(target));2880 const target = self.dg.module.getTarget();
2881 const alignment = ptr_ty.ptrAlignment(target);
2882 alloca_inst.setAlignment(alignment);
2840 return alloca_inst;2883 return alloca_inst;
2841 }2884 }
28422885
src/codegen/llvm/bindings.zig+3
...@@ -137,6 +137,9 @@ pub const Value = opaque {...@@ -137,6 +137,9 @@ pub const Value = opaque {
137 pub const constIntToPtr = LLVMConstIntToPtr;137 pub const constIntToPtr = LLVMConstIntToPtr;
138 extern fn LLVMConstIntToPtr(ConstantVal: *const Value, ToType: *const Type) *const Value;138 extern fn LLVMConstIntToPtr(ConstantVal: *const Value, ToType: *const Type) *const Value;
139139
140 pub const constPtrToInt = LLVMConstPtrToInt;
141 extern fn LLVMConstPtrToInt(ConstantVal: *const Value, ToType: *const Type) *const Value;
142
140 pub const setWeak = LLVMSetWeak;143 pub const setWeak = LLVMSetWeak;
141 extern fn LLVMSetWeak(CmpXchgInst: *const Value, IsWeak: Bool) void;144 extern fn LLVMSetWeak(CmpXchgInst: *const Value, IsWeak: Bool) void;
142145
src/type.zig+11-12
...@@ -1420,6 +1420,15 @@ pub const Type = extern union {...@@ -1420,6 +1420,15 @@ pub const Type = extern union {
1420 .@"anyframe",1420 .@"anyframe",
1421 .anyframe_T,1421 .anyframe_T,
1422 .@"opaque",1422 .@"opaque",
1423 .single_const_pointer,
1424 .single_mut_pointer,
1425 .many_const_pointer,
1426 .many_mut_pointer,
1427 .c_const_pointer,
1428 .c_mut_pointer,
1429 .const_slice,
1430 .mut_slice,
1431 .pointer,
1423 => true,1432 => true,
14241433
1425 .function => !self.castTag(.function).?.data.is_generic,1434 .function => !self.castTag(.function).?.data.is_generic,
...@@ -1480,17 +1489,7 @@ pub const Type = extern union {...@@ -1480,17 +1489,7 @@ pub const Type = extern union {
1480 .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,1489 .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
1481 .array_u8 => self.arrayLen() != 0,1490 .array_u8 => self.arrayLen() != 0,
14821491
1483 .array_sentinel,1492 .array_sentinel => self.childType().hasCodeGenBits(),
1484 .single_const_pointer,
1485 .single_mut_pointer,
1486 .many_const_pointer,
1487 .many_mut_pointer,
1488 .c_const_pointer,
1489 .c_mut_pointer,
1490 .const_slice,
1491 .mut_slice,
1492 .pointer,
1493 => self.childType().hasCodeGenBits(),
14941493
1495 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0,1494 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0,
14961495
...@@ -2370,7 +2369,7 @@ pub const Type = extern union {...@@ -2370,7 +2369,7 @@ pub const Type = extern union {
2370 .optional => {2369 .optional => {
2371 var buf: Payload.ElemType = undefined;2370 var buf: Payload.ElemType = undefined;
2372 const child_type = self.optionalChild(&buf);2371 const child_type = self.optionalChild(&buf);
2373 // optionals of zero sized pointers behave like bools2372 // optionals of zero sized types behave like bools, not pointers
2374 if (!child_type.hasCodeGenBits()) return false;2373 if (!child_type.hasCodeGenBits()) return false;
2375 if (child_type.zigTypeTag() != .Pointer) return false;2374 if (child_type.zigTypeTag() != .Pointer) return false;
23762375
test/behavior/basic.zig+1-1
...@@ -246,7 +246,7 @@ fn testTakeAddressOfParameter(f: f32) !void {...@@ -246,7 +246,7 @@ fn testTakeAddressOfParameter(f: f32) !void {
246}246}
247247
248test "pointer to void return type" {248test "pointer to void return type" {
249 testPointerToVoidReturnType() catch unreachable;249 try testPointerToVoidReturnType();
250}250}
251fn testPointerToVoidReturnType() anyerror!void {251fn testPointerToVoidReturnType() anyerror!void {
252 const a = testPointerToVoidReturnType2();252 const a = testPointerToVoidReturnType2();
test/behavior/optional.zig+8
...@@ -36,3 +36,11 @@ test "self-referential struct through a slice of optional" {...@@ -36,3 +36,11 @@ test "self-referential struct through a slice of optional" {
36 var n = S.Node.new();36 var n = S.Node.new();
37 try expect(n.data == null);37 try expect(n.data == null);
38}38}
39
40pub const EmptyStruct = struct {};
41
42test "optional pointer to size zero struct" {
43 var e = EmptyStruct{};
44 var o: ?*EmptyStruct = &e;
45 try expect(o != null);
46}
test/behavior/optional_stage1.zig-8
...@@ -3,14 +3,6 @@ const testing = std.testing;...@@ -3,14 +3,6 @@ const testing = std.testing;
3const expect = testing.expect;3const expect = testing.expect;
4const expectEqual = testing.expectEqual;4const expectEqual = testing.expectEqual;
55
6pub const EmptyStruct = struct {};
7
8test "optional pointer to size zero struct" {
9 var e = EmptyStruct{};
10 var o: ?*EmptyStruct = &e;
11 try expect(o != null);
12}
13
14test "equality compare nullable pointers" {6test "equality compare nullable pointers" {
15 try testNullPtrsEql();7 try testNullPtrsEql();
16 comptime try testNullPtrsEql();8 comptime try testNullPtrsEql();