aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-01-28 02:49:58 +0000
committerMatthew Lugg <mlugg@mlugg.co.uk>2025-01-29 06:35:22 +0000
commit71d16106ad76bb31bc4e17dc37f8d8b5498a12dd (patch)
treef8db4c1ed772e3188447afad05156dd6d1c664fc /src/Sema.zig
parent97ccf3504f0b4787b86fa882312aba01ab980121 (diff)
downloadzig-71d16106ad76bb31bc4e17dc37f8d8b5498a12dd.tar.gz
zig-71d16106ad76bb31bc4e17dc37f8d8b5498a12dd.zip
Sema: `@memcpy` changes
* The langspec definition of `@memcpy` has been changed so that the source and destination element types must be in-memory coercible, allowing all such calls to be raw copying operations, not actually applying any coercions. * Implement aliasing check for comptime `@memcpy`; a compile error will now be emitted if the arguments alias. * Implement more efficient comptime `@memcpy` by loading and storing a whole array at once, similar to how `@memset` is implemented.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig118
1 files changed, 66 insertions, 52 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 72b05d078d..f38a82659c 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -25793,7 +25793,6 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
const pt = sema.pt;
const zcu = pt.zcu;
- const target = zcu.getTarget();
if (dest_ty.isConstPtr(zcu)) {
return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{});
@@ -25814,6 +25813,30 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
return sema.failWithOwnedErrorMsg(block, msg);
}
+ const dest_elem_ty = dest_ty.indexablePtrElem(zcu);
+ const src_elem_ty = src_ty.indexablePtrElem(zcu);
+
+ const imc = try sema.coerceInMemoryAllowed(
+ block,
+ dest_elem_ty,
+ src_elem_ty,
+ false,
+ zcu.getTarget(),
+ dest_src,
+ src_src,
+ null,
+ );
+ if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(
+ src,
+ "pointer element type '{}' cannot coerce into element type '{}'",
+ .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try imc.report(sema, src, msg);
+ break :msg msg;
+ });
+
var len_val: ?Value = null;
if (dest_len != .none and src_len != .none) check: {
@@ -25855,61 +25878,52 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
}
}
- const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
+ 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;
+
+ const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val;
+ const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val;
+
+ 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 (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
- if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
- const len_u64 = try len_val.?.toUnsignedIntSema(pt);
- const len = try sema.usizeCast(block, dest_src, len_u64);
- for (0..len) |i| {
- const elem_index = try pt.intRef(Type.usize, i);
- const dest_elem_ptr = try sema.elemPtrOneLayerOnly(
- block,
- src,
- dest_ptr,
- elem_index,
- src,
- true, // init
- false, // oob_safety
- );
- const src_elem_ptr = try sema.elemPtrOneLayerOnly(
- block,
- src,
- src_ptr,
- elem_index,
- src,
- false, // init
- false, // oob_safety
- );
- const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src);
- try sema.storePtr2(
- block,
- src,
- dest_elem_ptr,
- dest_src,
- uncoerced_elem,
- src_src,
- .store,
- );
- }
- return;
- } else break :rs src_src;
- } else dest_src;
- // If in-memory coercion is not allowed, explode this memcpy call into a
- // for loop that copies element-wise.
- // Likewise if this is an iterable rather than a pointer, do the same
- // lowering. The AIR instruction requires pointers with element types of
- // equal ABI size.
+ // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy
+ // as one load and store of an array, rather than N loads and stores of individual elements.
- if (dest_ty.zigTypeTag(zcu) != .pointer or src_ty.zigTypeTag(zcu) != .pointer) {
- return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
- }
+ const array_ty = try pt.arrayType(.{
+ .child = dest_elem_ty.toIntern(),
+ .len = len_u64,
+ });
- const dest_elem_ty = dest_ty.elemType2(zcu);
- const src_elem_ty = src_ty.elemType2(zcu);
- if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src, null)) {
- return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
- }
+ const dest_array_ptr_ty = try pt.ptrType(info: {
+ var info = dest_ty.ptrInfo(zcu);
+ info.flags.size = .one;
+ info.child = array_ty.toIntern();
+ break :info info;
+ });
+ const src_array_ptr_ty = try pt.ptrType(info: {
+ var info = src_ty.ptrInfo(zcu);
+ info.flags.size = .one;
+ info.child = array_ty.toIntern();
+ break :info info;
+ });
+
+ const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty);
+ const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty);
+
+ const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src;
+ try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty);
+ return;
+ };
// If the length is comptime-known, then upgrade src and destination types
// into pointer-to-array. At this point we know they are both pointers