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-10-07 00:30:52+02:00
log4bf080551c3dfa89fb6c9f2c6cd8001cc460162d
treeb6b2c5df1512e3f7d2c8c29e56c2bbe5195815f3
parent66cd872dff0ac0cb4b960d703af1db3f43354091
signature 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| {
......@@ -8144,32 +8135,6 @@ fn llvmAllocaAddressSpace(target: *const std.Target) Builder.AddrSpace {
81448135 };
81458136}
81468137
8147/// Due to an LLVM bug, calls to `@llvm.memset.inline.*` with large constant length arguments cause
8148/// LLVM to crash. As a mitigation, this function returns `true` if we should avoid emitting a
8149/// memset call of the given length.
8150///
8151/// Most of our call sites are just setting memory to `undefined`, so can simply skip the memset
8152/// call if we return `true`.
8153///
8154/// Upstream issue: https://github.com/llvm/llvm-project/issues/189161
8155/// Zig issue: https://codeberg.org/ziglang/zig/issues/31701
8156fn needMemsetWorkaround(fg: *const FuncGen, maybe_len: ?u64) bool {
8157 if (!fg.disable_intrinsics) {
8158 // The bug is limited to `@llvm.memset.inline.*`: normal memset calls are fine.
8159 return false;
8160 }
8161 const len = maybe_len orelse {
8162 // We don't think the length is constant, but a trivial optimization on LLVM's side could
8163 // turn it into one and potentially trigger the bug. Therefore, always apply the workaround
8164 // if the length is not a known constant.
8165 return true;
8166 };
8167 // Empirically, the crash first happens at 1048561 bytes, which is 1 MiB less 15 bytes. To be
8168 // safe (just in case the limit is target-specific or something like that), let's just set the
8169 // cap at half of that, i.e. 512 KiB.
8170 return len > 1024 * 512;
8171}
8172
81738138const mips_clobber_overrides = std.StaticStringMap(enum {
81748139 @"$msair",
81758140 @"$msacsr",
test/tests.zig-4
......@@ -2919,10 +2919,6 @@ fn addOneModuleTest(
29192919 });
29202920 these_tests.linkage = test_target.linkage;
29212921 these_tests.use_new_linker = test_target.new_linker;
2922 // https://codeberg.org/ziglang/zig/issues/31701
2923 if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) {
2924 if (options.no_builtin) these_tests.root_module.no_builtin = true;
2925 }
29262922 // https://codeberg.org/ziglang/zig/issues/31702
29272923 if (mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc")) {
29282924 these_tests.root_module.stack_protector = false;