authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-21 18:03:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-25 11:23:40-07:00
logedb5e493e6d9a4478b3d9c06aa590694757d8c03
tree5d5ccca94129e3a0e177845dbb5a9b251271cd07
parenta5c910adb610ae530db99f10aa77aaed3e85e830

update `@memcpy` to require equal src and dest lens

* Sema: upgrade operands to array pointers if possible when emitting AIR. * Implement safety checks for length mismatch and aliasing. * AIR: make ptrtoint support slice operands. Implement in LLVM backend. * C backend: implement new `@memset` semantics. `@memcpy` is not done yet.

12 files changed, 280 insertions(+), 78 deletions(-)

doc/langref.html.in+14-12
...@@ -8683,18 +8683,20 @@ test "integer cast panic" {...@@ -8683,18 +8683,20 @@ test "integer cast panic" {
8683 {#header_open|@memcpy#}8683 {#header_open|@memcpy#}
8684 <pre>{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}</pre>8684 <pre>{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}</pre>
8685 <p>This function copies bytes from one region of memory to another.</p>8685 <p>This function copies bytes from one region of memory to another.</p>
8686 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, or a mutable pointer to an array.8686 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or
8687 It may have any alignment, and it may have any element type.</p>8687 a mutable many-item {#link|pointer|Pointer#}. It may have any
8688 <p>{#syntax#}source{#endsyntax#} must be an array, pointer, or a slice8688 alignment, and it may have any element type.</p>
8689 with the same element type as {#syntax#}dest{#endsyntax#}. It may have8689 <p>Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a
8690 any alignment. Only {#syntax#}const{#endsyntax#} access is required. It8690 mutable pointer to an array, or a mutable many-item
8691 is sliced from 0 to the same length as8691 {#link|pointer|Pointer#}. It may have any alignment, and it may have any
8692 {#syntax#}dest{#endsyntax#}, triggering the same set of safety checks and8692 element type.</p>
8693 possible compile errors as8693 <p>The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#}
8694 {#syntax#}source[0..dest.len]{#endsyntax#}.</p>8694 into the {#syntax#}dest{#endsyntax#} element type. The element types may have
8695 <p>It is illegal for {#syntax#}dest{#endsyntax#} and8695 different ABI size, however, that may incur a performance penalty.</p>
8696 {#syntax#}source[0..dest.len]{#endsyntax#} to overlap. If safety8696 <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and
8697 checks are enabled, there will be a runtime check for such overlapping.</p>8697 {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided,
8698 they must be equal.</p>
8699 <p>Finally, the two memory regions must not overlap.</p>
8698 {#header_close#}8700 {#header_close#}
86998701
8700 {#header_open|@memset#}8702 {#header_open|@memset#}
lib/std/builtin.zig+2
...@@ -1002,6 +1002,8 @@ pub const panic_messages = struct {...@@ -1002,6 +1002,8 @@ pub const panic_messages = struct {
1002 pub const index_out_of_bounds = "index out of bounds";1002 pub const index_out_of_bounds = "index out of bounds";
1003 pub const start_index_greater_than_end = "start index is larger than end index";1003 pub const start_index_greater_than_end = "start index is larger than end index";
1004 pub const for_len_mismatch = "for loop over objects with non-equal lengths";1004 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
1005 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
1006 pub const memcpy_alias = "@memcpy arguments alias";
1005};1007};
10061008
1007pub noinline fn returnError(st: *StackTrace) void {1009pub noinline fn returnError(st: *StackTrace) void {
lib/std/crypto/aegis.zig+2-2
...@@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type {...@@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type {
209 acc |= (computed_tag[j] ^ tag[j]);209 acc |= (computed_tag[j] ^ tag[j]);
210 }210 }
211 if (acc != 0) {211 if (acc != 0) {
212 @memset(m.ptr, undefined, m.len);212 @memset(m, undefined);
213 return error.AuthenticationFailed;213 return error.AuthenticationFailed;
214 }214 }
215 }215 }
...@@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type {...@@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type {
390 acc |= (computed_tag[j] ^ tag[j]);390 acc |= (computed_tag[j] ^ tag[j]);
391 }391 }
392 if (acc != 0) {392 if (acc != 0) {
393 @memset(m.ptr, undefined, m.len);393 @memset(m, undefined);
394 return error.AuthenticationFailed;394 return error.AuthenticationFailed;
395 }395 }
396 }396 }
lib/std/hash/murmur.zig+5-6
...@@ -99,9 +99,8 @@ pub const Murmur2_64 = struct {...@@ -99,9 +99,8 @@ pub const Murmur2_64 = struct {
9999
100 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {100 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
101 const m: u64 = 0xc6a4a7935bd1e995;101 const m: u64 = 0xc6a4a7935bd1e995;
102 const len = @as(u64, str.len);102 var h1: u64 = seed ^ (@as(u64, str.len) *% m);
103 var h1: u64 = seed ^ (len *% m);103 for (@ptrCast([*]align(1) const u64, str.ptr)[0..str.len / 8]) |v| {
104 for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| {
105 var k1: u64 = v;104 var k1: u64 = v;
106 if (native_endian == .Big)105 if (native_endian == .Big)
107 k1 = @byteSwap(k1);106 k1 = @byteSwap(k1);
...@@ -111,11 +110,11 @@ pub const Murmur2_64 = struct {...@@ -111,11 +110,11 @@ pub const Murmur2_64 = struct {
111 h1 ^= k1;110 h1 ^= k1;
112 h1 *%= m;111 h1 *%= m;
113 }112 }
114 const rest = len & 7;113 const rest = str.len & 7;
115 const offset = len - rest;114 const offset = str.len - rest;
116 if (rest > 0) {115 if (rest > 0) {
117 var k1: u64 = 0;116 var k1: u64 = 0;
118 @memcpy(@ptrCast([*]u8, &k1)[0..@intCast(usize, rest)], @ptrCast([*]const u8, &str[@intCast(usize, offset)]));117 @memcpy(@ptrCast([*]u8, &k1)[0..rest], str[offset..]);
119 if (native_endian == .Big)118 if (native_endian == .Big)
120 k1 = @byteSwap(k1);119 k1 = @byteSwap(k1);
121 h1 ^= k1;120 h1 ^= k1;
lib/std/mem/Allocator.zig+2-1
...@@ -282,7 +282,8 @@ pub fn reallocAdvanced(...@@ -282,7 +282,8 @@ pub fn reallocAdvanced(
282282
283 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse283 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse
284 return error.OutOfMemory;284 return error.OutOfMemory;
285 @memcpy(new_mem[0..@min(byte_count, old_byte_slice.len)], old_byte_slice);285 const copy_len = @min(byte_count, old_byte_slice.len);
286 @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]);
286 // TODO https://github.com/ziglang/zig/issues/4298287 // TODO https://github.com/ziglang/zig/issues/4298
287 @memset(old_byte_slice, undefined);288 @memset(old_byte_slice, undefined);
288 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);289 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);
src/Air.zig+1
...@@ -462,6 +462,7 @@ pub const Inst = struct {...@@ -462,6 +462,7 @@ pub const Inst = struct {
462 /// Uses the `ty_op` field.462 /// Uses the `ty_op` field.
463 load,463 load,
464 /// Converts a pointer to its address. Result type is always `usize`.464 /// Converts a pointer to its address. Result type is always `usize`.
465 /// Pointer type size may be any, including slice.
465 /// Uses the `un_op` field.466 /// Uses the `un_op` field.
466 ptrtoint,467 ptrtoint,
467 /// Given a boolean, returns 0 or 1.468 /// Given a boolean, returns 0 or 1.
src/AstGen.zig+1-1
...@@ -8455,7 +8455,7 @@ fn builtinCall(...@@ -8455,7 +8455,7 @@ fn builtinCall(
8455 .memcpy => {8455 .memcpy => {
8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
8457 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),8457 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8458 .rhs = try expr(gz, scope, .{ .rl = .ref }, params[1]),8458 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
8459 });8459 });
8460 return rvalue(gz, ri, .void_value, node);8460 return rvalue(gz, ri, .void_value, node);
8461 },8461 },
src/Liveness/Verify.zig+2-7
...@@ -254,6 +254,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -254,6 +254,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
254 .set_union_tag,254 .set_union_tag,
255 .min,255 .min,
256 .max,256 .max,
257 .memset,
258 .memcpy,
257 => {259 => {
258 const bin_op = data[inst].bin_op;260 const bin_op = data[inst].bin_op;
259 try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });261 try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -306,13 +308,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -306,13 +308,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
306 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;308 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;
307 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });309 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });
308 },310 },
309 .memset,
310 .memcpy,
311 => {
312 const pl_op = data[inst].pl_op;
313 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
314 try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
315 },
316 .cmpxchg_strong,311 .cmpxchg_strong,
317 .cmpxchg_weak,312 .cmpxchg_weak,
318 => {313 => {
src/Sema.zig+160-15
...@@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
3386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;3386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3387 const src = inst_data.src();3387 const src = inst_data.src();
3388 const object = try sema.resolveInst(inst_data.operand);3388 const object = try sema.resolveInst(inst_data.operand);
3389 const object_ty = sema.typeOf(object);
3390
3391 const is_pointer_to = object_ty.isSinglePointer();
33923389
3393 const array_ty = if (is_pointer_to)3390 return indexablePtrLen(sema, block, src, object);
3394 object_ty.childType()3391}
3395 else
3396 object_ty;
33973392
3393fn indexablePtrLen(
3394 sema: *Sema,
3395 block: *Block,
3396 src: LazySrcLoc,
3397 object: Air.Inst.Ref,
3398) CompileError!Air.Inst.Ref {
3399 const object_ty = sema.typeOf(object);
3400 const is_pointer_to = object_ty.isSinglePointer();
3401 const array_ty = if (is_pointer_to) object_ty.childType() else object_ty;
3398 try checkIndexable(sema, block, src, array_ty);3402 try checkIndexable(sema, block, src, array_ty);
3403 return sema.fieldVal(block, src, object, "len", src);
3404}
33993405
3406fn indexablePtrLenOrNone(
3407 sema: *Sema,
3408 block: *Block,
3409 src: LazySrcLoc,
3410 object: Air.Inst.Ref,
3411) CompileError!Air.Inst.Ref {
3412 const object_ty = sema.typeOf(object);
3413 const array_ty = t: {
3414 const ptr_size = object_ty.ptrSizeOrNull() orelse break :t object_ty;
3415 break :t switch (ptr_size) {
3416 .Many => return .none,
3417 .One => object_ty.childType(),
3418 else => object_ty,
3419 };
3420 };
3421 try checkIndexable(sema, block, src, array_ty);
3400 return sema.fieldVal(block, src, object, "len", src);3422 return sema.fieldVal(block, src, object, "len", src);
3401}3423}
34023424
...@@ -21773,6 +21795,29 @@ fn analyzeMinMax(...@@ -21773,6 +21795,29 @@ fn analyzeMinMax(
21773 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);21795 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
21774}21796}
2177521797
21798fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
21799 const mod = sema.mod;
21800 const info = sema.typeOf(ptr).ptrInfo().data;
21801 if (info.size == .One) {
21802 // Already an array pointer.
21803 return ptr;
21804 }
21805 const new_ty = try Type.ptr(sema.arena, mod, .{
21806 .pointee_type = try Type.array(sema.arena, len, info.sentinel, info.pointee_type, mod),
21807 .sentinel = null,
21808 .@"align" = info.@"align",
21809 .@"addrspace" = info.@"addrspace",
21810 .mutable = info.mutable,
21811 .@"allowzero" = info.@"allowzero",
21812 .@"volatile" = info.@"volatile",
21813 .size = .One,
21814 });
21815 if (info.size == .Slice) {
21816 return block.addTyOp(.slice_ptr, new_ty, ptr);
21817 }
21818 return block.addBitCast(new_ty, ptr);
21819}
21820
21776fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {21821fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
21777 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;21822 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
21778 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;21823 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
...@@ -21780,27 +21825,125 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -21780,27 +21825,125 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
21780 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };21825 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
21781 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };21826 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
21782 const dest_ptr = try sema.resolveInst(extra.lhs);21827 const dest_ptr = try sema.resolveInst(extra.lhs);
21783 const src_ptr_ptr = try sema.resolveInst(extra.rhs);21828 const src_ptr = try sema.resolveInst(extra.rhs);
21784 const dest_ptr_ty = sema.typeOf(dest_ptr);21829 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
21785 try checkSliceOrArrayType(sema, block, dest_src, dest_ptr_ty);21830 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
21831
21832 if (dest_len == .none and src_len == .none) {
21833 const msg = msg: {
21834 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
21835 errdefer msg.destroy(sema.gpa);
21836 try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{
21837 sema.typeOf(dest_ptr).fmt(sema.mod),
21838 });
21839 try sema.errNote(block, src_src, msg, "source type {} provides no length", .{
21840 sema.typeOf(src_ptr).fmt(sema.mod),
21841 });
21842 break :msg msg;
21843 };
21844 return sema.failWithOwnedErrorMsg(msg);
21845 }
2178621846
21787 const dest_len = try sema.fieldVal(block, dest_src, dest_ptr, "len", dest_src);21847 var len_val: ?Value = null;
21788 const src_ptr = try sema.analyzeSlice(block, src_src, src_ptr_ptr, .zero_usize, dest_len, .none, .unneeded, src_src, src_src, src_src);21848
21849 if (dest_len != .none and src_len != .none) check: {
21850 // If we can check at compile-time, no need for runtime safety.
21851 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
21852 len_val = dest_len_val;
21853 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
21854 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
21855 const msg = msg: {
21856 const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{});
21857 errdefer msg.destroy(sema.gpa);
21858 try sema.errNote(block, dest_src, msg, "length {} here", .{
21859 dest_len_val.fmtValue(Type.usize, sema.mod),
21860 });
21861 try sema.errNote(block, src_src, msg, "length {} here", .{
21862 src_len_val.fmtValue(Type.usize, sema.mod),
21863 });
21864 break :msg msg;
21865 };
21866 return sema.failWithOwnedErrorMsg(msg);
21867 }
21868 break :check;
21869 }
21870 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
21871 len_val = src_len_val;
21872 }
21873
21874 if (block.wantSafety()) {
21875 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
21876 try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch);
21877 }
21878 }
2178921879
21790 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {21880 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
21791 if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;21881 if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;
21792 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| {21882 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| {
21793 if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src;21883 _ = src_ptr_val;
21794 return sema.fail(block, src, "TODO: @memcpy at comptime", .{});21884 return sema.fail(block, src, "TODO: @memcpy at comptime", .{});
21795 } else break :rs src_src;21885 } else break :rs src_src;
21796 } else dest_src;21886 } else dest_src;
2179721887
21798 try sema.requireRuntimeBlock(block, src, runtime_src);21888 try sema.requireRuntimeBlock(block, src, runtime_src);
21889
21890 const dest_ty = sema.typeOf(dest_ptr);
21891 const src_ty = sema.typeOf(src_ptr);
21892
21893 // If in-memory coercion is not allowed, explode this memcpy call into a
21894 // for loop that copies element-wise.
21895 // Likewise if this is an iterable rather than a pointer, do the same
21896 // lowering. The AIR instruction requires pointers with element types of
21897 // equal ABI size.
21898
21899 if (dest_ty.zigTypeTag() != .Pointer or src_ty.zigTypeTag() != .Pointer) {
21900 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
21901 }
21902
21903 const dest_elem_ty = dest_ty.elemType2();
21904 const src_elem_ty = src_ty.elemType2();
21905 const target = sema.mod.getTarget();
21906 if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
21907 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
21908 }
21909
21910 // If the length is comptime-known, then upgrade src and destination types
21911 // into pointer-to-array. At this point we know they are both pointers
21912 // already.
21913 var new_dest_ptr = dest_ptr;
21914 var new_src_ptr = src_ptr;
21915 if (len_val) |val| {
21916 const len = val.toUnsignedInt(target);
21917 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
21918 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
21919 }
21920
21921 // Aliasing safety check.
21922 if (block.wantSafety()) {
21923 const dest_int = try block.addUnOp(.ptrtoint, new_dest_ptr);
21924 const src_int = try block.addUnOp(.ptrtoint, new_src_ptr);
21925 const len = if (len_val) |v|
21926 try sema.addConstant(Type.usize, v)
21927 else if (dest_len != .none)
21928 dest_len
21929 else
21930 src_len;
21931
21932 // ok1: dest >= src + len
21933 // ok2: src >= dest + len
21934 const src_plus_len = try block.addBinOp(.add, src_int, len);
21935 const dest_plus_len = try block.addBinOp(.add, dest_int, len);
21936 const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len);
21937 const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len);
21938 const ok = try block.addBinOp(.bit_or, ok1, ok2);
21939 try sema.addSafetyCheck(block, ok, .memcpy_alias);
21940 }
21941
21799 _ = try block.addInst(.{21942 _ = try block.addInst(.{
21800 .tag = .memcpy,21943 .tag = .memcpy,
21801 .data = .{ .bin_op = .{21944 .data = .{ .bin_op = .{
21802 .lhs = dest_ptr,21945 .lhs = new_dest_ptr,
21803 .rhs = src_ptr,21946 .rhs = new_src_ptr,
21804 } },21947 } },
21805 });21948 });
21806}21949}
...@@ -22949,6 +23092,8 @@ pub const PanicId = enum {...@@ -22949,6 +23092,8 @@ pub const PanicId = enum {
22949 index_out_of_bounds,23092 index_out_of_bounds,
22950 start_index_greater_than_end,23093 start_index_greater_than_end,
22951 for_len_mismatch,23094 for_len_mismatch,
23095 memcpy_len_mismatch,
23096 memcpy_alias,
22952};23097};
2295323098
22954fn addSafetyCheck(23099fn addSafetyCheck(
src/codegen/c.zig+74-24
...@@ -6177,18 +6177,43 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa...@@ -6177,18 +6177,43 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
6177}6177}
61786178
6179fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {6179fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
6180 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6180 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
6181 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;6181 const dest_ty = f.air.typeOf(bin_op.lhs);
6182 const dest_ty = f.air.typeOf(pl_op.operand);6182 const dest_slice = try f.resolveInst(bin_op.lhs);
6183 const dest_ptr = try f.resolveInst(pl_op.operand);6183 const value = try f.resolveInst(bin_op.rhs);
6184 const value = try f.resolveInst(extra.lhs);6184 const elem_ty = f.air.typeOf(bin_op.rhs);
6185 const len = try f.resolveInst(extra.rhs);6185 const target = f.object.dg.module.getTarget();
61866186 const elem_abi_size = elem_ty.abiSize(target);
6187 const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
6187 const writer = f.object.writer();6188 const writer = f.object.writer();
6188 if (dest_ty.isVolatilePtr()) {6189
6189 var u8_ptr_pl = dest_ty.ptrInfo();6190 if (val_is_undef) {
6190 u8_ptr_pl.data.pointee_type = Type.u8;6191 try writer.writeAll("memset(");
6191 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);6192 switch (dest_ty.ptrSize()) {
6193 .Slice => {
6194 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
6195 try writer.writeAll(", 0xaa, ");
6196 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6197 if (elem_abi_size > 1) {
6198 try writer.print(" * {d});\n", .{elem_abi_size});
6199 } else {
6200 try writer.writeAll(");\n");
6201 }
6202 },
6203 .One => {
6204 const array_ty = dest_ty.childType();
6205 const len = array_ty.arrayLen() * elem_abi_size;
6206
6207 try f.writeCValue(writer, dest_slice, .FunctionArgument);
6208 try writer.print(", 0xaa, {d});\n", .{len});
6209 },
6210 .Many, .C => unreachable,
6211 }
6212 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6213 return .none;
6214 }
6215
6216 if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) {
6192 const index = try f.allocLocal(inst, Type.usize);6217 const index = try f.allocLocal(inst, Type.usize);
61936218
6194 try writer.writeAll("for (");6219 try writer.writeAll("for (");
...@@ -6198,36 +6223,61 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6198,36 +6223,61 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
6198 try writer.writeAll("; ");6223 try writer.writeAll("; ");
6199 try f.writeCValue(writer, index, .Other);6224 try f.writeCValue(writer, index, .Other);
6200 try writer.writeAll(" != ");6225 try writer.writeAll(" != ");
6201 try f.writeCValue(writer, len, .Other);6226 switch (dest_ty.ptrSize()) {
6227 .Slice => {
6228 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6229 },
6230 .One => {
6231 const array_ty = dest_ty.childType();
6232 const len = array_ty.arrayLen() * elem_abi_size;
6233 try writer.print("{d}", .{len});
6234 },
6235 .Many, .C => unreachable,
6236 }
6202 try writer.writeAll("; ");6237 try writer.writeAll("; ");
6203 try f.writeCValue(writer, index, .Other);6238 try f.writeCValue(writer, index, .Other);
6204 try writer.writeAll(" += ");6239 try writer.writeAll(" += ");
6205 try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);6240 try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);
6206 try writer.writeAll(") ((");6241 try writer.writeAll(") (");
6207 try f.renderType(writer, u8_ptr_ty);6242 switch (dest_ty.ptrSize()) {
6208 try writer.writeByte(')');6243 .Slice => try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }),
6209 try f.writeCValue(writer, dest_ptr, .FunctionArgument);6244 .One => try f.writeCValue(writer, dest_slice, .FunctionArgument),
6245 .Many, .C => unreachable,
6246 }
6210 try writer.writeAll(")[");6247 try writer.writeAll(")[");
6211 try f.writeCValue(writer, index, .Other);6248 try f.writeCValue(writer, index, .Other);
6212 try writer.writeAll("] = ");6249 try writer.writeAll("] = ");
6213 try f.writeCValue(writer, value, .FunctionArgument);6250 try f.writeCValue(writer, value, .FunctionArgument);
6214 try writer.writeAll(";\n");6251 try writer.writeAll(";\n");
62156252
6216 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });6253 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6217 try freeLocal(f, inst, index.new_local, 0);6254 try freeLocal(f, inst, index.new_local, 0);
62186255
6219 return .none;6256 return .none;
6220 }6257 }
62216258
6222 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6223 try writer.writeAll("memset(");6259 try writer.writeAll("memset(");
6224 try f.writeCValue(writer, dest_ptr, .FunctionArgument);6260 switch (dest_ty.ptrSize()) {
6225 try writer.writeAll(", ");6261 .Slice => {
6226 try f.writeCValue(writer, value, .FunctionArgument);6262 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
6227 try writer.writeAll(", ");6263 try writer.writeAll(", ");
6228 try f.writeCValue(writer, len, .FunctionArgument);6264 try f.writeCValue(writer, value, .FunctionArgument);
6229 try writer.writeAll(");\n");6265 try writer.writeAll(", ");
6266 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6267 try writer.writeAll(");\n");
6268 },
6269 .One => {
6270 const array_ty = dest_ty.childType();
6271 const len = array_ty.arrayLen() * elem_abi_size;
62306272
6273 try f.writeCValue(writer, dest_slice, .FunctionArgument);
6274 try writer.writeAll(", ");
6275 try f.writeCValue(writer, value, .FunctionArgument);
6276 try writer.print(", {d});\n", .{len});
6277 },
6278 .Many, .C => unreachable,
6279 }
6280 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6231 return .none;6281 return .none;
6232}6282}
62336283
src/codegen/llvm.zig+7-5
...@@ -5777,10 +5777,10 @@ pub const FuncGen = struct {...@@ -5777,10 +5777,10 @@ pub const FuncGen = struct {
5777 }5777 }
57785778
5779 fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {5779 fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
5780 switch (ty.ptrSize()) {5780 if (ty.isSlice()) {
5781 .Slice => return fg.builder.buildExtractValue(ptr, 0, ""),5781 return fg.builder.buildExtractValue(ptr, 0, "");
5782 .One => return ptr,5782 } else {
5783 .Many, .C => unreachable,5783 return ptr;
5784 }5784 }
5785 }5785 }
57865786
...@@ -7917,8 +7917,10 @@ pub const FuncGen = struct {...@@ -7917,8 +7917,10 @@ pub const FuncGen = struct {
7917 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7917 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7918 const un_op = self.air.instructions.items(.data)[inst].un_op;7918 const un_op = self.air.instructions.items(.data)[inst].un_op;
7919 const operand = try self.resolveInst(un_op);7919 const operand = try self.resolveInst(un_op);
7920 const ptr_ty = self.air.typeOf(un_op);
7921 const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty);
7920 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));7922 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
7921 return self.builder.buildPtrToInt(operand, dest_llvm_ty, "");7923 return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, "");
7922 }7924 }
79237925
7924 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7926 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
src/type.zig+10-5
...@@ -3843,9 +3843,14 @@ pub const Type = extern union {...@@ -3843,9 +3843,14 @@ pub const Type = extern union {
3843 };3843 };
3844 }3844 }
38453845
3846 /// Asserts the `Type` is a pointer.3846 /// Asserts `ty` is a pointer.
3847 pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size {3847 pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size {
3848 return switch (self.tag()) {3848 return ptrSizeOrNull(ty).?;
3849 }
3850
3851 /// Returns `null` if `ty` is not a pointer.
3852 pub fn ptrSizeOrNull(ty: Type) ?std.builtin.Type.Pointer.Size {
3853 return switch (ty.tag()) {
3849 .const_slice,3854 .const_slice,
3850 .mut_slice,3855 .mut_slice,
3851 .const_slice_u8,3856 .const_slice_u8,
...@@ -3870,9 +3875,9 @@ pub const Type = extern union {...@@ -3870,9 +3875,9 @@ pub const Type = extern union {
3870 .inferred_alloc_mut,3875 .inferred_alloc_mut,
3871 => .One,3876 => .One,
38723877
3873 .pointer => self.castTag(.pointer).?.data.size,3878 .pointer => ty.castTag(.pointer).?.data.size,
38743879
3875 else => unreachable,3880 else => null,
3876 };3881 };
3877 }3882 }
38783883