| author | |
| committer | |
| log | c225b780e3944014300555c3b45eaccd7bc7c8ae |
| tree | 4c36c2b86776a5c0116e7212bc31dd1f4bfe3f78 |
| parent | 58c00a829e8acf438b91e9f7e1729ce79be722fa |
| parent | 149031204c0c804a66571a751b259da95886640d |
| signature |
Sema: skip aliasing check and runtime operation for `@memcpy` of zero-bit type3 files changed, 58 insertions(+), 2 deletions(-)
src/Sema.zig+22-2| ... | @@ -24581,8 +24581,12 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I | ... | @@ -24581,8 +24581,12 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 24581 | 24581 | ||
| 24582 | const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); | 24582 | const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); |
| 24583 | 24583 | ||
| 24584 | // `len == 0` because `[0:s]T` always has a comptime-known splat. | 24584 | if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { |
| 24585 | if (!dest_ty.hasRuntimeBits(zcu) or len == 0) { | 24585 | return Air.internedToRef(val.toIntern()); |
| 24586 | } | ||
| 24587 | |||
| 24588 | // We also need this case because `[0:s]T` is not OPV. | ||
| 24589 | if (len == 0) { | ||
| 24586 | const empty_aggregate = try pt.intern(.{ .aggregate = .{ | 24590 | const empty_aggregate = try pt.intern(.{ .aggregate = .{ |
| 24587 | .ty = dest_ty.toIntern(), | 24591 | .ty = dest_ty.toIntern(), |
| 24588 | .storage = .{ .elems = &.{} }, | 24592 | .storage = .{ .elems = &.{} }, |
| ... | @@ -25924,6 +25928,22 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25924,6 +25928,22 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25924 | } | 25928 | } |
| 25925 | } | 25929 | } |
| 25926 | 25930 | ||
| 25931 | zero_bit: { | ||
| 25932 | const src_comptime = try src_elem_ty.comptimeOnlySema(pt); | ||
| 25933 | const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt); | ||
| 25934 | assert(src_comptime == dest_comptime); // IMC | ||
| 25935 | if (src_comptime) break :zero_bit; | ||
| 25936 | |||
| 25937 | const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); | ||
| 25938 | const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); | ||
| 25939 | assert(src_has_bits == dest_has_bits); // IMC | ||
| 25940 | if (src_has_bits) break :zero_bit; | ||
| 25941 | |||
| 25942 | // The element type is zero-bit. We've done all validation (aside from the aliasing check, | ||
| 25943 | // which we must skip) so we're done. | ||
| 25944 | return; | ||
| 25945 | } | ||
| 25946 | |||
| 25927 | const runtime_src = rs: { | 25947 | const runtime_src = rs: { |
| 25928 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; | 25948 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; |
| 25929 | const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; | 25949 | const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; |
src/Type.zig+7| ... | @@ -452,6 +452,13 @@ pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool { | ... | @@ -452,6 +452,13 @@ pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool { |
| 452 | return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; | 452 | return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; |
| 453 | } | 453 | } |
| 454 | 454 | ||
| 455 | pub fn hasRuntimeBitsIgnoreComptimeSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { | ||
| 456 | return hasRuntimeBitsInner(ty, true, .sema, pt.zcu, pt.tid) catch |err| switch (err) { | ||
| 457 | error.NeedLazy => unreachable, // this would require a resolve strat of lazy | ||
| 458 | else => |e| return e, | ||
| 459 | }; | ||
| 460 | } | ||
| 461 | |||
| 455 | /// true if and only if the type takes up space in memory at runtime. | 462 | /// true if and only if the type takes up space in memory at runtime. |
| 456 | /// There are two reasons a type will return false: | 463 | /// There are two reasons a type will return false: |
| 457 | /// * the type is a comptime-only type. For example, the type `type` itself. | 464 | /// * the type is a comptime-only type. For example, the type `type` itself. |
test/behavior/memcpy.zig+29| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const assert = std.debug.assert; | ||
| 4 | 5 | ||
| 5 | test "memcpy and memset intrinsics" { | 6 | test "memcpy and memset intrinsics" { |
| 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -99,3 +100,31 @@ comptime { | ... | @@ -99,3 +100,31 @@ comptime { |
| 99 | s.set("hello"); | 100 | s.set("hello"); |
| 100 | if (!std.mem.eql(u8, s.buffer[0..5], "hello")) @compileError("bad"); | 101 | if (!std.mem.eql(u8, s.buffer[0..5], "hello")) @compileError("bad"); |
| 101 | } | 102 | } |
| 103 | |||
| 104 | test "@memcpy comptime-only type" { | ||
| 105 | const in: [4]type = .{ u8, u16, u32, u64 }; | ||
| 106 | comptime var out: [4]type = undefined; | ||
| 107 | @memcpy(&out, &in); | ||
| 108 | |||
| 109 | comptime assert(out[0] == u8); | ||
| 110 | comptime assert(out[1] == u16); | ||
| 111 | comptime assert(out[2] == u32); | ||
| 112 | comptime assert(out[3] == u64); | ||
| 113 | } | ||
| 114 | |||
| 115 | test "@memcpy zero-bit type with aliasing" { | ||
| 116 | const S = struct { | ||
| 117 | fn doTheTest() void { | ||
| 118 | var buf: [3]void = @splat({}); | ||
| 119 | const slice: []void = &buf; | ||
| 120 | // These two pointers are the same, but it's still not considered aliasing because | ||
| 121 | // the input and output slices both correspond to zero bits of memory. | ||
| 122 | @memcpy(slice, slice); | ||
| 123 | comptime assert(buf[0] == {}); | ||
| 124 | comptime assert(buf[1] == {}); | ||
| 125 | comptime assert(buf[2] == {}); | ||
| 126 | } | ||
| 127 | }; | ||
| 128 | S.doTheTest(); | ||
| 129 | comptime S.doTheTest(); | ||
| 130 | } |