authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-26 10:01:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-26 10:01:54-07:00
log3c66850e4296ce2e0f9e0d25bc537aa489f4603e
treeae4b78d3e1ee15253ee353a8c9d972a1034f6fc6
parentd0311e28b397d173f0d60c403985047ec952a172
parentbadad16f88ac7e1eb84eadf76e13b4dc346d4ced
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15278 from ziglang/memcpy-memset

change semantics of `@memcpy` and `@memset`

57 files changed, 1270 insertions(+), 516 deletions(-)

doc/langref.html.in+21-31
......@@ -8681,40 +8681,30 @@ test "integer cast panic" {
86818681 {#header_close#}
86828682
86838683 {#header_open|@memcpy#}
8684 <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize) void{#endsyntax#}</pre>
8685 <p>
8686 This function copies bytes from one region of memory to another. {#syntax#}dest{#endsyntax#} and
8687 {#syntax#}source{#endsyntax#} are both pointers and must not overlap.
8688 </p>
8689 <p>
8690 This function is a low level intrinsic with no safety mechanisms. Most code
8691 should not use this function, instead using something like this:
8692 </p>
8693 <pre>{#syntax#}for (dest, source[0..byte_count]) |*d, s| d.* = s;{#endsyntax#}</pre>
8694 <p>
8695 The optimizer is intelligent enough to turn the above snippet into a memcpy.
8696 </p>
8697 <p>There is also a standard library function for this:</p>
8698 <pre>{#syntax#}const mem = @import("std").mem;
8699mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#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>
8686 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or
8687 a mutable many-item {#link|pointer|Pointers#}. It may have any
8688 alignment, and it may have any element type.</p>
8689 <p>Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a
8690 mutable pointer to an array, or a mutable many-item
8691 {#link|pointer|Pointers#}. It may have any alignment, and it may have any
8692 element type.</p>
8693 <p>The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#}
8694 into the {#syntax#}dest{#endsyntax#} element type. The element types may have
8695 different ABI size, however, that may incur a performance penalty.</p>
8696 <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and
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>
87008700 {#header_close#}
87018701
87028702 {#header_open|@memset#}
8703 <pre>{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize) void{#endsyntax#}</pre>
8704 <p>
8705 This function sets a region of memory to {#syntax#}c{#endsyntax#}. {#syntax#}dest{#endsyntax#} is a pointer.
8706 </p>
8707 <p>
8708 This function is a low level intrinsic with no safety mechanisms. Most
8709 code should not use this function, instead using something like this:
8710 </p>
8711 <pre>{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}</pre>
8712 <p>
8713 The optimizer is intelligent enough to turn the above snippet into a memset.
8714 </p>
8715 <p>There is also a standard library function for this:</p>
8716 <pre>{#syntax#}const mem = @import("std").mem;
8717mem.set(u8, dest, c);{#endsyntax#}</pre>
8703 <pre>{#syntax#}@memset(dest, elem) void{#endsyntax#}</pre>
8704 <p>This function sets all the elements of a memory region to {#syntax#}elem{#endsyntax#}.</p>
8705 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice or a mutable pointer to an array.
8706 It may have any alignment, and it may have any element type.</p>
8707 <p>{#syntax#}elem{#endsyntax#} is coerced to the element type of {#syntax#}dest{#endsyntax#}.</p>
87188708 {#header_close#}
87198709
87208710 {#header_open|@min#}
lib/compiler_rt/atomics.zig+6-6
......@@ -121,22 +121,22 @@ fn __atomic_load(size: u32, src: [*]u8, dest: [*]u8, model: i32) callconv(.C) vo
121121 _ = model;
122122 var sl = spinlocks.get(@ptrToInt(src));
123123 defer sl.release();
124 @memcpy(dest, src, size);
124 @memcpy(dest[0..size], src);
125125}
126126
127127fn __atomic_store(size: u32, dest: [*]u8, src: [*]u8, model: i32) callconv(.C) void {
128128 _ = model;
129129 var sl = spinlocks.get(@ptrToInt(dest));
130130 defer sl.release();
131 @memcpy(dest, src, size);
131 @memcpy(dest[0..size], src);
132132}
133133
134134fn __atomic_exchange(size: u32, ptr: [*]u8, val: [*]u8, old: [*]u8, model: i32) callconv(.C) void {
135135 _ = model;
136136 var sl = spinlocks.get(@ptrToInt(ptr));
137137 defer sl.release();
138 @memcpy(old, ptr, size);
139 @memcpy(ptr, val, size);
138 @memcpy(old[0..size], ptr);
139 @memcpy(ptr[0..size], val);
140140}
141141
142142fn __atomic_compare_exchange(
......@@ -155,10 +155,10 @@ fn __atomic_compare_exchange(
155155 if (expected[i] != b) break;
156156 } else {
157157 // The two objects, ptr and expected, are equal
158 @memcpy(ptr, desired, size);
158 @memcpy(ptr[0..size], desired);
159159 return 1;
160160 }
161 @memcpy(expected, ptr, size);
161 @memcpy(expected[0..size], ptr);
162162 return 0;
163163}
164164
lib/compiler_rt/emutls.zig+2-2
......@@ -139,10 +139,10 @@ const ObjectArray = struct {
139139
140140 if (control.default_value) |value| {
141141 // default value: copy the content to newly allocated object.
142 @memcpy(data, @ptrCast([*]const u8, value), size);
142 @memcpy(data[0..size], @ptrCast([*]const u8, value));
143143 } else {
144144 // no default: return zeroed memory.
145 @memset(data, 0, size);
145 @memset(data[0..size], 0);
146146 }
147147
148148 self.slots[index] = @ptrCast(*anyopaque, data);
lib/std/array_hash_map.zig+2-2
......@@ -1893,7 +1893,7 @@ const IndexHeader = struct {
18931893 const index_size = hash_map.capacityIndexSize(new_bit_index);
18941894 const nbytes = @sizeOf(IndexHeader) + index_size * len;
18951895 const bytes = try allocator.alignedAlloc(u8, @alignOf(IndexHeader), nbytes);
1896 @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader));
1896 @memset(bytes[@sizeOf(IndexHeader)..], 0xff);
18971897 const result = @ptrCast(*IndexHeader, bytes.ptr);
18981898 result.* = .{
18991899 .bit_index = new_bit_index,
......@@ -1914,7 +1914,7 @@ const IndexHeader = struct {
19141914 const index_size = hash_map.capacityIndexSize(header.bit_index);
19151915 const ptr = @ptrCast([*]align(@alignOf(IndexHeader)) u8, header);
19161916 const nbytes = @sizeOf(IndexHeader) + header.length() * index_size;
1917 @memset(ptr + @sizeOf(IndexHeader), 0xff, nbytes - @sizeOf(IndexHeader));
1917 @memset(ptr[@sizeOf(IndexHeader)..nbytes], 0xff);
19181918 }
19191919
19201920 // Verify that the header has sufficient alignment to produce aligned arrays.
lib/std/array_list.zig+4-12
......@@ -121,7 +121,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
121121
122122 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
123123 mem.copy(T, new_memory, self.items);
124 @memset(@ptrCast([*]u8, self.items.ptr), undefined, self.items.len * @sizeOf(T));
124 @memset(self.items, undefined);
125125 self.clearAndFree();
126126 return new_memory;
127127 }
......@@ -281,11 +281,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
281281 const new_len = old_len + items.len;
282282 assert(new_len <= self.capacity);
283283 self.items.len = new_len;
284 @memcpy(
285 @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len),
286 @ptrCast([*]const u8, items.ptr),
287 items.len * @sizeOf(T),
288 );
284 @memcpy(self.items[old_len..][0..items.len], items);
289285 }
290286
291287 pub const Writer = if (T != u8)
......@@ -601,7 +597,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
601597
602598 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
603599 mem.copy(T, new_memory, self.items);
604 @memset(@ptrCast([*]u8, self.items.ptr), undefined, self.items.len * @sizeOf(T));
600 @memset(self.items, undefined);
605601 self.clearAndFree(allocator);
606602 return new_memory;
607603 }
......@@ -740,11 +736,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
740736 const new_len = old_len + items.len;
741737 assert(new_len <= self.capacity);
742738 self.items.len = new_len;
743 @memcpy(
744 @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len),
745 @ptrCast([*]const u8, items.ptr),
746 items.len * @sizeOf(T),
747 );
739 @memcpy(self.items[old_len..][0..items.len], items);
748740 }
749741
750742 pub const WriterContext = struct {
lib/std/builtin.zig+2
......@@ -1002,6 +1002,8 @@ pub const panic_messages = struct {
10021002 pub const index_out_of_bounds = "index out of bounds";
10031003 pub const start_index_greater_than_end = "start index is larger than end index";
10041004 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";
10051007};
10061008
10071009pub noinline fn returnError(st: *StackTrace) void {
lib/std/c/darwin.zig+1-1
......@@ -3670,7 +3670,7 @@ pub const MachTask = extern struct {
36703670 else => |err| return unexpectedKernError(err),
36713671 }
36723672
3673 @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read);
3673 @memcpy(out_buf[0..curr_bytes_read], @intToPtr([*]const u8, vm_memory));
36743674 _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
36753675
36763676 out_buf = out_buf[curr_bytes_read..];
lib/std/crypto/aegis.zig+2-2
......@@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type {
209209 acc |= (computed_tag[j] ^ tag[j]);
210210 }
211211 if (acc != 0) {
212 @memset(m.ptr, undefined, m.len);
212 @memset(m, undefined);
213213 return error.AuthenticationFailed;
214214 }
215215 }
......@@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type {
390390 acc |= (computed_tag[j] ^ tag[j]);
391391 }
392392 if (acc != 0) {
393 @memset(m.ptr, undefined, m.len);
393 @memset(m, undefined);
394394 return error.AuthenticationFailed;
395395 }
396396 }
lib/std/crypto/aes_gcm.zig+1-1
......@@ -91,7 +91,7 @@ fn AesGcm(comptime Aes: anytype) type {
9191 acc |= (computed_tag[p] ^ tag[p]);
9292 }
9393 if (acc != 0) {
94 @memset(m.ptr, undefined, m.len);
94 @memset(m, undefined);
9595 return error.AuthenticationFailed;
9696 }
9797
lib/std/crypto/tls/Client.zig+1-1
......@@ -531,7 +531,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
531531 const pub_key = subject.pubKey();
532532 if (pub_key.len > main_cert_pub_key_buf.len)
533533 return error.CertificatePublicKeyInvalid;
534 @memcpy(&main_cert_pub_key_buf, pub_key.ptr, pub_key.len);
534 @memcpy(main_cert_pub_key_buf[0..pub_key.len], pub_key);
535535 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);
536536 } else {
537537 try prev_cert.verify(subject, now_sec);
lib/std/crypto/utils.zig+3-3
......@@ -135,11 +135,11 @@ pub fn timingSafeSub(comptime T: type, a: []const T, b: []const T, result: []T,
135135/// Sets a slice to zeroes.
136136/// Prevents the store from being optimized out.
137137pub fn secureZero(comptime T: type, s: []T) void {
138 // NOTE: We do not use a volatile slice cast here since LLVM cannot
139 // see that it can be replaced by a memset.
138 // TODO: implement `@memset` for non-byte-sized element type in the llvm backend
139 //@memset(@as([]volatile T, s), 0);
140140 const ptr = @ptrCast([*]volatile u8, s.ptr);
141141 const length = s.len * @sizeOf(T);
142 @memset(ptr, 0, length);
142 @memset(ptr[0..length], 0);
143143}
144144
145145test "crypto.utils.timingSafeEql" {
lib/std/fifo.zig+4-4
......@@ -104,7 +104,7 @@ pub fn LinearFifo(
104104 }
105105 { // set unused area to undefined
106106 const unused = mem.sliceAsBytes(self.buf[self.count..]);
107 @memset(unused.ptr, undefined, unused.len);
107 @memset(unused, undefined);
108108 }
109109 }
110110
......@@ -182,12 +182,12 @@ pub fn LinearFifo(
182182 const slice = self.readableSliceMut(0);
183183 if (slice.len >= count) {
184184 const unused = mem.sliceAsBytes(slice[0..count]);
185 @memset(unused.ptr, undefined, unused.len);
185 @memset(unused, undefined);
186186 } else {
187187 const unused = mem.sliceAsBytes(slice[0..]);
188 @memset(unused.ptr, undefined, unused.len);
188 @memset(unused, undefined);
189189 const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]);
190 @memset(unused2.ptr, undefined, unused2.len);
190 @memset(unused2, undefined);
191191 }
192192 }
193193 if (autoalign and self.count == count) {
lib/std/hash/murmur.zig+8-14
......@@ -99,9 +99,8 @@ pub const Murmur2_64 = struct {
9999
100100 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
101101 const m: u64 = 0xc6a4a7935bd1e995;
102 const len = @as(u64, str.len);
103 var h1: u64 = seed ^ (len *% m);
104 for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| {
102 var h1: u64 = seed ^ (@as(u64, str.len) *% m);
103 for (@ptrCast([*]align(1) const u64, str.ptr)[0 .. str.len / 8]) |v| {
105104 var k1: u64 = v;
106105 if (native_endian == .Big)
107106 k1 = @byteSwap(k1);
......@@ -111,11 +110,11 @@ pub const Murmur2_64 = struct {
111110 h1 ^= k1;
112111 h1 *%= m;
113112 }
114 const rest = len & 7;
115 const offset = len - rest;
113 const rest = str.len & 7;
114 const offset = str.len - rest;
116115 if (rest > 0) {
117116 var k1: u64 = 0;
118 @memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[@intCast(usize, offset)]), @intCast(usize, rest));
117 @memcpy(@ptrCast([*]u8, &k1)[0..rest], str[offset..]);
119118 if (native_endian == .Big)
120119 k1 = @byteSwap(k1);
121120 h1 ^= k1;
......@@ -282,13 +281,8 @@ pub const Murmur3_32 = struct {
282281
283282fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
284283 const hashbytes = hashbits / 8;
285 var key: [256]u8 = undefined;
286 var hashes: [hashbytes * 256]u8 = undefined;
287 var final: [hashbytes]u8 = undefined;
288
289 @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key)));
290 @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes)));
291 @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final)));
284 var key: [256]u8 = [1]u8{0} ** 256;
285 var hashes: [hashbytes * 256]u8 = [1]u8{0} ** (hashbytes * 256);
292286
293287 var i: u32 = 0;
294288 while (i < 256) : (i += 1) {
......@@ -297,7 +291,7 @@ fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
297291 var h = hash_fn(key[0..i], 256 - i);
298292 if (native_endian == .Big)
299293 h = @byteSwap(h);
300 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
294 @memcpy(hashes[i * hashbytes ..][0..hashbytes], @ptrCast([*]u8, &h));
301295 }
302296
303297 return @truncate(u32, hash_fn(&hashes, 0));
lib/std/hash_map.zig+1-1
......@@ -1449,7 +1449,7 @@ pub fn HashMapUnmanaged(
14491449 }
14501450
14511451 fn initMetadatas(self: *Self) void {
1452 @memset(@ptrCast([*]u8, self.metadata.?), 0, @sizeOf(Metadata) * self.capacity());
1452 @memset(@ptrCast([*]u8, self.metadata.?)[0 .. @sizeOf(Metadata) * self.capacity()], 0);
14531453 }
14541454
14551455 // This counts the number of occupied slots (not counting tombstones), which is
lib/std/heap/general_purpose_allocator.zig+3-3
......@@ -759,7 +759,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
759759 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
760760 if (new_size_class <= size_class) {
761761 if (old_mem.len > new_size) {
762 @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);
762 @memset(old_mem[new_size..], undefined);
763763 }
764764 if (config.verbose_log) {
765765 log.info("small resize {d} bytes at {*} to {d}", .{
......@@ -911,7 +911,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
911911 self.empty_buckets = bucket;
912912 }
913913 } else {
914 @memset(old_mem.ptr, undefined, old_mem.len);
914 @memset(old_mem, undefined);
915915 }
916916 if (config.safety) {
917917 assert(self.small_allocations.remove(@ptrToInt(old_mem.ptr)));
......@@ -1011,7 +1011,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
10111011 };
10121012 self.buckets[bucket_index] = ptr;
10131013 // Set the used bits to all zeroes
1014 @memset(@as(*[1]u8, ptr.usedBits(0)), 0, usedBitsCount(size_class));
1014 @memset(@as([*]u8, @as(*[1]u8, ptr.usedBits(0)))[0..usedBitsCount(size_class)], 0);
10151015 return ptr;
10161016 }
10171017 };
lib/std/math/big/int_test.zig+4-4
......@@ -2756,7 +2756,7 @@ test "big int conversion read twos complement with padding" {
27562756
27572757 var buffer1 = try testing.allocator.alloc(u8, 16);
27582758 defer testing.allocator.free(buffer1);
2759 @memset(buffer1.ptr, 0xaa, buffer1.len);
2759 @memset(buffer1, 0xaa);
27602760
27612761 // writeTwosComplement:
27622762 // (1) should not write beyond buffer[0..abi_size]
......@@ -2773,7 +2773,7 @@ test "big int conversion read twos complement with padding" {
27732773 a.toConst().writeTwosComplement(buffer1[0..16], .Big);
27742774 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }));
27752775
2776 @memset(buffer1.ptr, 0xaa, buffer1.len);
2776 @memset(buffer1, 0xaa);
27772777 try a.set(-0x01_02030405_06070809_0a0b0c0d);
27782778 bit_count = 12 * 8 + 2;
27792779
......@@ -2794,7 +2794,7 @@ test "big int write twos complement +/- zero" {
27942794
27952795 var buffer1 = try testing.allocator.alloc(u8, 16);
27962796 defer testing.allocator.free(buffer1);
2797 @memset(buffer1.ptr, 0xaa, buffer1.len);
2797 @memset(buffer1, 0xaa);
27982798
27992799 // Test zero
28002800
......@@ -2807,7 +2807,7 @@ test "big int write twos complement +/- zero" {
28072807 m.toConst().writeTwosComplement(buffer1[0..16], .Big);
28082808 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
28092809
2810 @memset(buffer1.ptr, 0xaa, buffer1.len);
2810 @memset(buffer1, 0xaa);
28112811 m.positive = false;
28122812
28132813 // Test negative zero
lib/std/mem/Allocator.zig+5-4
......@@ -215,7 +215,7 @@ pub fn allocAdvancedWithRetAddr(
215215 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
216216 const byte_ptr = self.rawAlloc(byte_count, log2a(a), return_address) orelse return Error.OutOfMemory;
217217 // TODO: https://github.com/ziglang/zig/issues/4298
218 @memset(byte_ptr, undefined, byte_count);
218 @memset(byte_ptr[0..byte_count], undefined);
219219 const byte_slice = byte_ptr[0..byte_count];
220220 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
221221}
......@@ -282,9 +282,10 @@ pub fn reallocAdvanced(
282282
283283 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse
284284 return error.OutOfMemory;
285 @memcpy(new_mem, old_byte_slice.ptr, @min(byte_count, old_byte_slice.len));
285 const copy_len = @min(byte_count, old_byte_slice.len);
286 @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]);
286287 // TODO https://github.com/ziglang/zig/issues/4298
287 @memset(old_byte_slice.ptr, undefined, old_byte_slice.len);
288 @memset(old_byte_slice, undefined);
288289 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);
289290
290291 return mem.bytesAsSlice(T, @alignCast(Slice.alignment, new_mem[0..byte_count]));
......@@ -299,7 +300,7 @@ pub fn free(self: Allocator, memory: anytype) void {
299300 if (bytes_len == 0) return;
300301 const non_const_ptr = @constCast(bytes.ptr);
301302 // TODO: https://github.com/ziglang/zig/issues/4298
302 @memset(non_const_ptr, undefined, bytes_len);
303 @memset(non_const_ptr[0..bytes_len], undefined);
303304 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());
304305}
305306
lib/std/multi_array_list.zig+1-2
......@@ -360,11 +360,10 @@ pub fn MultiArrayList(comptime T: type) type {
360360 if (@sizeOf(field_info.type) != 0) {
361361 const field = @intToEnum(Field, i);
362362 const dest_slice = self_slice.items(field)[new_len..];
363 const byte_count = dest_slice.len * @sizeOf(field_info.type);
364363 // We use memset here for more efficient codegen in safety-checked,
365364 // valgrind-enabled builds. Otherwise the valgrind client request
366365 // will be repeated for every element.
367 @memset(@ptrCast([*]u8, dest_slice.ptr), undefined, byte_count);
366 @memset(dest_slice, undefined);
368367 }
369368 }
370369 self.len = new_len;
lib/std/net.zig+3-3
......@@ -1020,7 +1020,7 @@ fn linuxLookupName(
10201020 for (addrs.items, 0..) |*addr, i| {
10211021 var key: i32 = 0;
10221022 var sa6: os.sockaddr.in6 = undefined;
1023 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr.in6));
1023 @memset(@ptrCast([*]u8, &sa6)[0..@sizeOf(os.sockaddr.in6)], 0);
10241024 var da6 = os.sockaddr.in6{
10251025 .family = os.AF.INET6,
10261026 .scope_id = addr.addr.in6.sa.scope_id,
......@@ -1029,7 +1029,7 @@ fn linuxLookupName(
10291029 .addr = [1]u8{0} ** 16,
10301030 };
10311031 var sa4: os.sockaddr.in = undefined;
1032 @memset(@ptrCast([*]u8, &sa4), 0, @sizeOf(os.sockaddr.in));
1032 @memset(@ptrCast([*]u8, &sa4)[0..@sizeOf(os.sockaddr.in)], 0);
10331033 var da4 = os.sockaddr.in{
10341034 .family = os.AF.INET,
10351035 .port = 65535,
......@@ -1577,7 +1577,7 @@ fn resMSendRc(
15771577
15781578 // Get local address and open/bind a socket
15791579 var sa: Address = undefined;
1580 @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address));
1580 @memset(@ptrCast([*]u8, &sa)[0..@sizeOf(Address)], 0);
15811581 sa.any.family = family;
15821582 try os.bind(fd, &sa.any, sl);
15831583
lib/std/os.zig+5-5
......@@ -5217,7 +5217,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
52175217 .macos, .ios, .watchos, .tvos => {
52185218 // On macOS, we can use F.GETPATH fcntl command to query the OS for
52195219 // the path to the file descriptor.
5220 @memset(out_buffer, 0, MAX_PATH_BYTES);
5220 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
52215221 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
52225222 .SUCCESS => {},
52235223 .BADF => return error.FileNotFound,
......@@ -5308,7 +5308,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
53085308 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 6, .minor = 0 }) == .lt) {
53095309 @compileError("querying for canonical path of a handle is unsupported on this host");
53105310 }
5311 @memset(out_buffer, 0, MAX_PATH_BYTES);
5311 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
53125312 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
53135313 .SUCCESS => {},
53145314 .BADF => return error.FileNotFound,
......@@ -5322,7 +5322,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
53225322 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 10, .minor = 0 }) == .lt) {
53235323 @compileError("querying for canonical path of a handle is unsupported on this host");
53245324 }
5325 @memset(out_buffer, 0, MAX_PATH_BYTES);
5325 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
53265326 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
53275327 .SUCCESS => {},
53285328 .ACCES => return error.AccessDenied,
......@@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
55485548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
55495549 // >= rather than > to make room for the null byte
55505550 if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
5551 mem.copy(u8, &path_with_null, file_path);
5551 @memcpy(path_with_null[0..file_path.len], file_path);
55525552 path_with_null[file_path.len] = 0;
55535553 return path_with_null;
55545554}
......@@ -5720,7 +5720,7 @@ pub fn res_mkquery(
57205720
57215721 // Construct query template - ID will be filled later
57225722 var q: [280]u8 = undefined;
5723 @memset(&q, 0, n);
5723 @memset(q[0..n], 0);
57245724 q[2] = @as(u8, op) * 8 + 1;
57255725 q[5] = 1;
57265726 mem.copy(u8, q[13..], name);
lib/std/os/linux.zig+3-3
......@@ -1184,7 +1184,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
11841184 .mask = undefined,
11851185 .restorer = @ptrCast(k_sigaction_funcs.restorer, restorer_fn),
11861186 };
1187 @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &new.mask), mask_size);
1187 @memcpy(@ptrCast([*]u8, &ksa.mask)[0..mask_size], @ptrCast([*]const u8, &new.mask));
11881188 }
11891189
11901190 const ksa_arg = if (act != null) @ptrToInt(&ksa) else 0;
......@@ -1200,7 +1200,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
12001200 if (oact) |old| {
12011201 old.handler.handler = oldksa.handler;
12021202 old.flags = @truncate(c_uint, oldksa.flags);
1203 @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &oldksa.mask), mask_size);
1203 @memcpy(@ptrCast([*]u8, &old.mask)[0..mask_size], @ptrCast([*]const u8, &oldksa.mask));
12041204 }
12051205
12061206 return 0;
......@@ -1515,7 +1515,7 @@ pub fn sched_yield() usize {
15151515pub fn sched_getaffinity(pid: pid_t, size: usize, set: *cpu_set_t) usize {
15161516 const rc = syscall3(.sched_getaffinity, @bitCast(usize, @as(isize, pid)), size, @ptrToInt(set));
15171517 if (@bitCast(isize, rc) < 0) return rc;
1518 if (rc < size) @memset(@ptrCast([*]u8, set) + rc, 0, size - rc);
1518 if (rc < size) @memset(@ptrCast([*]u8, set)[rc..size], 0);
15191519 return 0;
15201520}
15211521
lib/std/os/windows.zig+3-3
......@@ -755,9 +755,9 @@ pub fn CreateSymbolicLink(
755755 };
756756
757757 std.mem.copy(u8, buffer[0..], std.mem.asBytes(&symlink_data));
758 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);
758 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. target_path.len * 2], @ptrCast([*]const u8, target_path));
759759 const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;
760 @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);
760 @memcpy(buffer[paths_start..][0 .. target_path.len * 2], @ptrCast([*]const u8, target_path));
761761 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
762762}
763763
......@@ -1179,7 +1179,7 @@ pub fn GetFinalPathNameByHandle(
11791179 var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
11801180 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
11811181 input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);
1182 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, volume_name_u16.ptr), volume_name_u16.len * 2);
1182 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @ptrCast([*]const u8, volume_name_u16.ptr));
11831183
11841184 DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
11851185 error.AccessDenied => unreachable,
lib/std/zig/c_builtins.zig+5-5
......@@ -152,7 +152,7 @@ pub inline fn __builtin___memset_chk(
152152
153153pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {
154154 const dst_cast = @ptrCast([*c]u8, dst);
155 @memset(dst_cast, @bitCast(u8, @truncate(i8, val)), len);
155 @memset(dst_cast[0..len], @bitCast(u8, @truncate(i8, val)));
156156 return dst;
157157}
158158
......@@ -171,10 +171,10 @@ pub inline fn __builtin_memcpy(
171171 noalias src: ?*const anyopaque,
172172 len: usize,
173173) ?*anyopaque {
174 const dst_cast = @ptrCast([*c]u8, dst);
175 const src_cast = @ptrCast([*c]const u8, src);
176
177 @memcpy(dst_cast, src_cast, len);
174 if (len > 0) @memcpy(
175 @ptrCast([*]u8, dst.?)[0..len],
176 @ptrCast([*]const u8, src.?),
177 );
178178 return dst;
179179}
180180
src/Air.zig+40-8
......@@ -138,12 +138,14 @@ pub const Inst = struct {
138138 /// The offset is in element type units, not bytes.
139139 /// Wrapping is undefined behavior.
140140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
141 /// The pointer may be a slice.
141142 /// Uses the `ty_pl` field. Payload is `Bin`.
142143 ptr_add,
143144 /// Subtract an offset from a pointer, returning a new pointer.
144145 /// The offset is in element type units, not bytes.
145146 /// Wrapping is undefined behavior.
146147 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
148 /// The pointer may be a slice.
147149 /// Uses the `ty_pl` field. Payload is `Bin`.
148150 ptr_sub,
149151 /// Given two operands which can be floats, integers, or vectors, returns the
......@@ -462,6 +464,7 @@ pub const Inst = struct {
462464 /// Uses the `ty_op` field.
463465 load,
464466 /// Converts a pointer to its address. Result type is always `usize`.
467 /// Pointer type size may be any, including slice.
465468 /// Uses the `un_op` field.
466469 ptrtoint,
467470 /// Given a boolean, returns 0 or 1.
......@@ -484,7 +487,16 @@ pub const Inst = struct {
484487 /// Write a value to a pointer. LHS is pointer, RHS is value.
485488 /// Result type is always void.
486489 /// Uses the `bin_op` field.
490 /// The value to store may be undefined, in which case the destination
491 /// memory region has undefined bytes after this instruction is
492 /// evaluated. In such case ignoring this instruction is legal
493 /// lowering.
487494 store,
495 /// Same as `store`, except if the value to store is undefined, the
496 /// memory region should be filled with 0xaa bytes, and any other
497 /// safety metadata such as Valgrind integrations should be notified of
498 /// this memory region being undefined.
499 store_safe,
488500 /// Indicates the program counter will never get to this instruction.
489501 /// Result type is always noreturn; no instructions in a block follow this one.
490502 unreach,
......@@ -632,17 +644,33 @@ pub const Inst = struct {
632644 /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`.
633645 select,
634646
635 /// Given dest ptr, value, and len, set all elements at dest to value.
647 /// Given dest pointer and value, set all elements at dest to value.
648 /// Dest pointer is either a slice or a pointer to array.
649 /// The element type may be any type, and the slice may have any alignment.
636650 /// Result type is always void.
637 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
638 /// value, `rhs` is the length.
639 /// The element type may be any type, not just u8.
651 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value.
652 /// The element value may be undefined, in which case the destination
653 /// memory region has undefined bytes after this instruction is
654 /// evaluated. In such case ignoring this instruction is legal
655 /// lowering.
656 /// If the length is compile-time known (due to the destination being a
657 /// pointer-to-array), then it is guaranteed to be greater than zero.
640658 memset,
641 /// Given dest ptr, src ptr, and len, copy len elements from src to dest.
659 /// Same as `memset`, except if the element value is undefined, the memory region
660 /// should be filled with 0xaa bytes, and any other safety metadata such as Valgrind
661 /// integrations should be notified of this memory region being undefined.
662 memset_safe,
663 /// Given dest pointer and source pointer, copy elements from source to dest.
664 /// Dest pointer is either a slice or a pointer to array.
665 /// The dest element type may be any type.
666 /// Source pointer must have same element type as dest element type.
667 /// Dest slice may have any alignment; source pointer may have any alignment.
668 /// The two memory regions must not overlap.
642669 /// Result type is always void.
643 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
644 /// src ptr, `rhs` is the length.
645 /// The element type may be any type, not just u8.
670 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
671 /// If the length is compile-time known (due to the destination or
672 /// source being a pointer-to-array), then it is guaranteed to be
673 /// greater than zero.
646674 memcpy,
647675
648676 /// Uses the `ty_pl` field with payload `Cmpxchg`.
......@@ -1226,12 +1254,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
12261254 .dbg_var_ptr,
12271255 .dbg_var_val,
12281256 .store,
1257 .store_safe,
12291258 .fence,
12301259 .atomic_store_unordered,
12311260 .atomic_store_monotonic,
12321261 .atomic_store_release,
12331262 .atomic_store_seq_cst,
12341263 .memset,
1264 .memset_safe,
12351265 .memcpy,
12361266 .set_union_tag,
12371267 .prefetch,
......@@ -1406,11 +1436,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {
14061436 .ret,
14071437 .ret_load,
14081438 .store,
1439 .store_safe,
14091440 .unreach,
14101441 .optional_payload_ptr_set,
14111442 .errunion_payload_ptr_set,
14121443 .set_union_tag,
14131444 .memset,
1445 .memset_safe,
14141446 .memcpy,
14151447 .cmpxchg_weak,
14161448 .cmpxchg_strong,
src/AstGen.zig+6-8
......@@ -8453,18 +8453,16 @@ fn builtinCall(
84538453 return rvalue(gz, ri, result, node);
84548454 },
84558455 .memcpy => {
8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
8457 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
8458 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),
8459 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
8457 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8458 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
84608459 });
84618460 return rvalue(gz, ri, .void_value, node);
84628461 },
84638462 .memset => {
8464 _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
8465 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
8466 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),
8467 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8463 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
8464 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8465 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
84688466 });
84698467 return rvalue(gz, ri, .void_value, node);
84708468 },
src/BuiltinFn.zig+2-2
......@@ -615,14 +615,14 @@ pub const list = list: {
615615 "@memcpy",
616616 .{
617617 .tag = .memcpy,
618 .param_count = 3,
618 .param_count = 2,
619619 },
620620 },
621621 .{
622622 "@memset",
623623 .{
624624 .tag = .memset,
625 .param_count = 3,
625 .param_count = 2,
626626 },
627627 },
628628 .{
src/Liveness.zig+8-17
......@@ -299,11 +299,15 @@ pub fn categorizeOperand(
299299 },
300300
301301 .store,
302 .store_safe,
302303 .atomic_store_unordered,
303304 .atomic_store_monotonic,
304305 .atomic_store_release,
305306 .atomic_store_seq_cst,
306307 .set_union_tag,
308 .memset,
309 .memset_safe,
310 .memcpy,
307311 => {
308312 const o = air_datas[inst].bin_op;
309313 if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
......@@ -597,16 +601,6 @@ pub fn categorizeOperand(
597601 if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write);
598602 return .write;
599603 },
600 .memset,
601 .memcpy,
602 => {
603 const pl_op = air_datas[inst].pl_op;
604 const extra = air.extraData(Air.Bin, pl_op.payload).data;
605 if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
606 if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write);
607 if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 2, .write);
608 return .write;
609 },
610604
611605 .br => {
612606 const br = air_datas[inst].br;
......@@ -972,6 +966,7 @@ fn analyzeInst(
972966 .bool_and,
973967 .bool_or,
974968 .store,
969 .store_safe,
975970 .array_elem_val,
976971 .slice_elem_val,
977972 .ptr_elem_val,
......@@ -987,6 +982,9 @@ fn analyzeInst(
987982 .set_union_tag,
988983 .min,
989984 .max,
985 .memset,
986 .memset_safe,
987 .memcpy,
990988 => {
991989 const o = inst_datas[inst].bin_op;
992990 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });
......@@ -1234,13 +1232,6 @@ fn analyzeInst(
12341232 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
12351233 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.operand, .none });
12361234 },
1237 .memset,
1238 .memcpy,
1239 => {
1240 const pl_op = inst_datas[inst].pl_op;
1241 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1242 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.lhs, extra.rhs });
1243 },
12441235
12451236 .br => return analyzeInstBr(a, pass, data, inst),
12461237
src/Liveness/Verify.zig+4-7
......@@ -239,6 +239,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
239239 .bool_and,
240240 .bool_or,
241241 .store,
242 .store_safe,
242243 .array_elem_val,
243244 .slice_elem_val,
244245 .ptr_elem_val,
......@@ -254,6 +255,9 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
254255 .set_union_tag,
255256 .min,
256257 .max,
258 .memset,
259 .memset_safe,
260 .memcpy,
257261 => {
258262 const bin_op = data[inst].bin_op;
259263 try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });
......@@ -306,13 +310,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
306310 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;
307311 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });
308312 },
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 },
316313 .cmpxchg_strong,
317314 .cmpxchg_weak,
318315 => {
src/Sema.zig+303-75
......@@ -2500,7 +2500,7 @@ fn coerceResultPtr(
25002500
25012501 // The last one is always `store`.
25022502 const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];
2503 if (air_tags[trash_inst] != .store) {
2503 if (air_tags[trash_inst] != .store and air_tags[trash_inst] != .store_safe) {
25042504 // no store instruction is generated for zero sized types
25052505 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);
25062506 } else {
......@@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
33863386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
33873387 const src = inst_data.src();
33883388 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)
3394 object_ty.childType()
3395 else
3396 object_ty;
3390 return indexablePtrLen(sema, block, src, object);
3391}
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;
33983402 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);
34003422 return sema.fieldVal(block, src, object, "len", src);
34013423}
34023424
......@@ -3502,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
35023524 const candidate = block.instructions.items[search_index];
35033525 switch (air_tags[candidate]) {
35043526 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3505 .store => break candidate,
3527 .store, .store_safe => break candidate,
35063528 else => break :ct,
35073529 }
35083530 };
......@@ -3728,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
37283750 const candidate = block.instructions.items[search_index];
37293751 switch (air_tags[candidate]) {
37303752 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3731 .store => break candidate,
3753 .store, .store_safe => break candidate,
37323754 else => break :ct,
37333755 }
37343756 };
......@@ -3838,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
38383860 assert(replacement_block.instructions.items.len > 0);
38393861 break :result sub_ptr;
38403862 },
3841 .store => result: {
3863 .store, .store_safe => result: {
38423864 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;
38433865 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);
38443866 break :result .void_value;
......@@ -4220,7 +4242,10 @@ fn validateUnionInit(
42204242 while (block_index > 0) : (block_index -= 1) {
42214243 const store_inst = block.instructions.items[block_index];
42224244 if (store_inst == field_ptr_air_inst) break;
4223 if (air_tags[store_inst] != .store) continue;
4245 switch (air_tags[store_inst]) {
4246 .store, .store_safe => {},
4247 else => continue,
4248 }
42244249 const bin_op = air_datas[store_inst].bin_op;
42254250 var lhs = bin_op.lhs;
42264251 if (Air.refToIndex(lhs)) |lhs_index| {
......@@ -4432,7 +4457,10 @@ fn validateStructInit(
44324457 struct_is_comptime = false;
44334458 continue :field;
44344459 }
4435 if (air_tags[store_inst] != .store) continue;
4460 switch (air_tags[store_inst]) {
4461 .store, .store_safe => {},
4462 else => continue,
4463 }
44364464 const bin_op = air_datas[store_inst].bin_op;
44374465 var lhs = bin_op.lhs;
44384466 {
......@@ -4660,7 +4688,10 @@ fn zirValidateArrayInit(
46604688 array_is_comptime = false;
46614689 continue :outer;
46624690 }
4663 if (air_tags[store_inst] != .store) continue;
4691 switch (air_tags[store_inst]) {
4692 .store, .store_safe => {},
4693 else => continue,
4694 }
46644695 const bin_op = air_datas[store_inst].bin_op;
46654696 var lhs = bin_op.lhs;
46664697 {
......@@ -5003,7 +5034,12 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
50035034
50045035 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };
50055036 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };
5006 const air_tag: Air.Inst.Tag = if (is_ret) .ret_ptr else .store;
5037 const air_tag: Air.Inst.Tag = if (is_ret)
5038 .ret_ptr
5039 else if (block.wantSafety())
5040 .store_safe
5041 else
5042 .store;
50075043 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
50085044}
50095045
......@@ -9861,8 +9897,11 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
98619897 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
98629898 const array_ptr = try sema.resolveInst(extra.lhs);
98639899 const start = try sema.resolveInst(extra.start);
9900 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9901 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9902 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
98649903
9865 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded);
9904 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src);
98669905}
98679906
98689907fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9875,8 +9914,11 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
98759914 const array_ptr = try sema.resolveInst(extra.lhs);
98769915 const start = try sema.resolveInst(extra.start);
98779916 const end = try sema.resolveInst(extra.end);
9917 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9918 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9919 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
98789920
9879 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded);
9921 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src);
98809922}
98819923
98829924fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9891,8 +9933,11 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
98919933 const start = try sema.resolveInst(extra.start);
98929934 const end = try sema.resolveInst(extra.end);
98939935 const sentinel = try sema.resolveInst(extra.sentinel);
9936 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9937 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9938 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
98949939
9895 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
9940 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src);
98969941}
98979942
98989943fn zirSwitchCapture(
......@@ -21748,90 +21793,270 @@ fn analyzeMinMax(
2174821793 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
2174921794}
2175021795
21796fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
21797 const mod = sema.mod;
21798 const info = sema.typeOf(ptr).ptrInfo().data;
21799 if (info.size == .One) {
21800 // Already an array pointer.
21801 return ptr;
21802 }
21803 const new_ty = try Type.ptr(sema.arena, mod, .{
21804 .pointee_type = try Type.array(sema.arena, len, info.sentinel, info.pointee_type, mod),
21805 .sentinel = null,
21806 .@"align" = info.@"align",
21807 .@"addrspace" = info.@"addrspace",
21808 .mutable = info.mutable,
21809 .@"allowzero" = info.@"allowzero",
21810 .@"volatile" = info.@"volatile",
21811 .size = .One,
21812 });
21813 if (info.size == .Slice) {
21814 return block.addTyOp(.slice_ptr, new_ty, ptr);
21815 }
21816 return block.addBitCast(new_ty, ptr);
21817}
21818
2175121819fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
2175221820 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
21753 const extra = sema.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
21821 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
2175421822 const src = inst_data.src();
2175521823 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2175621824 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
21757 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
21758 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);
21759
21760 // TODO AstGen's coerced_ty cannot handle volatile here
21761 var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data;
21762 dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr();
21763 const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info);
21764 const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src);
21765
21766 const uncasted_src_ptr = try sema.resolveInst(extra.source);
21767 var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data;
21768 src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr();
21769 const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info);
21770 const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src);
21771 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
21825 const dest_ptr = try sema.resolveInst(extra.lhs);
21826 const src_ptr = try sema.resolveInst(extra.rhs);
21827 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
21828 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
21829 const target = sema.mod.getTarget();
21830
21831 if (dest_len == .none and src_len == .none) {
21832 const msg = msg: {
21833 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
21834 errdefer msg.destroy(sema.gpa);
21835 try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{
21836 sema.typeOf(dest_ptr).fmt(sema.mod),
21837 });
21838 try sema.errNote(block, src_src, msg, "source type {} provides no length", .{
21839 sema.typeOf(src_ptr).fmt(sema.mod),
21840 });
21841 break :msg msg;
21842 };
21843 return sema.failWithOwnedErrorMsg(msg);
21844 }
21845
21846 var len_val: ?Value = null;
21847
21848 if (dest_len != .none and src_len != .none) check: {
21849 // If we can check at compile-time, no need for runtime safety.
21850 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
21851 len_val = dest_len_val;
21852 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
21853 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) {
21854 const msg = msg: {
21855 const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{});
21856 errdefer msg.destroy(sema.gpa);
21857 try sema.errNote(block, dest_src, msg, "length {} here", .{
21858 dest_len_val.fmtValue(Type.usize, sema.mod),
21859 });
21860 try sema.errNote(block, src_src, msg, "length {} here", .{
21861 src_len_val.fmtValue(Type.usize, sema.mod),
21862 });
21863 break :msg msg;
21864 };
21865 return sema.failWithOwnedErrorMsg(msg);
21866 }
21867 break :check;
21868 }
21869 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
21870 len_val = src_len_val;
21871 }
21872
21873 if (block.wantSafety()) {
21874 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
21875 try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch);
21876 }
21877 }
2177221878
2177321879 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
2177421880 if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;
21775 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| {
21776 if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src;
21777 if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {
21778 _ = len_val;
21779 return sema.fail(block, src, "TODO: Sema.zirMemcpy at comptime", .{});
21780 } else break :rs len_src;
21881 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
21882 const len_u64 = (try len_val.?.getUnsignedIntAdvanced(target, sema)).?;
21883 const len = try sema.usizeCast(block, dest_src, len_u64);
21884 for (0..len) |i| {
21885 const elem_index = try sema.addIntUnsigned(Type.usize, i);
21886 const dest_elem_ptr = try sema.elemPtr(
21887 block,
21888 src,
21889 dest_ptr,
21890 elem_index,
21891 src,
21892 true, // init
21893 false, // oob_safety
21894 );
21895 const src_elem_ptr = try sema.elemPtr(
21896 block,
21897 src,
21898 src_ptr,
21899 elem_index,
21900 src,
21901 false, // init
21902 false, // oob_safety
21903 );
21904 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src);
21905 try sema.storePtr2(
21906 block,
21907 src,
21908 dest_elem_ptr,
21909 dest_src,
21910 uncoerced_elem,
21911 src_src,
21912 .store,
21913 );
21914 }
21915 return;
2178121916 } else break :rs src_src;
2178221917 } else dest_src;
2178321918
21919 const dest_ty = sema.typeOf(dest_ptr);
21920 const src_ty = sema.typeOf(src_ptr);
21921
21922 // If in-memory coercion is not allowed, explode this memcpy call into a
21923 // for loop that copies element-wise.
21924 // Likewise if this is an iterable rather than a pointer, do the same
21925 // lowering. The AIR instruction requires pointers with element types of
21926 // equal ABI size.
21927
21928 if (dest_ty.zigTypeTag() != .Pointer or src_ty.zigTypeTag() != .Pointer) {
21929 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{});
21930 }
21931
21932 const dest_elem_ty = dest_ty.elemType2();
21933 const src_elem_ty = src_ty.elemType2();
21934 if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) {
21935 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{});
21936 }
21937
21938 // If the length is comptime-known, then upgrade src and destination types
21939 // into pointer-to-array. At this point we know they are both pointers
21940 // already.
21941 var new_dest_ptr = dest_ptr;
21942 var new_src_ptr = src_ptr;
21943 if (len_val) |val| {
21944 const len = val.toUnsignedInt(target);
21945 if (len == 0) {
21946 // This AIR instruction guarantees length > 0 if it is comptime-known.
21947 return;
21948 }
21949 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
21950 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
21951 }
21952
21953 if (dest_len != .none) {
21954 // Change the src from slice to a many pointer, to avoid multiple ptr
21955 // slice extractions in AIR instructions.
21956 const new_src_ptr_ty = sema.typeOf(new_src_ptr);
21957 if (new_src_ptr_ty.isSlice()) {
21958 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
21959 }
21960 }
21961
2178421962 try sema.requireRuntimeBlock(block, src, runtime_src);
21963
21964 // Aliasing safety check.
21965 if (block.wantSafety()) {
21966 const len = if (len_val) |v|
21967 try sema.addConstant(Type.usize, v)
21968 else if (dest_len != .none)
21969 dest_len
21970 else
21971 src_len;
21972
21973 // Extract raw pointer from dest slice. The AIR instructions could support them, but
21974 // it would cause redundant machine code instructions.
21975 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
21976 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice())
21977 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
21978 else
21979 new_dest_ptr;
21980
21981 // ok1: dest >= src + len
21982 // ok2: src >= dest + len
21983 const src_plus_len = try sema.analyzePtrArithmetic(block, src, new_src_ptr, len, .ptr_add, src_src, src);
21984 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
21985 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
21986 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
21987 const ok = try block.addBinOp(.bit_or, ok1, ok2);
21988 try sema.addSafetyCheck(block, ok, .memcpy_alias);
21989 }
21990
2178521991 _ = try block.addInst(.{
2178621992 .tag = .memcpy,
21787 .data = .{ .pl_op = .{
21788 .operand = dest_ptr,
21789 .payload = try sema.addExtra(Air.Bin{
21790 .lhs = src_ptr,
21791 .rhs = len,
21792 }),
21993 .data = .{ .bin_op = .{
21994 .lhs = new_dest_ptr,
21995 .rhs = new_src_ptr,
2179321996 } },
2179421997 });
2179521998}
2179621999
2179722000fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
2179822001 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
21799 const extra = sema.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
22002 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
2180022003 const src = inst_data.src();
2180122004 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2180222005 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
21803 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
21804 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);
21805
21806 // TODO AstGen's coerced_ty cannot handle volatile here
21807 var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data;
21808 ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr();
21809 const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
21810 const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src);
22006 const dest_ptr = try sema.resolveInst(extra.lhs);
22007 const uncoerced_elem = try sema.resolveInst(extra.rhs);
22008 const dest_ptr_ty = sema.typeOf(dest_ptr);
22009 try checkIndexable(sema, block, dest_src, dest_ptr_ty);
2181122010
21812 const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src);
21813 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
22011 const dest_elem_ty = dest_ptr_ty.elemType2();
22012 const target = sema.mod.getTarget();
2181422013
2181522014 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: {
22015 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, "len", dest_src);
22016 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse
22017 break :rs dest_src;
22018 const len_u64 = (try len_val.getUnsignedIntAdvanced(target, sema)).?;
22019 const len = try sema.usizeCast(block, dest_src, len_u64);
22020 if (len == 0) {
22021 // This AIR instruction guarantees length > 0 if it is comptime-known.
22022 return;
22023 }
22024
2181622025 if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src;
21817 if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {
21818 if (try sema.resolveMaybeUndefVal(value)) |val| {
21819 _ = len_val;
21820 _ = val;
21821 return sema.fail(block, src, "TODO: Sema.zirMemset at comptime", .{});
21822 } else break :rs value_src;
21823 } else break :rs len_src;
22026 if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| {
22027 for (0..len) |i| {
22028 const elem_index = try sema.addIntUnsigned(Type.usize, i);
22029 const elem_ptr = try sema.elemPtr(
22030 block,
22031 src,
22032 dest_ptr,
22033 elem_index,
22034 src,
22035 true, // init
22036 false, // oob_safety
22037 );
22038 try sema.storePtr2(
22039 block,
22040 src,
22041 elem_ptr,
22042 dest_src,
22043 uncoerced_elem,
22044 value_src,
22045 .store,
22046 );
22047 }
22048 return;
22049 } else break :rs value_src;
2182422050 } else dest_src;
2182522051
22052 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
22053
2182622054 try sema.requireRuntimeBlock(block, src, runtime_src);
2182722055 _ = try block.addInst(.{
21828 .tag = .memset,
21829 .data = .{ .pl_op = .{
21830 .operand = dest_ptr,
21831 .payload = try sema.addExtra(Air.Bin{
21832 .lhs = value,
21833 .rhs = len,
21834 }),
22056 .tag = if (block.wantSafety()) .memset_safe else .memset,
22057 .data = .{ .bin_op = .{
22058 .lhs = dest_ptr,
22059 .rhs = elem,
2183522060 } },
2183622061 });
2183722062}
......@@ -22948,6 +23173,8 @@ pub const PanicId = enum {
2294823173 index_out_of_bounds,
2294923174 start_index_greater_than_end,
2295023175 for_len_mismatch,
23176 memcpy_len_mismatch,
23177 memcpy_alias,
2295123178};
2295223179
2295323180fn addSafetyCheck(
......@@ -26521,7 +26748,8 @@ fn storePtr(
2652126748 ptr: Air.Inst.Ref,
2652226749 uncasted_operand: Air.Inst.Ref,
2652326750) CompileError!void {
26524 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, .store);
26751 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
26752 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
2652526753}
2652626754
2652726755fn storePtr2(
......@@ -28768,10 +28996,10 @@ fn analyzeSlice(
2876828996 uncasted_end_opt: Air.Inst.Ref,
2876928997 sentinel_opt: Air.Inst.Ref,
2877028998 sentinel_src: LazySrcLoc,
28999 ptr_src: LazySrcLoc,
29000 start_src: LazySrcLoc,
29001 end_src: LazySrcLoc,
2877129002) CompileError!Air.Inst.Ref {
28772 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = src.node_offset.x };
28773 const start_src: LazySrcLoc = .{ .node_offset_slice_start = src.node_offset.x };
28774 const end_src: LazySrcLoc = .{ .node_offset_slice_end = src.node_offset.x };
2877529003 // Slice expressions can operate on a variable whose type is an array. This requires
2877629004 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
2877729005 const ptr_ptr_ty = sema.typeOf(ptr_ptr);
src/Zir.zig+2-14
......@@ -922,10 +922,10 @@ pub const Inst = struct {
922922 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
923923 field_parent_ptr,
924924 /// Implements the `@memcpy` builtin.
925 /// Uses the `pl_node` union field with payload `Memcpy`.
925 /// Uses the `pl_node` union field with payload `Bin`.
926926 memcpy,
927927 /// Implements the `@memset` builtin.
928 /// Uses the `pl_node` union field with payload `Memset`.
928 /// Uses the `pl_node` union field with payload `Bin`.
929929 memset,
930930 /// Implements the `@min` builtin.
931931 /// Uses the `pl_node` union field with payload `Bin`
......@@ -3501,18 +3501,6 @@ pub const Inst = struct {
35013501 field_ptr: Ref,
35023502 };
35033503
3504 pub const Memcpy = struct {
3505 dest: Ref,
3506 source: Ref,
3507 byte_count: Ref,
3508 };
3509
3510 pub const Memset = struct {
3511 dest: Ref,
3512 byte: Ref,
3513 byte_count: Ref,
3514 };
3515
35163504 pub const Shuffle = struct {
35173505 elem_type: Ref,
35183506 a: Ref,
src/arch/aarch64/CodeGen.zig+16-4
......@@ -764,7 +764,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
764764 .ptrtoint => try self.airPtrToInt(inst),
765765 .ret => try self.airRet(inst),
766766 .ret_load => try self.airRetLoad(inst),
767 .store => try self.airStore(inst),
767 .store => try self.airStore(inst, false),
768 .store_safe => try self.airStore(inst, true),
768769 .struct_field_ptr=> try self.airStructFieldPtr(inst),
769770 .struct_field_val=> try self.airStructFieldVal(inst),
770771 .array_to_slice => try self.airArrayToSlice(inst),
......@@ -775,7 +776,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
775776 .atomic_rmw => try self.airAtomicRmw(inst),
776777 .atomic_load => try self.airAtomicLoad(inst),
777778 .memcpy => try self.airMemcpy(inst),
778 .memset => try self.airMemset(inst),
779 .memset => try self.airMemset(inst, false),
780 .memset_safe => try self.airMemset(inst, true),
779781 .set_union_tag => try self.airSetUnionTag(inst),
780782 .get_union_tag => try self.airGetUnionTag(inst),
781783 .clz => try self.airClz(inst),
......@@ -4035,7 +4037,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
40354037 }
40364038}
40374039
4038fn airStore(self: *Self, inst: Air.Inst.Index) !void {
4040fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
4041 if (safety) {
4042 // TODO if the value is undef, write 0xaa bytes to dest
4043 } else {
4044 // TODO if the value is undef, don't lower this instruction
4045 }
40394046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
40404047 const ptr = try self.resolveInst(bin_op.lhs);
40414048 const value = try self.resolveInst(bin_op.rhs);
......@@ -5975,8 +5982,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
59755982 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
59765983}
59775984
5978fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
5985fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
59795986 _ = inst;
5987 if (safety) {
5988 // TODO if the value is undef, write 0xaa bytes to dest
5989 } else {
5990 // TODO if the value is undef, don't lower this instruction
5991 }
59805992 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
59815993}
59825994
src/arch/arm/CodeGen.zig+16-4
......@@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
748748 .ptrtoint => try self.airPtrToInt(inst),
749749 .ret => try self.airRet(inst),
750750 .ret_load => try self.airRetLoad(inst),
751 .store => try self.airStore(inst),
751 .store => try self.airStore(inst, false),
752 .store_safe => try self.airStore(inst, true),
752753 .struct_field_ptr=> try self.airStructFieldPtr(inst),
753754 .struct_field_val=> try self.airStructFieldVal(inst),
754755 .array_to_slice => try self.airArrayToSlice(inst),
......@@ -759,7 +760,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
759760 .atomic_rmw => try self.airAtomicRmw(inst),
760761 .atomic_load => try self.airAtomicLoad(inst),
761762 .memcpy => try self.airMemcpy(inst),
762 .memset => try self.airMemset(inst),
763 .memset => try self.airMemset(inst, false),
764 .memset_safe => try self.airMemset(inst, true),
763765 .set_union_tag => try self.airSetUnionTag(inst),
764766 .get_union_tag => try self.airGetUnionTag(inst),
765767 .clz => try self.airClz(inst),
......@@ -2835,7 +2837,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28352837 }
28362838}
28372839
2838fn airStore(self: *Self, inst: Air.Inst.Index) !void {
2840fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2841 if (safety) {
2842 // TODO if the value is undef, write 0xaa bytes to dest
2843 } else {
2844 // TODO if the value is undef, don't lower this instruction
2845 }
28392846 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28402847 const ptr = try self.resolveInst(bin_op.lhs);
28412848 const value = try self.resolveInst(bin_op.rhs);
......@@ -5921,7 +5928,12 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
59215928 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
59225929}
59235930
5924fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
5931fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
5932 if (safety) {
5933 // TODO if the value is undef, write 0xaa bytes to dest
5934 } else {
5935 // TODO if the value is undef, don't lower this instruction
5936 }
59255937 _ = inst;
59265938 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
59275939}
src/arch/riscv64/CodeGen.zig+16-4
......@@ -578,7 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
578578 .ptrtoint => try self.airPtrToInt(inst),
579579 .ret => try self.airRet(inst),
580580 .ret_load => try self.airRetLoad(inst),
581 .store => try self.airStore(inst),
581 .store => try self.airStore(inst, false),
582 .store_safe => try self.airStore(inst, true),
582583 .struct_field_ptr=> try self.airStructFieldPtr(inst),
583584 .struct_field_val=> try self.airStructFieldVal(inst),
584585 .array_to_slice => try self.airArrayToSlice(inst),
......@@ -589,7 +590,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
589590 .atomic_rmw => try self.airAtomicRmw(inst),
590591 .atomic_load => try self.airAtomicLoad(inst),
591592 .memcpy => try self.airMemcpy(inst),
592 .memset => try self.airMemset(inst),
593 .memset => try self.airMemset(inst, false),
594 .memset_safe => try self.airMemset(inst, true),
593595 .set_union_tag => try self.airSetUnionTag(inst),
594596 .get_union_tag => try self.airGetUnionTag(inst),
595597 .clz => try self.airClz(inst),
......@@ -1572,7 +1574,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
15721574 }
15731575}
15741576
1575fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1577fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1578 if (safety) {
1579 // TODO if the value is undef, write 0xaa bytes to dest
1580 } else {
1581 // TODO if the value is undef, don't lower this instruction
1582 }
15761583 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
15771584 const ptr = try self.resolveInst(bin_op.lhs);
15781585 const value = try self.resolveInst(bin_op.rhs);
......@@ -2421,8 +2428,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
24212428 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
24222429}
24232430
2424fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
2431fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
24252432 _ = inst;
2433 if (safety) {
2434 // TODO if the value is undef, write 0xaa bytes to dest
2435 } else {
2436 // TODO if the value is undef, don't lower this instruction
2437 }
24262438 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
24272439}
24282440
src/arch/sparc64/CodeGen.zig+16-4
......@@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
593593 .ptrtoint => try self.airPtrToInt(inst),
594594 .ret => try self.airRet(inst),
595595 .ret_load => try self.airRetLoad(inst),
596 .store => try self.airStore(inst),
596 .store => try self.airStore(inst, false),
597 .store_safe => try self.airStore(inst, true),
597598 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
598599 .struct_field_val=> try self.airStructFieldVal(inst),
599600 .array_to_slice => try self.airArrayToSlice(inst),
......@@ -605,7 +606,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
605606 .atomic_rmw => try self.airAtomicRmw(inst),
606607 .atomic_load => try self.airAtomicLoad(inst),
607608 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
608 .memset => try self.airMemset(inst),
609 .memset => try self.airMemset(inst, false),
610 .memset_safe => try self.airMemset(inst, true),
609611 .set_union_tag => try self.airSetUnionTag(inst),
610612 .get_union_tag => try self.airGetUnionTag(inst),
611613 .clz => try self.airClz(inst),
......@@ -1764,7 +1766,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
17641766 return self.finishAirBookkeeping();
17651767}
17661768
1767fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
1769fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1770 if (safety) {
1771 // TODO if the value is undef, write 0xaa bytes to dest
1772 } else {
1773 // TODO if the value is undef, don't lower this instruction
1774 }
17681775 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
17691776 const extra = self.air.extraData(Air.Bin, pl_op.payload);
17701777
......@@ -2401,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
24012408 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
24022409}
24032410
2404fn airStore(self: *Self, inst: Air.Inst.Index) !void {
2411fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2412 if (safety) {
2413 // TODO if the value is undef, write 0xaa bytes to dest
2414 } else {
2415 // TODO if the value is undef, don't lower this instruction
2416 }
24052417 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
24062418 const ptr = try self.resolveInst(bin_op.lhs);
24072419 const value = try self.resolveInst(bin_op.rhs);
src/arch/wasm/CodeGen.zig+63-24
......@@ -1883,7 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18831883
18841884 .load => func.airLoad(inst),
18851885 .loop => func.airLoop(inst),
1886 .memset => func.airMemset(inst),
1886 .memset => func.airMemset(inst, false),
1887 .memset_safe => func.airMemset(inst, true),
18871888 .not => func.airNot(inst),
18881889 .optional_payload => func.airOptionalPayload(inst),
18891890 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),
......@@ -1913,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19131914 .slice_ptr => func.airSlicePtr(inst),
19141915 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),
19151916 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),
1916 .store => func.airStore(inst),
1917 .store => func.airStore(inst, false),
1918 .store_safe => func.airStore(inst, true),
19171919
19181920 .set_union_tag => func.airSetUnionTag(inst),
19191921 .struct_field_ptr => func.airStructFieldPtr(inst),
......@@ -2221,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22212223 func.finishAir(inst, value, &.{});
22222224}
22232225
2224fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2226fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2227 if (safety) {
2228 // TODO if the value is undef, write 0xaa bytes to dest
2229 } else {
2230 // TODO if the value is undef, don't lower this instruction
2231 }
22252232 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
22262233
22272234 const lhs = try func.resolveInst(bin_op.lhs);
......@@ -4148,9 +4155,7 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41484155 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
41494156
41504157 const operand = try func.resolveInst(ty_op.operand);
4151 const len = try func.load(operand, Type.usize, func.ptrSize());
4152 const result = try len.toLocal(func, Type.usize);
4153 func.finishAir(inst, result, &.{ty_op.operand});
4158 func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand});
41544159}
41554160
41564161fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -4208,9 +4213,17 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42084213fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42094214 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
42104215 const operand = try func.resolveInst(ty_op.operand);
4216 func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand});
4217}
4218
4219fn slicePtr(func: *CodeGen, operand: WValue) InnerError!WValue {
42114220 const ptr = try func.load(operand, Type.usize, 0);
4212 const result = try ptr.toLocal(func, Type.usize);
4213 func.finishAir(inst, result, &.{ty_op.operand});
4221 return ptr.toLocal(func, Type.usize);
4222}
4223
4224fn sliceLen(func: *CodeGen, operand: WValue) InnerError!WValue {
4225 const len = try func.load(operand, Type.usize, func.ptrSize());
4226 return len.toLocal(func, Type.usize);
42144227}
42154228
42164229fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -4274,8 +4287,10 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42744287fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42754288 const un_op = func.air.instructions.items(.data)[inst].un_op;
42764289 const operand = try func.resolveInst(un_op);
4277
4278 const result = switch (operand) {
4290 const ptr_ty = func.air.typeOf(un_op);
4291 const result = if (ptr_ty.isSlice())
4292 try func.slicePtr(operand)
4293 else switch (operand) {
42794294 // for stack offset, return a pointer to this offset.
42804295 .stack_offset => try func.buildPointerOffset(operand, 0, .new),
42814296 else => func.reuseOperand(un_op, operand),
......@@ -4375,16 +4390,25 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
43754390 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
43764391}
43774392
4378fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4379 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4380 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
4393fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4394 if (safety) {
4395 // TODO if the value is undef, write 0xaa bytes to dest
4396 } else {
4397 // TODO if the value is undef, don't lower this instruction
4398 }
4399 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
43814400
4382 const ptr = try func.resolveInst(pl_op.operand);
4383 const value = try func.resolveInst(bin_op.lhs);
4384 const len = try func.resolveInst(bin_op.rhs);
4401 const ptr = try func.resolveInst(bin_op.lhs);
4402 const ptr_ty = func.air.typeOf(bin_op.lhs);
4403 const value = try func.resolveInst(bin_op.rhs);
4404 const len = switch (ptr_ty.ptrSize()) {
4405 .Slice => try func.sliceLen(ptr),
4406 .One => @as(WValue, .{ .imm32 = @intCast(u32, ptr_ty.childType().arrayLen()) }),
4407 .C, .Many => unreachable,
4408 };
43854409 try func.memset(ptr, len, value);
43864410
4387 func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
4411 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43884412}
43894413
43904414/// Sets a region of memory at `ptr` to the value of `value`
......@@ -5155,15 +5179,30 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51555179 func.finishAir(inst, result, &.{extra.field_ptr});
51565180}
51575181
5182fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {
5183 if (ptr_ty.isSlice()) {
5184 return func.slicePtr(ptr);
5185 } else {
5186 return ptr;
5187 }
5188}
5189
51585190fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5159 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
5160 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
5161 const dst = try func.resolveInst(pl_op.operand);
5162 const src = try func.resolveInst(bin_op.lhs);
5163 const len = try func.resolveInst(bin_op.rhs);
5164 try func.memcpy(dst, src, len);
5191 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5192 const dst = try func.resolveInst(bin_op.lhs);
5193 const dst_ty = func.air.typeOf(bin_op.lhs);
5194 const src = try func.resolveInst(bin_op.rhs);
5195 const src_ty = func.air.typeOf(bin_op.rhs);
5196 const len = switch (dst_ty.ptrSize()) {
5197 .Slice => try func.sliceLen(dst),
5198 .One => @as(WValue, .{ .imm64 = dst_ty.childType().arrayLen() }),
5199 .C, .Many => unreachable,
5200 };
5201 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);
5202 const src_ptr = try func.sliceOrArrayPtr(src, src_ty);
5203 try func.memcpy(dst_ptr, src_ptr, len);
51655204
5166 func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
5205 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
51675206}
51685207
51695208fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/arch/x86_64/CodeGen.zig+130-22
......@@ -1035,7 +1035,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10351035 .ptrtoint => try self.airPtrToInt(inst),
10361036 .ret => try self.airRet(inst),
10371037 .ret_load => try self.airRetLoad(inst),
1038 .store => try self.airStore(inst),
1038 .store => try self.airStore(inst, false),
1039 .store_safe => try self.airStore(inst, true),
10391040 .struct_field_ptr=> try self.airStructFieldPtr(inst),
10401041 .struct_field_val=> try self.airStructFieldVal(inst),
10411042 .array_to_slice => try self.airArrayToSlice(inst),
......@@ -1046,7 +1047,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10461047 .atomic_rmw => try self.airAtomicRmw(inst),
10471048 .atomic_load => try self.airAtomicLoad(inst),
10481049 .memcpy => try self.airMemcpy(inst),
1049 .memset => try self.airMemset(inst),
1050 .memset => try self.airMemset(inst, false),
1051 .memset_safe => try self.airMemset(inst, true),
10501052 .set_union_tag => try self.airSetUnionTag(inst),
10511053 .get_union_tag => try self.airGetUnionTag(inst),
10521054 .clz => try self.airClz(inst),
......@@ -3935,7 +3937,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
39353937 }
39363938}
39373939
3938fn airStore(self: *Self, inst: Air.Inst.Index) !void {
3940fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
3941 if (safety) {
3942 // TODO if the value is undef, write 0xaa bytes to dest
3943 } else {
3944 // TODO if the value is undef, don't lower this instruction
3945 }
39393946 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
39403947 const ptr = try self.resolveInst(bin_op.lhs);
39413948 const ptr_ty = self.air.typeOf(bin_op.lhs);
......@@ -7678,6 +7685,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
76787685fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
76797686 const un_op = self.air.instructions.items(.data)[inst].un_op;
76807687 const result = result: {
7688 // TODO: handle case where the operand is a slice not a raw pointer
76817689 const src_mcv = try self.resolveInst(un_op);
76827690 if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv;
76837691
......@@ -8148,64 +8156,164 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
81488156 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
81498157}
81508158
8151fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
8152 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8153 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8159fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
8160 if (safety) {
8161 // TODO if the value is undef, write 0xaa bytes to dest
8162 } else {
8163 // TODO if the value is undef, don't lower this instruction
8164 }
81548165
8155 const dst_ptr = try self.resolveInst(pl_op.operand);
8166 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8167
8168 const dst_ptr = try self.resolveInst(bin_op.lhs);
8169 const dst_ptr_ty = self.air.typeOf(bin_op.lhs);
81568170 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
81578171 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
81588172 else => null,
81598173 };
81608174 defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);
81618175
8162 const src_val = try self.resolveInst(extra.lhs);
8176 const src_val = try self.resolveInst(bin_op.rhs);
8177 const elem_ty = self.air.typeOf(bin_op.rhs);
81638178 const src_val_lock: ?RegisterLock = switch (src_val) {
81648179 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
81658180 else => null,
81668181 };
81678182 defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock);
81688183
8169 const len = try self.resolveInst(extra.rhs);
8170 const len_lock: ?RegisterLock = switch (len) {
8171 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8172 else => null,
8173 };
8174 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
8184 const elem_abi_size = @intCast(u31, elem_ty.abiSize(self.target.*));
8185
8186 if (elem_abi_size == 1) {
8187 const ptr = switch (dst_ptr_ty.ptrSize()) {
8188 // TODO: this only handles slices stored in the stack
8189 .Slice => @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 0 }),
8190 .One => dst_ptr,
8191 .C, .Many => unreachable,
8192 };
8193 const len = switch (dst_ptr_ty.ptrSize()) {
8194 // TODO: this only handles slices stored in the stack
8195 .Slice => @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 8 }),
8196 .One => @as(MCValue, .{ .immediate = dst_ptr_ty.childType().arrayLen() }),
8197 .C, .Many => unreachable,
8198 };
8199 const len_lock: ?RegisterLock = switch (len) {
8200 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8201 else => null,
8202 };
8203 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
8204
8205 try self.genInlineMemset(ptr, src_val, len, .{});
8206 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
8207 }
8208
8209 // Store the first element, and then rely on memcpy copying forwards.
8210 // Length zero requires a runtime check - so we handle arrays specially
8211 // here to elide it.
8212 switch (dst_ptr_ty.ptrSize()) {
8213 .Slice => {
8214 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
8215 const slice_ptr_ty = dst_ptr_ty.slicePtrFieldType(&buf);
8216
8217 // TODO: this only handles slices stored in the stack
8218 const ptr = @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 0 });
8219 const len = @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 8 });
8220
8221 // Used to store the number of elements for comparison.
8222 // After comparison, updated to store number of bytes needed to copy.
8223 const len_reg = try self.register_manager.allocReg(null, gp);
8224 const len_mcv: MCValue = .{ .register = len_reg };
8225 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg);
8226 defer self.register_manager.unlockReg(len_lock);
8227
8228 try self.asmRegisterMemory(.mov, len_reg, Memory.sib(.qword, .{
8229 .base = .rbp,
8230 .disp = -len.stack_offset,
8231 }));
8232
8233 const skip_reloc = try self.asmJccReloc(undefined, .z);
8234 try self.store(ptr, src_val, slice_ptr_ty, elem_ty);
8235
8236 const second_elem_ptr_reg = try self.register_manager.allocReg(null, gp);
8237 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
8238 const second_elem_ptr_lock = self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg);
8239 defer self.register_manager.unlockReg(second_elem_ptr_lock);
8240
8241 try self.asmRegisterMemory(
8242 .lea,
8243 second_elem_ptr_reg,
8244 Memory.sib(.qword, .{
8245 .base = try self.copyToTmpRegister(Type.usize, ptr),
8246 .disp = elem_abi_size,
8247 }),
8248 );
8249
8250 try self.genBinOpMir(.sub, Type.usize, len_mcv, .{ .immediate = 1 });
8251 try self.asmRegisterRegisterImmediate(.imul, len_reg, len_reg, Immediate.u(elem_abi_size));
8252 try self.genInlineMemcpy(second_elem_ptr_mcv, ptr, len_mcv, .{});
81758253
8176 try self.genInlineMemset(dst_ptr, src_val, len, .{});
8254 try self.performReloc(skip_reloc);
8255 },
8256 .One => {
8257 const len = dst_ptr_ty.childType().arrayLen();
8258 assert(len != 0); // prevented by Sema
8259 try self.store(dst_ptr, src_val, dst_ptr_ty, elem_ty);
8260
8261 const second_elem_ptr_reg = try self.register_manager.allocReg(null, gp);
8262 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
8263 const second_elem_ptr_lock = self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg);
8264 defer self.register_manager.unlockReg(second_elem_ptr_lock);
81778265
8178 return self.finishAir(inst, .unreach, .{ pl_op.operand, extra.lhs, extra.rhs });
8266 try self.asmRegisterMemory(
8267 .lea,
8268 second_elem_ptr_reg,
8269 Memory.sib(.qword, .{
8270 .base = try self.copyToTmpRegister(Type.usize, dst_ptr),
8271 .disp = elem_abi_size,
8272 }),
8273 );
8274
8275 const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len - 1) };
8276 try self.genInlineMemcpy(second_elem_ptr_mcv, dst_ptr, bytes_to_copy, .{});
8277 },
8278 .C, .Many => unreachable,
8279 }
8280
8281 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
81798282}
81808283
81818284fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
8182 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8183 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8285 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
81848286
8185 const dst_ptr = try self.resolveInst(pl_op.operand);
8287 const dst_ptr = try self.resolveInst(bin_op.lhs);
8288 const dst_ptr_ty = self.air.typeOf(bin_op.lhs);
81868289 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
81878290 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
81888291 else => null,
81898292 };
81908293 defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);
81918294
8192 const src_ptr = try self.resolveInst(extra.lhs);
8295 const src_ptr = try self.resolveInst(bin_op.rhs);
81938296 const src_ptr_lock: ?RegisterLock = switch (src_ptr) {
81948297 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
81958298 else => null,
81968299 };
81978300 defer if (src_ptr_lock) |lock| self.register_manager.unlockReg(lock);
81988301
8199 const len = try self.resolveInst(extra.rhs);
8302 const len = switch (dst_ptr_ty.ptrSize()) {
8303 .Slice => @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 8 }),
8304 .One => @as(MCValue, .{ .immediate = dst_ptr_ty.childType().arrayLen() }),
8305 .C, .Many => unreachable,
8306 };
82008307 const len_lock: ?RegisterLock = switch (len) {
82018308 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
82028309 else => null,
82038310 };
82048311 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
82058312
8313 // TODO: dst_ptr and src_ptr could be slices rather than raw pointers
82068314 try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{});
82078315
8208 return self.finishAir(inst, .unreach, .{ pl_op.operand, extra.lhs, extra.rhs });
8316 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
82098317}
82108318
82118319fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
src/codegen/c.zig+151-65
......@@ -2924,7 +2924,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
29242924 .load => try airLoad(f, inst),
29252925 .ret => try airRet(f, inst, false),
29262926 .ret_load => try airRet(f, inst, true),
2927 .store => try airStore(f, inst),
2927 .store => try airStore(f, inst, false),
2928 .store_safe => try airStore(f, inst, true),
29282929 .loop => try airLoop(f, inst),
29292930 .cond_br => try airCondBr(f, inst),
29302931 .br => try airBr(f, inst),
......@@ -2935,7 +2936,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
29352936 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
29362937 .atomic_rmw => try airAtomicRmw(f, inst),
29372938 .atomic_load => try airAtomicLoad(f, inst),
2938 .memset => try airMemset(f, inst),
2939 .memset => try airMemset(f, inst, false),
2940 .memset_safe => try airMemset(f, inst, true),
29392941 .memcpy => try airMemcpy(f, inst),
29402942 .set_union_tag => try airSetUnionTag(f, inst),
29412943 .get_union_tag => try airGetUnionTag(f, inst),
......@@ -3574,19 +3576,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
35743576 return local;
35753577}
35763578
3577fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
3578 if (f.wantSafety()) {
3579 const writer = f.object.writer();
3580 try writer.writeAll("memset(");
3581 try f.writeCValue(writer, dest_ptr, .FunctionArgument);
3582 try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)});
3583 try f.renderType(writer, lhs_child_ty);
3584 try writer.writeAll("));\n");
3585 }
3586 return .none;
3587}
3588
3589fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3579fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
35903580 // *a = b;
35913581 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
35923582
......@@ -3597,18 +3587,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
35973587 const ptr_val = try f.resolveInst(bin_op.lhs);
35983588 const src_ty = f.air.typeOf(bin_op.rhs);
35993589
3600 // TODO Sema should emit a different instruction when the store should
3601 // possibly do the safety 0xaa bytes for undefined.
3602 const src_val_is_undefined =
3603 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
3604 if (src_val_is_undefined) {
3605 if (ptr_info.host_size == 0) {
3606 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3607 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);
3608 } else if (!f.wantSafety()) {
3609 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3610 return .none;
3590 const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
3591
3592 if (val_is_undef) {
3593 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3594 if (safety and ptr_info.host_size == 0) {
3595 const writer = f.object.writer();
3596 try writer.writeAll("memset(");
3597 try f.writeCValue(writer, ptr_val, .FunctionArgument);
3598 try writer.writeAll(", 0xaa, sizeof(");
3599 try f.renderType(writer, ptr_info.pointee_type);
3600 try writer.writeAll("));\n");
36113601 }
3602 return .none;
36123603 }
36133604
36143605 const target = f.object.dg.module.getTarget();
......@@ -3844,8 +3835,8 @@ fn airCmpOp(
38443835 data: anytype,
38453836 operator: std.math.CompareOperator,
38463837) !CValue {
3847 const operand_ty = f.air.typeOf(data.lhs);
3848 const scalar_ty = operand_ty.scalarType();
3838 const lhs_ty = f.air.typeOf(data.lhs);
3839 const scalar_ty = lhs_ty.scalarType();
38493840
38503841 const target = f.object.dg.module.getTarget();
38513842 const scalar_bits = scalar_ty.bitSize(target);
......@@ -3866,17 +3857,21 @@ fn airCmpOp(
38663857 const rhs = try f.resolveInst(data.rhs);
38673858 try reap(f, inst, &.{ data.lhs, data.rhs });
38683859
3860 const rhs_ty = f.air.typeOf(data.rhs);
3861 const need_cast = lhs_ty.isSinglePointer() != rhs_ty.isSinglePointer();
38693862 const writer = f.object.writer();
38703863 const local = try f.allocLocal(inst, inst_ty);
3871 const v = try Vectorize.start(f, inst, writer, operand_ty);
3864 const v = try Vectorize.start(f, inst, writer, lhs_ty);
38723865 try f.writeCValue(writer, local, .Other);
38733866 try v.elem(f, writer);
38743867 try writer.writeAll(" = ");
3868 if (need_cast) try writer.writeAll("(void*)");
38753869 try f.writeCValue(writer, lhs, .Other);
38763870 try v.elem(f, writer);
38773871 try writer.writeByte(' ');
38783872 try writer.writeAll(compareOperatorC(operator));
38793873 try writer.writeByte(' ');
3874 if (need_cast) try writer.writeAll("(void*)");
38803875 try f.writeCValue(writer, rhs, .Other);
38813876 try v.elem(f, writer);
38823877 try writer.writeAll(";\n");
......@@ -5784,6 +5779,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
57845779 const un_op = f.air.instructions.items(.data)[inst].un_op;
57855780
57865781 const operand = try f.resolveInst(un_op);
5782 const operand_ty = f.air.typeOf(un_op);
57875783 try reap(f, inst, &.{un_op});
57885784 const inst_ty = f.air.typeOfIndex(inst);
57895785 const writer = f.object.writer();
......@@ -5793,7 +5789,11 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
57935789 try writer.writeAll(" = (");
57945790 try f.renderType(writer, inst_ty);
57955791 try writer.writeByte(')');
5796 try f.writeCValue(writer, operand, .Other);
5792 if (operand_ty.isSlice()) {
5793 try f.writeCValueMember(writer, operand, .{ .identifier = "len" });
5794 } else {
5795 try f.writeCValue(writer, operand, .Other);
5796 }
57975797 try writer.writeAll(";\n");
57985798 return local;
57995799}
......@@ -6186,19 +6186,66 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
61866186 return .none;
61876187}
61886188
6189fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
6190 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
6191 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
6192 const dest_ty = f.air.typeOf(pl_op.operand);
6193 const dest_ptr = try f.resolveInst(pl_op.operand);
6194 const value = try f.resolveInst(extra.lhs);
6195 const len = try f.resolveInst(extra.rhs);
6189fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void {
6190 if (ptr_ty.isSlice()) {
6191 try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" });
6192 } else {
6193 try f.writeCValue(writer, ptr, .FunctionArgument);
6194 }
6195}
61966196
6197fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6198 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
6199 const dest_ty = f.air.typeOf(bin_op.lhs);
6200 const dest_slice = try f.resolveInst(bin_op.lhs);
6201 const value = try f.resolveInst(bin_op.rhs);
6202 const elem_ty = f.air.typeOf(bin_op.rhs);
6203 const target = f.object.dg.module.getTarget();
6204 const elem_abi_size = elem_ty.abiSize(target);
6205 const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
61976206 const writer = f.object.writer();
6198 if (dest_ty.isVolatilePtr()) {
6199 var u8_ptr_pl = dest_ty.ptrInfo();
6200 u8_ptr_pl.data.pointee_type = Type.u8;
6201 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
6207
6208 if (val_is_undef) {
6209 if (!safety) {
6210 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6211 return .none;
6212 }
6213
6214 try writer.writeAll("memset(");
6215 switch (dest_ty.ptrSize()) {
6216 .Slice => {
6217 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
6218 try writer.writeAll(", 0xaa, ");
6219 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6220 if (elem_abi_size > 1) {
6221 try writer.print(" * {d});\n", .{elem_abi_size});
6222 } else {
6223 try writer.writeAll(");\n");
6224 }
6225 },
6226 .One => {
6227 const array_ty = dest_ty.childType();
6228 const len = array_ty.arrayLen() * elem_abi_size;
6229
6230 try f.writeCValue(writer, dest_slice, .FunctionArgument);
6231 try writer.print(", 0xaa, {d});\n", .{len});
6232 },
6233 .Many, .C => unreachable,
6234 }
6235 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6236 return .none;
6237 }
6238
6239 if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) {
6240 // For the assignment in this loop, the array pointer needs to get
6241 // casted to a regular pointer, otherwise an error like this occurs:
6242 // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable
6243 var elem_ptr_ty_pl: Type.Payload.ElemType = .{
6244 .base = .{ .tag = .c_mut_pointer },
6245 .data = elem_ty,
6246 };
6247 const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base);
6248
62026249 const index = try f.allocLocal(inst, Type.usize);
62036250
62046251 try writer.writeAll("for (");
......@@ -6208,56 +6255,95 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
62086255 try writer.writeAll("; ");
62096256 try f.writeCValue(writer, index, .Other);
62106257 try writer.writeAll(" != ");
6211 try f.writeCValue(writer, len, .Other);
6212 try writer.writeAll("; ");
6258 switch (dest_ty.ptrSize()) {
6259 .Slice => {
6260 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6261 },
6262 .One => {
6263 const array_ty = dest_ty.childType();
6264 try writer.print("{d}", .{array_ty.arrayLen()});
6265 },
6266 .Many, .C => unreachable,
6267 }
6268 try writer.writeAll("; ++");
62136269 try f.writeCValue(writer, index, .Other);
6214 try writer.writeAll(" += ");
6215 try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);
62166270 try writer.writeAll(") ((");
6217 try f.renderType(writer, u8_ptr_ty);
6271 try f.renderType(writer, elem_ptr_ty);
62186272 try writer.writeByte(')');
6219 try f.writeCValue(writer, dest_ptr, .FunctionArgument);
6273 try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
62206274 try writer.writeAll(")[");
62216275 try f.writeCValue(writer, index, .Other);
62226276 try writer.writeAll("] = ");
62236277 try f.writeCValue(writer, value, .FunctionArgument);
62246278 try writer.writeAll(";\n");
62256279
6226 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6280 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
62276281 try freeLocal(f, inst, index.new_local, 0);
62286282
62296283 return .none;
62306284 }
62316285
6232 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
62336286 try writer.writeAll("memset(");
6234 try f.writeCValue(writer, dest_ptr, .FunctionArgument);
6235 try writer.writeAll(", ");
6236 try f.writeCValue(writer, value, .FunctionArgument);
6237 try writer.writeAll(", ");
6238 try f.writeCValue(writer, len, .FunctionArgument);
6239 try writer.writeAll(");\n");
6287 switch (dest_ty.ptrSize()) {
6288 .Slice => {
6289 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
6290 try writer.writeAll(", ");
6291 try f.writeCValue(writer, value, .FunctionArgument);
6292 try writer.writeAll(", ");
6293 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
6294 try writer.writeAll(");\n");
6295 },
6296 .One => {
6297 const array_ty = dest_ty.childType();
6298 const len = array_ty.arrayLen() * elem_abi_size;
62406299
6300 try f.writeCValue(writer, dest_slice, .FunctionArgument);
6301 try writer.writeAll(", ");
6302 try f.writeCValue(writer, value, .FunctionArgument);
6303 try writer.print(", {d});\n", .{len});
6304 },
6305 .Many, .C => unreachable,
6306 }
6307 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
62416308 return .none;
62426309}
62436310
62446311fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
6245 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
6246 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
6247 const dest_ptr = try f.resolveInst(pl_op.operand);
6248 const src_ptr = try f.resolveInst(extra.lhs);
6249 const len = try f.resolveInst(extra.rhs);
6250 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6312 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
6313 const dest_ptr = try f.resolveInst(bin_op.lhs);
6314 const src_ptr = try f.resolveInst(bin_op.rhs);
6315 const dest_ty = f.air.typeOf(bin_op.lhs);
6316 const src_ty = f.air.typeOf(bin_op.rhs);
6317 const target = f.object.dg.module.getTarget();
62516318 const writer = f.object.writer();
62526319
62536320 try writer.writeAll("memcpy(");
6254 try f.writeCValue(writer, dest_ptr, .FunctionArgument);
6321 try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
62556322 try writer.writeAll(", ");
6256 try f.writeCValue(writer, src_ptr, .FunctionArgument);
6323 try writeSliceOrPtr(f, writer, src_ptr, src_ty);
62576324 try writer.writeAll(", ");
6258 try f.writeCValue(writer, len, .FunctionArgument);
6259 try writer.writeAll(");\n");
6325 switch (dest_ty.ptrSize()) {
6326 .Slice => {
6327 const elem_ty = dest_ty.childType();
6328 const elem_abi_size = elem_ty.abiSize(target);
6329 try f.writeCValueMember(writer, dest_ptr, .{ .identifier = "len" });
6330 if (elem_abi_size > 1) {
6331 try writer.print(" * {d});\n", .{elem_abi_size});
6332 } else {
6333 try writer.writeAll(");\n");
6334 }
6335 },
6336 .One => {
6337 const array_ty = dest_ty.childType();
6338 const elem_ty = array_ty.childType();
6339 const elem_abi_size = elem_ty.abiSize(target);
6340 const len = array_ty.arrayLen() * elem_abi_size;
6341 try writer.print("{d});\n", .{len});
6342 },
6343 .Many, .C => unreachable,
6344 }
62606345
6346 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
62616347 return .none;
62626348}
62636349
src/codegen/llvm.zig+180-71
......@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {
46494649 .not => try self.airNot(inst),
46504650 .ret => try self.airRet(inst),
46514651 .ret_load => try self.airRetLoad(inst),
4652 .store => try self.airStore(inst),
4652 .store => try self.airStore(inst, false),
4653 .store_safe => try self.airStore(inst, true),
46534654 .assembly => try self.airAssembly(inst),
46544655 .slice_ptr => try self.airSliceField(inst, 0),
46554656 .slice_len => try self.airSliceField(inst, 1),
......@@ -4672,7 +4673,8 @@ pub const FuncGen = struct {
46724673 .fence => try self.airFence(inst),
46734674 .atomic_rmw => try self.airAtomicRmw(inst),
46744675 .atomic_load => try self.airAtomicLoad(inst),
4675 .memset => try self.airMemset(inst),
4676 .memset => try self.airMemset(inst, false),
4677 .memset_safe => try self.airMemset(inst, true),
46764678 .memcpy => try self.airMemcpy(inst),
46774679 .set_union_tag => try self.airSetUnionTag(inst),
46784680 .get_union_tag => try self.airGetUnionTag(inst),
......@@ -5776,6 +5778,36 @@ pub const FuncGen = struct {
57765778 return result;
57775779 }
57785780
5781 fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
5782 if (ty.isSlice()) {
5783 return fg.builder.buildExtractValue(ptr, 0, "");
5784 } else {
5785 return ptr;
5786 }
5787 }
5788
5789 fn sliceOrArrayLenInBytes(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
5790 const target = fg.dg.module.getTarget();
5791 const llvm_usize_ty = fg.context.intType(target.cpu.arch.ptrBitWidth());
5792 switch (ty.ptrSize()) {
5793 .Slice => {
5794 const len = fg.builder.buildExtractValue(ptr, 1, "");
5795 const elem_ty = ty.childType();
5796 const abi_size = elem_ty.abiSize(target);
5797 if (abi_size == 1) return len;
5798 const abi_size_llvm_val = llvm_usize_ty.constInt(abi_size, .False);
5799 return fg.builder.buildMul(len, abi_size_llvm_val, "");
5800 },
5801 .One => {
5802 const array_ty = ty.childType();
5803 const elem_ty = array_ty.childType();
5804 const abi_size = elem_ty.abiSize(target);
5805 return llvm_usize_ty.constInt(array_ty.arrayLen() * abi_size, .False);
5806 },
5807 .Many, .C => unreachable,
5808 }
5809 }
5810
57795811 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
57805812 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
57815813 const operand = try self.resolveInst(ty_op.operand);
......@@ -7261,39 +7293,53 @@ pub const FuncGen = struct {
72617293 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
72627294 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
72637295 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7264 const base_ptr = try self.resolveInst(bin_op.lhs);
7296 const ptr = try self.resolveInst(bin_op.lhs);
72657297 const offset = try self.resolveInst(bin_op.rhs);
72667298 const ptr_ty = self.air.typeOf(bin_op.lhs);
72677299 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7268 if (ptr_ty.ptrSize() == .One) {
7269 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7270 const indices: [2]*llvm.Value = .{
7271 self.context.intType(32).constNull(), offset,
7272 };
7273 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7274 } else {
7275 const indices: [1]*llvm.Value = .{offset};
7276 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7300 switch (ptr_ty.ptrSize()) {
7301 .One => {
7302 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7303 const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
7304 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7305 },
7306 .C, .Many => {
7307 const indices: [1]*llvm.Value = .{offset};
7308 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7309 },
7310 .Slice => {
7311 const base = self.builder.buildExtractValue(ptr, 0, "");
7312 const indices: [1]*llvm.Value = .{offset};
7313 return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
7314 },
72777315 }
72787316 }
72797317
72807318 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
72817319 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
72827320 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7283 const base_ptr = try self.resolveInst(bin_op.lhs);
7321 const ptr = try self.resolveInst(bin_op.lhs);
72847322 const offset = try self.resolveInst(bin_op.rhs);
72857323 const negative_offset = self.builder.buildNeg(offset, "");
72867324 const ptr_ty = self.air.typeOf(bin_op.lhs);
72877325 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7288 if (ptr_ty.ptrSize() == .One) {
7289 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7290 const indices: [2]*llvm.Value = .{
7291 self.context.intType(32).constNull(), negative_offset,
7292 };
7293 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7294 } else {
7295 const indices: [1]*llvm.Value = .{negative_offset};
7296 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7326 switch (ptr_ty.ptrSize()) {
7327 .One => {
7328 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7329 const indices: [2]*llvm.Value = .{
7330 self.context.intType(32).constNull(), negative_offset,
7331 };
7332 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7333 },
7334 .C, .Many => {
7335 const indices: [1]*llvm.Value = .{negative_offset};
7336 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7337 },
7338 .Slice => {
7339 const base = self.builder.buildExtractValue(ptr, 0, "");
7340 const indices: [1]*llvm.Value = .{negative_offset};
7341 return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
7342 },
72977343 }
72987344 }
72997345
......@@ -7887,8 +7933,10 @@ pub const FuncGen = struct {
78877933 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
78887934 const un_op = self.air.instructions.items(.data)[inst].un_op;
78897935 const operand = try self.resolveInst(un_op);
7936 const ptr_ty = self.air.typeOf(un_op);
7937 const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty);
78907938 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
7891 return self.builder.buildPtrToInt(operand, dest_llvm_ty, "");
7939 return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, "");
78927940 }
78937941
78947942 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
......@@ -8082,48 +8130,36 @@ pub const FuncGen = struct {
80828130 return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());
80838131 }
80848132
8085 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8133 fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value {
80868134 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
80878135 const dest_ptr = try self.resolveInst(bin_op.lhs);
80888136 const ptr_ty = self.air.typeOf(bin_op.lhs);
80898137 const operand_ty = ptr_ty.childType();
80908138
8091 // TODO Sema should emit a different instruction when the store should
8092 // possibly do the safety 0xaa bytes for undefined.
80938139 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
80948140 if (val_is_undef) {
8095 {
8096 // TODO let's handle this in AIR rather than by having each backend
8097 // check the optimization mode of the compilation because the plan is
8098 // to support setting the optimization mode at finer grained scopes
8099 // which happens in Sema. Codegen should not be aware of this logic.
8100 // I think this comment is basically the same as the other TODO comment just
8101 // above but I'm leaving them both here to make it look super messy and
8102 // thereby bait contributors (or let's be honest, probably myself) into
8103 // fixing this instead of letting it rot.
8104 const safety = switch (self.dg.module.comp.bin_file.options.optimize_mode) {
8105 .ReleaseSmall, .ReleaseFast => false,
8106 .Debug, .ReleaseSafe => true,
8107 };
8108 if (!safety) {
8109 return null;
8110 }
8111 }
8141 // Even if safety is disabled, we still emit a memset to undefined since it conveys
8142 // extra information to LLVM. However, safety makes the difference between using
8143 // 0xaa or actual undefined for the fill byte.
8144 const u8_llvm_ty = self.context.intType(8);
8145 const fill_byte = if (safety)
8146 u8_llvm_ty.constInt(0xaa, .False)
8147 else
8148 u8_llvm_ty.getUndef();
81128149 const target = self.dg.module.getTarget();
81138150 const operand_size = operand_ty.abiSize(target);
8114 const u8_llvm_ty = self.context.intType(8);
8115 const fill_char = u8_llvm_ty.constInt(0xaa, .False);
8116 const dest_ptr_align = ptr_ty.ptrAlignment(target);
81178151 const usize_llvm_ty = try self.dg.lowerType(Type.usize);
81188152 const len = usize_llvm_ty.constInt(operand_size, .False);
8119 _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8120 if (self.dg.module.comp.bin_file.options.valgrind) {
8153 const dest_ptr_align = ptr_ty.ptrAlignment(target);
8154 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8155 if (safety and self.dg.module.comp.bin_file.options.valgrind) {
81218156 self.valgrindMarkUndef(dest_ptr, len);
81228157 }
8123 } else {
8124 const src_operand = try self.resolveInst(bin_op.rhs);
8125 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
8158 return null;
81268159 }
8160
8161 const src_operand = try self.resolveInst(bin_op.rhs);
8162 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
81278163 return null;
81288164 }
81298165
......@@ -8373,34 +8409,107 @@ pub const FuncGen = struct {
83738409 return null;
83748410 }
83758411
8376 fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8377 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8378 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8379 const dest_ptr = try self.resolveInst(pl_op.operand);
8380 const ptr_ty = self.air.typeOf(pl_op.operand);
8381 const value = try self.resolveInst(extra.lhs);
8382 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false;
8383 const len = try self.resolveInst(extra.rhs);
8384 const u8_llvm_ty = self.context.intType(8);
8385 const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value;
8412 fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value {
8413 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8414 const dest_slice = try self.resolveInst(bin_op.lhs);
8415 const ptr_ty = self.air.typeOf(bin_op.lhs);
8416 const elem_ty = self.air.typeOf(bin_op.rhs);
83868417 const target = self.dg.module.getTarget();
8418 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
83878419 const dest_ptr_align = ptr_ty.ptrAlignment(target);
8388 _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8420 const u8_llvm_ty = self.context.intType(8);
8421 const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty);
8422
8423 if (val_is_undef) {
8424 // Even if safety is disabled, we still emit a memset to undefined since it conveys
8425 // extra information to LLVM. However, safety makes the difference between using
8426 // 0xaa or actual undefined for the fill byte.
8427 const fill_byte = if (safety)
8428 u8_llvm_ty.constInt(0xaa, .False)
8429 else
8430 u8_llvm_ty.getUndef();
8431 const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
8432 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8433
8434 if (safety and self.dg.module.comp.bin_file.options.valgrind) {
8435 self.valgrindMarkUndef(dest_ptr, len);
8436 }
8437 return null;
8438 }
8439
8440 const value = try self.resolveInst(bin_op.rhs);
8441 const elem_abi_size = elem_ty.abiSize(target);
83898442
8390 if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
8391 self.valgrindMarkUndef(dest_ptr, len);
8443 if (elem_abi_size == 1) {
8444 // In this case we can take advantage of LLVM's intrinsic.
8445 const fill_byte = self.builder.buildBitCast(value, u8_llvm_ty, "");
8446 const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
8447 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8448 return null;
83928449 }
8450
8451 // non-byte-sized element. lower with a loop. something like this:
8452
8453 // entry:
8454 // ...
8455 // %end_ptr = getelementptr %ptr, %len
8456 // br loop
8457 // loop:
8458 // %it_ptr = phi body %next_ptr, entry %ptr
8459 // %end = cmp eq %it_ptr, %end_ptr
8460 // cond_br %end body, end
8461 // body:
8462 // store %it_ptr, %value
8463 // %next_ptr = getelementptr %it_ptr, 1
8464 // br loop
8465 // end:
8466 // ...
8467 const entry_block = self.builder.getInsertBlock();
8468 const loop_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetLoop");
8469 const body_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetBody");
8470 const end_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetEnd");
8471
8472 const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth());
8473 const len = switch (ptr_ty.ptrSize()) {
8474 .Slice => self.builder.buildExtractValue(dest_slice, 1, ""),
8475 .One => llvm_usize_ty.constInt(ptr_ty.childType().arrayLen(), .False),
8476 .Many, .C => unreachable,
8477 };
8478 const elem_llvm_ty = try self.dg.lowerType(elem_ty);
8479 const len_gep = [_]*llvm.Value{len};
8480 const end_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, dest_ptr, &len_gep, len_gep.len, "");
8481 _ = self.builder.buildBr(loop_block);
8482
8483 self.builder.positionBuilderAtEnd(loop_block);
8484 const it_ptr = self.builder.buildPhi(self.context.pointerType(0), "");
8485 const end = self.builder.buildICmp(.NE, it_ptr, end_ptr, "");
8486 _ = self.builder.buildCondBr(end, body_block, end_block);
8487
8488 self.builder.positionBuilderAtEnd(body_block);
8489 const store_inst = self.builder.buildStore(value, it_ptr);
8490 store_inst.setAlignment(@min(elem_ty.abiAlignment(target), dest_ptr_align));
8491 const one_gep = [_]*llvm.Value{llvm_usize_ty.constInt(1, .False)};
8492 const next_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, it_ptr, &one_gep, one_gep.len, "");
8493 _ = self.builder.buildBr(loop_block);
8494
8495 self.builder.positionBuilderAtEnd(end_block);
8496
8497 const incoming_values: [2]*llvm.Value = .{ next_ptr, dest_ptr };
8498 const incoming_blocks: [2]*llvm.BasicBlock = .{ body_block, entry_block };
8499 it_ptr.addIncoming(&incoming_values, &incoming_blocks, 2);
8500
83938501 return null;
83948502 }
83958503
83968504 fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8397 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8398 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8399 const dest_ptr = try self.resolveInst(pl_op.operand);
8400 const dest_ptr_ty = self.air.typeOf(pl_op.operand);
8401 const src_ptr = try self.resolveInst(extra.lhs);
8402 const src_ptr_ty = self.air.typeOf(extra.lhs);
8403 const len = try self.resolveInst(extra.rhs);
8505 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8506 const dest_slice = try self.resolveInst(bin_op.lhs);
8507 const dest_ptr_ty = self.air.typeOf(bin_op.lhs);
8508 const src_slice = try self.resolveInst(bin_op.rhs);
8509 const src_ptr_ty = self.air.typeOf(bin_op.rhs);
8510 const src_ptr = self.sliceOrArrayPtr(src_slice, src_ptr_ty);
8511 const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
8512 const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
84048513 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
84058514 const target = self.dg.module.getTarget();
84068515 _ = self.builder.buildMemCpy(
src/print_air.zig+4-24
......@@ -140,6 +140,7 @@ const Writer = struct {
140140 .bool_and,
141141 .bool_or,
142142 .store,
143 .store_safe,
143144 .array_elem_val,
144145 .slice_elem_val,
145146 .ptr_elem_val,
......@@ -169,6 +170,9 @@ const Writer = struct {
169170 .cmp_gte_optimized,
170171 .cmp_gt_optimized,
171172 .cmp_neq_optimized,
173 .memcpy,
174 .memset,
175 .memset_safe,
172176 => try w.writeBinOp(s, inst),
173177
174178 .is_null,
......@@ -315,8 +319,6 @@ const Writer = struct {
315319 .atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
316320 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),
317321 .atomic_rmw => try w.writeAtomicRmw(s, inst),
318 .memcpy => try w.writeMemcpy(s, inst),
319 .memset => try w.writeMemset(s, inst),
320322 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
321323 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
322324 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
......@@ -591,17 +593,6 @@ const Writer = struct {
591593 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
592594 }
593595
594 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
595 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
596 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
597
598 try w.writeOperand(s, inst, 0, pl_op.operand);
599 try s.writeAll(", ");
600 try w.writeOperand(s, inst, 1, extra.lhs);
601 try s.writeAll(", ");
602 try w.writeOperand(s, inst, 2, extra.rhs);
603 }
604
605596 fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
606597 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
607598 const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
......@@ -610,17 +601,6 @@ const Writer = struct {
610601 try s.print(", {d}", .{extra.field_index});
611602 }
612603
613 fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
614 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
615 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
616
617 try w.writeOperand(s, inst, 0, pl_op.operand);
618 try s.writeAll(", ");
619 try w.writeOperand(s, inst, 1, extra.lhs);
620 try s.writeAll(", ");
621 try w.writeOperand(s, inst, 2, extra.rhs);
622 }
623
624604 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
625605 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
626606 const val = w.air.values[ty_pl.payload];
src/print_zir.zig+2-28
......@@ -277,8 +277,6 @@ const Writer = struct {
277277 .atomic_load => try self.writeAtomicLoad(stream, inst),
278278 .atomic_store => try self.writeAtomicStore(stream, inst),
279279 .atomic_rmw => try self.writeAtomicRmw(stream, inst),
280 .memcpy => try self.writeMemcpy(stream, inst),
281 .memset => try self.writeMemset(stream, inst),
282280 .shuffle => try self.writeShuffle(stream, inst),
283281 .mul_add => try self.writeMulAdd(stream, inst),
284282 .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
......@@ -346,6 +344,8 @@ const Writer = struct {
346344 .vector_type,
347345 .max,
348346 .min,
347 .memcpy,
348 .memset,
349349 .elem_ptr_node,
350350 .elem_val_node,
351351 .elem_ptr,
......@@ -1000,32 +1000,6 @@ const Writer = struct {
10001000 try self.writeSrc(stream, inst_data.src());
10011001 }
10021002
1003 fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1004 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1005 const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
1006
1007 try self.writeInstRef(stream, extra.dest);
1008 try stream.writeAll(", ");
1009 try self.writeInstRef(stream, extra.source);
1010 try stream.writeAll(", ");
1011 try self.writeInstRef(stream, extra.byte_count);
1012 try stream.writeAll(") ");
1013 try self.writeSrc(stream, inst_data.src());
1014 }
1015
1016 fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1017 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1018 const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
1019
1020 try self.writeInstRef(stream, extra.dest);
1021 try stream.writeAll(", ");
1022 try self.writeInstRef(stream, extra.byte);
1023 try stream.writeAll(", ");
1024 try self.writeInstRef(stream, extra.byte_count);
1025 try stream.writeAll(") ");
1026 try self.writeSrc(stream, inst_data.src());
1027 }
1028
10291003 fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
10301004 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
10311005 const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
src/type.zig+10-5
......@@ -3843,9 +3843,14 @@ pub const Type = extern union {
38433843 };
38443844 }
38453845
3846 /// Asserts the `Type` is a pointer.
3847 pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size {
3848 return switch (self.tag()) {
3846 /// Asserts `ty` is a pointer.
3847 pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size {
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()) {
38493854 .const_slice,
38503855 .mut_slice,
38513856 .const_slice_u8,
......@@ -3870,9 +3875,9 @@ pub const Type = extern union {
38703875 .inferred_alloc_mut,
38713876 => .One,
38723877
3873 .pointer => self.castTag(.pointer).?.data.size,
3878 .pointer => ty.castTag(.pointer).?.data.size,
38743879
3875 else => unreachable,
3880 else => null,
38763881 };
38773882 }
38783883
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/basic.zig+72-4
......@@ -353,22 +353,90 @@ fn f2(x: bool) []const u8 {
353353 return (if (x) &fA else &fB)();
354354}
355355
356test "@memset on array pointers" {
357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
359 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
360 if (builtin.zig_backend == .stage2_wasm) {
361 // TODO: implement memset when element ABI size > 1
362 return error.SkipZigTest;
363 }
364
365 try testMemsetArray();
366 try comptime testMemsetArray();
367}
368
369fn testMemsetArray() !void {
370 {
371 // memset array to non-undefined, ABI size == 1
372 var foo: [20]u8 = undefined;
373 @memset(&foo, 'A');
374 try expect(foo[0] == 'A');
375 try expect(foo[11] == 'A');
376 try expect(foo[19] == 'A');
377 }
378 {
379 // memset array to non-undefined, ABI size > 1
380 var foo: [20]u32 = undefined;
381 @memset(&foo, 1234);
382 try expect(foo[0] == 1234);
383 try expect(foo[11] == 1234);
384 try expect(foo[19] == 1234);
385 }
386}
387
388test "@memset on slices" {
389 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
390 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
391 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
392 if (builtin.zig_backend == .stage2_wasm) {
393 // TODO: implement memset when element ABI size > 1
394 // TODO: implement memset on slices
395 return error.SkipZigTest;
396 }
397
398 try testMemsetSlice();
399 try comptime testMemsetSlice();
400}
401
402fn testMemsetSlice() !void {
403 {
404 // memset slice to non-undefined, ABI size == 1
405 var array: [20]u8 = undefined;
406 var len = array.len;
407 var slice = array[0..len];
408 @memset(slice, 'A');
409 try expect(slice[0] == 'A');
410 try expect(slice[11] == 'A');
411 try expect(slice[19] == 'A');
412 }
413 {
414 // memset slice to non-undefined, ABI size > 1
415 var array: [20]u32 = undefined;
416 var len = array.len;
417 var slice = array[0..len];
418 @memset(slice, 1234);
419 try expect(slice[0] == 1234);
420 try expect(slice[11] == 1234);
421 try expect(slice[19] == 1234);
422 }
423}
424
356425test "memcpy and memset intrinsics" {
357426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
358427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
359428 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
360429
361430 try testMemcpyMemset();
362 // TODO add comptime test coverage
363 //comptime try testMemcpyMemset();
431 try comptime testMemcpyMemset();
364432}
365433
366434fn testMemcpyMemset() !void {
367435 var foo: [20]u8 = undefined;
368436 var bar: [20]u8 = undefined;
369437
370 @memset(&foo, 'A', foo.len);
371 @memcpy(&bar, &foo, bar.len);
438 @memset(&foo, 'A');
439 @memcpy(&bar, &foo);
372440
373441 try expect(bar[0] == 'A');
374442 try expect(bar[11] == 'A');
test/behavior/bugs/718.zig+1-1
......@@ -14,7 +14,7 @@ test "zero keys with @memset" {
1414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1515 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1616
17 @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@TypeOf(keys)));
17 @memset(@ptrCast([*]u8, &keys)[0..@sizeOf(@TypeOf(keys))], 0);
1818 try expect(!keys.up);
1919 try expect(!keys.down);
2020 try expect(!keys.left);
test/behavior/builtin_functions_returning_void_or_noreturn.zig+2-2
......@@ -17,8 +17,8 @@ test {
1717 try testing.expectEqual(void, @TypeOf(@breakpoint()));
1818 try testing.expectEqual({}, @export(x, .{ .name = "x" }));
1919 try testing.expectEqual({}, @fence(.Acquire));
20 try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0));
21 try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0));
20 try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1)[0..0], @intToPtr([*]u8, 1)[0..0]));
21 try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1)[0..0], undefined));
2222 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
2323 try testing.expectEqual({}, @prefetch(&val, .{}));
2424 try testing.expectEqual({}, @setAlignStack(16));
test/behavior/struct.zig+2-2
......@@ -91,7 +91,7 @@ test "structs" {
9191 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9292
9393 var foo: StructFoo = undefined;
94 @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
94 @memset(@ptrCast([*]u8, &foo)[0..@sizeOf(StructFoo)], 0);
9595 foo.a += 1;
9696 foo.b = foo.a == 1;
9797 try testFoo(foo);
......@@ -498,7 +498,7 @@ test "packed struct fields are ordered from LSB to MSB" {
498498
499499 var all: u64 = 0x7765443322221111;
500500 var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined;
501 @memcpy(&bytes, @ptrCast([*]u8, &all), 8);
501 @memcpy(bytes[0..8], @ptrCast([*]u8, &all));
502502 var bitfields = @ptrCast(*Bitfields, &bytes).*;
503503
504504 try expect(bitfields.f1 == 0x1111);
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig+22-5
......@@ -2,18 +2,35 @@ pub export fn entry() void {
22 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
33 var slice: []u8 = &buf;
44 const a: u32 = 1234;
5 @memcpy(slice, @ptrCast([*]const u8, &a), 4);
5 @memcpy(slice.ptr, @ptrCast([*]const u8, &a));
66}
77pub export fn entry1() void {
88 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
99 var ptr: *u8 = &buf[0];
10 @memcpy(ptr, 0, 4);
10 @memcpy(ptr, 0);
11}
12pub export fn entry2() void {
13 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
14 var ptr: *u8 = &buf[0];
15 @memset(ptr, 0);
16}
17pub export fn non_matching_lengths() void {
18 var buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
19 var buf2: [6]u8 = .{ 1, 2, 3, 4, 5, 6 };
20 @memcpy(&buf2, &buf1);
1121}
1222
1323// error
1424// backend=stage2
1525// target=native
1626//
17// :5:13: error: expected type '[*]u8', found '[]u8'
18// :10:13: error: expected type '[*]u8', found '*u8'
19// :10:13: note: a single pointer cannot cast into a many pointer
27// :5:5: error: unknown @memcpy length
28// :5:18: note: destination type [*]u8 provides no length
29// :5:24: note: source type [*]align(4) const u8 provides no length
30// :10:13: error: type 'u8' does not support indexing
31// :10:13: note: for loop operand must be an array, slice, tuple, or vector
32// :15:13: error: type '*u8' does not support indexing
33// :15:13: note: for loop operand must be an array, slice, tuple, or vector
34// :20:5: error: non-matching @memcpy lengths
35// :20:13: note: length 6 here
36// :20:20: note: length 5 here
test/cases/safety/@tagName on corrupted enum value.zig +1-1
......@@ -15,7 +15,7 @@ const E = enum(u32) {
1515
1616pub fn main() !void {
1717 var e: E = undefined;
18 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
18 @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55);
1919 var n = @tagName(e);
2020 _ = n;
2121 return error.TestFailed;
test/cases/safety/@tagName on corrupted union value.zig +1-1
......@@ -15,7 +15,7 @@ const U = union(enum(u32)) {
1515
1616pub fn main() !void {
1717 var u: U = undefined;
18 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
18 @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55);
1919 var t: @typeInfo(U).Union.tag_type.? = u;
2020 var n = @tagName(t);
2121 _ = n;
test/cases/safety/memcpy_alias.zig created+17
......@@ -0,0 +1,17 @@
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, "@memcpy arguments alias")) {
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 @memcpy(buffer[0..len], buffer[4 .. 4 + len]);
14}
15// run
16// backend=llvm
17// target=native
test/cases/safety/memcpy_len_mismatch.zig created+17
......@@ -0,0 +1,17 @@
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, "@memcpy 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 @memcpy(buffer[0..len], buffer[len .. len + 4]);
14}
15// run
16// backend=llvm
17// target=native
test/cases/safety/memset_array_undefined_bytes.zig created+18
......@@ -0,0 +1,18 @@
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, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 var buffer = [6]u8{ 1, 2, 3, 4, 5, 6 };
12 @memset(&buffer, undefined);
13 var x: u8 = buffer[1];
14 x += buffer[2];
15}
16// run
17// backend=llvm
18// target=native
test/cases/safety/memset_array_undefined_large.zig created+18
......@@ -0,0 +1,18 @@
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, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 var buffer = [6]i32{ 1, 2, 3, 4, 5, 6 };
12 @memset(&buffer, undefined);
13 var x: i32 = buffer[1];
14 x += buffer[2];
15}
16// run
17// backend=llvm
18// target=native
test/cases/safety/memset_slice_undefined_bytes.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, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 var buffer = [6]u8{ 1, 2, 3, 4, 5, 6 };
12 var len = buffer.len;
13 @memset(buffer[0..len], undefined);
14 var x: u8 = buffer[1];
15 x += buffer[2];
16}
17// run
18// backend=llvm
19// target=native
test/cases/safety/memset_slice_undefined_large.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, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 var buffer = [6]i32{ 1, 2, 3, 4, 5, 6 };
12 var len = buffer.len;
13 @memset(buffer[0..len], undefined);
14 var x: i32 = buffer[1];
15 x += buffer[2];
16}
17// run
18// backend=llvm
19// target=native
test/cases/safety/switch on corrupted enum value.zig +1-1
......@@ -15,7 +15,7 @@ const E = enum(u32) {
1515
1616pub fn main() !void {
1717 var e: E = undefined;
18 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
18 @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55);
1919 switch (e) {
2020 .X, .Y => @breakpoint(),
2121 }
test/cases/safety/switch on corrupted union value.zig +1-1
......@@ -15,7 +15,7 @@ const U = union(enum(u32)) {
1515
1616pub fn main() !void {
1717 var u: U = undefined;
18 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
18 @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55);
1919 switch (u) {
2020 .X, .Y => @breakpoint(),
2121 }