From d97441d37ef08813187f1b44ec29612619104585 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 1 Feb 2025 09:46:29 +0000 Subject: Sema: fix `@splat` of OPV arrays --- src/Sema.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Sema.zig b/src/Sema.zig index bc9d5ea55f..caf457a989 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24581,8 +24581,12 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); - // `len == 0` because `[0:s]T` always has a comptime-known splat. - if (!dest_ty.hasRuntimeBits(zcu) or len == 0) { + if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { + return Air.internedToRef(val.toIntern()); + } + + // We also need this case because `[0:s]T` is not OPV. + if (len == 0) { const empty_aggregate = try pt.intern(.{ .aggregate = .{ .ty = dest_ty.toIntern(), .storage = .{ .elems = &.{} }, -- cgit v1.2.3 From 149031204c0c804a66571a751b259da95886640d Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 1 Feb 2025 09:47:01 +0000 Subject: Sema: skip aliasing check and runtime operation for `@memcpy` of zero-bit type This check isn't valid in such cases, because the source and destination pointers both refer to zero bits of memory, meaning they effectively never alias. Resolves: #21655 --- src/Sema.zig | 16 ++++++++++++++++ src/Type.zig | 7 +++++++ test/behavior/memcpy.zig | 29 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) (limited to 'src') diff --git a/src/Sema.zig b/src/Sema.zig index caf457a989..93b367b9d8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25928,6 +25928,22 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } } + zero_bit: { + const src_comptime = try src_elem_ty.comptimeOnlySema(pt); + const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt); + assert(src_comptime == dest_comptime); // IMC + if (src_comptime) break :zero_bit; + + const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); + const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); + assert(src_has_bits == dest_has_bits); // IMC + if (src_has_bits) break :zero_bit; + + // The element type is zero-bit. We've done all validation (aside from the aliasing check, + // which we must skip) so we're done. + return; + } + const runtime_src = rs: { const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; diff --git a/src/Type.zig b/src/Type.zig index e6fc9c7d6a..403caad668 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -452,6 +452,13 @@ pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool { return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; } +pub fn hasRuntimeBitsIgnoreComptimeSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { + return hasRuntimeBitsInner(ty, true, .sema, pt.zcu, pt.tid) catch |err| switch (err) { + error.NeedLazy => unreachable, // this would require a resolve strat of lazy + else => |e| return e, + }; +} + /// true if and only if the type takes up space in memory at runtime. /// There are two reasons a type will return false: /// * the type is a comptime-only type. For example, the type `type` itself. diff --git a/test/behavior/memcpy.zig b/test/behavior/memcpy.zig index fa9203713d..4963be80f5 100644 --- a/test/behavior/memcpy.zig +++ b/test/behavior/memcpy.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; +const assert = std.debug.assert; test "memcpy and memset intrinsics" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -99,3 +100,31 @@ comptime { s.set("hello"); if (!std.mem.eql(u8, s.buffer[0..5], "hello")) @compileError("bad"); } + +test "@memcpy comptime-only type" { + const in: [4]type = .{ u8, u16, u32, u64 }; + comptime var out: [4]type = undefined; + @memcpy(&out, &in); + + comptime assert(out[0] == u8); + comptime assert(out[1] == u16); + comptime assert(out[2] == u32); + comptime assert(out[3] == u64); +} + +test "@memcpy zero-bit type with aliasing" { + const S = struct { + fn doTheTest() void { + var buf: [3]void = @splat({}); + const slice: []void = &buf; + // These two pointers are the same, but it's still not considered aliasing because + // the input and output slices both correspond to zero bits of memory. + @memcpy(slice, slice); + comptime assert(buf[0] == {}); + comptime assert(buf[1] == {}); + comptime assert(buf[2] == {}); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} -- cgit v1.2.3