| author | |
| committer | |
| log | d5c1d24964b0ee8ad37beb8e2a907e8caa645e07 |
| tree | e7756f5ac6c7d3eb80fb9ab5845c123fbab425fa |
| parent | b67d1810be3234c363ee2929ffcc91083bfb0ae5 |
* 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); |
| 608 | 608 | ||
| 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); |
| 1574 | 1574 | ||
| 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); |
| 1577 | 1584 | ||
| 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 | }; |
| 1582 | 1589 | ||
| 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 | } |
| 1588 | 1601 | ||
| 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); |
| 1637 | 1650 | ||
| 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 | } | ||
| 1640 | 1656 | ||
| 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 | } |
| 1651 | 1671 | ||
| 1652 | fn airErrUnionPayload( | 1672 | fn airErrUnionPayload( |
| ... | @@ -2050,8 +2070,6 @@ pub const FuncGen = struct { | ... | @@ -2050,8 +2070,6 @@ pub const FuncGen = struct { |
| 2050 | ); | 2070 | ); |
| 2051 | 2071 | ||
| 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); | ||
| 2055 | 2073 | ||
| 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, ""); |
| 2061 | 2079 | ||
| 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 | } |
| 2066 | 2083 | ||
| 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 | } |
| 2193 | 2193 | ||
| 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 optional | 2232 | /// 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 bools | 2242 | // 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; | ||
| 2206 | 2245 | ||
| 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 | |||
| 57 | test "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; |
| 3 | const expectEqual = std.testing.expectEqual; | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | 5 | ||
| 6 | test "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 | |||
| 26 | test "128-bit cmpxchg" { | 6 | test "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(); |