| ... | @@ -1071,7 +1071,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo | ... | @@ -1071,7 +1071,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 1071 | const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; | 1071 | const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 1072 | const ret_ty_align = ret_ty.abiAlignment(zcu); | 1072 | const ret_ty_align = ret_ty.abiAlignment(zcu); |
| 1073 | | 1073 | |
| 1074 | if (val_is_undef and safety and !self.needMemsetWorkaround(ret_ty.abiSize(zcu))) { | 1074 | if (val_is_undef and safety) { |
| 1075 | const rp = switch (self.ret_ptr) { | 1075 | const rp = switch (self.ret_ptr) { |
| 1076 | .none => try self.buildZigAlloca(ret_ty, .none), | 1076 | .none => try self.buildZigAlloca(ret_ty, .none), |
| 1077 | else => |rp| rp, | 1077 | else => |rp| rp, |
| ... | @@ -5069,7 +5069,7 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu | ... | @@ -5069,7 +5069,7 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu |
| 5069 | }; | 5069 | }; |
| 5070 | | 5070 | |
| 5071 | const val_is_undef = if (bin_op.rhs.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; | 5071 | const val_is_undef = if (bin_op.rhs.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 5072 | if (val_is_undef and !fg.needMemsetWorkaround(elem_ty.abiSize(zcu))) { | 5072 | if (val_is_undef) { |
| 5073 | const owner_mod = fg.ownerModule(); | 5073 | const owner_mod = fg.ownerModule(); |
| 5074 | | 5074 | |
| 5075 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | 5075 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | @@ -5585,11 +5585,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5585,11 +5585,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5585 | | 5585 | |
| 5586 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); | 5586 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); |
| 5587 | | 5587 | |
| 5588 | const allow_byte_memset = !self.needMemsetWorkaround(switch (ptr_ty.ptrSize(zcu)) { | | |
| 5589 | .one => ptr_ty.childType(zcu).abiSize(zcu), | | |
| 5590 | .slice => null, | | |
| 5591 | .many, .c => unreachable, | | |
| 5592 | }); | | |
| 5593 | const len_bytes = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | 5588 | const len_bytes = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 5594 | | 5589 | |
| 5595 | try self.lowerMemset( | 5590 | try self.lowerMemset( |
| ... | @@ -5600,7 +5595,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5600,7 +5595,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5600 | len_bytes, | 5595 | len_bytes, |
| 5601 | access_kind, | 5596 | access_kind, |
| 5602 | safety, | 5597 | safety, |
| 5603 | allow_byte_memset, | | |
| 5604 | ); | 5598 | ); |
| 5605 | return .none; | 5599 | return .none; |
| 5606 | } | 5600 | } |
| ... | @@ -5614,12 +5608,11 @@ fn lowerMemset( | ... | @@ -5614,12 +5608,11 @@ fn lowerMemset( |
| 5614 | len_bytes: Builder.Value, | 5608 | len_bytes: Builder.Value, |
| 5615 | access_kind: Builder.MemoryAccessKind, | 5609 | access_kind: Builder.MemoryAccessKind, |
| 5616 | safety: bool, | 5610 | safety: bool, |
| 5617 | allow_byte_memset: bool, | | |
| 5618 | ) Allocator.Error!void { | 5611 | ) Allocator.Error!void { |
| 5619 | const o = self.object; | 5612 | const o = self.object; |
| 5620 | const zcu = o.zcu; | 5613 | const zcu = o.zcu; |
| 5621 | | 5614 | |
| 5622 | if (allow_byte_memset) if (elem_ref.toInterned()) |elem_ip_index| { | 5615 | if (elem_ref.toInterned()) |elem_ip_index| { |
| 5623 | const elem_val: Value = .fromInterned(elem_ip_index); | 5616 | const elem_val: Value = .fromInterned(elem_ip_index); |
| 5624 | if (elem_val.isUndef(zcu)) { | 5617 | if (elem_val.isUndef(zcu)) { |
| 5625 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | 5618 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | @@ -5660,13 +5653,12 @@ fn lowerMemset( | ... | @@ -5660,13 +5653,12 @@ fn lowerMemset( |
| 5660 | ); | 5653 | ); |
| 5661 | return; | 5654 | return; |
| 5662 | } | 5655 | } |
| 5663 | }; | 5656 | } |
| 5664 | | 5657 | |
| 5665 | const value = try self.resolveInst(elem_ref); | 5658 | const value = try self.resolveInst(elem_ref); |
| 5666 | const elem_abi_size = elem_ty.abiSize(zcu); | 5659 | const elem_abi_size = elem_ty.abiSize(zcu); |
| 5667 | | 5660 | |
| 5668 | intrinsic: { | 5661 | intrinsic: { |
| 5669 | if (!allow_byte_memset) break :intrinsic; | | |
| 5670 | if (elem_abi_size != 1) break :intrinsic; | 5662 | if (elem_abi_size != 1) break :intrinsic; |
| 5671 | // To use LLVM's intrinsic, we need to convert the operand to a raw 8-bit integer value. | 5663 | // To use LLVM's intrinsic, we need to convert the operand to a raw 8-bit integer value. |
| 5672 | const fill_byte: Builder.Value = byte: { | 5664 | const fill_byte: Builder.Value = byte: { |
| ... | @@ -6023,7 +6015,6 @@ fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value | ... | @@ -6023,7 +6015,6 @@ fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value |
| 6023 | len_bytes_llvm, | 6015 | len_bytes_llvm, |
| 6024 | .normal, | 6016 | .normal, |
| 6025 | false, | 6017 | false, |
| 6026 | !self.needMemsetWorkaround(len_bytes), | | |
| 6027 | ); | 6018 | ); |
| 6028 | | 6019 | |
| 6029 | if (array_info.sentinel) |sent_val| { | 6020 | if (array_info.sentinel) |sent_val| { |
| ... | @@ -8143,32 +8134,6 @@ fn llvmAllocaAddressSpace(target: *const std.Target) Builder.AddrSpace { | ... | @@ -8143,32 +8134,6 @@ fn llvmAllocaAddressSpace(target: *const std.Target) Builder.AddrSpace { |
| 8143 | }; | 8134 | }; |
| 8144 | } | 8135 | } |
| 8145 | | 8136 | |
| 8146 | /// Due to an LLVM bug, calls to `@llvm.memset.inline.*` with large constant length arguments cause | | |
| 8147 | /// LLVM to crash. As a mitigation, this function returns `true` if we should avoid emitting a | | |
| 8148 | /// memset call of the given length. | | |
| 8149 | /// | | |
| 8150 | /// Most of our call sites are just setting memory to `undefined`, so can simply skip the memset | | |
| 8151 | /// call if we return `true`. | | |
| 8152 | /// | | |
| 8153 | /// Upstream issue: https://github.com/llvm/llvm-project/issues/189161 | | |
| 8154 | /// Zig issue: https://codeberg.org/ziglang/zig/issues/31701 | | |
| 8155 | fn needMemsetWorkaround(fg: *const FuncGen, maybe_len: ?u64) bool { | | |
| 8156 | if (!fg.disable_intrinsics) { | | |
| 8157 | // The bug is limited to `@llvm.memset.inline.*`: normal memset calls are fine. | | |
| 8158 | return false; | | |
| 8159 | } | | |
| 8160 | const len = maybe_len orelse { | | |
| 8161 | // We don't think the length is constant, but a trivial optimization on LLVM's side could | | |
| 8162 | // turn it into one and potentially trigger the bug. Therefore, always apply the workaround | | |
| 8163 | // if the length is not a known constant. | | |
| 8164 | return true; | | |
| 8165 | }; | | |
| 8166 | // Empirically, the crash first happens at 1048561 bytes, which is 1 MiB less 15 bytes. To be | | |
| 8167 | // safe (just in case the limit is target-specific or something like that), let's just set the | | |
| 8168 | // cap at half of that, i.e. 512 KiB. | | |
| 8169 | return len > 1024 * 512; | | |
| 8170 | } | | |
| 8171 | | | |
| 8172 | const mips_clobber_overrides = std.StaticStringMap(enum { | 8137 | const mips_clobber_overrides = std.StaticStringMap(enum { |
| 8173 | @"$msair", | 8138 | @"$msair", |
| 8174 | @"$msacsr", | 8139 | @"$msacsr", |