| ... | ... | @@ -1071,7 +1071,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 1071 | 1071 | const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 1072 | 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 | 1075 | const rp = switch (self.ret_ptr) { |
| 1076 | 1076 | .none => try self.buildZigAlloca(ret_ty, .none), |
| 1077 | 1077 | else => |rp| rp, |
| ... | ... | @@ -5069,7 +5069,7 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu |
| 5069 | 5069 | }; |
| 5070 | 5070 | |
| 5071 | 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 | 5073 | const owner_mod = fg.ownerModule(); |
| 5074 | 5074 | |
| 5075 | 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 | 5585 | |
| 5586 | 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 | 5588 | const len_bytes = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 5594 | 5589 | |
| 5595 | 5590 | try self.lowerMemset( |
| ... | ... | @@ -5600,7 +5595,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5600 | 5595 | len_bytes, |
| 5601 | 5596 | access_kind, |
| 5602 | 5597 | safety, |
| 5603 | | allow_byte_memset, |
| 5604 | 5598 | ); |
| 5605 | 5599 | return .none; |
| 5606 | 5600 | } |
| ... | ... | @@ -5614,12 +5608,11 @@ fn lowerMemset( |
| 5614 | 5608 | len_bytes: Builder.Value, |
| 5615 | 5609 | access_kind: Builder.MemoryAccessKind, |
| 5616 | 5610 | safety: bool, |
| 5617 | | allow_byte_memset: bool, |
| 5618 | 5611 | ) Allocator.Error!void { |
| 5619 | 5612 | const o = self.object; |
| 5620 | 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 | 5616 | const elem_val: Value = .fromInterned(elem_ip_index); |
| 5624 | 5617 | if (elem_val.isUndef(zcu)) { |
| 5625 | 5618 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | ... | @@ -5660,13 +5653,12 @@ fn lowerMemset( |
| 5660 | 5653 | ); |
| 5661 | 5654 | return; |
| 5662 | 5655 | } |
| 5663 | | }; |
| 5656 | } |
| 5664 | 5657 | |
| 5665 | 5658 | const value = try self.resolveInst(elem_ref); |
| 5666 | 5659 | const elem_abi_size = elem_ty.abiSize(zcu); |
| 5667 | 5660 | |
| 5668 | 5661 | intrinsic: { |
| 5669 | | if (!allow_byte_memset) break :intrinsic; |
| 5670 | 5662 | if (elem_abi_size != 1) break :intrinsic; |
| 5671 | 5663 | // To use LLVM's intrinsic, we need to convert the operand to a raw 8-bit integer value. |
| 5672 | 5664 | const fill_byte: Builder.Value = byte: { |
| ... | ... | @@ -6023,7 +6015,6 @@ fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value |
| 6023 | 6015 | len_bytes_llvm, |
| 6024 | 6016 | .normal, |
| 6025 | 6017 | false, |
| 6026 | | !self.needMemsetWorkaround(len_bytes), |
| 6027 | 6018 | ); |
| 6028 | 6019 | |
| 6029 | 6020 | if (array_info.sentinel) |sent_val| { |
| ... | ... | @@ -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 | 8137 | const mips_clobber_overrides = std.StaticStringMap(enum { |
| 8173 | 8138 | @"$msair", |
| 8174 | 8139 | @"$msacsr", |