authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-08-04 11:30:06+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-08-31 23:14:46+02:00
log0a4a865644b6da11cfe99fdd794b80c08b656870
treef5c274f6e0c018efb04846979b96df4dfd9bca8f
parent9a29bf480336fa7c237a0b2bc1ca99d0762bcf24
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Revert "llvm: workaround crash on large inline memset"

This reverts commit 84c2d809ec90aa3d5934adac33551f82656625ee. closes https://codeberg.org/ziglang/zig/issues/31701

2 files changed, 4 insertions(+), 43 deletions(-)

src/codegen/llvm/FuncGen.zig+4-39
......@@ -1071,7 +1071,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo
10711071 const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false;
10721072 const ret_ty_align = ret_ty.abiAlignment(zcu);
10731073
1074 if (val_is_undef and safety and !self.needMemsetWorkaround(ret_ty.abiSize(zcu))) {
1074 if (val_is_undef and safety) {
10751075 const rp = switch (self.ret_ptr) {
10761076 .none => try self.buildZigAlloca(ret_ty, .none),
10771077 else => |rp| rp,
......@@ -5069,7 +5069,7 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu
50695069 };
50705070
50715071 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) {
50735073 const owner_mod = fg.ownerModule();
50745074
50755075 // 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
55855585
55865586 self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu));
55875587
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 });
55935588 const len_bytes = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
55945589
55955590 try self.lowerMemset(
......@@ -5600,7 +5595,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error
56005595 len_bytes,
56015596 access_kind,
56025597 safety,
5603 allow_byte_memset,
56045598 );
56055599 return .none;
56065600}
......@@ -5614,12 +5608,11 @@ fn lowerMemset(
56145608 len_bytes: Builder.Value,
56155609 access_kind: Builder.MemoryAccessKind,
56165610 safety: bool,
5617 allow_byte_memset: bool,
56185611) Allocator.Error!void {
56195612 const o = self.object;
56205613 const zcu = o.zcu;
56215614
5622 if (allow_byte_memset) if (elem_ref.toInterned()) |elem_ip_index| {
5615 if (elem_ref.toInterned()) |elem_ip_index| {
56235616 const elem_val: Value = .fromInterned(elem_ip_index);
56245617 if (elem_val.isUndef(zcu)) {
56255618 // Even if safety is disabled, we still emit a memset to undefined since it conveys
......@@ -5660,13 +5653,12 @@ fn lowerMemset(
56605653 );
56615654 return;
56625655 }
5663 };
5656 }
56645657
56655658 const value = try self.resolveInst(elem_ref);
56665659 const elem_abi_size = elem_ty.abiSize(zcu);
56675660
56685661 intrinsic: {
5669 if (!allow_byte_memset) break :intrinsic;
56705662 if (elem_abi_size != 1) break :intrinsic;
56715663 // To use LLVM's intrinsic, we need to convert the operand to a raw 8-bit integer value.
56725664 const fill_byte: Builder.Value = byte: {
......@@ -6023,7 +6015,6 @@ fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
60236015 len_bytes_llvm,
60246016 .normal,
60256017 false,
6026 !self.needMemsetWorkaround(len_bytes),
60276018 );
60286019
60296020 if (array_info.sentinel) |sent_val| {
......@@ -8143,32 +8134,6 @@ fn llvmAllocaAddressSpace(target: *const std.Target) Builder.AddrSpace {
81438134 };
81448135}
81458136
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
8155fn 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
81728137const mips_clobber_overrides = std.StaticStringMap(enum {
81738138 @"$msair",
81748139 @"$msacsr",
test/tests.zig-4
......@@ -2895,10 +2895,6 @@ fn addOneModuleTest(
28952895 });
28962896 these_tests.linkage = test_target.linkage;
28972897 these_tests.use_new_linker = test_target.new_linker;
2898 // https://codeberg.org/ziglang/zig/issues/31701
2899 if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) {
2900 if (options.no_builtin) these_tests.root_module.no_builtin = true;
2901 }
29022898 // https://codeberg.org/ziglang/zig/issues/31702
29032899 if (mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc")) {
29042900 these_tests.root_module.stack_protector = false;