authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-15 19:55:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-15 19:55:57-07:00
logd5c1d24964b0ee8ad37beb8e2a907e8caa645e07
treee7756f5ac6c7d3eb80fb9ab5845c123fbab425fa
parentb67d1810be3234c363ee2929ffcc91083bfb0ae5

stage2: fix "cmpxchg with ptr" test case

* Sema: fix atomic operand checking to allow pointers. * LLVM backend: implement pointer-like optional constants. * LLVM backend: fix `is_non_null` and `optional_payload` instructions to support pointer-like optionals. * Type: introduce `isPtrAtRuntime` method. * Type: fix `isPtrLikeOptional` to get the correct answer for allowzero pointers and slices.

5 files changed, 113 insertions(+), 49 deletions(-)

src/Sema.zig+10-6
...@@ -7518,12 +7518,16 @@ fn checkAtomicOperandType(...@@ -7518,12 +7518,16 @@ fn checkAtomicOperandType(
7518 return;7518 return;
7519 },7519 },
7520 .Bool => return, // Will be treated as `u8`.7520 .Bool => return, // Will be treated as `u8`.
7521 else => return sema.mod.fail(7521 else => {
7522 &block.base,7522 if (ty.isPtrAtRuntime()) return;
7523 ty_src,7523
7524 "expected bool, integer, float, enum, or pointer type; found {}",7524 return sema.mod.fail(
7525 .{ty},7525 &block.base,
7526 ),7526 ty_src,
7527 "expected bool, integer, float, enum, or pointer type; found {}",
7528 .{ty},
7529 );
7530 },
7527 };7531 };
7528 const bit_count = int_ty.intInfo(target).bits;7532 const bit_count = int_ty.intInfo(target).bits;
7529 if (bit_count > max_atomic_bits) {7533 if (bit_count > max_atomic_bits) {
src/codegen/llvm.zig+39-22
...@@ -602,18 +602,18 @@ pub const DeclGen = struct {...@@ -602,18 +602,18 @@ pub const DeclGen = struct {
602 return elem_type.arrayType(@intCast(c_uint, total_len));602 return elem_type.arrayType(@intCast(c_uint, total_len));
603 },603 },
604 .Optional => {604 .Optional => {
605 if (!t.isPtrLikeOptional()) {605 var buf: Type.Payload.ElemType = undefined;
606 var buf: Type.Payload.ElemType = undefined;606 const child_type = t.optionalChild(&buf);
607 const child_type = t.optionalChild(&buf);607 const payload_llvm_ty = try self.llvmType(child_type);
608608
609 const optional_types: [2]*const llvm.Type = .{609 if (t.isPtrLikeOptional()) {
610 try self.llvmType(child_type),610 return payload_llvm_ty;
611 self.context.intType(1),
612 };
613 return self.context.structType(&optional_types, 2, .False);
614 } else {
615 return self.todo("implement optional pointers as actual pointers", .{});
616 }611 }
612
613 const fields: [2]*const llvm.Type = .{
614 payload_llvm_ty, self.context.intType(1),
615 };
616 return self.context.structType(&fields, fields.len, .False);
617 },617 },
618 .ErrorUnion => {618 .ErrorUnion => {
619 const error_type = t.errorUnionSet();619 const error_type = t.errorUnionSet();
...@@ -1573,6 +1573,13 @@ pub const FuncGen = struct {...@@ -1573,6 +1573,13 @@ pub const FuncGen = struct {
1573 const operand = try self.resolveInst(un_op);1573 const operand = try self.resolveInst(un_op);
15741574
1575 if (operand_is_ptr) {1575 if (operand_is_ptr) {
1576 const operand_ty = self.air.typeOf(un_op).elemType();
1577 if (operand_ty.isPtrLikeOptional()) {
1578 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
1579 const loaded = self.builder.buildLoad(operand, "");
1580 return self.builder.buildICmp(.NE, loaded, operand_llvm_ty.constNull(), "");
1581 }
1582
1576 const index_type = self.context.intType(32);1583 const index_type = self.context.intType(32);
15771584
1578 var indices: [2]*const llvm.Value = .{1585 var indices: [2]*const llvm.Value = .{
...@@ -1581,9 +1588,15 @@ pub const FuncGen = struct {...@@ -1581,9 +1588,15 @@ pub const FuncGen = struct {
1581 };1588 };
15821589
1583 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), "");1590 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), "");
1584 } else {
1585 return self.builder.buildExtractValue(operand, 1, "");
1586 }1591 }
1592
1593 const operand_ty = self.air.typeOf(un_op);
1594 if (operand_ty.isPtrLikeOptional()) {
1595 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
1596 return self.builder.buildICmp(.NE, operand, operand_llvm_ty.constNull(), "");
1597 }
1598
1599 return self.builder.buildExtractValue(operand, 1, "");
1587 }1600 }
15881601
1589 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {1602 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
...@@ -1636,17 +1649,24 @@ pub const FuncGen = struct {...@@ -1636,17 +1649,24 @@ pub const FuncGen = struct {
1636 const operand = try self.resolveInst(ty_op.operand);1649 const operand = try self.resolveInst(ty_op.operand);
16371650
1638 if (operand_is_ptr) {1651 if (operand_is_ptr) {
1639 const index_type = self.context.intType(32);1652 const operand_ty = self.air.typeOf(ty_op.operand).elemType();
1653 if (operand_ty.isPtrLikeOptional()) {
1654 return self.builder.buildLoad(operand, "");
1655 }
16401656
1657 const index_type = self.context.intType(32);
1641 var indices: [2]*const llvm.Value = .{1658 var indices: [2]*const llvm.Value = .{
1642 index_type.constNull(),1659 index_type.constNull(), index_type.constNull(),
1643 index_type.constNull(),
1644 };1660 };
1645
1646 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");1661 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");
1647 } else {
1648 return self.builder.buildExtractValue(operand, 0, "");
1649 }1662 }
1663
1664 const operand_ty = self.air.typeOf(ty_op.operand);
1665 if (operand_ty.isPtrLikeOptional()) {
1666 return operand;
1667 }
1668
1669 return self.builder.buildExtractValue(operand, 0, "");
1650 }1670 }
16511671
1652 fn airErrUnionPayload(1672 fn airErrUnionPayload(
...@@ -2050,8 +2070,6 @@ pub const FuncGen = struct {...@@ -2050,8 +2070,6 @@ pub const FuncGen = struct {
2050 );2070 );
20512071
2052 const optional_ty = self.air.typeOfIndex(inst);2072 const optional_ty = self.air.typeOfIndex(inst);
2053 var buffer: Type.Payload.ElemType = undefined;
2054 const child_ty = optional_ty.optionalChild(&buffer);
20552073
2056 var payload = self.builder.buildExtractValue(result, 0, "");2074 var payload = self.builder.buildExtractValue(result, 0, "");
2057 if (opt_abi_ty != null) {2075 if (opt_abi_ty != null) {
...@@ -2060,8 +2078,7 @@ pub const FuncGen = struct {...@@ -2060,8 +2078,7 @@ pub const FuncGen = struct {
2060 const success_bit = self.builder.buildExtractValue(result, 1, "");2078 const success_bit = self.builder.buildExtractValue(result, 1, "");
20612079
2062 if (optional_ty.isPtrLikeOptional()) {2080 if (optional_ty.isPtrLikeOptional()) {
2063 const child_llvm_ty = try self.dg.llvmType(child_ty);2081 return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, "");
2064 return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, "");
2065 }2082 }
20662083
2067 const optional_llvm_ty = try self.dg.llvmType(optional_ty);2084 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
src/type.zig+44-1
...@@ -2191,6 +2191,44 @@ pub const Type = extern union {...@@ -2191,6 +2191,44 @@ pub const Type = extern union {
2191 };2191 };
2192 }2192 }
21932193
2194 pub fn isPtrAtRuntime(self: Type) bool {
2195 switch (self.tag()) {
2196 .c_const_pointer,
2197 .c_mut_pointer,
2198 .many_const_pointer,
2199 .many_mut_pointer,
2200 .manyptr_const_u8,
2201 .manyptr_u8,
2202 .optional_single_const_pointer,
2203 .optional_single_mut_pointer,
2204 .single_const_pointer,
2205 .single_const_pointer_to_comptime_int,
2206 .single_mut_pointer,
2207 => return true,
2208
2209 .pointer => switch (self.castTag(.pointer).?.data.size) {
2210 .Slice => return false,
2211 .One, .Many, .C => return true,
2212 },
2213
2214 .optional => {
2215 var buf: Payload.ElemType = undefined;
2216 const child_type = self.optionalChild(&buf);
2217 // optionals of zero sized pointers behave like bools
2218 if (!child_type.hasCodeGenBits()) return false;
2219 if (child_type.zigTypeTag() != .Pointer) return false;
2220
2221 const info = child_type.ptrInfo().data;
2222 switch (info.size) {
2223 .Slice, .C => return false,
2224 .Many, .One => return !info.@"allowzero",
2225 }
2226 },
2227
2228 else => return false,
2229 }
2230 }
2231
2194 /// Asserts that the type is an optional2232 /// Asserts that the type is an optional
2195 pub fn isPtrLikeOptional(self: Type) bool {2233 pub fn isPtrLikeOptional(self: Type) bool {
2196 switch (self.tag()) {2234 switch (self.tag()) {
...@@ -2203,8 +2241,13 @@ pub const Type = extern union {...@@ -2203,8 +2241,13 @@ pub const Type = extern union {
2203 const child_type = self.optionalChild(&buf);2241 const child_type = self.optionalChild(&buf);
2204 // optionals of zero sized pointers behave like bools2242 // optionals of zero sized pointers behave like bools
2205 if (!child_type.hasCodeGenBits()) return false;2243 if (!child_type.hasCodeGenBits()) return false;
2244 if (child_type.zigTypeTag() != .Pointer) return false;
22062245
2207 return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();2246 const info = child_type.ptrInfo().data;
2247 switch (info.size) {
2248 .Slice, .C => return false,
2249 .Many, .One => return !info.@"allowzero",
2250 }
2208 },2251 },
2209 else => unreachable,2252 else => unreachable,
2210 }2253 }
test/behavior/atomics.zig+20
...@@ -53,3 +53,23 @@ fn testAtomicLoad(ptr: *u8) !void {...@@ -53,3 +53,23 @@ fn testAtomicLoad(ptr: *u8) !void {
53 const x = @atomicLoad(u8, ptr, .SeqCst);53 const x = @atomicLoad(u8, ptr, .SeqCst);
54 try expect(x == 42);54 try expect(x == 42);
55}55}
56
57test "cmpxchg with ptr" {
58 var data1: i32 = 1234;
59 var data2: i32 = 5678;
60 var data3: i32 = 9101;
61 var x: *i32 = &data1;
62 if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| {
63 try expect(x1 == &data1);
64 } else {
65 @panic("cmpxchg should have failed");
66 }
67
68 while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| {
69 try expect(x1 == &data1);
70 }
71 try expect(x == &data3);
72
73 try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null);
74 try expect(x == &data2);
75}
test/behavior/atomics_stage1.zig-20
...@@ -3,26 +3,6 @@ const expect = std.testing.expect;...@@ -3,26 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const builtin = @import("builtin");4const builtin = @import("builtin");
55
6test "cmpxchg with ptr" {
7 var data1: i32 = 1234;
8 var data2: i32 = 5678;
9 var data3: i32 = 9101;
10 var x: *i32 = &data1;
11 if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| {
12 try expect(x1 == &data1);
13 } else {
14 @panic("cmpxchg should have failed");
15 }
16
17 while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| {
18 try expect(x1 == &data1);
19 }
20 try expect(x == &data3);
21
22 try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null);
23 try expect(x == &data2);
24}
25
26test "128-bit cmpxchg" {6test "128-bit cmpxchg" {
27 try test_u128_cmpxchg();7 try test_u128_cmpxchg();
28 comptime try test_u128_cmpxchg();8 comptime try test_u128_cmpxchg();