From d5c1d24964b0ee8ad37beb8e2a907e8caa645e07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Sep 2021 19:55:57 -0700 Subject: 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. --- src/Sema.zig | 16 ++++++++------ src/codegen/llvm.zig | 61 +++++++++++++++++++++++++++++++++------------------- src/type.zig | 45 +++++++++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/Sema.zig b/src/Sema.zig index c163178890..19e0f41b98 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7518,12 +7518,16 @@ fn checkAtomicOperandType( return; }, .Bool => return, // Will be treated as `u8`. - else => return sema.mod.fail( - &block.base, - ty_src, - "expected bool, integer, float, enum, or pointer type; found {}", - .{ty}, - ), + else => { + if (ty.isPtrAtRuntime()) return; + + return sema.mod.fail( + &block.base, + ty_src, + "expected bool, integer, float, enum, or pointer type; found {}", + .{ty}, + ); + }, }; const bit_count = int_ty.intInfo(target).bits; if (bit_count > max_atomic_bits) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index af25ae51b1..5720756cc2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -602,18 +602,18 @@ pub const DeclGen = struct { return elem_type.arrayType(@intCast(c_uint, total_len)); }, .Optional => { - if (!t.isPtrLikeOptional()) { - var buf: Type.Payload.ElemType = undefined; - const child_type = t.optionalChild(&buf); + var buf: Type.Payload.ElemType = undefined; + const child_type = t.optionalChild(&buf); + const payload_llvm_ty = try self.llvmType(child_type); - const optional_types: [2]*const llvm.Type = .{ - try self.llvmType(child_type), - self.context.intType(1), - }; - return self.context.structType(&optional_types, 2, .False); - } else { - return self.todo("implement optional pointers as actual pointers", .{}); + if (t.isPtrLikeOptional()) { + return payload_llvm_ty; } + + const fields: [2]*const llvm.Type = .{ + payload_llvm_ty, self.context.intType(1), + }; + return self.context.structType(&fields, fields.len, .False); }, .ErrorUnion => { const error_type = t.errorUnionSet(); @@ -1573,6 +1573,13 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); if (operand_is_ptr) { + const operand_ty = self.air.typeOf(un_op).elemType(); + if (operand_ty.isPtrLikeOptional()) { + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const loaded = self.builder.buildLoad(operand, ""); + return self.builder.buildICmp(.NE, loaded, operand_llvm_ty.constNull(), ""); + } + const index_type = self.context.intType(32); var indices: [2]*const llvm.Value = .{ @@ -1581,9 +1588,15 @@ pub const FuncGen = struct { }; return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), ""); - } else { - return self.builder.buildExtractValue(operand, 1, ""); } + + const operand_ty = self.air.typeOf(un_op); + if (operand_ty.isPtrLikeOptional()) { + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + return self.builder.buildICmp(.NE, operand, operand_llvm_ty.constNull(), ""); + } + + return self.builder.buildExtractValue(operand, 1, ""); } fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value { @@ -1636,17 +1649,24 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); if (operand_is_ptr) { - const index_type = self.context.intType(32); + const operand_ty = self.air.typeOf(ty_op.operand).elemType(); + if (operand_ty.isPtrLikeOptional()) { + return self.builder.buildLoad(operand, ""); + } + const index_type = self.context.intType(32); var indices: [2]*const llvm.Value = .{ - index_type.constNull(), - index_type.constNull(), + index_type.constNull(), index_type.constNull(), }; - return self.builder.buildInBoundsGEP(operand, &indices, 2, ""); - } else { - return self.builder.buildExtractValue(operand, 0, ""); } + + const operand_ty = self.air.typeOf(ty_op.operand); + if (operand_ty.isPtrLikeOptional()) { + return operand; + } + + return self.builder.buildExtractValue(operand, 0, ""); } fn airErrUnionPayload( @@ -2050,8 +2070,6 @@ pub const FuncGen = struct { ); const optional_ty = self.air.typeOfIndex(inst); - var buffer: Type.Payload.ElemType = undefined; - const child_ty = optional_ty.optionalChild(&buffer); var payload = self.builder.buildExtractValue(result, 0, ""); if (opt_abi_ty != null) { @@ -2060,8 +2078,7 @@ pub const FuncGen = struct { const success_bit = self.builder.buildExtractValue(result, 1, ""); if (optional_ty.isPtrLikeOptional()) { - const child_llvm_ty = try self.dg.llvmType(child_ty); - return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, ""); + return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); } const optional_llvm_ty = try self.dg.llvmType(optional_ty); diff --git a/src/type.zig b/src/type.zig index 2403893133..d9a641474f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2191,6 +2191,44 @@ pub const Type = extern union { }; } + pub fn isPtrAtRuntime(self: Type) bool { + switch (self.tag()) { + .c_const_pointer, + .c_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .manyptr_const_u8, + .manyptr_u8, + .optional_single_const_pointer, + .optional_single_mut_pointer, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + .single_mut_pointer, + => return true, + + .pointer => switch (self.castTag(.pointer).?.data.size) { + .Slice => return false, + .One, .Many, .C => return true, + }, + + .optional => { + var buf: Payload.ElemType = undefined; + const child_type = self.optionalChild(&buf); + // optionals of zero sized pointers behave like bools + if (!child_type.hasCodeGenBits()) return false; + if (child_type.zigTypeTag() != .Pointer) return false; + + const info = child_type.ptrInfo().data; + switch (info.size) { + .Slice, .C => return false, + .Many, .One => return !info.@"allowzero", + } + }, + + else => return false, + } + } + /// Asserts that the type is an optional pub fn isPtrLikeOptional(self: Type) bool { switch (self.tag()) { @@ -2203,8 +2241,13 @@ pub const Type = extern union { const child_type = self.optionalChild(&buf); // optionals of zero sized pointers behave like bools if (!child_type.hasCodeGenBits()) return false; + if (child_type.zigTypeTag() != .Pointer) return false; - return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr(); + const info = child_type.ptrInfo().data; + switch (info.size) { + .Slice, .C => return false, + .Many, .One => return !info.@"allowzero", + } }, else => unreachable, } -- cgit v1.2.3