mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
commit
1b76d4c53a
@ -5149,6 +5149,23 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
{#syntax#}std.crypto.secureZero{#endsyntax#}</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@memmove#}
|
||||
<pre>{#syntax#}@memmove(dest, source) void{#endsyntax#}</pre>
|
||||
<p>This function copies bytes from one region of memory to another, but unlike
|
||||
{#link|@memcpy#} the regions may overlap.</p>
|
||||
<p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or
|
||||
a mutable many-item {#link|pointer|Pointers#}. It may have any
|
||||
alignment, and it may have any element type.</p>
|
||||
<p>{#syntax#}source{#endsyntax#} must be a slice, a pointer to
|
||||
an array, or a many-item {#link|pointer|Pointers#}. It may
|
||||
have any alignment, and it may have any element type.</p>
|
||||
<p>The {#syntax#}source{#endsyntax#} element type must have the same in-memory
|
||||
representation as the {#syntax#}dest{#endsyntax#} element type.</p>
|
||||
<p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and
|
||||
{#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided,
|
||||
they must be equal.</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@min#}
|
||||
<pre>{#syntax#}@min(...) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
|
||||
@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type {
|
||||
@branchHint(.cold);
|
||||
call("@memcpy arguments alias", @returnAddress());
|
||||
}
|
||||
pub fn memmoveLenMismatch() noreturn {
|
||||
@branchHint(.cold);
|
||||
call("@memmove arguments have non-equal lengths", @returnAddress());
|
||||
}
|
||||
pub fn noreturnReturned() noreturn {
|
||||
@branchHint(.cold);
|
||||
call("'noreturn' function returned", @returnAddress());
|
||||
|
||||
@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn {
|
||||
@trap();
|
||||
}
|
||||
|
||||
pub fn memmoveLenMismatch() noreturn {
|
||||
@branchHint(.cold);
|
||||
@trap();
|
||||
}
|
||||
|
||||
pub fn noreturnReturned() noreturn {
|
||||
@branchHint(.cold);
|
||||
@trap();
|
||||
|
||||
@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn {
|
||||
call("@memcpy arguments alias", null);
|
||||
}
|
||||
|
||||
pub fn memmoveLenMismatch() noreturn {
|
||||
call("@memmove arguments have non-equal lengths", null);
|
||||
}
|
||||
|
||||
pub fn noreturnReturned() noreturn {
|
||||
call("'noreturn' function returned", null);
|
||||
}
|
||||
|
||||
@ -232,6 +232,7 @@ test "Allocator alloc and remap with zero-bit type" {
|
||||
/// Copy all of source into dest at position 0.
|
||||
/// dest.len must be >= source.len.
|
||||
/// If the slices overlap, dest.ptr must be <= src.ptr.
|
||||
/// This function is deprecated; use @memmove instead.
|
||||
pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {
|
||||
for (dest[0..source.len], source) |*d, s| d.* = s;
|
||||
}
|
||||
@ -239,6 +240,7 @@ pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {
|
||||
/// Copy all of source into dest at position 0.
|
||||
/// dest.len must be >= source.len.
|
||||
/// If the slices overlap, dest.ptr must be >= src.ptr.
|
||||
/// This function is deprecated; use @memmove instead.
|
||||
pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
|
||||
// TODO instead of manually doing this check for the whole array
|
||||
// and turning off runtime safety, the compiler should detect loops like
|
||||
|
||||
@ -2919,6 +2919,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.set_runtime_safety,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.memmove,
|
||||
.validate_deref,
|
||||
.validate_destructure,
|
||||
.save_err_ret_index,
|
||||
@ -9717,6 +9718,13 @@ fn builtinCall(
|
||||
});
|
||||
return rvalue(gz, ri, .void_value, node);
|
||||
},
|
||||
.memmove => {
|
||||
_ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{
|
||||
.lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
|
||||
.rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
|
||||
});
|
||||
return rvalue(gz, ri, .void_value, node);
|
||||
},
|
||||
.shuffle => {
|
||||
const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
|
||||
.elem_type = try typeExpr(gz, scope, params[0]),
|
||||
|
||||
@ -1055,7 +1055,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
|
||||
_ = try astrl.expr(args[2], block, ResultInfo.none);
|
||||
return false;
|
||||
},
|
||||
.memcpy => {
|
||||
.memcpy, .memmove => {
|
||||
_ = try astrl.expr(args[0], block, ResultInfo.none);
|
||||
_ = try astrl.expr(args[1], block, ResultInfo.none);
|
||||
return false;
|
||||
|
||||
@ -68,6 +68,7 @@ pub const Tag = enum {
|
||||
max,
|
||||
memcpy,
|
||||
memset,
|
||||
memmove,
|
||||
min,
|
||||
wasm_memory_size,
|
||||
wasm_memory_grow,
|
||||
@ -641,6 +642,13 @@ pub const list = list: {
|
||||
.param_count = 2,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@memmove",
|
||||
.{
|
||||
.tag = .memmove,
|
||||
.param_count = 2,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@min",
|
||||
.{
|
||||
|
||||
@ -986,6 +986,9 @@ pub const Inst = struct {
|
||||
/// Implements the `@memset` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Bin`.
|
||||
memset,
|
||||
/// Implements the `@memmove` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Bin`.
|
||||
memmove,
|
||||
/// Implements the `@min` builtin for 2 args.
|
||||
/// Uses the `pl_node` union field with payload `Bin`
|
||||
min,
|
||||
@ -1272,6 +1275,7 @@ pub const Inst = struct {
|
||||
.max,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.memmove,
|
||||
.min,
|
||||
.c_import,
|
||||
.@"resume",
|
||||
@ -1355,6 +1359,7 @@ pub const Inst = struct {
|
||||
.set_runtime_safety,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.memmove,
|
||||
.check_comptime_control_flow,
|
||||
.@"defer",
|
||||
.defer_err_code,
|
||||
@ -1832,6 +1837,7 @@ pub const Inst = struct {
|
||||
.max = .pl_node,
|
||||
.memcpy = .pl_node,
|
||||
.memset = .pl_node,
|
||||
.memmove = .pl_node,
|
||||
.min = .pl_node,
|
||||
.c_import = .pl_node,
|
||||
|
||||
@ -4291,6 +4297,7 @@ fn findTrackableInner(
|
||||
.mul_add,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.memmove,
|
||||
.min,
|
||||
.max,
|
||||
.alloc,
|
||||
|
||||
@ -6125,6 +6125,36 @@ pub const WipFunction = struct {
|
||||
return value.unwrap().instruction;
|
||||
}
|
||||
|
||||
pub fn callMemMove(
|
||||
self: *WipFunction,
|
||||
dst: Value,
|
||||
dst_align: Alignment,
|
||||
src: Value,
|
||||
src_align: Alignment,
|
||||
len: Value,
|
||||
kind: MemoryAccessKind,
|
||||
) Allocator.Error!Instruction.Index {
|
||||
var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })};
|
||||
var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })};
|
||||
const value = try self.callIntrinsic(
|
||||
.normal,
|
||||
try self.builder.fnAttrs(&.{
|
||||
.none,
|
||||
.none,
|
||||
try self.builder.attrs(&dst_attrs),
|
||||
try self.builder.attrs(&src_attrs),
|
||||
}),
|
||||
.memmove,
|
||||
&.{ dst.typeOfWip(self), src.typeOfWip(self), len.typeOfWip(self) },
|
||||
&.{ dst, src, len, switch (kind) {
|
||||
.normal => Value.false,
|
||||
.@"volatile" => Value.true,
|
||||
} },
|
||||
undefined,
|
||||
);
|
||||
return value.unwrap().instruction;
|
||||
}
|
||||
|
||||
pub fn callMemSet(
|
||||
self: *WipFunction,
|
||||
dst: Value,
|
||||
|
||||
@ -481,6 +481,7 @@
|
||||
|
||||
zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t);
|
||||
zig_extern void *memset (void *, int, size_t);
|
||||
zig_extern void *memmove (void *, void const *, size_t);
|
||||
|
||||
/* ================ Bool and 8/16/32/64-bit Integer Support ================= */
|
||||
|
||||
|
||||
14
src/Air.zig
14
src/Air.zig
@ -730,6 +730,18 @@ pub const Inst = struct {
|
||||
/// source being a pointer-to-array), then it is guaranteed to be
|
||||
/// greater than zero.
|
||||
memcpy,
|
||||
/// Given dest pointer and source pointer, copy elements from source to dest.
|
||||
/// Dest pointer is either a slice or a pointer to array.
|
||||
/// The dest element type may be any type.
|
||||
/// Source pointer must have same element type as dest element type.
|
||||
/// Dest slice may have any alignment; source pointer may have any alignment.
|
||||
/// The two memory regions may overlap.
|
||||
/// Result type is always void.
|
||||
/// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
|
||||
/// If the length is compile-time known (due to the destination or
|
||||
/// source being a pointer-to-array), then it is guaranteed to be
|
||||
/// greater than zero.
|
||||
memmove,
|
||||
|
||||
/// Uses the `ty_pl` field with payload `Cmpxchg`.
|
||||
cmpxchg_weak,
|
||||
@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
.set_union_tag,
|
||||
.prefetch,
|
||||
.set_err_return_trace,
|
||||
@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
.cmpxchg_weak,
|
||||
.cmpxchg_strong,
|
||||
.atomic_store_unordered,
|
||||
|
||||
@ -83,6 +83,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
.atomic_store_unordered,
|
||||
.atomic_store_monotonic,
|
||||
.atomic_store_release,
|
||||
|
||||
@ -300,6 +300,7 @@ pub fn categorizeOperand(
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
=> {
|
||||
const o = air_datas[@intFromEnum(inst)].bin_op;
|
||||
if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
|
||||
@ -936,6 +937,7 @@ fn analyzeInst(
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
=> {
|
||||
const o = inst_datas[@intFromEnum(inst)].bin_op;
|
||||
return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });
|
||||
|
||||
@ -267,6 +267,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
||||
.memset,
|
||||
.memset_safe,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
=> {
|
||||
const bin_op = data[@intFromEnum(inst)].bin_op;
|
||||
try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
|
||||
57
src/Sema.zig
57
src/Sema.zig
@ -1583,6 +1583,11 @@ fn analyzeBodyInner(
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.memmove => {
|
||||
try sema.zirMemmove(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.check_comptime_control_flow => {
|
||||
if (!block.isComptime()) {
|
||||
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
|
||||
}
|
||||
|
||||
fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
return sema.analyzeCopy(block, inst, .memcpy);
|
||||
}
|
||||
|
||||
fn zirMemmove(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
return sema.analyzeCopy(block, inst, .memmove);
|
||||
}
|
||||
|
||||
fn analyzeCopy(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
op: enum { memcpy, memmove },
|
||||
) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const src = block.nodeOffset(inst_data.src_node);
|
||||
@ -25625,12 +25643,12 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
const zcu = pt.zcu;
|
||||
|
||||
if (dest_ty.isConstPtr(zcu)) {
|
||||
return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});
|
||||
return sema.fail(block, dest_src, "cannot {s} to constant pointer", .{@tagName(op)});
|
||||
}
|
||||
|
||||
if (dest_len == .none and src_len == .none) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(src, "unknown @memcpy length", .{});
|
||||
const msg = try sema.errMsg(src, "unknown @{s} length", .{@tagName(op)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{
|
||||
dest_ty.fmt(pt),
|
||||
@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
|
||||
if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(src, "non-matching @memcpy lengths", .{});
|
||||
const msg = try sema.errMsg(src, "non-matching @{s} lengths", .{@tagName(op)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(dest_src, msg, "length {} here", .{
|
||||
dest_len_val.fmtValueSema(pt, sema),
|
||||
@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
|
||||
if (block.wantSafety()) {
|
||||
const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
|
||||
try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch);
|
||||
const panic_id: Zcu.SimplePanicId = switch (op) {
|
||||
.memcpy => .memcpy_len_mismatch,
|
||||
.memmove => .memmove_len_mismatch,
|
||||
};
|
||||
try sema.addSafetyCheck(block, src, ok, panic_id);
|
||||
}
|
||||
} else if (dest_len != .none) {
|
||||
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
|
||||
return;
|
||||
}
|
||||
|
||||
const check_aliasing = switch (op) {
|
||||
.memcpy => true,
|
||||
.memmove => false,
|
||||
};
|
||||
|
||||
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;
|
||||
@ -25733,12 +25760,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
|
||||
const len_u64 = try len_val.?.toUnsignedIntSema(pt);
|
||||
|
||||
if (Value.doPointersOverlap(
|
||||
raw_src_ptr,
|
||||
raw_dest_ptr,
|
||||
len_u64,
|
||||
zcu,
|
||||
)) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
|
||||
if (check_aliasing) {
|
||||
if (Value.doPointersOverlap(
|
||||
raw_src_ptr,
|
||||
raw_dest_ptr,
|
||||
len_u64,
|
||||
zcu,
|
||||
)) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
|
||||
}
|
||||
|
||||
if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
|
||||
|
||||
@ -25810,7 +25839,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
try sema.validateRuntimeValue(block, src_src, src_ptr);
|
||||
|
||||
// Aliasing safety check.
|
||||
if (block.wantSafety()) {
|
||||
if (check_aliasing and block.wantSafety()) {
|
||||
const len = if (len_val) |v|
|
||||
Air.internedToRef(v.toIntern())
|
||||
else if (dest_len != .none)
|
||||
@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
||||
}
|
||||
|
||||
_ = try block.addInst(.{
|
||||
.tag = .memcpy,
|
||||
.tag = switch (op) {
|
||||
.memcpy => .memcpy,
|
||||
.memmove => .memmove,
|
||||
},
|
||||
.data = .{ .bin_op = .{
|
||||
.lhs = new_dest_ptr,
|
||||
.rhs = new_src_ptr,
|
||||
@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ
|
||||
.@"panic.forLenMismatch",
|
||||
.@"panic.memcpyLenMismatch",
|
||||
.@"panic.memcpyAlias",
|
||||
.@"panic.memmoveLenMismatch",
|
||||
.@"panic.noreturnReturned",
|
||||
=> try pt.funcType(.{
|
||||
.param_types = &.{},
|
||||
|
||||
@ -302,6 +302,7 @@ pub const BuiltinDecl = enum {
|
||||
@"panic.forLenMismatch",
|
||||
@"panic.memcpyLenMismatch",
|
||||
@"panic.memcpyAlias",
|
||||
@"panic.memmoveLenMismatch",
|
||||
@"panic.noreturnReturned",
|
||||
|
||||
VaList,
|
||||
@ -379,6 +380,7 @@ pub const BuiltinDecl = enum {
|
||||
.@"panic.forLenMismatch",
|
||||
.@"panic.memcpyLenMismatch",
|
||||
.@"panic.memcpyAlias",
|
||||
.@"panic.memmoveLenMismatch",
|
||||
.@"panic.noreturnReturned",
|
||||
=> .func,
|
||||
};
|
||||
@ -446,6 +448,7 @@ pub const SimplePanicId = enum {
|
||||
for_len_mismatch,
|
||||
memcpy_len_mismatch,
|
||||
memcpy_alias,
|
||||
memmove_len_mismatch,
|
||||
noreturn_returned,
|
||||
|
||||
pub fn toBuiltin(id: SimplePanicId) BuiltinDecl {
|
||||
@ -470,6 +473,7 @@ pub const SimplePanicId = enum {
|
||||
.for_len_mismatch => .@"panic.forLenMismatch",
|
||||
.memcpy_len_mismatch => .@"panic.memcpyLenMismatch",
|
||||
.memcpy_alias => .@"panic.memcpyAlias",
|
||||
.memmove_len_mismatch => .@"panic.memmoveLenMismatch",
|
||||
.noreturn_returned => .@"panic.noreturnReturned",
|
||||
// zig fmt: on
|
||||
};
|
||||
|
||||
@ -760,6 +760,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.atomic_rmw => try self.airAtomicRmw(inst),
|
||||
.atomic_load => try self.airAtomicLoad(inst),
|
||||
.memcpy => try self.airMemcpy(inst),
|
||||
.memmove => try self.airMemmove(inst),
|
||||
.memset => try self.airMemset(inst, false),
|
||||
.memset_safe => try self.airMemset(inst, true),
|
||||
.set_union_tag => try self.airSetUnionTag(inst),
|
||||
@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airMemmove(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
|
||||
@ -749,6 +749,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.atomic_rmw => try self.airAtomicRmw(inst),
|
||||
.atomic_load => try self.airAtomicLoad(inst),
|
||||
.memcpy => try self.airMemcpy(inst),
|
||||
.memmove => try self.airMemmove(inst),
|
||||
.memset => try self.airMemset(inst, false),
|
||||
.memset_safe => try self.airMemset(inst, true),
|
||||
.set_union_tag => try self.airSetUnionTag(inst),
|
||||
@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airMemmove(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
|
||||
@ -1581,6 +1581,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
|
||||
.atomic_rmw => try func.airAtomicRmw(inst),
|
||||
.atomic_load => try func.airAtomicLoad(inst),
|
||||
.memcpy => try func.airMemcpy(inst),
|
||||
.memmove => try func.airMemmove(inst),
|
||||
.memset => try func.airMemset(inst, false),
|
||||
.memset_safe => try func.airMemset(inst, true),
|
||||
.set_union_tag => try func.airSetUnionTag(inst),
|
||||
@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void {
|
||||
return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn airMemmove(func: *Func, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return func.fail("TODO implement airMemmove for riscv64", .{});
|
||||
}
|
||||
|
||||
fn airTagName(func: *Func, inst: Air.Inst.Index) !void {
|
||||
const pt = func.pt;
|
||||
|
||||
|
||||
@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.atomic_rmw => try self.airAtomicRmw(inst),
|
||||
.atomic_load => try self.airAtomicLoad(inst),
|
||||
.memcpy => @panic("TODO try self.airMemcpy(inst)"),
|
||||
.memmove => @panic("TODO try self.airMemmove(inst)"),
|
||||
.memset => try self.airMemset(inst, false),
|
||||
.memset_safe => try self.airMemset(inst, true),
|
||||
.set_union_tag => try self.airSetUnionTag(inst),
|
||||
|
||||
@ -2061,6 +2061,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||
.c_va_copy,
|
||||
.c_va_end,
|
||||
.c_va_start,
|
||||
.memmove,
|
||||
=> |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
|
||||
|
||||
.atomic_load => cg.airAtomicLoad(inst),
|
||||
|
||||
@ -89453,6 +89453,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
|
||||
.memset => try cg.airMemset(inst, false),
|
||||
.memset_safe => try cg.airMemset(inst, true),
|
||||
.memcpy => try cg.airMemcpy(inst),
|
||||
.memmove => try cg.airMemmove(inst),
|
||||
.cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),
|
||||
.atomic_load => try cg.airAtomicLoad(inst),
|
||||
.atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
|
||||
@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn airMemmove(self: *CodeGen, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void {
|
||||
const pt = self.pt;
|
||||
const zcu = pt.zcu;
|
||||
|
||||
@ -3349,6 +3349,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
|
||||
.memset => try airMemset(f, inst, false),
|
||||
.memset_safe => try airMemset(f, inst, true),
|
||||
.memcpy => try airMemcpy(f, inst),
|
||||
.memmove => try airMemmove(f, inst),
|
||||
.set_union_tag => try airSetUnionTag(f, inst),
|
||||
.get_union_tag => try airGetUnionTag(f, inst),
|
||||
.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 {
|
||||
}
|
||||
|
||||
fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return copyOp(f, inst, .memcpy);
|
||||
}
|
||||
|
||||
fn airMemmove(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return copyOp(f, inst, .memmove);
|
||||
}
|
||||
|
||||
fn copyOp(f: *Function, inst: Air.Inst.Index, op: enum { memcpy, memmove }) !CValue {
|
||||
const pt = f.object.dg.pt;
|
||||
const zcu = pt.zcu;
|
||||
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 {
|
||||
try writeArrayLen(f, writer, dest_ptr, dest_ty);
|
||||
try writer.writeAll(" != 0) ");
|
||||
}
|
||||
try writer.writeAll("memcpy(");
|
||||
const function_paren = switch (op) {
|
||||
.memcpy => "memcpy(",
|
||||
.memmove => "memmove(",
|
||||
};
|
||||
try writer.writeAll(function_paren);
|
||||
try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
|
||||
try writer.writeAll(", ");
|
||||
try writeSliceOrPtr(f, writer, src_ptr, src_ty);
|
||||
|
||||
@ -4940,6 +4940,7 @@ pub const FuncGen = struct {
|
||||
.memset => try self.airMemset(inst, false),
|
||||
.memset_safe => try self.airMemset(inst, true),
|
||||
.memcpy => try self.airMemcpy(inst),
|
||||
.memmove => try self.airMemmove(inst),
|
||||
.set_union_tag => try self.airSetUnionTag(inst),
|
||||
.get_union_tag => try self.airGetUnionTag(inst),
|
||||
.clz => try self.airClzCtz(inst, .ctlz),
|
||||
@ -9926,6 +9927,32 @@ pub const FuncGen = struct {
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn airMemmove(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
|
||||
const o = self.ng.object;
|
||||
const pt = o.pt;
|
||||
const zcu = pt.zcu;
|
||||
const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
|
||||
const dest_slice = try self.resolveInst(bin_op.lhs);
|
||||
const dest_ptr_ty = self.typeOf(bin_op.lhs);
|
||||
const src_slice = try self.resolveInst(bin_op.rhs);
|
||||
const src_ptr_ty = self.typeOf(bin_op.rhs);
|
||||
const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty);
|
||||
const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
|
||||
const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
|
||||
const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or
|
||||
dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
|
||||
|
||||
_ = try self.wip.callMemMove(
|
||||
dest_ptr,
|
||||
dest_ptr_ty.ptrAlignment(zcu).toLlvm(),
|
||||
src_ptr,
|
||||
src_ptr_ty.ptrAlignment(zcu).toLlvm(),
|
||||
len,
|
||||
access_kind,
|
||||
);
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
|
||||
const o = self.ng.object;
|
||||
const pt = o.pt;
|
||||
|
||||
@ -3344,6 +3344,7 @@ const NavGen = struct {
|
||||
.slice => try self.airSlice(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.memcpy => return self.airMemcpy(inst),
|
||||
.memmove => return self.airMemmove(inst),
|
||||
|
||||
.slice_ptr => try self.airSliceField(inst, 0),
|
||||
.slice_len => try self.airSliceField(inst, 1),
|
||||
@ -4914,6 +4915,11 @@ const NavGen = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn airMemmove(self: *NavGen, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airMemcpy for spirv", .{});
|
||||
}
|
||||
|
||||
fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef {
|
||||
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
|
||||
const field_ty = self.typeOfIndex(inst);
|
||||
|
||||
@ -160,6 +160,7 @@ const Writer = struct {
|
||||
.cmp_gt_optimized,
|
||||
.cmp_neq_optimized,
|
||||
.memcpy,
|
||||
.memmove,
|
||||
.memset,
|
||||
.memset_safe,
|
||||
=> try w.writeBinOp(s, inst),
|
||||
|
||||
@ -413,6 +413,7 @@ const Writer = struct {
|
||||
.min,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.memmove,
|
||||
.elem_ptr_node,
|
||||
.elem_val_node,
|
||||
.elem_ptr,
|
||||
|
||||
@ -55,6 +55,7 @@ test {
|
||||
_ = @import("behavior/member_func.zig");
|
||||
_ = @import("behavior/memcpy.zig");
|
||||
_ = @import("behavior/memset.zig");
|
||||
_ = @import("behavior/memmove.zig");
|
||||
_ = @import("behavior/merge_error_sets.zig");
|
||||
_ = @import("behavior/muladd.zig");
|
||||
_ = @import("behavior/multiple_externs_with_conflicting_types.zig");
|
||||
|
||||
@ -6,6 +6,7 @@ var x: u8 = 1;
|
||||
|
||||
// This excludes builtin functions that return void or noreturn that cannot be tested.
|
||||
test {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
@ -17,6 +18,7 @@ test {
|
||||
try testing.expectEqual(void, @TypeOf(@breakpoint()));
|
||||
try testing.expectEqual({}, @export(&x, .{ .name = "x" }));
|
||||
try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
|
||||
try testing.expectEqual({}, @memmove(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
|
||||
try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined));
|
||||
try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
|
||||
try testing.expectEqual({}, @prefetch(&val, .{}));
|
||||
|
||||
183
test/behavior/memmove.zig
Normal file
183
test/behavior/memmove.zig
Normal file
@ -0,0 +1,183 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
test "memmove and memset intrinsics" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
try testMemmoveMemset();
|
||||
try comptime testMemmoveMemset();
|
||||
}
|
||||
|
||||
fn testMemmoveMemset() !void {
|
||||
var foo: [20]u8 = undefined;
|
||||
|
||||
@memset(foo[0..10], 'A');
|
||||
@memset(foo[10..20], 'B');
|
||||
|
||||
try expect(foo[0] == 'A');
|
||||
try expect(foo[11] == 'B');
|
||||
try expect(foo[19] == 'B');
|
||||
|
||||
@memmove(foo[10..20], foo[0..10]);
|
||||
|
||||
try expect(foo[0] == 'A');
|
||||
try expect(foo[11] == 'A');
|
||||
try expect(foo[19] == 'A');
|
||||
}
|
||||
|
||||
test "@memmove with both operands single-ptr-to-array, one is null-terminated" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
try testMemmoveBothSinglePtrArrayOneIsNullTerminated();
|
||||
try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated();
|
||||
}
|
||||
|
||||
fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void {
|
||||
var buf: [100]u8 = undefined;
|
||||
const suffix = "hello";
|
||||
@memmove(buf[buf.len - suffix.len ..], suffix);
|
||||
try expect(buf[95] == 'h');
|
||||
try expect(buf[96] == 'e');
|
||||
try expect(buf[97] == 'l');
|
||||
try expect(buf[98] == 'l');
|
||||
try expect(buf[99] == 'o');
|
||||
|
||||
const start = buf.len - suffix.len - 3;
|
||||
const end = start + suffix.len;
|
||||
@memmove(buf[start..end], buf[buf.len - suffix.len ..]);
|
||||
try expect(buf[92] == 'h');
|
||||
try expect(buf[93] == 'e');
|
||||
try expect(buf[94] == 'l');
|
||||
try expect(buf[95] == 'l');
|
||||
try expect(buf[96] == 'o');
|
||||
try expect(buf[97] == 'l');
|
||||
try expect(buf[98] == 'l');
|
||||
try expect(buf[99] == 'o');
|
||||
|
||||
@memmove(buf[start + 2 .. end + 2], buf[start..end]);
|
||||
try expect(buf[92] == 'h');
|
||||
try expect(buf[93] == 'e');
|
||||
try expect(buf[94] == 'h');
|
||||
try expect(buf[95] == 'e');
|
||||
try expect(buf[96] == 'l');
|
||||
try expect(buf[97] == 'l');
|
||||
try expect(buf[98] == 'o');
|
||||
try expect(buf[99] == 'o');
|
||||
}
|
||||
|
||||
test "@memmove dest many pointer" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
try testMemmoveDestManyPtr();
|
||||
try comptime testMemmoveDestManyPtr();
|
||||
}
|
||||
|
||||
fn testMemmoveDestManyPtr() !void {
|
||||
var str = "hello".*;
|
||||
var buf: [8]u8 = undefined;
|
||||
var len: usize = 5;
|
||||
_ = &len;
|
||||
@memmove(@as([*]u8, @ptrCast(&buf)), @as([*]const u8, @ptrCast(&str))[0..len]);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'l');
|
||||
try expect(buf[3] == 'l');
|
||||
try expect(buf[4] == 'o');
|
||||
@memmove(buf[3..].ptr, buf[0..len]);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'l');
|
||||
try expect(buf[3] == 'h');
|
||||
try expect(buf[4] == 'e');
|
||||
try expect(buf[5] == 'l');
|
||||
try expect(buf[6] == 'l');
|
||||
try expect(buf[7] == 'o');
|
||||
@memmove(buf[2..7].ptr, buf[3 .. len + 3]);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'h');
|
||||
try expect(buf[3] == 'e');
|
||||
try expect(buf[4] == 'l');
|
||||
try expect(buf[5] == 'l');
|
||||
try expect(buf[6] == 'o');
|
||||
try expect(buf[7] == 'o');
|
||||
}
|
||||
|
||||
test "@memmove slice" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
try testMemmoveSlice();
|
||||
try comptime testMemmoveSlice();
|
||||
}
|
||||
|
||||
fn testMemmoveSlice() !void {
|
||||
var buf: [8]u8 = undefined;
|
||||
const dst1: []u8 = buf[0..5];
|
||||
const dst2: []u8 = buf[3..8];
|
||||
const dst3: []u8 = buf[2..7];
|
||||
const src: []const u8 = "hello";
|
||||
@memmove(dst1, src);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'l');
|
||||
try expect(buf[3] == 'l');
|
||||
try expect(buf[4] == 'o');
|
||||
@memmove(dst2, dst1);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'l');
|
||||
try expect(buf[3] == 'h');
|
||||
try expect(buf[4] == 'e');
|
||||
try expect(buf[5] == 'l');
|
||||
try expect(buf[6] == 'l');
|
||||
try expect(buf[7] == 'o');
|
||||
@memmove(dst3, dst2);
|
||||
try expect(buf[0] == 'h');
|
||||
try expect(buf[1] == 'e');
|
||||
try expect(buf[2] == 'h');
|
||||
try expect(buf[3] == 'e');
|
||||
try expect(buf[4] == 'l');
|
||||
try expect(buf[5] == 'l');
|
||||
try expect(buf[6] == 'o');
|
||||
try expect(buf[7] == 'o');
|
||||
}
|
||||
|
||||
comptime {
|
||||
const S = struct {
|
||||
buffer: [8]u8 = undefined,
|
||||
fn set(self: *@This(), items: []const u8) void {
|
||||
@memmove(self.buffer[0..items.len], items);
|
||||
@memmove(self.buffer[3..], self.buffer[0..items.len]);
|
||||
@memmove(self.buffer[2 .. 2 + items.len], self.buffer[3..]);
|
||||
}
|
||||
};
|
||||
|
||||
var s = S{};
|
||||
s.set("hello");
|
||||
if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");
|
||||
}
|
||||
218
test/cases/compile_errors/@memmove_type_mismatch.zig
Normal file
218
test/cases/compile_errors/@memmove_type_mismatch.zig
Normal file
@ -0,0 +1,218 @@
|
||||
export fn foo() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn bar() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn baz() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: [*]align(1) u16 = @ptrCast(&buf);
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn qux() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn quux() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn quuux() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: [*]align(1) u16 = @ptrCast(&buf);
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn foo2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
const src: []u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn bar2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
const src: []u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn baz2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: [*]align(1) u16 = @ptrCast(&buf);
|
||||
const src: []u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn qux2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
const src: *[8]u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn quux2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
const src: *[8]u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
export fn quuux2() void {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: [*]align(1) u16 = @ptrCast(&buf);
|
||||
const src: *[8]u8 = &buf;
|
||||
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []u8 = &buf;
|
||||
const src: [*]align(1) u16 = @ptrCast(&buf);
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *[8]u8 = &buf;
|
||||
const src: [*]align(1) u16 = @ptrCast(&buf);
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
const src: []u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
const src: []u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: [*]align(1) u16 = @ptrCast(&buf);
|
||||
const src: []u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
|
||||
const src: *[8]u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: *align(1) [8]u16 = @ptrCast(&buf);
|
||||
const src: *[8]u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
comptime {
|
||||
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
const dest: [*]align(1) u16 = @ptrCast(&buf);
|
||||
const src: *[8]u8 = &buf;
|
||||
@memmove(dest, src);
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :6:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :6:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :14:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :14:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :22:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :22:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :30:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :30:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :38:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :38:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :46:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :46:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :54:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :62:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :70:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :78:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :86:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :94:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :101:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :101:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :108:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :108:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :115:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :115:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :122:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :122:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :129:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :129:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :136:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
|
||||
// :136:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
|
||||
// :143:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :150:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :157:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :164:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :171:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
// :178:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
|
||||
@ -29,6 +29,7 @@ pub const panic = struct {
|
||||
pub const forLenMismatch = simple_panic.forLenMismatch;
|
||||
pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;
|
||||
pub const memcpyAlias = simple_panic.memcpyAlias;
|
||||
pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch;
|
||||
pub const noreturnReturned = simple_panic.noreturnReturned;
|
||||
};
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ pub const panic = struct {
|
||||
pub const forLenMismatch = simple_panic.forLenMismatch;
|
||||
pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch;
|
||||
pub const memcpyAlias = simple_panic.memcpyAlias;
|
||||
pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch;
|
||||
pub const noreturnReturned = simple_panic.noreturnReturned;
|
||||
};
|
||||
|
||||
|
||||
@ -63,6 +63,14 @@ export fn far() void {
|
||||
@memset(&rt, elem);
|
||||
}
|
||||
|
||||
export fn bax() void {
|
||||
comptime var x: [2]u32 = undefined;
|
||||
x = .{ 1, 2 };
|
||||
|
||||
var rt: [2]u32 = undefined;
|
||||
@memmove(&rt, &x);
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :5:19: error: runtime value contains reference to comptime var
|
||||
@ -92,3 +100,6 @@ export fn far() void {
|
||||
// :63:18: error: runtime value contains reference to comptime var
|
||||
// :63:18: note: comptime var pointers are not available at runtime
|
||||
// :59:27: note: 'runtime_value' points to comptime var declared here
|
||||
// :71:19: error: runtime value contains reference to comptime var
|
||||
// :71:19: note: comptime var pointers are not available at runtime
|
||||
// :67:30: note: 'runtime_value' points to comptime var declared here
|
||||
|
||||
@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void {
|
||||
var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
@memcpy(&buf1, &buf2);
|
||||
}
|
||||
pub export fn memset_array() void {
|
||||
pub export fn memcpy_array() void {
|
||||
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
@memcpy(buf, 1);
|
||||
}
|
||||
pub export fn entry_memmove() void {
|
||||
var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
const slice: []u8 = &buf;
|
||||
const a: u32 = 1234;
|
||||
@memmove(slice.ptr, @as([*]const u8, @ptrCast(&a)));
|
||||
}
|
||||
pub export fn entry1_memmove() void {
|
||||
var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
const ptr: *u8 = &buf[0];
|
||||
@memmove(ptr, 0);
|
||||
}
|
||||
pub export fn non_matching_lengths_memmove() void {
|
||||
var buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
var buf2: [6]u8 = .{ 1, 2, 3, 4, 5, 6 };
|
||||
@memmove(&buf2, &buf1);
|
||||
}
|
||||
pub export fn memcpy_const_dest_ptr_memmove() void {
|
||||
const buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
@memmove(&buf1, &buf2);
|
||||
}
|
||||
pub export fn memmove_array() void {
|
||||
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
@memmove(buf, 1);
|
||||
}
|
||||
pub export fn memset_array() void {
|
||||
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
||||
@memset(buf, 1);
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
@ -51,3 +80,16 @@ pub export fn memset_array() void {
|
||||
// :29:13: error: cannot memcpy to constant pointer
|
||||
// :33:13: error: type '[5]u8' is not an indexable pointer
|
||||
// :33:13: note: operand must be a slice, a many pointer or a pointer to an array
|
||||
// :39:5: error: unknown @memmove length
|
||||
// :39:19: note: destination type '[*]u8' provides no length
|
||||
// :39:25: note: source type '[*]const u8' provides no length
|
||||
// :44:14: error: type '*u8' is not an indexable pointer
|
||||
// :44:14: note: operand must be a slice, a many pointer or a pointer to an array
|
||||
// :49:5: error: non-matching @memmove lengths
|
||||
// :49:14: note: length 6 here
|
||||
// :49:21: note: length 5 here
|
||||
// :54:14: error: cannot memmove to constant pointer
|
||||
// :58:14: error: type '[5]u8' is not an indexable pointer
|
||||
// :58:14: note: operand must be a slice, a many pointer or a pointer to an array
|
||||
// :62:13: error: type '[5]u8' is not an indexable pointer
|
||||
// :62:13: note: operand must be a slice, a many pointer or a pointer to an array
|
||||
|
||||
@ -12,6 +12,7 @@ pub fn main() !void {
|
||||
var len: usize = 5;
|
||||
_ = &len;
|
||||
@memcpy(buffer[0..len], buffer[4 .. 4 + len]);
|
||||
return error.TestFailed;
|
||||
}
|
||||
// run
|
||||
// backend=stage2,llvm
|
||||
|
||||
@ -12,6 +12,7 @@ pub fn main() !void {
|
||||
var len: usize = 5;
|
||||
_ = &len;
|
||||
@memcpy(buffer[0..len], buffer[len .. len + 4]);
|
||||
return error.TestFailed;
|
||||
}
|
||||
// run
|
||||
// backend=stage2,llvm
|
||||
|
||||
19
test/cases/safety/memmove_len_mismatch.zig
Normal file
19
test/cases/safety/memmove_len_mismatch.zig
Normal file
@ -0,0 +1,19 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
|
||||
_ = stack_trace;
|
||||
if (std.mem.eql(u8, message, "@memmove arguments have non-equal lengths")) {
|
||||
std.process.exit(0);
|
||||
}
|
||||
std.process.exit(1);
|
||||
}
|
||||
pub fn main() !void {
|
||||
var buffer = [2]u8{ 1, 2 } ** 5;
|
||||
var len: usize = 5;
|
||||
_ = &len;
|
||||
@memmove(buffer[0..len], buffer[len .. len + 4]);
|
||||
return error.TestFailed;
|
||||
}
|
||||
// run
|
||||
// backend=llvm
|
||||
// target=native
|
||||
@ -39,6 +39,7 @@ pub const panic = struct {
|
||||
pub const forLenMismatch = no_panic.forLenMismatch;
|
||||
pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
|
||||
pub const memcpyAlias = no_panic.memcpyAlias;
|
||||
pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
|
||||
pub const noreturnReturned = no_panic.noreturnReturned;
|
||||
};
|
||||
fn myPanic(msg: []const u8, _: ?usize) noreturn {
|
||||
@ -86,6 +87,7 @@ pub const panic = struct {
|
||||
pub const forLenMismatch = no_panic.forLenMismatch;
|
||||
pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
|
||||
pub const memcpyAlias = no_panic.memcpyAlias;
|
||||
pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
|
||||
pub const noreturnReturned = no_panic.noreturnReturned;
|
||||
};
|
||||
fn myPanic(msg: []const u8, _: ?usize) noreturn {
|
||||
@ -133,6 +135,7 @@ pub const panic = struct {
|
||||
pub const forLenMismatch = no_panic.forLenMismatch;
|
||||
pub const memcpyLenMismatch = no_panic.memcpyLenMismatch;
|
||||
pub const memcpyAlias = no_panic.memcpyAlias;
|
||||
pub const memmoveLenMismatch = no_panic.memmoveLenMismatch;
|
||||
pub const noreturnReturned = no_panic.noreturnReturned;
|
||||
};
|
||||
fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
|
||||
|
||||
@ -5,6 +5,7 @@ test {
|
||||
const source = foo();
|
||||
|
||||
@memcpy(dest, source);
|
||||
@memmove(dest, source);
|
||||
@memset(dest, 4);
|
||||
@memset(dest, undefined);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user