authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-27 14:39:21-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-04-27 14:39:21-04:00
log1b76d4c53adafebcbfcf0476276375353139dacb
tree5854ca686d0d016e2d109766fa17742fff27c6ac
parent227d2b15e449db1e84788d2c87e4f49100d316ca
parent2e9c1553ef40e9f21c2241294b8942369ef9007a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22605 from dweiller/memmove

add `@memmove` builtin

41 files changed, 714 insertions(+), 15 deletions(-)

doc/langref.html.in+17
...@@ -5149,6 +5149,23 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5149,6 +5149,23 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5149 {#syntax#}std.crypto.secureZero{#endsyntax#}</p>5149 {#syntax#}std.crypto.secureZero{#endsyntax#}</p>
5150 {#header_close#}5150 {#header_close#}
51515151
5152 {#header_open|@memmove#}
5153 <pre>{#syntax#}@memmove(dest, source) void{#endsyntax#}</pre>
5154 <p>This function copies bytes from one region of memory to another, but unlike
5155 {#link|@memcpy#} the regions may overlap.</p>
5156 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or
5157 a mutable many-item {#link|pointer|Pointers#}. It may have any
5158 alignment, and it may have any element type.</p>
5159 <p>{#syntax#}source{#endsyntax#} must be a slice, a pointer to
5160 an array, or a many-item {#link|pointer|Pointers#}. It may
5161 have any alignment, and it may have any element type.</p>
5162 <p>The {#syntax#}source{#endsyntax#} element type must have the same in-memory
5163 representation as the {#syntax#}dest{#endsyntax#} element type.</p>
5164 <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and
5165 {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided,
5166 they must be equal.</p>
5167 {#header_close#}
5168
5152 {#header_open|@min#}5169 {#header_open|@min#}
5153 <pre>{#syntax#}@min(...) T{#endsyntax#}</pre>5170 <pre>{#syntax#}@min(...) T{#endsyntax#}</pre>
5154 <p>5171 <p>
lib/std/debug.zig+4
...@@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type {...@@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type {
134 @branchHint(.cold);134 @branchHint(.cold);
135 call("@memcpy arguments alias", @returnAddress());135 call("@memcpy arguments alias", @returnAddress());
136 }136 }
137 pub fn memmoveLenMismatch() noreturn {
138 @branchHint(.cold);
139 call("@memmove arguments have non-equal lengths", @returnAddress());
140 }
137 pub fn noreturnReturned() noreturn {141 pub fn noreturnReturned() noreturn {
138 @branchHint(.cold);142 @branchHint(.cold);
139 call("'noreturn' function returned", @returnAddress());143 call("'noreturn' function returned", @returnAddress());
lib/std/debug/no_panic.zig+5
...@@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn {...@@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn {
135 @trap();135 @trap();
136}136}
137137
138pub fn memmoveLenMismatch() noreturn {
139 @branchHint(.cold);
140 @trap();
141}
142
138pub fn noreturnReturned() noreturn {143pub fn noreturnReturned() noreturn {
139 @branchHint(.cold);144 @branchHint(.cold);
140 @trap();145 @trap();
lib/std/debug/simple_panic.zig+4
...@@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn {...@@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn {
128 call("@memcpy arguments alias", null);128 call("@memcpy arguments alias", null);
129}129}
130130
131pub fn memmoveLenMismatch() noreturn {
132 call("@memmove arguments have non-equal lengths", null);
133}
134
131pub fn noreturnReturned() noreturn {135pub fn noreturnReturned() noreturn {
132 call("'noreturn' function returned", null);136 call("'noreturn' function returned", null);
133}137}
lib/std/mem.zig+2
...@@ -232,6 +232,7 @@ test "Allocator alloc and remap with zero-bit type" {...@@ -232,6 +232,7 @@ test "Allocator alloc and remap with zero-bit type" {
232/// Copy all of source into dest at position 0.232/// Copy all of source into dest at position 0.
233/// dest.len must be >= source.len.233/// dest.len must be >= source.len.
234/// If the slices overlap, dest.ptr must be <= src.ptr.234/// If the slices overlap, dest.ptr must be <= src.ptr.
235/// This function is deprecated; use @memmove instead.
235pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {236pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {
236 for (dest[0..source.len], source) |*d, s| d.* = s;237 for (dest[0..source.len], source) |*d, s| d.* = s;
237}238}
...@@ -239,6 +240,7 @@ pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {...@@ -239,6 +240,7 @@ pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {
239/// Copy all of source into dest at position 0.240/// Copy all of source into dest at position 0.
240/// dest.len must be >= source.len.241/// dest.len must be >= source.len.
241/// If the slices overlap, dest.ptr must be >= src.ptr.242/// If the slices overlap, dest.ptr must be >= src.ptr.
243/// This function is deprecated; use @memmove instead.
242pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {244pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
243 // TODO instead of manually doing this check for the whole array245 // TODO instead of manually doing this check for the whole array
244 // and turning off runtime safety, the compiler should detect loops like246 // and turning off runtime safety, the compiler should detect loops like
lib/std/zig/AstGen.zig+8
...@@ -2919,6 +2919,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2919,6 +2919,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2919 .set_runtime_safety,2919 .set_runtime_safety,
2920 .memcpy,2920 .memcpy,
2921 .memset,2921 .memset,
2922 .memmove,
2922 .validate_deref,2923 .validate_deref,
2923 .validate_destructure,2924 .validate_destructure,
2924 .save_err_ret_index,2925 .save_err_ret_index,
...@@ -9717,6 +9718,13 @@ fn builtinCall(...@@ -9717,6 +9718,13 @@ fn builtinCall(
9717 });9718 });
9718 return rvalue(gz, ri, .void_value, node);9719 return rvalue(gz, ri, .void_value, node);
9719 },9720 },
9721 .memmove => {
9722 _ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{
9723 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9724 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
9725 });
9726 return rvalue(gz, ri, .void_value, node);
9727 },
9720 .shuffle => {9728 .shuffle => {
9721 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{9729 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
9722 .elem_type = try typeExpr(gz, scope, params[0]),9730 .elem_type = try typeExpr(gz, scope, params[0]),
lib/std/zig/AstRlAnnotate.zig+1-1
...@@ -1055,7 +1055,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -1055,7 +1055,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
1055 _ = try astrl.expr(args[2], block, ResultInfo.none);1055 _ = try astrl.expr(args[2], block, ResultInfo.none);
1056 return false;1056 return false;
1057 },1057 },
1058 .memcpy => {1058 .memcpy, .memmove => {
1059 _ = try astrl.expr(args[0], block, ResultInfo.none);1059 _ = try astrl.expr(args[0], block, ResultInfo.none);
1060 _ = try astrl.expr(args[1], block, ResultInfo.none);1060 _ = try astrl.expr(args[1], block, ResultInfo.none);
1061 return false;1061 return false;
lib/std/zig/BuiltinFn.zig+8
...@@ -68,6 +68,7 @@ pub const Tag = enum {...@@ -68,6 +68,7 @@ pub const Tag = enum {
68 max,68 max,
69 memcpy,69 memcpy,
70 memset,70 memset,
71 memmove,
71 min,72 min,
72 wasm_memory_size,73 wasm_memory_size,
73 wasm_memory_grow,74 wasm_memory_grow,
...@@ -641,6 +642,13 @@ pub const list = list: {...@@ -641,6 +642,13 @@ pub const list = list: {
641 .param_count = 2,642 .param_count = 2,
642 },643 },
643 },644 },
645 .{
646 "@memmove",
647 .{
648 .tag = .memmove,
649 .param_count = 2,
650 },
651 },
644 .{652 .{
645 "@min",653 "@min",
646 .{654 .{
lib/std/zig/Zir.zig+7
...@@ -986,6 +986,9 @@ pub const Inst = struct {...@@ -986,6 +986,9 @@ pub const Inst = struct {
986 /// Implements the `@memset` builtin.986 /// Implements the `@memset` builtin.
987 /// Uses the `pl_node` union field with payload `Bin`.987 /// Uses the `pl_node` union field with payload `Bin`.
988 memset,988 memset,
989 /// Implements the `@memmove` builtin.
990 /// Uses the `pl_node` union field with payload `Bin`.
991 memmove,
989 /// Implements the `@min` builtin for 2 args.992 /// Implements the `@min` builtin for 2 args.
990 /// Uses the `pl_node` union field with payload `Bin`993 /// Uses the `pl_node` union field with payload `Bin`
991 min,994 min,
...@@ -1272,6 +1275,7 @@ pub const Inst = struct {...@@ -1272,6 +1275,7 @@ pub const Inst = struct {
1272 .max,1275 .max,
1273 .memcpy,1276 .memcpy,
1274 .memset,1277 .memset,
1278 .memmove,
1275 .min,1279 .min,
1276 .c_import,1280 .c_import,
1277 .@"resume",1281 .@"resume",
...@@ -1355,6 +1359,7 @@ pub const Inst = struct {...@@ -1355,6 +1359,7 @@ pub const Inst = struct {
1355 .set_runtime_safety,1359 .set_runtime_safety,
1356 .memcpy,1360 .memcpy,
1357 .memset,1361 .memset,
1362 .memmove,
1358 .check_comptime_control_flow,1363 .check_comptime_control_flow,
1359 .@"defer",1364 .@"defer",
1360 .defer_err_code,1365 .defer_err_code,
...@@ -1832,6 +1837,7 @@ pub const Inst = struct {...@@ -1832,6 +1837,7 @@ pub const Inst = struct {
1832 .max = .pl_node,1837 .max = .pl_node,
1833 .memcpy = .pl_node,1838 .memcpy = .pl_node,
1834 .memset = .pl_node,1839 .memset = .pl_node,
1840 .memmove = .pl_node,
1835 .min = .pl_node,1841 .min = .pl_node,
1836 .c_import = .pl_node,1842 .c_import = .pl_node,
18371843
...@@ -4291,6 +4297,7 @@ fn findTrackableInner(...@@ -4291,6 +4297,7 @@ fn findTrackableInner(
4291 .mul_add,4297 .mul_add,
4292 .memcpy,4298 .memcpy,
4293 .memset,4299 .memset,
4300 .memmove,
4294 .min,4301 .min,
4295 .max,4302 .max,
4296 .alloc,4303 .alloc,
lib/std/zig/llvm/Builder.zig+30
...@@ -6125,6 +6125,36 @@ pub const WipFunction = struct {...@@ -6125,6 +6125,36 @@ pub const WipFunction = struct {
6125 return value.unwrap().instruction;6125 return value.unwrap().instruction;
6126 }6126 }
61276127
6128 pub fn callMemMove(
6129 self: *WipFunction,
6130 dst: Value,
6131 dst_align: Alignment,
6132 src: Value,
6133 src_align: Alignment,
6134 len: Value,
6135 kind: MemoryAccessKind,
6136 ) Allocator.Error!Instruction.Index {
6137 var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })};
6138 var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })};
6139 const value = try self.callIntrinsic(
6140 .normal,
6141 try self.builder.fnAttrs(&.{
6142 .none,
6143 .none,
6144 try self.builder.attrs(&dst_attrs),
6145 try self.builder.attrs(&src_attrs),
6146 }),
6147 .memmove,
6148 &.{ dst.typeOfWip(self), src.typeOfWip(self), len.typeOfWip(self) },
6149 &.{ dst, src, len, switch (kind) {
6150 .normal => Value.false,
6151 .@"volatile" => Value.true,
6152 } },
6153 undefined,
6154 );
6155 return value.unwrap().instruction;
6156 }
6157
6128 pub fn callMemSet(6158 pub fn callMemSet(
6129 self: *WipFunction,6159 self: *WipFunction,
6130 dst: Value,6160 dst: Value,
lib/zig.h+1
...@@ -481,6 +481,7 @@...@@ -481,6 +481,7 @@
481481
482zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t);482zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t);
483zig_extern void *memset (void *, int, size_t);483zig_extern void *memset (void *, int, size_t);
484zig_extern void *memmove (void *, void const *, size_t);
484485
485/* ================ Bool and 8/16/32/64-bit Integer Support ================= */486/* ================ Bool and 8/16/32/64-bit Integer Support ================= */
486487
src/Air.zig+14
...@@ -730,6 +730,18 @@ pub const Inst = struct {...@@ -730,6 +730,18 @@ pub const Inst = struct {
730 /// source being a pointer-to-array), then it is guaranteed to be730 /// source being a pointer-to-array), then it is guaranteed to be
731 /// greater than zero.731 /// greater than zero.
732 memcpy,732 memcpy,
733 /// Given dest pointer and source pointer, copy elements from source to dest.
734 /// Dest pointer is either a slice or a pointer to array.
735 /// The dest element type may be any type.
736 /// Source pointer must have same element type as dest element type.
737 /// Dest slice may have any alignment; source pointer may have any alignment.
738 /// The two memory regions may overlap.
739 /// Result type is always void.
740 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
741 /// If the length is compile-time known (due to the destination or
742 /// source being a pointer-to-array), then it is guaranteed to be
743 /// greater than zero.
744 memmove,
733745
734 /// Uses the `ty_pl` field with payload `Cmpxchg`.746 /// Uses the `ty_pl` field with payload `Cmpxchg`.
735 cmpxchg_weak,747 cmpxchg_weak,
...@@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1533 .memset,1545 .memset,
1534 .memset_safe,1546 .memset_safe,
1535 .memcpy,1547 .memcpy,
1548 .memmove,
1536 .set_union_tag,1549 .set_union_tag,
1537 .prefetch,1550 .prefetch,
1538 .set_err_return_trace,1551 .set_err_return_trace,
...@@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1696 .memset,1709 .memset,
1697 .memset_safe,1710 .memset_safe,
1698 .memcpy,1711 .memcpy,
1712 .memmove,
1699 .cmpxchg_weak,1713 .cmpxchg_weak,
1700 .cmpxchg_strong,1714 .cmpxchg_strong,
1701 .atomic_store_unordered,1715 .atomic_store_unordered,
src/Air/types_resolved.zig+1
...@@ -83,6 +83,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {...@@ -83,6 +83,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
83 .memset,83 .memset,
84 .memset_safe,84 .memset_safe,
85 .memcpy,85 .memcpy,
86 .memmove,
86 .atomic_store_unordered,87 .atomic_store_unordered,
87 .atomic_store_monotonic,88 .atomic_store_monotonic,
88 .atomic_store_release,89 .atomic_store_release,
src/Liveness.zig+2
...@@ -300,6 +300,7 @@ pub fn categorizeOperand(...@@ -300,6 +300,7 @@ pub fn categorizeOperand(
300 .memset,300 .memset,
301 .memset_safe,301 .memset_safe,
302 .memcpy,302 .memcpy,
303 .memmove,
303 => {304 => {
304 const o = air_datas[@intFromEnum(inst)].bin_op;305 const o = air_datas[@intFromEnum(inst)].bin_op;
305 if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);306 if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
...@@ -936,6 +937,7 @@ fn analyzeInst(...@@ -936,6 +937,7 @@ fn analyzeInst(
936 .memset,937 .memset,
937 .memset_safe,938 .memset_safe,
938 .memcpy,939 .memcpy,
940 .memmove,
939 => {941 => {
940 const o = inst_datas[@intFromEnum(inst)].bin_op;942 const o = inst_datas[@intFromEnum(inst)].bin_op;
941 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });943 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });
src/Liveness/Verify.zig+1
...@@ -267,6 +267,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -267,6 +267,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
267 .memset,267 .memset,
268 .memset_safe,268 .memset_safe,
269 .memcpy,269 .memcpy,
270 .memmove,
270 => {271 => {
271 const bin_op = data[@intFromEnum(inst)].bin_op;272 const bin_op = data[@intFromEnum(inst)].bin_op;
272 try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none });273 try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none });
src/Sema.zig+45-12
...@@ -1583,6 +1583,11 @@ fn analyzeBodyInner(...@@ -1583,6 +1583,11 @@ fn analyzeBodyInner(
1583 i += 1;1583 i += 1;
1584 continue;1584 continue;
1585 },1585 },
1586 .memmove => {
1587 try sema.zirMemmove(block, inst);
1588 i += 1;
1589 continue;
1590 },
1586 .check_comptime_control_flow => {1591 .check_comptime_control_flow => {
1587 if (!block.isComptime()) {1592 if (!block.isComptime()) {
1588 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;1593 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
...@@ -25610,6 +25615,19 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A...@@ -25610,6 +25615,19 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A
25610}25615}
2561125616
25612fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {25617fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
25618 return sema.analyzeCopy(block, inst, .memcpy);
25619}
25620
25621fn zirMemmove(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
25622 return sema.analyzeCopy(block, inst, .memmove);
25623}
25624
25625fn analyzeCopy(
25626 sema: *Sema,
25627 block: *Block,
25628 inst: Zir.Inst.Index,
25629 op: enum { memcpy, memmove },
25630) CompileError!void {
25613 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;25631 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
25614 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;25632 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
25615 const src = block.nodeOffset(inst_data.src_node);25633 const src = block.nodeOffset(inst_data.src_node);
...@@ -25625,12 +25643,12 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25625,12 +25643,12 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25625 const zcu = pt.zcu;25643 const zcu = pt.zcu;
2562625644
25627 if (dest_ty.isConstPtr(zcu)) {25645 if (dest_ty.isConstPtr(zcu)) {
25628 return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});25646 return sema.fail(block, dest_src, "cannot {s} to constant pointer", .{@tagName(op)});
25629 }25647 }
2563025648
25631 if (dest_len == .none and src_len == .none) {25649 if (dest_len == .none and src_len == .none) {
25632 const msg = msg: {25650 const msg = msg: {
25633 const msg = try sema.errMsg(src, "unknown @memcpy length", .{});25651 const msg = try sema.errMsg(src, "unknown @{s} length", .{@tagName(op)});
25634 errdefer msg.destroy(sema.gpa);25652 errdefer msg.destroy(sema.gpa);
25635 try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{25653 try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{
25636 dest_ty.fmt(pt),25654 dest_ty.fmt(pt),
...@@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25676 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {25694 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
25677 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {25695 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
25678 const msg = msg: {25696 const msg = msg: {
25679 const msg = try sema.errMsg(src, "non-matching @memcpy lengths", .{});25697 const msg = try sema.errMsg(src, "non-matching @{s} lengths", .{@tagName(op)});
25680 errdefer msg.destroy(sema.gpa);25698 errdefer msg.destroy(sema.gpa);
25681 try sema.errNote(dest_src, msg, "length {} here", .{25699 try sema.errNote(dest_src, msg, "length {} here", .{
25682 dest_len_val.fmtValueSema(pt, sema),25700 dest_len_val.fmtValueSema(pt, sema),
...@@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
2569625714
25697 if (block.wantSafety()) {25715 if (block.wantSafety()) {
25698 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);25716 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
25699 try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch);25717 const panic_id: Zcu.SimplePanicId = switch (op) {
25718 .memcpy => .memcpy_len_mismatch,
25719 .memmove => .memmove_len_mismatch,
25720 };
25721 try sema.addSafetyCheck(block, src, ok, panic_id);
25700 }25722 }
25701 } else if (dest_len != .none) {25723 } else if (dest_len != .none) {
25702 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {25724 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
...@@ -25724,6 +25746,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25724,6 +25746,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25724 return;25746 return;
25725 }25747 }
2572625748
25749 const check_aliasing = switch (op) {
25750 .memcpy => true,
25751 .memmove => false,
25752 };
25753
25727 const runtime_src = rs: {25754 const runtime_src = rs: {
25728 const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;25755 const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
25729 const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src;25756 const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src;
...@@ -25733,12 +25760,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25733,12 +25760,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
2573325760
25734 const len_u64 = try len_val.?.toUnsignedIntSema(pt);25761 const len_u64 = try len_val.?.toUnsignedIntSema(pt);
2573525762
25736 if (Value.doPointersOverlap(25763 if (check_aliasing) {
25737 raw_src_ptr,25764 if (Value.doPointersOverlap(
25738 raw_dest_ptr,25765 raw_src_ptr,
25739 len_u64,25766 raw_dest_ptr,
25740 zcu,25767 len_u64,
25741 )) return sema.fail(block, src, "'@memcpy' arguments alias", .{});25768 zcu,
25769 )) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
25770 }
2574225771
25743 if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;25772 if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
2574425773
...@@ -25810,7 +25839,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25810,7 +25839,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25810 try sema.validateRuntimeValue(block, src_src, src_ptr);25839 try sema.validateRuntimeValue(block, src_src, src_ptr);
2581125840
25812 // Aliasing safety check.25841 // Aliasing safety check.
25813 if (block.wantSafety()) {25842 if (check_aliasing and block.wantSafety()) {
25814 const len = if (len_val) |v|25843 const len = if (len_val) |v|
25815 Air.internedToRef(v.toIntern())25844 Air.internedToRef(v.toIntern())
25816 else if (dest_len != .none)25845 else if (dest_len != .none)
...@@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25853 }25882 }
2585425883
25855 _ = try block.addInst(.{25884 _ = try block.addInst(.{
25856 .tag = .memcpy,25885 .tag = switch (op) {
25886 .memcpy => .memcpy,
25887 .memmove => .memmove,
25888 },
25857 .data = .{ .bin_op = .{25889 .data = .{ .bin_op = .{
25858 .lhs = new_dest_ptr,25890 .lhs = new_dest_ptr,
25859 .rhs = new_src_ptr,25891 .rhs = new_src_ptr,
...@@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ...@@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ
38078 .@"panic.forLenMismatch",38110 .@"panic.forLenMismatch",
38079 .@"panic.memcpyLenMismatch",38111 .@"panic.memcpyLenMismatch",
38080 .@"panic.memcpyAlias",38112 .@"panic.memcpyAlias",
38113 .@"panic.memmoveLenMismatch",
38081 .@"panic.noreturnReturned",38114 .@"panic.noreturnReturned",
38082 => try pt.funcType(.{38115 => try pt.funcType(.{
38083 .param_types = &.{},38116 .param_types = &.{},
src/Zcu.zig+4
...@@ -302,6 +302,7 @@ pub const BuiltinDecl = enum {...@@ -302,6 +302,7 @@ pub const BuiltinDecl = enum {
302 @"panic.forLenMismatch",302 @"panic.forLenMismatch",
303 @"panic.memcpyLenMismatch",303 @"panic.memcpyLenMismatch",
304 @"panic.memcpyAlias",304 @"panic.memcpyAlias",
305 @"panic.memmoveLenMismatch",
305 @"panic.noreturnReturned",306 @"panic.noreturnReturned",
306307
307 VaList,308 VaList,
...@@ -379,6 +380,7 @@ pub const BuiltinDecl = enum {...@@ -379,6 +380,7 @@ pub const BuiltinDecl = enum {
379 .@"panic.forLenMismatch",380 .@"panic.forLenMismatch",
380 .@"panic.memcpyLenMismatch",381 .@"panic.memcpyLenMismatch",
381 .@"panic.memcpyAlias",382 .@"panic.memcpyAlias",
383 .@"panic.memmoveLenMismatch",
382 .@"panic.noreturnReturned",384 .@"panic.noreturnReturned",
383 => .func,385 => .func,
384 };386 };
...@@ -446,6 +448,7 @@ pub const SimplePanicId = enum {...@@ -446,6 +448,7 @@ pub const SimplePanicId = enum {
446 for_len_mismatch,448 for_len_mismatch,
447 memcpy_len_mismatch,449 memcpy_len_mismatch,
448 memcpy_alias,450 memcpy_alias,
451 memmove_len_mismatch,
449 noreturn_returned,452 noreturn_returned,
450453
451 pub fn toBuiltin(id: SimplePanicId) BuiltinDecl {454 pub fn toBuiltin(id: SimplePanicId) BuiltinDecl {
...@@ -470,6 +473,7 @@ pub const SimplePanicId = enum {...@@ -470,6 +473,7 @@ pub const SimplePanicId = enum {
470 .for_len_mismatch => .@"panic.forLenMismatch",473 .for_len_mismatch => .@"panic.forLenMismatch",
471 .memcpy_len_mismatch => .@"panic.memcpyLenMismatch",474 .memcpy_len_mismatch => .@"panic.memcpyLenMismatch",
472 .memcpy_alias => .@"panic.memcpyAlias",475 .memcpy_alias => .@"panic.memcpyAlias",
476 .memmove_len_mismatch => .@"panic.memmoveLenMismatch",
473 .noreturn_returned => .@"panic.noreturnReturned",477 .noreturn_returned => .@"panic.noreturnReturned",
474 // zig fmt: on478 // zig fmt: on
475 };479 };
src/arch/aarch64/CodeGen.zig+6
...@@ -760,6 +760,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -760,6 +760,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
760 .atomic_rmw => try self.airAtomicRmw(inst),760 .atomic_rmw => try self.airAtomicRmw(inst),
761 .atomic_load => try self.airAtomicLoad(inst),761 .atomic_load => try self.airAtomicLoad(inst),
762 .memcpy => try self.airMemcpy(inst),762 .memcpy => try self.airMemcpy(inst),
763 .memmove => try self.airMemmove(inst),
763 .memset => try self.airMemset(inst, false),764 .memset => try self.airMemset(inst, false),
764 .memset_safe => try self.airMemset(inst, true),765 .memset_safe => try self.airMemset(inst, true),
765 .set_union_tag => try self.airSetUnionTag(inst),766 .set_union_tag => try self.airSetUnionTag(inst),
...@@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
5993 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});5994 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
5994}5995}
59955996
5997fn airMemmove(self: *Self, inst: Air.Inst.Index) InnerError!void {
5998 _ = inst;
5999 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
6000}
6001
5996fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {6002fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
5997 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6003 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5998 const operand = try self.resolveInst(un_op);6004 const operand = try self.resolveInst(un_op);
src/arch/arm/CodeGen.zig+6
...@@ -749,6 +749,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -749,6 +749,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
749 .atomic_rmw => try self.airAtomicRmw(inst),749 .atomic_rmw => try self.airAtomicRmw(inst),
750 .atomic_load => try self.airAtomicLoad(inst),750 .atomic_load => try self.airAtomicLoad(inst),
751 .memcpy => try self.airMemcpy(inst),751 .memcpy => try self.airMemcpy(inst),
752 .memmove => try self.airMemmove(inst),
752 .memset => try self.airMemset(inst, false),753 .memset => try self.airMemset(inst, false),
753 .memset_safe => try self.airMemset(inst, true),754 .memset_safe => try self.airMemset(inst, true),
754 .set_union_tag => try self.airSetUnionTag(inst),755 .set_union_tag => try self.airSetUnionTag(inst),
...@@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {...@@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
5963 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});5964 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
5964}5965}
59655966
5967fn airMemmove(self: *Self, inst: Air.Inst.Index) !void {
5968 _ = inst;
5969 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
5970}
5971
5966fn airTagName(self: *Self, inst: Air.Inst.Index) !void {5972fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
5967 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5973 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5968 const operand = try self.resolveInst(un_op);5974 const operand = try self.resolveInst(un_op);
src/arch/riscv64/CodeGen.zig+6
...@@ -1581,6 +1581,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1581,6 +1581,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1581 .atomic_rmw => try func.airAtomicRmw(inst),1581 .atomic_rmw => try func.airAtomicRmw(inst),
1582 .atomic_load => try func.airAtomicLoad(inst),1582 .atomic_load => try func.airAtomicLoad(inst),
1583 .memcpy => try func.airMemcpy(inst),1583 .memcpy => try func.airMemcpy(inst),
1584 .memmove => try func.airMemmove(inst),
1584 .memset => try func.airMemset(inst, false),1585 .memset => try func.airMemset(inst, false),
1585 .memset_safe => try func.airMemset(inst, true),1586 .memset_safe => try func.airMemset(inst, true),
1586 .set_union_tag => try func.airSetUnionTag(inst),1587 .set_union_tag => try func.airSetUnionTag(inst),
...@@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void {...@@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void {
7919 return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });7920 return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
7920}7921}
79217922
7923fn airMemmove(func: *Func, inst: Air.Inst.Index) !void {
7924 _ = inst;
7925 return func.fail("TODO implement airMemmove for riscv64", .{});
7926}
7927
7922fn airTagName(func: *Func, inst: Air.Inst.Index) !void {7928fn airTagName(func: *Func, inst: Air.Inst.Index) !void {
7923 const pt = func.pt;7929 const pt = func.pt;
79247930
src/arch/sparc64/CodeGen.zig+1
...@@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
604 .atomic_rmw => try self.airAtomicRmw(inst),604 .atomic_rmw => try self.airAtomicRmw(inst),
605 .atomic_load => try self.airAtomicLoad(inst),605 .atomic_load => try self.airAtomicLoad(inst),
606 .memcpy => @panic("TODO try self.airMemcpy(inst)"),606 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
607 .memmove => @panic("TODO try self.airMemmove(inst)"),
607 .memset => try self.airMemset(inst, false),608 .memset => try self.airMemset(inst, false),
608 .memset_safe => try self.airMemset(inst, true),609 .memset_safe => try self.airMemset(inst, true),
609 .set_union_tag => try self.airSetUnionTag(inst),610 .set_union_tag => try self.airSetUnionTag(inst),
src/arch/wasm/CodeGen.zig+1
...@@ -2061,6 +2061,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2061,6 +2061,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2061 .c_va_copy,2061 .c_va_copy,
2062 .c_va_end,2062 .c_va_end,
2063 .c_va_start,2063 .c_va_start,
2064 .memmove,
2064 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),2065 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
20652066
2066 .atomic_load => cg.airAtomicLoad(inst),2067 .atomic_load => cg.airAtomicLoad(inst),
src/arch/x86_64/CodeGen.zig+6
...@@ -89453,6 +89453,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -89453,6 +89453,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
89453 .memset => try cg.airMemset(inst, false),89453 .memset => try cg.airMemset(inst, false),
89454 .memset_safe => try cg.airMemset(inst, true),89454 .memset_safe => try cg.airMemset(inst, true),
89455 .memcpy => try cg.airMemcpy(inst),89455 .memcpy => try cg.airMemcpy(inst),
89456 .memmove => try cg.airMemmove(inst),
89456 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),89457 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),
89457 .atomic_load => try cg.airAtomicLoad(inst),89458 .atomic_load => try cg.airAtomicLoad(inst),
89458 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),89459 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
...@@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void {
106472 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });106473 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
106473}106474}
106474106475
106476fn airMemmove(self: *CodeGen, inst: Air.Inst.Index) !void {
106477 _ = inst;
106478 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
106479}
106480
106475fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void {106481fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void {
106476 const pt = self.pt;106482 const pt = self.pt;
106477 const zcu = pt.zcu;106483 const zcu = pt.zcu;
src/codegen/c.zig+14-1
...@@ -3349,6 +3349,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -3349,6 +3349,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
3349 .memset => try airMemset(f, inst, false),3349 .memset => try airMemset(f, inst, false),
3350 .memset_safe => try airMemset(f, inst, true),3350 .memset_safe => try airMemset(f, inst, true),
3351 .memcpy => try airMemcpy(f, inst),3351 .memcpy => try airMemcpy(f, inst),
3352 .memmove => try airMemmove(f, inst),
3352 .set_union_tag => try airSetUnionTag(f, inst),3353 .set_union_tag => try airSetUnionTag(f, inst),
3353 .get_union_tag => try airGetUnionTag(f, inst),3354 .get_union_tag => try airGetUnionTag(f, inst),
3354 .clz => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "clz", .bits),3355 .clz => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "clz", .bits),
...@@ -6976,6 +6977,14 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -6976,6 +6977,14 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6976}6977}
69776978
6978fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {6979fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
6980 return copyOp(f, inst, .memcpy);
6981}
6982
6983fn airMemmove(f: *Function, inst: Air.Inst.Index) !CValue {
6984 return copyOp(f, inst, .memmove);
6985}
6986
6987fn copyOp(f: *Function, inst: Air.Inst.Index, op: enum { memcpy, memmove }) !CValue {
6979 const pt = f.object.dg.pt;6988 const pt = f.object.dg.pt;
6980 const zcu = pt.zcu;6989 const zcu = pt.zcu;
6981 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6990 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -6990,7 +6999,11 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6990,7 +6999,11 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
6990 try writeArrayLen(f, writer, dest_ptr, dest_ty);6999 try writeArrayLen(f, writer, dest_ptr, dest_ty);
6991 try writer.writeAll(" != 0) ");7000 try writer.writeAll(" != 0) ");
6992 }7001 }
6993 try writer.writeAll("memcpy(");7002 const function_paren = switch (op) {
7003 .memcpy => "memcpy(",
7004 .memmove => "memmove(",
7005 };
7006 try writer.writeAll(function_paren);
6994 try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);7007 try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
6995 try writer.writeAll(", ");7008 try writer.writeAll(", ");
6996 try writeSliceOrPtr(f, writer, src_ptr, src_ty);7009 try writeSliceOrPtr(f, writer, src_ptr, src_ty);
src/codegen/llvm.zig+27
...@@ -4940,6 +4940,7 @@ pub const FuncGen = struct {...@@ -4940,6 +4940,7 @@ pub const FuncGen = struct {
4940 .memset => try self.airMemset(inst, false),4940 .memset => try self.airMemset(inst, false),
4941 .memset_safe => try self.airMemset(inst, true),4941 .memset_safe => try self.airMemset(inst, true),
4942 .memcpy => try self.airMemcpy(inst),4942 .memcpy => try self.airMemcpy(inst),
4943 .memmove => try self.airMemmove(inst),
4943 .set_union_tag => try self.airSetUnionTag(inst),4944 .set_union_tag => try self.airSetUnionTag(inst),
4944 .get_union_tag => try self.airGetUnionTag(inst),4945 .get_union_tag => try self.airGetUnionTag(inst),
4945 .clz => try self.airClzCtz(inst, .ctlz),4946 .clz => try self.airClzCtz(inst, .ctlz),
...@@ -9926,6 +9927,32 @@ pub const FuncGen = struct {...@@ -9926,6 +9927,32 @@ pub const FuncGen = struct {
9926 return .none;9927 return .none;
9927 }9928 }
99289929
9930 fn airMemmove(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
9931 const o = self.ng.object;
9932 const pt = o.pt;
9933 const zcu = pt.zcu;
9934 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
9935 const dest_slice = try self.resolveInst(bin_op.lhs);
9936 const dest_ptr_ty = self.typeOf(bin_op.lhs);
9937 const src_slice = try self.resolveInst(bin_op.rhs);
9938 const src_ptr_ty = self.typeOf(bin_op.rhs);
9939 const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty);
9940 const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
9941 const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
9942 const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or
9943 dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
9944
9945 _ = try self.wip.callMemMove(
9946 dest_ptr,
9947 dest_ptr_ty.ptrAlignment(zcu).toLlvm(),
9948 src_ptr,
9949 src_ptr_ty.ptrAlignment(zcu).toLlvm(),
9950 len,
9951 access_kind,
9952 );
9953 return .none;
9954 }
9955
9929 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {9956 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
9930 const o = self.ng.object;9957 const o = self.ng.object;
9931 const pt = o.pt;9958 const pt = o.pt;
src/codegen/spirv.zig+6
...@@ -3344,6 +3344,7 @@ const NavGen = struct {...@@ -3344,6 +3344,7 @@ const NavGen = struct {
3344 .slice => try self.airSlice(inst),3344 .slice => try self.airSlice(inst),
3345 .aggregate_init => try self.airAggregateInit(inst),3345 .aggregate_init => try self.airAggregateInit(inst),
3346 .memcpy => return self.airMemcpy(inst),3346 .memcpy => return self.airMemcpy(inst),
3347 .memmove => return self.airMemmove(inst),
33473348
3348 .slice_ptr => try self.airSliceField(inst, 0),3349 .slice_ptr => try self.airSliceField(inst, 0),
3349 .slice_len => try self.airSliceField(inst, 1),3350 .slice_len => try self.airSliceField(inst, 1),
...@@ -4914,6 +4915,11 @@ const NavGen = struct {...@@ -4914,6 +4915,11 @@ const NavGen = struct {
4914 });4915 });
4915 }4916 }
49164917
4918 fn airMemmove(self: *NavGen, inst: Air.Inst.Index) !void {
4919 _ = inst;
4920 return self.fail("TODO implement airMemcpy for spirv", .{});
4921 }
4922
4917 fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef {4923 fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef {
4918 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4924 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4919 const field_ty = self.typeOfIndex(inst);4925 const field_ty = self.typeOfIndex(inst);
src/print_air.zig+1
...@@ -160,6 +160,7 @@ const Writer = struct {...@@ -160,6 +160,7 @@ const Writer = struct {
160 .cmp_gt_optimized,160 .cmp_gt_optimized,
161 .cmp_neq_optimized,161 .cmp_neq_optimized,
162 .memcpy,162 .memcpy,
163 .memmove,
163 .memset,164 .memset,
164 .memset_safe,165 .memset_safe,
165 => try w.writeBinOp(s, inst),166 => try w.writeBinOp(s, inst),
src/print_zir.zig+1
...@@ -413,6 +413,7 @@ const Writer = struct {...@@ -413,6 +413,7 @@ const Writer = struct {
413 .min,413 .min,
414 .memcpy,414 .memcpy,
415 .memset,415 .memset,
416 .memmove,
416 .elem_ptr_node,417 .elem_ptr_node,
417 .elem_val_node,418 .elem_val_node,
418 .elem_ptr,419 .elem_ptr,
test/behavior.zig+1
...@@ -55,6 +55,7 @@ test {...@@ -55,6 +55,7 @@ test {
55 _ = @import("behavior/member_func.zig");55 _ = @import("behavior/member_func.zig");
56 _ = @import("behavior/memcpy.zig");56 _ = @import("behavior/memcpy.zig");
57 _ = @import("behavior/memset.zig");57 _ = @import("behavior/memset.zig");
58 _ = @import("behavior/memmove.zig");
58 _ = @import("behavior/merge_error_sets.zig");59 _ = @import("behavior/merge_error_sets.zig");
59 _ = @import("behavior/muladd.zig");60 _ = @import("behavior/muladd.zig");
60 _ = @import("behavior/multiple_externs_with_conflicting_types.zig");61 _ = @import("behavior/multiple_externs_with_conflicting_types.zig");
test/behavior/builtin_functions_returning_void_or_noreturn.zig+2
...@@ -6,6 +6,7 @@ var x: u8 = 1;...@@ -6,6 +6,7 @@ var x: u8 = 1;
66
7// This excludes builtin functions that return void or noreturn that cannot be tested.7// This excludes builtin functions that return void or noreturn that cannot be tested.
8test {8test {
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -17,6 +18,7 @@ test {...@@ -17,6 +18,7 @@ test {
17 try testing.expectEqual(void, @TypeOf(@breakpoint()));18 try testing.expectEqual(void, @TypeOf(@breakpoint()));
18 try testing.expectEqual({}, @export(&x, .{ .name = "x" }));19 try testing.expectEqual({}, @export(&x, .{ .name = "x" }));
19 try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));20 try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
21 try testing.expectEqual({}, @memmove(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
20 try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined));22 try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined));
21 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));23 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
22 try testing.expectEqual({}, @prefetch(&val, .{}));24 try testing.expectEqual({}, @prefetch(&val, .{}));
test/behavior/memmove.zig created+183
...@@ -0,0 +1,183 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "memmove and memset intrinsics" {
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
12 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13
14 try testMemmoveMemset();
15 try comptime testMemmoveMemset();
16}
17
18fn testMemmoveMemset() !void {
19 var foo: [20]u8 = undefined;
20
21 @memset(foo[0..10], 'A');
22 @memset(foo[10..20], 'B');
23
24 try expect(foo[0] == 'A');
25 try expect(foo[11] == 'B');
26 try expect(foo[19] == 'B');
27
28 @memmove(foo[10..20], foo[0..10]);
29
30 try expect(foo[0] == 'A');
31 try expect(foo[11] == 'A');
32 try expect(foo[19] == 'A');
33}
34
35test "@memmove with both operands single-ptr-to-array, one is null-terminated" {
36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
42 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
43
44 try testMemmoveBothSinglePtrArrayOneIsNullTerminated();
45 try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated();
46}
47
48fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void {
49 var buf: [100]u8 = undefined;
50 const suffix = "hello";
51 @memmove(buf[buf.len - suffix.len ..], suffix);
52 try expect(buf[95] == 'h');
53 try expect(buf[96] == 'e');
54 try expect(buf[97] == 'l');
55 try expect(buf[98] == 'l');
56 try expect(buf[99] == 'o');
57
58 const start = buf.len - suffix.len - 3;
59 const end = start + suffix.len;
60 @memmove(buf[start..end], buf[buf.len - suffix.len ..]);
61 try expect(buf[92] == 'h');
62 try expect(buf[93] == 'e');
63 try expect(buf[94] == 'l');
64 try expect(buf[95] == 'l');
65 try expect(buf[96] == 'o');
66 try expect(buf[97] == 'l');
67 try expect(buf[98] == 'l');
68 try expect(buf[99] == 'o');
69
70 @memmove(buf[start + 2 .. end + 2], buf[start..end]);
71 try expect(buf[92] == 'h');
72 try expect(buf[93] == 'e');
73 try expect(buf[94] == 'h');
74 try expect(buf[95] == 'e');
75 try expect(buf[96] == 'l');
76 try expect(buf[97] == 'l');
77 try expect(buf[98] == 'o');
78 try expect(buf[99] == 'o');
79}
80
81test "@memmove dest many pointer" {
82 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
89
90 try testMemmoveDestManyPtr();
91 try comptime testMemmoveDestManyPtr();
92}
93
94fn testMemmoveDestManyPtr() !void {
95 var str = "hello".*;
96 var buf: [8]u8 = undefined;
97 var len: usize = 5;
98 _ = &len;
99 @memmove(@as([*]u8, @ptrCast(&buf)), @as([*]const u8, @ptrCast(&str))[0..len]);
100 try expect(buf[0] == 'h');
101 try expect(buf[1] == 'e');
102 try expect(buf[2] == 'l');
103 try expect(buf[3] == 'l');
104 try expect(buf[4] == 'o');
105 @memmove(buf[3..].ptr, buf[0..len]);
106 try expect(buf[0] == 'h');
107 try expect(buf[1] == 'e');
108 try expect(buf[2] == 'l');
109 try expect(buf[3] == 'h');
110 try expect(buf[4] == 'e');
111 try expect(buf[5] == 'l');
112 try expect(buf[6] == 'l');
113 try expect(buf[7] == 'o');
114 @memmove(buf[2..7].ptr, buf[3 .. len + 3]);
115 try expect(buf[0] == 'h');
116 try expect(buf[1] == 'e');
117 try expect(buf[2] == 'h');
118 try expect(buf[3] == 'e');
119 try expect(buf[4] == 'l');
120 try expect(buf[5] == 'l');
121 try expect(buf[6] == 'o');
122 try expect(buf[7] == 'o');
123}
124
125test "@memmove slice" {
126 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
127 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
128 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
129 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
130 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
131 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
132 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
133
134 try testMemmoveSlice();
135 try comptime testMemmoveSlice();
136}
137
138fn testMemmoveSlice() !void {
139 var buf: [8]u8 = undefined;
140 const dst1: []u8 = buf[0..5];
141 const dst2: []u8 = buf[3..8];
142 const dst3: []u8 = buf[2..7];
143 const src: []const u8 = "hello";
144 @memmove(dst1, src);
145 try expect(buf[0] == 'h');
146 try expect(buf[1] == 'e');
147 try expect(buf[2] == 'l');
148 try expect(buf[3] == 'l');
149 try expect(buf[4] == 'o');
150 @memmove(dst2, dst1);
151 try expect(buf[0] == 'h');
152 try expect(buf[1] == 'e');
153 try expect(buf[2] == 'l');
154 try expect(buf[3] == 'h');
155 try expect(buf[4] == 'e');
156 try expect(buf[5] == 'l');
157 try expect(buf[6] == 'l');
158 try expect(buf[7] == 'o');
159 @memmove(dst3, dst2);
160 try expect(buf[0] == 'h');
161 try expect(buf[1] == 'e');
162 try expect(buf[2] == 'h');
163 try expect(buf[3] == 'e');
164 try expect(buf[4] == 'l');
165 try expect(buf[5] == 'l');
166 try expect(buf[6] == 'o');
167 try expect(buf[7] == 'o');
168}
169
170comptime {
171 const S = struct {
172 buffer: [8]u8 = undefined,
173 fn set(self: *@This(), items: []const u8) void {
174 @memmove(self.buffer[0..items.len], items);
175 @memmove(self.buffer[3..], self.buffer[0..items.len]);
176 @memmove(self.buffer[2 .. 2 + items.len], self.buffer[3..]);
177 }
178 };
179
180 var s = S{};
181 s.set("hello");
182 if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");
183}
test/cases/compile_errors/@memmove_type_mismatch.zig created+218
...@@ -0,0 +1,218 @@
1export fn foo() void {
2 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
3 const dest: []u8 = &buf;
4 const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
5
6 @memmove(dest, src);
7}
8
9export fn bar() void {
10 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
11 const dest: []u8 = &buf;
12 const src: *align(1) [8]u16 = @ptrCast(&buf);
13
14 @memmove(dest, src);
15}
16
17export fn baz() void {
18 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
19 const dest: []u8 = &buf;
20 const src: [*]align(1) u16 = @ptrCast(&buf);
21
22 @memmove(dest, src);
23}
24
25export fn qux() void {
26 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
27 const dest: *[8]u8 = &buf;
28 const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
29
30 @memmove(dest, src);
31}
32
33export fn quux() void {
34 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
35 const dest: *[8]u8 = &buf;
36 const src: *align(1) [8]u16 = @ptrCast(&buf);
37
38 @memmove(dest, src);
39}
40
41export fn quuux() void {
42 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
43 const dest: *[8]u8 = &buf;
44 const src: [*]align(1) u16 = @ptrCast(&buf);
45
46 @memmove(dest, src);
47}
48
49export fn foo2() void {
50 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
51 const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
52 const src: []u8 = &buf;
53
54 @memmove(dest, src);
55}
56
57export fn bar2() void {
58 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
59 const dest: *align(1) [8]u16 = @ptrCast(&buf);
60 const src: []u8 = &buf;
61
62 @memmove(dest, src);
63}
64
65export fn baz2() void {
66 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
67 const dest: [*]align(1) u16 = @ptrCast(&buf);
68 const src: []u8 = &buf;
69
70 @memmove(dest, src);
71}
72
73export fn qux2() void {
74 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
75 const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
76 const src: *[8]u8 = &buf;
77
78 @memmove(dest, src);
79}
80
81export fn quux2() void {
82 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
83 const dest: *align(1) [8]u16 = @ptrCast(&buf);
84 const src: *[8]u8 = &buf;
85
86 @memmove(dest, src);
87}
88
89export fn quuux2() void {
90 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
91 const dest: [*]align(1) u16 = @ptrCast(&buf);
92 const src: *[8]u8 = &buf;
93
94 @memmove(dest, src);
95}
96
97comptime {
98 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
99 const dest: []u8 = &buf;
100 const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
101 @memmove(dest, src);
102}
103
104comptime {
105 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
106 const dest: []u8 = &buf;
107 const src: *align(1) [8]u16 = @ptrCast(&buf);
108 @memmove(dest, src);
109}
110
111comptime {
112 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
113 const dest: []u8 = &buf;
114 const src: [*]align(1) u16 = @ptrCast(&buf);
115 @memmove(dest, src);
116}
117
118comptime {
119 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
120 const dest: *[8]u8 = &buf;
121 const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
122 @memmove(dest, src);
123}
124
125comptime {
126 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
127 const dest: *[8]u8 = &buf;
128 const src: *align(1) [8]u16 = @ptrCast(&buf);
129 @memmove(dest, src);
130}
131
132comptime {
133 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
134 const dest: *[8]u8 = &buf;
135 const src: [*]align(1) u16 = @ptrCast(&buf);
136 @memmove(dest, src);
137}
138
139comptime {
140 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
141 const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
142 const src: []u8 = &buf;
143 @memmove(dest, src);
144}
145
146comptime {
147 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
148 const dest: *align(1) [8]u16 = @ptrCast(&buf);
149 const src: []u8 = &buf;
150 @memmove(dest, src);
151}
152
153comptime {
154 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
155 const dest: [*]align(1) u16 = @ptrCast(&buf);
156 const src: []u8 = &buf;
157 @memmove(dest, src);
158}
159
160comptime {
161 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
162 const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
163 const src: *[8]u8 = &buf;
164 @memmove(dest, src);
165}
166
167comptime {
168 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
169 const dest: *align(1) [8]u16 = @ptrCast(&buf);
170 const src: *[8]u8 = &buf;
171 @memmove(dest, src);
172}
173
174comptime {
175 var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
176 const dest: [*]align(1) u16 = @ptrCast(&buf);
177 const src: *[8]u8 = &buf;
178 @memmove(dest, src);
179}
180
181// error
182//
183// :6:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
184// :6:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
185// :14:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
186// :14:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
187// :22:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
188// :22:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
189// :30:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
190// :30:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
191// :38:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
192// :38:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
193// :46:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
194// :46:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
195// :54:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
196// :62:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
197// :70:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
198// :78:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
199// :86:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
200// :94:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
201// :101:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
202// :101:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
203// :108:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
204// :108:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
205// :115:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
206// :115:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
207// :122:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
208// :122:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
209// :129:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
210// :129:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
211// :136:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
212// :136:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
213// :143:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
214// :150:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
215// :157:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
216// :164:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
217// :171:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
218// :178:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
test/cases/compile_errors/bad_panic_call_signature.zig+1
...@@ -29,6 +29,7 @@ pub const panic = struct {...@@ -29,6 +29,7 @@ pub const panic = struct {
29 pub const forLenMismatch = simple_panic.forLenMismatch;29 pub const forLenMismatch = simple_panic.forLenMismatch;
30 pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;30 pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;
31 pub const memcpyAlias = simple_panic.memcpyAlias;31 pub const memcpyAlias = simple_panic.memcpyAlias;
32 pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch;
32 pub const noreturnReturned = simple_panic.noreturnReturned;33 pub const noreturnReturned = simple_panic.noreturnReturned;
33};34};
3435
test/cases/compile_errors/bad_panic_generic_signature.zig+1
...@@ -25,6 +25,7 @@ pub const panic = struct {...@@ -25,6 +25,7 @@ pub const panic = struct {
25 pub const forLenMismatch = simple_panic.forLenMismatch;25 pub const forLenMismatch = simple_panic.forLenMismatch;
26 pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;26 pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;
27 pub const memcpyAlias = simple_panic.memcpyAlias;27 pub const memcpyAlias = simple_panic.memcpyAlias;
28 pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch;
28 pub const noreturnReturned = simple_panic.noreturnReturned;29 pub const noreturnReturned = simple_panic.noreturnReturned;
29};30};
3031
test/cases/compile_errors/comptime_var_referenced_at_runtime.zig+11
...@@ -63,6 +63,14 @@ export fn far() void {...@@ -63,6 +63,14 @@ export fn far() void {
63 @memset(&rt, elem);63 @memset(&rt, elem);
64}64}
6565
66export fn bax() void {
67 comptime var x: [2]u32 = undefined;
68 x = .{ 1, 2 };
69
70 var rt: [2]u32 = undefined;
71 @memmove(&rt, &x);
72}
73
66// error74// error
67//75//
68// :5:19: error: runtime value contains reference to comptime var76// :5:19: error: runtime value contains reference to comptime var
...@@ -92,3 +100,6 @@ export fn far() void {...@@ -92,3 +100,6 @@ export fn far() void {
92// :63:18: error: runtime value contains reference to comptime var100// :63:18: error: runtime value contains reference to comptime var
93// :63:18: note: comptime var pointers are not available at runtime101// :63:18: note: comptime var pointers are not available at runtime
94// :59:27: note: 'runtime_value' points to comptime var declared here102// :59:27: note: 'runtime_value' points to comptime var declared here
103// :71:19: error: runtime value contains reference to comptime var
104// :71:19: note: comptime var pointers are not available at runtime
105// :67:30: note: 'runtime_value' points to comptime var declared here
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig+43-1
...@@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void {...@@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void {
28 var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };28 var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
29 @memcpy(&buf1, &buf2);29 @memcpy(&buf1, &buf2);
30}30}
31pub export fn memset_array() void {31pub export fn memcpy_array() void {
32 const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };32 const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
33 @memcpy(buf, 1);33 @memcpy(buf, 1);
34}34}
35pub export fn entry_memmove() void {
36 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
37 const slice: []u8 = &buf;
38 const a: u32 = 1234;
39 @memmove(slice.ptr, @as([*]const u8, @ptrCast(&a)));
40}
41pub export fn entry1_memmove() void {
42 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
43 const ptr: *u8 = &buf[0];
44 @memmove(ptr, 0);
45}
46pub export fn non_matching_lengths_memmove() void {
47 var buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
48 var buf2: [6]u8 = .{ 1, 2, 3, 4, 5, 6 };
49 @memmove(&buf2, &buf1);
50}
51pub export fn memcpy_const_dest_ptr_memmove() void {
52 const buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
53 var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
54 @memmove(&buf1, &buf2);
55}
56pub export fn memmove_array() void {
57 const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
58 @memmove(buf, 1);
59}
60pub export fn memset_array() void {
61 const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
62 @memset(buf, 1);
63}
3564
36// error65// error
37// backend=stage266// backend=stage2
...@@ -51,3 +80,16 @@ pub export fn memset_array() void {...@@ -51,3 +80,16 @@ pub export fn memset_array() void {
51// :29:13: error: cannot memcpy to constant pointer80// :29:13: error: cannot memcpy to constant pointer
52// :33:13: error: type '[5]u8' is not an indexable pointer81// :33:13: error: type '[5]u8' is not an indexable pointer
53// :33:13: note: operand must be a slice, a many pointer or a pointer to an array82// :33:13: note: operand must be a slice, a many pointer or a pointer to an array
83// :39:5: error: unknown @memmove length
84// :39:19: note: destination type '[*]u8' provides no length
85// :39:25: note: source type '[*]const u8' provides no length
86// :44:14: error: type '*u8' is not an indexable pointer
87// :44:14: note: operand must be a slice, a many pointer or a pointer to an array
88// :49:5: error: non-matching @memmove lengths
89// :49:14: note: length 6 here
90// :49:21: note: length 5 here
91// :54:14: error: cannot memmove to constant pointer
92// :58:14: error: type '[5]u8' is not an indexable pointer
93// :58:14: note: operand must be a slice, a many pointer or a pointer to an array
94// :62:13: error: type '[5]u8' is not an indexable pointer
95// :62:13: note: operand must be a slice, a many pointer or a pointer to an array
test/cases/safety/memcpy_alias.zig+1
...@@ -12,6 +12,7 @@ pub fn main() !void {...@@ -12,6 +12,7 @@ pub fn main() !void {
12 var len: usize = 5;12 var len: usize = 5;
13 _ = &len;13 _ = &len;
14 @memcpy(buffer[0..len], buffer[4 .. 4 + len]);14 @memcpy(buffer[0..len], buffer[4 .. 4 + len]);
15 return error.TestFailed;
15}16}
16// run17// run
17// backend=stage2,llvm18// backend=stage2,llvm
test/cases/safety/memcpy_len_mismatch.zig+1
...@@ -12,6 +12,7 @@ pub fn main() !void {...@@ -12,6 +12,7 @@ pub fn main() !void {
12 var len: usize = 5;12 var len: usize = 5;
13 _ = &len;13 _ = &len;
14 @memcpy(buffer[0..len], buffer[len .. len + 4]);14 @memcpy(buffer[0..len], buffer[len .. len + 4]);
15 return error.TestFailed;
15}16}
16// run17// run
17// backend=stage2,llvm18// backend=stage2,llvm
test/cases/safety/memmove_len_mismatch.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "@memmove arguments have non-equal lengths")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 var buffer = [2]u8{ 1, 2 } ** 5;
12 var len: usize = 5;
13 _ = &len;
14 @memmove(buffer[0..len], buffer[len .. len + 4]);
15 return error.TestFailed;
16}
17// run
18// backend=llvm
19// target=native
test/incremental/change_panic_handler_explicit+3
...@@ -39,6 +39,7 @@ pub const panic = struct {...@@ -39,6 +39,7 @@ pub const panic = struct {
39 pub const forLenMismatch = no_panic.forLenMismatch;39 pub const forLenMismatch = no_panic.forLenMismatch;
40 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;40 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
41 pub const memcpyAlias = no_panic.memcpyAlias;41 pub const memcpyAlias = no_panic.memcpyAlias;
42 pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
42 pub const noreturnReturned = no_panic.noreturnReturned;43 pub const noreturnReturned = no_panic.noreturnReturned;
43};44};
44fn myPanic(msg: []const u8, _: ?usize) noreturn {45fn myPanic(msg: []const u8, _: ?usize) noreturn {
...@@ -86,6 +87,7 @@ pub const panic = struct {...@@ -86,6 +87,7 @@ pub const panic = struct {
86 pub const forLenMismatch = no_panic.forLenMismatch;87 pub const forLenMismatch = no_panic.forLenMismatch;
87 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;88 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
88 pub const memcpyAlias = no_panic.memcpyAlias;89 pub const memcpyAlias = no_panic.memcpyAlias;
90 pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
89 pub const noreturnReturned = no_panic.noreturnReturned;91 pub const noreturnReturned = no_panic.noreturnReturned;
90};92};
91fn myPanic(msg: []const u8, _: ?usize) noreturn {93fn myPanic(msg: []const u8, _: ?usize) noreturn {
...@@ -133,6 +135,7 @@ pub const panic = struct {...@@ -133,6 +135,7 @@ pub const panic = struct {
133 pub const forLenMismatch = no_panic.forLenMismatch;135 pub const forLenMismatch = no_panic.forLenMismatch;
134 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;136 pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
135 pub const memcpyAlias = no_panic.memcpyAlias;137 pub const memcpyAlias = no_panic.memcpyAlias;
138 pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
136 pub const noreturnReturned = no_panic.noreturnReturned;139 pub const noreturnReturned = no_panic.noreturnReturned;
137};140};
138fn myPanicNew(msg: []const u8, _: ?usize) noreturn {141fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
test/standalone/zerolength_check/src/main.zig+1
...@@ -5,6 +5,7 @@ test {...@@ -5,6 +5,7 @@ test {
5 const source = foo();5 const source = foo();
66
7 @memcpy(dest, source);7 @memcpy(dest, source);
8 @memmove(dest, source);
8 @memset(dest, 4);9 @memset(dest, 4);
9 @memset(dest, undefined);10 @memset(dest, undefined);
1011