| ... | ... | @@ -1499,7 +1499,7 @@ pub const Object = struct { |
| 1499 | 1499 | break :blk fwd_decl; |
| 1500 | 1500 | }; |
| 1501 | 1501 | |
| 1502 | | const non_null_ty = Type.bool; |
| 1502 | const non_null_ty = Type.u8; |
| 1503 | 1503 | const payload_size = child_ty.abiSize(target); |
| 1504 | 1504 | const payload_align = child_ty.abiAlignment(target); |
| 1505 | 1505 | const non_null_size = non_null_ty.abiSize(target); |
| ... | ... | @@ -2530,16 +2530,16 @@ pub const DeclGen = struct { |
| 2530 | 2530 | var buf: Type.Payload.ElemType = undefined; |
| 2531 | 2531 | const child_ty = t.optionalChild(&buf); |
| 2532 | 2532 | if (!child_ty.hasRuntimeBitsIgnoreComptime()) { |
| 2533 | | return dg.context.intType(1); |
| 2533 | return dg.context.intType(8); |
| 2534 | 2534 | } |
| 2535 | 2535 | const payload_llvm_ty = try dg.lowerType(child_ty); |
| 2536 | 2536 | if (t.optionalReprIsPayload()) { |
| 2537 | 2537 | return payload_llvm_ty; |
| 2538 | 2538 | } |
| 2539 | 2539 | |
| 2540 | | comptime assert(optional_layout_version == 2); |
| 2540 | comptime assert(optional_layout_version == 3); |
| 2541 | 2541 | var fields_buf: [3]*const llvm.Type = .{ |
| 2542 | | payload_llvm_ty, dg.context.intType(1), undefined, |
| 2542 | payload_llvm_ty, dg.context.intType(8), undefined, |
| 2543 | 2543 | }; |
| 2544 | 2544 | const offset = child_ty.abiSize(target) + 1; |
| 2545 | 2545 | const abi_size = t.abiSize(target); |
| ... | ... | @@ -3134,12 +3134,13 @@ pub const DeclGen = struct { |
| 3134 | 3134 | else => unreachable, |
| 3135 | 3135 | }, |
| 3136 | 3136 | .Optional => { |
| 3137 | | comptime assert(optional_layout_version == 2); |
| 3137 | comptime assert(optional_layout_version == 3); |
| 3138 | 3138 | var buf: Type.Payload.ElemType = undefined; |
| 3139 | 3139 | const payload_ty = tv.ty.optionalChild(&buf); |
| 3140 | | const llvm_i1 = dg.context.intType(1); |
| 3140 | |
| 3141 | const llvm_i8 = dg.context.intType(8); |
| 3141 | 3142 | const is_pl = !tv.val.isNull(); |
| 3142 | | const non_null_bit = if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(); |
| 3143 | const non_null_bit = if (is_pl) llvm_i8.constInt(1, .False) else llvm_i8.constNull(); |
| 3143 | 3144 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3144 | 3145 | return non_null_bit; |
| 3145 | 3146 | } |
| ... | ... | @@ -4041,10 +4042,10 @@ pub const FuncGen = struct { |
| 4041 | 4042 | .cmp_vector => try self.airCmpVector(inst), |
| 4042 | 4043 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 4043 | 4044 | |
| 4044 | | .is_non_null => try self.airIsNonNull(inst, false, false, .NE), |
| 4045 | | .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE), |
| 4046 | | .is_null => try self.airIsNonNull(inst, false, true , .EQ), |
| 4047 | | .is_null_ptr => try self.airIsNonNull(inst, true , true , .EQ), |
| 4045 | .is_non_null => try self.airIsNonNull(inst, false, .NE), |
| 4046 | .is_non_null_ptr => try self.airIsNonNull(inst, true , .NE), |
| 4047 | .is_null => try self.airIsNonNull(inst, false, .EQ), |
| 4048 | .is_null_ptr => try self.airIsNonNull(inst, true , .EQ), |
| 4048 | 4049 | |
| 4049 | 4050 | .is_non_err => try self.airIsErr(inst, .EQ, false), |
| 4050 | 4051 | .is_non_err_ptr => try self.airIsErr(inst, .EQ, true), |
| ... | ... | @@ -5633,7 +5634,6 @@ pub const FuncGen = struct { |
| 5633 | 5634 | self: *FuncGen, |
| 5634 | 5635 | inst: Air.Inst.Index, |
| 5635 | 5636 | operand_is_ptr: bool, |
| 5636 | | invert: bool, |
| 5637 | 5637 | pred: llvm.IntPredicate, |
| 5638 | 5638 | ) !?*const llvm.Value { |
| 5639 | 5639 | if (self.liveness.isUnused(inst)) return null; |
| ... | ... | @@ -5648,20 +5648,19 @@ pub const FuncGen = struct { |
| 5648 | 5648 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); |
| 5649 | 5649 | } |
| 5650 | 5650 | |
| 5651 | comptime assert(optional_layout_version == 3); |
| 5652 | |
| 5651 | 5653 | var buf: Type.Payload.ElemType = undefined; |
| 5652 | 5654 | const payload_ty = optional_ty.optionalChild(&buf); |
| 5653 | 5655 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5654 | 5656 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; |
| 5655 | | if (invert) { |
| 5656 | | return self.builder.buildNot(loaded, ""); |
| 5657 | | } else { |
| 5658 | | return loaded; |
| 5659 | | } |
| 5657 | const llvm_i8 = self.dg.context.intType(8); |
| 5658 | return self.builder.buildICmp(pred, loaded, llvm_i8.constNull(), ""); |
| 5660 | 5659 | } |
| 5661 | 5660 | |
| 5662 | 5661 | const is_by_ref = operand_is_ptr or isByRef(optional_ty); |
| 5663 | 5662 | const non_null_bit = self.optIsNonNull(operand, is_by_ref); |
| 5664 | | if (invert) { |
| 5663 | if (pred == .EQ) { |
| 5665 | 5664 | return self.builder.buildNot(non_null_bit, ""); |
| 5666 | 5665 | } else { |
| 5667 | 5666 | return non_null_bit; |
| ... | ... | @@ -5740,15 +5739,17 @@ pub const FuncGen = struct { |
| 5740 | 5739 | } |
| 5741 | 5740 | |
| 5742 | 5741 | fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 5742 | comptime assert(optional_layout_version == 3); |
| 5743 | |
| 5743 | 5744 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5744 | 5745 | const operand = try self.resolveInst(ty_op.operand); |
| 5745 | 5746 | const optional_ty = self.air.typeOf(ty_op.operand).childType(); |
| 5746 | 5747 | const result_ty = self.air.getRefType(ty_op.ty); |
| 5747 | 5748 | var buf: Type.Payload.ElemType = undefined; |
| 5748 | 5749 | const payload_ty = optional_ty.optionalChild(&buf); |
| 5749 | | const non_null_bit = self.context.intType(1).constAllOnes(); |
| 5750 | const non_null_bit = self.context.intType(8).constInt(1, .False); |
| 5750 | 5751 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5751 | | // We have a pointer to a i1. We need to set it to 1 and then return the same pointer. |
| 5752 | // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. |
| 5752 | 5753 | _ = self.builder.buildStore(non_null_bit, operand); |
| 5753 | 5754 | |
| 5754 | 5755 | // TODO once we update to LLVM 14 this bitcast won't be necessary. |
| ... | ... | @@ -5914,8 +5915,8 @@ pub const FuncGen = struct { |
| 5914 | 5915 | |
| 5915 | 5916 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5916 | 5917 | const payload_ty = self.air.typeOf(ty_op.operand); |
| 5917 | | const non_null_bit = self.context.intType(1).constAllOnes(); |
| 5918 | | comptime assert(optional_layout_version == 2); |
| 5918 | const non_null_bit = self.context.intType(8).constInt(1, .False); |
| 5919 | comptime assert(optional_layout_version == 3); |
| 5919 | 5920 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; |
| 5920 | 5921 | const operand = try self.resolveInst(ty_op.operand); |
| 5921 | 5922 | const optional_ty = self.air.typeOfIndex(inst); |
| ... | ... | @@ -7345,10 +7346,12 @@ pub const FuncGen = struct { |
| 7345 | 7346 | return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); |
| 7346 | 7347 | } |
| 7347 | 7348 | |
| 7349 | comptime assert(optional_layout_version == 3); |
| 7348 | 7350 | const optional_llvm_ty = try self.dg.lowerType(optional_ty); |
| 7349 | 7351 | const non_null_bit = self.builder.buildNot(success_bit, ""); |
| 7352 | const non_null_field = self.builder.buildZExt(non_null_bit, self.dg.context.intType(8), ""); |
| 7350 | 7353 | const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); |
| 7351 | | return self.builder.buildInsertValue(partial, non_null_bit, 1, ""); |
| 7354 | return self.builder.buildInsertValue(partial, non_null_field, 1, ""); |
| 7352 | 7355 | } |
| 7353 | 7356 | |
| 7354 | 7357 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -8331,19 +8334,24 @@ pub const FuncGen = struct { |
| 8331 | 8334 | |
| 8332 | 8335 | /// Assumes the optional is not pointer-like and payload has bits. |
| 8333 | 8336 | fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { |
| 8334 | | if (is_by_ref) { |
| 8335 | | const index_type = self.context.intType(32); |
| 8337 | const field = b: { |
| 8338 | if (is_by_ref) { |
| 8339 | const index_type = self.context.intType(32); |
| 8336 | 8340 | |
| 8337 | | const indices: [2]*const llvm.Value = .{ |
| 8338 | | index_type.constNull(), |
| 8339 | | index_type.constInt(1, .False), |
| 8340 | | }; |
| 8341 | const indices: [2]*const llvm.Value = .{ |
| 8342 | index_type.constNull(), |
| 8343 | index_type.constInt(1, .False), |
| 8344 | }; |
| 8341 | 8345 | |
| 8342 | | const field_ptr = self.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); |
| 8343 | | return self.builder.buildLoad(field_ptr, ""); |
| 8344 | | } |
| 8346 | const field_ptr = self.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); |
| 8347 | break :b self.builder.buildLoad(field_ptr, ""); |
| 8348 | } |
| 8349 | |
| 8350 | break :b self.builder.buildExtractValue(opt_handle, 1, ""); |
| 8351 | }; |
| 8352 | comptime assert(optional_layout_version == 3); |
| 8345 | 8353 | |
| 8346 | | return self.builder.buildExtractValue(opt_handle, 1, ""); |
| 8354 | return self.builder.buildICmp(.NE, field, self.context.intType(8).constInt(0, .False), ""); |
| 8347 | 8355 | } |
| 8348 | 8356 | |
| 8349 | 8357 | /// Assumes the optional is not pointer-like and payload has bits. |
| ... | ... | @@ -9369,7 +9377,10 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { |
| 9369 | 9377 | /// We can do this because for all types, Zig ABI alignment >= LLVM ABI |
| 9370 | 9378 | /// alignment. |
| 9371 | 9379 | const struct_layout_version = 2; |
| 9372 | | const optional_layout_version = 2; |
| 9380 | |
| 9381 | // TODO: Restore the non_null field to i1 once |
| 9382 | // https://github.com/llvm/llvm-project/issues/56585/ is fixed |
| 9383 | const optional_layout_version = 3; |
| 9373 | 9384 | |
| 9374 | 9385 | /// We use the least significant bit of the pointer address to tell us |
| 9375 | 9386 | /// whether the type is fully resolved. Types that are only fwd declared |