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" {...@@ -8681,40 +8681,30 @@ test "integer cast panic" {
8681 {#header_close#}8681 {#header_close#}
86828682
8683 {#header_open|@memcpy#}8683 {#header_open|@memcpy#}
8684 <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize) void{#endsyntax#}</pre>8684 <pre>{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}</pre>
8685 <p>8685 <p>This function copies bytes from one region of memory to another.</p>
8686 This function copies bytes from one region of memory to another. {#syntax#}dest{#endsyntax#} and8686 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or
8687 {#syntax#}source{#endsyntax#} are both pointers and must not overlap.8687 a mutable many-item {#link|pointer|Pointers#}. It may have any
8688 </p>8688 alignment, and it may have any element type.</p>
8689 <p>8689 <p>Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a
8690 This function is a low level intrinsic with no safety mechanisms. Most code8690 mutable pointer to an array, or a mutable many-item
8691 should not use this function, instead using something like this:8691 {#link|pointer|Pointers#}. It may have any alignment, and it may have any
8692 </p>8692 element type.</p>
8693 <pre>{#syntax#}for (dest, source[0..byte_count]) |*d, s| d.* = s;{#endsyntax#}</pre>8693 <p>The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#}
8694 <p>8694 into the {#syntax#}dest{#endsyntax#} element type. The element types may have
8695 The optimizer is intelligent enough to turn the above snippet into a memcpy.8695 different ABI size, however, that may incur a performance penalty.</p>
8696 </p>8696 <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and
8697 <p>There is also a standard library function for this:</p>8697 {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided,
8698 <pre>{#syntax#}const mem = @import("std").mem;8698 they must be equal.</p>
8699mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>8699 <p>Finally, the two memory regions must not overlap.</p>
8700 {#header_close#}8700 {#header_close#}
87018701
8702 {#header_open|@memset#}8702 {#header_open|@memset#}
8703 <pre>{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize) void{#endsyntax#}</pre>8703 <pre>{#syntax#}@memset(dest, elem) void{#endsyntax#}</pre>
8704 <p>8704 <p>This function sets all the elements of a memory region to {#syntax#}elem{#endsyntax#}.</p>
8705 This function sets a region of memory to {#syntax#}c{#endsyntax#}. {#syntax#}dest{#endsyntax#} is a pointer.8705 <p>{#syntax#}dest{#endsyntax#} must be a mutable slice or a mutable pointer to an array.
8706 </p>8706 It may have any alignment, and it may have any element type.</p>
8707 <p>8707 <p>{#syntax#}elem{#endsyntax#} is coerced to the element type of {#syntax#}dest{#endsyntax#}.</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>
8718 {#header_close#}8708 {#header_close#}
87198709
8720 {#header_open|@min#}8710 {#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...@@ -121,22 +121,22 @@ fn __atomic_load(size: u32, src: [*]u8, dest: [*]u8, model: i32) callconv(.C) vo
121 _ = model;121 _ = model;
122 var sl = spinlocks.get(@ptrToInt(src));122 var sl = spinlocks.get(@ptrToInt(src));
123 defer sl.release();123 defer sl.release();
124 @memcpy(dest, src, size);124 @memcpy(dest[0..size], src);
125}125}
126126
127fn __atomic_store(size: u32, dest: [*]u8, src: [*]u8, model: i32) callconv(.C) void {127fn __atomic_store(size: u32, dest: [*]u8, src: [*]u8, model: i32) callconv(.C) void {
128 _ = model;128 _ = model;
129 var sl = spinlocks.get(@ptrToInt(dest));129 var sl = spinlocks.get(@ptrToInt(dest));
130 defer sl.release();130 defer sl.release();
131 @memcpy(dest, src, size);131 @memcpy(dest[0..size], src);
132}132}
133133
134fn __atomic_exchange(size: u32, ptr: [*]u8, val: [*]u8, old: [*]u8, model: i32) callconv(.C) void {134fn __atomic_exchange(size: u32, ptr: [*]u8, val: [*]u8, old: [*]u8, model: i32) callconv(.C) void {
135 _ = model;135 _ = model;
136 var sl = spinlocks.get(@ptrToInt(ptr));136 var sl = spinlocks.get(@ptrToInt(ptr));
137 defer sl.release();137 defer sl.release();
138 @memcpy(old, ptr, size);138 @memcpy(old[0..size], ptr);
139 @memcpy(ptr, val, size);139 @memcpy(ptr[0..size], val);
140}140}
141141
142fn __atomic_compare_exchange(142fn __atomic_compare_exchange(
...@@ -155,10 +155,10 @@ fn __atomic_compare_exchange(...@@ -155,10 +155,10 @@ fn __atomic_compare_exchange(
155 if (expected[i] != b) break;155 if (expected[i] != b) break;
156 } else {156 } else {
157 // The two objects, ptr and expected, are equal157 // The two objects, ptr and expected, are equal
158 @memcpy(ptr, desired, size);158 @memcpy(ptr[0..size], desired);
159 return 1;159 return 1;
160 }160 }
161 @memcpy(expected, ptr, size);161 @memcpy(expected[0..size], ptr);
162 return 0;162 return 0;
163}163}
164164
lib/compiler_rt/emutls.zig+2-2
...@@ -139,10 +139,10 @@ const ObjectArray = struct {...@@ -139,10 +139,10 @@ const ObjectArray = struct {
139139
140 if (control.default_value) |value| {140 if (control.default_value) |value| {
141 // default value: copy the content to newly allocated object.141 // 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));
143 } else {143 } else {
144 // no default: return zeroed memory.144 // no default: return zeroed memory.
145 @memset(data, 0, size);145 @memset(data[0..size], 0);
146 }146 }
147147
148 self.slots[index] = @ptrCast(*anyopaque, data);148 self.slots[index] = @ptrCast(*anyopaque, data);
lib/std/array_hash_map.zig+2-2
...@@ -1893,7 +1893,7 @@ const IndexHeader = struct {...@@ -1893,7 +1893,7 @@ const IndexHeader = struct {
1893 const index_size = hash_map.capacityIndexSize(new_bit_index);1893 const index_size = hash_map.capacityIndexSize(new_bit_index);
1894 const nbytes = @sizeOf(IndexHeader) + index_size * len;1894 const nbytes = @sizeOf(IndexHeader) + index_size * len;
1895 const bytes = try allocator.alignedAlloc(u8, @alignOf(IndexHeader), nbytes);1895 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);
1897 const result = @ptrCast(*IndexHeader, bytes.ptr);1897 const result = @ptrCast(*IndexHeader, bytes.ptr);
1898 result.* = .{1898 result.* = .{
1899 .bit_index = new_bit_index,1899 .bit_index = new_bit_index,
...@@ -1914,7 +1914,7 @@ const IndexHeader = struct {...@@ -1914,7 +1914,7 @@ const IndexHeader = struct {
1914 const index_size = hash_map.capacityIndexSize(header.bit_index);1914 const index_size = hash_map.capacityIndexSize(header.bit_index);
1915 const ptr = @ptrCast([*]align(@alignOf(IndexHeader)) u8, header);1915 const ptr = @ptrCast([*]align(@alignOf(IndexHeader)) u8, header);
1916 const nbytes = @sizeOf(IndexHeader) + header.length() * index_size;1916 const nbytes = @sizeOf(IndexHeader) + header.length() * index_size;
1917 @memset(ptr + @sizeOf(IndexHeader), 0xff, nbytes - @sizeOf(IndexHeader));1917 @memset(ptr[@sizeOf(IndexHeader)..nbytes], 0xff);
1918 }1918 }
19191919
1920 // Verify that the header has sufficient alignment to produce aligned arrays.1920 // 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 {...@@ -121,7 +121,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
121121
122 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);122 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
123 mem.copy(T, new_memory, self.items);123 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);
125 self.clearAndFree();125 self.clearAndFree();
126 return new_memory;126 return new_memory;
127 }127 }
...@@ -281,11 +281,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -281,11 +281,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
281 const new_len = old_len + items.len;281 const new_len = old_len + items.len;
282 assert(new_len <= self.capacity);282 assert(new_len <= self.capacity);
283 self.items.len = new_len;283 self.items.len = new_len;
284 @memcpy(284 @memcpy(self.items[old_len..][0..items.len], items);
285 @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len),
286 @ptrCast([*]const u8, items.ptr),
287 items.len * @sizeOf(T),
288 );
289 }285 }
290286
291 pub const Writer = if (T != u8)287 pub const Writer = if (T != u8)
...@@ -601,7 +597,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -601,7 +597,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
601597
602 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);598 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
603 mem.copy(T, new_memory, self.items);599 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);
605 self.clearAndFree(allocator);601 self.clearAndFree(allocator);
606 return new_memory;602 return new_memory;
607 }603 }
...@@ -740,11 +736,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -740,11 +736,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
740 const new_len = old_len + items.len;736 const new_len = old_len + items.len;
741 assert(new_len <= self.capacity);737 assert(new_len <= self.capacity);
742 self.items.len = new_len;738 self.items.len = new_len;
743 @memcpy(739 @memcpy(self.items[old_len..][0..items.len], items);
744 @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len),
745 @ptrCast([*]const u8, items.ptr),
746 items.len * @sizeOf(T),
747 );
748 }740 }
749741
750 pub const WriterContext = struct {742 pub const WriterContext = struct {
lib/std/builtin.zig+2
...@@ -1002,6 +1002,8 @@ pub const panic_messages = struct {...@@ -1002,6 +1002,8 @@ pub const panic_messages = struct {
1002 pub const index_out_of_bounds = "index out of bounds";1002 pub const index_out_of_bounds = "index out of bounds";
1003 pub const start_index_greater_than_end = "start index is larger than end index";1003 pub const start_index_greater_than_end = "start index is larger than end index";
1004 pub const for_len_mismatch = "for loop over objects with non-equal lengths";1004 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
1005 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
1006 pub const memcpy_alias = "@memcpy arguments alias";
1005};1007};
10061008
1007pub noinline fn returnError(st: *StackTrace) void {1009pub noinline fn returnError(st: *StackTrace) void {
lib/std/c/darwin.zig+1-1
...@@ -3670,7 +3670,7 @@ pub const MachTask = extern struct {...@@ -3670,7 +3670,7 @@ pub const MachTask = extern struct {
3670 else => |err| return unexpectedKernError(err),3670 else => |err| return unexpectedKernError(err),
3671 }3671 }
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));
3674 _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);3674 _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
36753675
3676 out_buf = out_buf[curr_bytes_read..];3676 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 {...@@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type {
209 acc |= (computed_tag[j] ^ tag[j]);209 acc |= (computed_tag[j] ^ tag[j]);
210 }210 }
211 if (acc != 0) {211 if (acc != 0) {
212 @memset(m.ptr, undefined, m.len);212 @memset(m, undefined);
213 return error.AuthenticationFailed;213 return error.AuthenticationFailed;
214 }214 }
215 }215 }
...@@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type {...@@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type {
390 acc |= (computed_tag[j] ^ tag[j]);390 acc |= (computed_tag[j] ^ tag[j]);
391 }391 }
392 if (acc != 0) {392 if (acc != 0) {
393 @memset(m.ptr, undefined, m.len);393 @memset(m, undefined);
394 return error.AuthenticationFailed;394 return error.AuthenticationFailed;
395 }395 }
396 }396 }
lib/std/crypto/aes_gcm.zig+1-1
...@@ -91,7 +91,7 @@ fn AesGcm(comptime Aes: anytype) type {...@@ -91,7 +91,7 @@ fn AesGcm(comptime Aes: anytype) type {
91 acc |= (computed_tag[p] ^ tag[p]);91 acc |= (computed_tag[p] ^ tag[p]);
92 }92 }
93 if (acc != 0) {93 if (acc != 0) {
94 @memset(m.ptr, undefined, m.len);94 @memset(m, undefined);
95 return error.AuthenticationFailed;95 return error.AuthenticationFailed;
96 }96 }
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...@@ -531,7 +531,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
531 const pub_key = subject.pubKey();531 const pub_key = subject.pubKey();
532 if (pub_key.len > main_cert_pub_key_buf.len)532 if (pub_key.len > main_cert_pub_key_buf.len)
533 return error.CertificatePublicKeyInvalid;533 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);
535 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);535 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);
536 } else {536 } else {
537 try prev_cert.verify(subject, now_sec);537 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,...@@ -135,11 +135,11 @@ pub fn timingSafeSub(comptime T: type, a: []const T, b: []const T, result: []T,
135/// Sets a slice to zeroes.135/// Sets a slice to zeroes.
136/// Prevents the store from being optimized out.136/// Prevents the store from being optimized out.
137pub fn secureZero(comptime T: type, s: []T) void {137pub fn secureZero(comptime T: type, s: []T) void {
138 // NOTE: We do not use a volatile slice cast here since LLVM cannot138 // TODO: implement `@memset` for non-byte-sized element type in the llvm backend
139 // see that it can be replaced by a memset.139 //@memset(@as([]volatile T, s), 0);
140 const ptr = @ptrCast([*]volatile u8, s.ptr);140 const ptr = @ptrCast([*]volatile u8, s.ptr);
141 const length = s.len * @sizeOf(T);141 const length = s.len * @sizeOf(T);
142 @memset(ptr, 0, length);142 @memset(ptr[0..length], 0);
143}143}
144144
145test "crypto.utils.timingSafeEql" {145test "crypto.utils.timingSafeEql" {
lib/std/fifo.zig+4-4
...@@ -104,7 +104,7 @@ pub fn LinearFifo(...@@ -104,7 +104,7 @@ pub fn LinearFifo(
104 }104 }
105 { // set unused area to undefined105 { // set unused area to undefined
106 const unused = mem.sliceAsBytes(self.buf[self.count..]);106 const unused = mem.sliceAsBytes(self.buf[self.count..]);
107 @memset(unused.ptr, undefined, unused.len);107 @memset(unused, undefined);
108 }108 }
109 }109 }
110110
...@@ -182,12 +182,12 @@ pub fn LinearFifo(...@@ -182,12 +182,12 @@ pub fn LinearFifo(
182 const slice = self.readableSliceMut(0);182 const slice = self.readableSliceMut(0);
183 if (slice.len >= count) {183 if (slice.len >= count) {
184 const unused = mem.sliceAsBytes(slice[0..count]);184 const unused = mem.sliceAsBytes(slice[0..count]);
185 @memset(unused.ptr, undefined, unused.len);185 @memset(unused, undefined);
186 } else {186 } else {
187 const unused = mem.sliceAsBytes(slice[0..]);187 const unused = mem.sliceAsBytes(slice[0..]);
188 @memset(unused.ptr, undefined, unused.len);188 @memset(unused, undefined);
189 const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]);189 const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]);
190 @memset(unused2.ptr, undefined, unused2.len);190 @memset(unused2, undefined);
191 }191 }
192 }192 }
193 if (autoalign and self.count == count) {193 if (autoalign and self.count == count) {
lib/std/hash/murmur.zig+8-14
...@@ -99,9 +99,8 @@ pub const Murmur2_64 = struct {...@@ -99,9 +99,8 @@ pub const Murmur2_64 = struct {
9999
100 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {100 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
101 const m: u64 = 0xc6a4a7935bd1e995;101 const m: u64 = 0xc6a4a7935bd1e995;
102 const len = @as(u64, str.len);102 var h1: u64 = seed ^ (@as(u64, str.len) *% m);
103 var h1: u64 = seed ^ (len *% m);103 for (@ptrCast([*]align(1) const u64, str.ptr)[0 .. str.len / 8]) |v| {
104 for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| {
105 var k1: u64 = v;104 var k1: u64 = v;
106 if (native_endian == .Big)105 if (native_endian == .Big)
107 k1 = @byteSwap(k1);106 k1 = @byteSwap(k1);
...@@ -111,11 +110,11 @@ pub const Murmur2_64 = struct {...@@ -111,11 +110,11 @@ pub const Murmur2_64 = struct {
111 h1 ^= k1;110 h1 ^= k1;
112 h1 *%= m;111 h1 *%= m;
113 }112 }
114 const rest = len & 7;113 const rest = str.len & 7;
115 const offset = len - rest;114 const offset = str.len - rest;
116 if (rest > 0) {115 if (rest > 0) {
117 var k1: u64 = 0;116 var k1: u64 = 0;
118 @memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[@intCast(usize, offset)]), @intCast(usize, rest));117 @memcpy(@ptrCast([*]u8, &k1)[0..rest], str[offset..]);
119 if (native_endian == .Big)118 if (native_endian == .Big)
120 k1 = @byteSwap(k1);119 k1 = @byteSwap(k1);
121 h1 ^= k1;120 h1 ^= k1;
...@@ -282,13 +281,8 @@ pub const Murmur3_32 = struct {...@@ -282,13 +281,8 @@ pub const Murmur3_32 = struct {
282281
283fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {282fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
284 const hashbytes = hashbits / 8;283 const hashbytes = hashbits / 8;
285 var key: [256]u8 = undefined;284 var key: [256]u8 = [1]u8{0} ** 256;
286 var hashes: [hashbytes * 256]u8 = undefined;285 var hashes: [hashbytes * 256]u8 = [1]u8{0} ** (hashbytes * 256);
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)));
292286
293 var i: u32 = 0;287 var i: u32 = 0;
294 while (i < 256) : (i += 1) {288 while (i < 256) : (i += 1) {
...@@ -297,7 +291,7 @@ fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {...@@ -297,7 +291,7 @@ fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
297 var h = hash_fn(key[0..i], 256 - i);291 var h = hash_fn(key[0..i], 256 - i);
298 if (native_endian == .Big)292 if (native_endian == .Big)
299 h = @byteSwap(h);293 h = @byteSwap(h);
300 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);294 @memcpy(hashes[i * hashbytes ..][0..hashbytes], @ptrCast([*]u8, &h));
301 }295 }
302296
303 return @truncate(u32, hash_fn(&hashes, 0));297 return @truncate(u32, hash_fn(&hashes, 0));
lib/std/hash_map.zig+1-1
...@@ -1449,7 +1449,7 @@ pub fn HashMapUnmanaged(...@@ -1449,7 +1449,7 @@ pub fn HashMapUnmanaged(
1449 }1449 }
14501450
1451 fn initMetadatas(self: *Self) void {1451 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);
1453 }1453 }
14541454
1455 // This counts the number of occupied slots (not counting tombstones), which is1455 // 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 {...@@ -759,7 +759,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
759 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);759 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
760 if (new_size_class <= size_class) {760 if (new_size_class <= size_class) {
761 if (old_mem.len > new_size) {761 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);
763 }763 }
764 if (config.verbose_log) {764 if (config.verbose_log) {
765 log.info("small resize {d} bytes at {*} to {d}", .{765 log.info("small resize {d} bytes at {*} to {d}", .{
...@@ -911,7 +911,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -911,7 +911,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
911 self.empty_buckets = bucket;911 self.empty_buckets = bucket;
912 }912 }
913 } else {913 } else {
914 @memset(old_mem.ptr, undefined, old_mem.len);914 @memset(old_mem, undefined);
915 }915 }
916 if (config.safety) {916 if (config.safety) {
917 assert(self.small_allocations.remove(@ptrToInt(old_mem.ptr)));917 assert(self.small_allocations.remove(@ptrToInt(old_mem.ptr)));
...@@ -1011,7 +1011,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -1011,7 +1011,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
1011 };1011 };
1012 self.buckets[bucket_index] = ptr;1012 self.buckets[bucket_index] = ptr;
1013 // Set the used bits to all zeroes1013 // 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);
1015 return ptr;1015 return ptr;
1016 }1016 }
1017 };1017 };
lib/std/math/big/int_test.zig+4-4
...@@ -2756,7 +2756,7 @@ test "big int conversion read twos complement with padding" {...@@ -2756,7 +2756,7 @@ test "big int conversion read twos complement with padding" {
27562756
2757 var buffer1 = try testing.allocator.alloc(u8, 16);2757 var buffer1 = try testing.allocator.alloc(u8, 16);
2758 defer testing.allocator.free(buffer1);2758 defer testing.allocator.free(buffer1);
2759 @memset(buffer1.ptr, 0xaa, buffer1.len);2759 @memset(buffer1, 0xaa);
27602760
2761 // writeTwosComplement:2761 // writeTwosComplement:
2762 // (1) should not write beyond buffer[0..abi_size]2762 // (1) should not write beyond buffer[0..abi_size]
...@@ -2773,7 +2773,7 @@ test "big int conversion read twos complement with padding" {...@@ -2773,7 +2773,7 @@ test "big int conversion read twos complement with padding" {
2773 a.toConst().writeTwosComplement(buffer1[0..16], .Big);2773 a.toConst().writeTwosComplement(buffer1[0..16], .Big);
2774 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 }));2774 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);
2777 try a.set(-0x01_02030405_06070809_0a0b0c0d);2777 try a.set(-0x01_02030405_06070809_0a0b0c0d);
2778 bit_count = 12 * 8 + 2;2778 bit_count = 12 * 8 + 2;
27792779
...@@ -2794,7 +2794,7 @@ test "big int write twos complement +/- zero" {...@@ -2794,7 +2794,7 @@ test "big int write twos complement +/- zero" {
27942794
2795 var buffer1 = try testing.allocator.alloc(u8, 16);2795 var buffer1 = try testing.allocator.alloc(u8, 16);
2796 defer testing.allocator.free(buffer1);2796 defer testing.allocator.free(buffer1);
2797 @memset(buffer1.ptr, 0xaa, buffer1.len);2797 @memset(buffer1, 0xaa);
27982798
2799 // Test zero2799 // Test zero
28002800
...@@ -2807,7 +2807,7 @@ test "big int write twos complement +/- zero" {...@@ -2807,7 +2807,7 @@ test "big int write twos complement +/- zero" {
2807 m.toConst().writeTwosComplement(buffer1[0..16], .Big);2807 m.toConst().writeTwosComplement(buffer1[0..16], .Big);
2808 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));2808 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
28092809
2810 @memset(buffer1.ptr, 0xaa, buffer1.len);2810 @memset(buffer1, 0xaa);
2811 m.positive = false;2811 m.positive = false;
28122812
2813 // Test negative zero2813 // Test negative zero
lib/std/mem/Allocator.zig+5-4
...@@ -215,7 +215,7 @@ pub fn allocAdvancedWithRetAddr(...@@ -215,7 +215,7 @@ pub fn allocAdvancedWithRetAddr(
215 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;215 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
216 const byte_ptr = self.rawAlloc(byte_count, log2a(a), return_address) orelse return Error.OutOfMemory;216 const byte_ptr = self.rawAlloc(byte_count, log2a(a), return_address) orelse return Error.OutOfMemory;
217 // TODO: https://github.com/ziglang/zig/issues/4298217 // TODO: https://github.com/ziglang/zig/issues/4298
218 @memset(byte_ptr, undefined, byte_count);218 @memset(byte_ptr[0..byte_count], undefined);
219 const byte_slice = byte_ptr[0..byte_count];219 const byte_slice = byte_ptr[0..byte_count];
220 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));220 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
221}221}
...@@ -282,9 +282,10 @@ pub fn reallocAdvanced(...@@ -282,9 +282,10 @@ pub fn reallocAdvanced(
282282
283 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse283 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse
284 return error.OutOfMemory;284 return error.OutOfMemory;
285 @memcpy(new_mem, 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]);
286 // TODO https://github.com/ziglang/zig/issues/4298287 // 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);
288 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);289 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);
289290
290 return mem.bytesAsSlice(T, @alignCast(Slice.alignment, new_mem[0..byte_count]));291 return mem.bytesAsSlice(T, @alignCast(Slice.alignment, new_mem[0..byte_count]));
...@@ -299,7 +300,7 @@ pub fn free(self: Allocator, memory: anytype) void {...@@ -299,7 +300,7 @@ pub fn free(self: Allocator, memory: anytype) void {
299 if (bytes_len == 0) return;300 if (bytes_len == 0) return;
300 const non_const_ptr = @constCast(bytes.ptr);301 const non_const_ptr = @constCast(bytes.ptr);
301 // TODO: https://github.com/ziglang/zig/issues/4298302 // 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);
303 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());304 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());
304}305}
305306
lib/std/multi_array_list.zig+1-2
...@@ -360,11 +360,10 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -360,11 +360,10 @@ pub fn MultiArrayList(comptime T: type) type {
360 if (@sizeOf(field_info.type) != 0) {360 if (@sizeOf(field_info.type) != 0) {
361 const field = @intToEnum(Field, i);361 const field = @intToEnum(Field, i);
362 const dest_slice = self_slice.items(field)[new_len..];362 const dest_slice = self_slice.items(field)[new_len..];
363 const byte_count = dest_slice.len * @sizeOf(field_info.type);
364 // We use memset here for more efficient codegen in safety-checked,363 // We use memset here for more efficient codegen in safety-checked,
365 // valgrind-enabled builds. Otherwise the valgrind client request364 // valgrind-enabled builds. Otherwise the valgrind client request
366 // will be repeated for every element.365 // will be repeated for every element.
367 @memset(@ptrCast([*]u8, dest_slice.ptr), undefined, byte_count);366 @memset(dest_slice, undefined);
368 }367 }
369 }368 }
370 self.len = new_len;369 self.len = new_len;
lib/std/net.zig+3-3
...@@ -1020,7 +1020,7 @@ fn linuxLookupName(...@@ -1020,7 +1020,7 @@ fn linuxLookupName(
1020 for (addrs.items, 0..) |*addr, i| {1020 for (addrs.items, 0..) |*addr, i| {
1021 var key: i32 = 0;1021 var key: i32 = 0;
1022 var sa6: os.sockaddr.in6 = undefined;1022 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);
1024 var da6 = os.sockaddr.in6{1024 var da6 = os.sockaddr.in6{
1025 .family = os.AF.INET6,1025 .family = os.AF.INET6,
1026 .scope_id = addr.addr.in6.sa.scope_id,1026 .scope_id = addr.addr.in6.sa.scope_id,
...@@ -1029,7 +1029,7 @@ fn linuxLookupName(...@@ -1029,7 +1029,7 @@ fn linuxLookupName(
1029 .addr = [1]u8{0} ** 16,1029 .addr = [1]u8{0} ** 16,
1030 };1030 };
1031 var sa4: os.sockaddr.in = undefined;1031 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);
1033 var da4 = os.sockaddr.in{1033 var da4 = os.sockaddr.in{
1034 .family = os.AF.INET,1034 .family = os.AF.INET,
1035 .port = 65535,1035 .port = 65535,
...@@ -1577,7 +1577,7 @@ fn resMSendRc(...@@ -1577,7 +1577,7 @@ fn resMSendRc(
15771577
1578 // Get local address and open/bind a socket1578 // Get local address and open/bind a socket
1579 var sa: Address = undefined;1579 var sa: Address = undefined;
1580 @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address));1580 @memset(@ptrCast([*]u8, &sa)[0..@sizeOf(Address)], 0);
1581 sa.any.family = family;1581 sa.any.family = family;
1582 try os.bind(fd, &sa.any, sl);1582 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 {...@@ -5217,7 +5217,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5217 .macos, .ios, .watchos, .tvos => {5217 .macos, .ios, .watchos, .tvos => {
5218 // On macOS, we can use F.GETPATH fcntl command to query the OS for5218 // On macOS, we can use F.GETPATH fcntl command to query the OS for
5219 // the path to the file descriptor.5219 // the path to the file descriptor.
5220 @memset(out_buffer, 0, MAX_PATH_BYTES);5220 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5221 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {5221 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5222 .SUCCESS => {},5222 .SUCCESS => {},
5223 .BADF => return error.FileNotFound,5223 .BADF => return error.FileNotFound,
...@@ -5308,7 +5308,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {...@@ -5308,7 +5308,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5308 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 6, .minor = 0 }) == .lt) {5308 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 6, .minor = 0 }) == .lt) {
5309 @compileError("querying for canonical path of a handle is unsupported on this host");5309 @compileError("querying for canonical path of a handle is unsupported on this host");
5310 }5310 }
5311 @memset(out_buffer, 0, MAX_PATH_BYTES);5311 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5312 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {5312 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5313 .SUCCESS => {},5313 .SUCCESS => {},
5314 .BADF => return error.FileNotFound,5314 .BADF => return error.FileNotFound,
...@@ -5322,7 +5322,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {...@@ -5322,7 +5322,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5322 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 10, .minor = 0 }) == .lt) {5322 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 10, .minor = 0 }) == .lt) {
5323 @compileError("querying for canonical path of a handle is unsupported on this host");5323 @compileError("querying for canonical path of a handle is unsupported on this host");
5324 }5324 }
5325 @memset(out_buffer, 0, MAX_PATH_BYTES);5325 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5326 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {5326 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5327 .SUCCESS => {},5327 .SUCCESS => {},
5328 .ACCES => return error.AccessDenied,5328 .ACCES => return error.AccessDenied,
...@@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {...@@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
5548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;5548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
5549 // >= rather than > to make room for the null byte5549 // >= rather than > to make room for the null byte
5550 if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;5550 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);
5552 path_with_null[file_path.len] = 0;5552 path_with_null[file_path.len] = 0;
5553 return path_with_null;5553 return path_with_null;
5554}5554}
...@@ -5720,7 +5720,7 @@ pub fn res_mkquery(...@@ -5720,7 +5720,7 @@ pub fn res_mkquery(
57205720
5721 // Construct query template - ID will be filled later5721 // Construct query template - ID will be filled later
5722 var q: [280]u8 = undefined;5722 var q: [280]u8 = undefined;
5723 @memset(&q, 0, n);5723 @memset(q[0..n], 0);
5724 q[2] = @as(u8, op) * 8 + 1;5724 q[2] = @as(u8, op) * 8 + 1;
5725 q[5] = 1;5725 q[5] = 1;
5726 mem.copy(u8, q[13..], name);5726 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...@@ -1184,7 +1184,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
1184 .mask = undefined,1184 .mask = undefined,
1185 .restorer = @ptrCast(k_sigaction_funcs.restorer, restorer_fn),1185 .restorer = @ptrCast(k_sigaction_funcs.restorer, restorer_fn),
1186 };1186 };
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));
1188 }1188 }
11891189
1190 const ksa_arg = if (act != null) @ptrToInt(&ksa) else 0;1190 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...@@ -1200,7 +1200,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
1200 if (oact) |old| {1200 if (oact) |old| {
1201 old.handler.handler = oldksa.handler;1201 old.handler.handler = oldksa.handler;
1202 old.flags = @truncate(c_uint, oldksa.flags);1202 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));
1204 }1204 }
12051205
1206 return 0;1206 return 0;
...@@ -1515,7 +1515,7 @@ pub fn sched_yield() usize {...@@ -1515,7 +1515,7 @@ pub fn sched_yield() usize {
1515pub fn sched_getaffinity(pid: pid_t, size: usize, set: *cpu_set_t) usize {1515pub fn sched_getaffinity(pid: pid_t, size: usize, set: *cpu_set_t) usize {
1516 const rc = syscall3(.sched_getaffinity, @bitCast(usize, @as(isize, pid)), size, @ptrToInt(set));1516 const rc = syscall3(.sched_getaffinity, @bitCast(usize, @as(isize, pid)), size, @ptrToInt(set));
1517 if (@bitCast(isize, rc) < 0) return rc;1517 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);
1519 return 0;1519 return 0;
1520}1520}
15211521
lib/std/os/windows.zig+3-3
...@@ -755,9 +755,9 @@ pub fn CreateSymbolicLink(...@@ -755,9 +755,9 @@ pub fn CreateSymbolicLink(
755 };755 };
756756
757 std.mem.copy(u8, buffer[0..], std.mem.asBytes(&symlink_data));757 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));
759 const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;759 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));
761 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);761 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
762}762}
763763
...@@ -1179,7 +1179,7 @@ pub fn GetFinalPathNameByHandle(...@@ -1179,7 +1179,7 @@ pub fn GetFinalPathNameByHandle(
1179 var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);1179 var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
1180 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);1180 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
1181 input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);1181 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
1184 DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {1184 DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
1185 error.AccessDenied => unreachable,1185 error.AccessDenied => unreachable,
lib/std/zig/c_builtins.zig+5-5
...@@ -152,7 +152,7 @@ pub inline fn __builtin___memset_chk(...@@ -152,7 +152,7 @@ pub inline fn __builtin___memset_chk(
152152
153pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {153pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {
154 const dst_cast = @ptrCast([*c]u8, dst);154 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)));
156 return dst;156 return dst;
157}157}
158158
...@@ -171,10 +171,10 @@ pub inline fn __builtin_memcpy(...@@ -171,10 +171,10 @@ pub inline fn __builtin_memcpy(
171 noalias src: ?*const anyopaque,171 noalias src: ?*const anyopaque,
172 len: usize,172 len: usize,
173) ?*anyopaque {173) ?*anyopaque {
174 const dst_cast = @ptrCast([*c]u8, dst);174 if (len > 0) @memcpy(
175 const src_cast = @ptrCast([*c]const u8, src);175 @ptrCast([*]u8, dst.?)[0..len],
176176 @ptrCast([*]const u8, src.?),
177 @memcpy(dst_cast, src_cast, len);177 );
178 return dst;178 return dst;
179}179}
180180
src/Air.zig+40-8
...@@ -138,12 +138,14 @@ pub const Inst = struct {...@@ -138,12 +138,14 @@ pub const Inst = struct {
138 /// The offset is in element type units, not bytes.138 /// The offset is in element type units, not bytes.
139 /// Wrapping is undefined behavior.139 /// Wrapping is undefined behavior.
140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
141 /// The pointer may be a slice.
141 /// Uses the `ty_pl` field. Payload is `Bin`.142 /// Uses the `ty_pl` field. Payload is `Bin`.
142 ptr_add,143 ptr_add,
143 /// Subtract an offset from a pointer, returning a new pointer.144 /// Subtract an offset from a pointer, returning a new pointer.
144 /// The offset is in element type units, not bytes.145 /// The offset is in element type units, not bytes.
145 /// Wrapping is undefined behavior.146 /// Wrapping is undefined behavior.
146 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.147 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
148 /// The pointer may be a slice.
147 /// Uses the `ty_pl` field. Payload is `Bin`.149 /// Uses the `ty_pl` field. Payload is `Bin`.
148 ptr_sub,150 ptr_sub,
149 /// Given two operands which can be floats, integers, or vectors, returns the151 /// Given two operands which can be floats, integers, or vectors, returns the
...@@ -462,6 +464,7 @@ pub const Inst = struct {...@@ -462,6 +464,7 @@ pub const Inst = struct {
462 /// Uses the `ty_op` field.464 /// Uses the `ty_op` field.
463 load,465 load,
464 /// Converts a pointer to its address. Result type is always `usize`.466 /// Converts a pointer to its address. Result type is always `usize`.
467 /// Pointer type size may be any, including slice.
465 /// Uses the `un_op` field.468 /// Uses the `un_op` field.
466 ptrtoint,469 ptrtoint,
467 /// Given a boolean, returns 0 or 1.470 /// Given a boolean, returns 0 or 1.
...@@ -484,7 +487,16 @@ pub const Inst = struct {...@@ -484,7 +487,16 @@ pub const Inst = struct {
484 /// Write a value to a pointer. LHS is pointer, RHS is value.487 /// Write a value to a pointer. LHS is pointer, RHS is value.
485 /// Result type is always void.488 /// Result type is always void.
486 /// Uses the `bin_op` field.489 /// 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.
487 store,494 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,
488 /// Indicates the program counter will never get to this instruction.500 /// Indicates the program counter will never get to this instruction.
489 /// Result type is always noreturn; no instructions in a block follow this one.501 /// Result type is always noreturn; no instructions in a block follow this one.
490 unreach,502 unreach,
...@@ -632,17 +644,33 @@ pub const Inst = struct {...@@ -632,17 +644,33 @@ pub const Inst = struct {
632 /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`.644 /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`.
633 select,645 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.
636 /// Result type is always void.650 /// Result type is always void.
637 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the651 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value.
638 /// value, `rhs` is the length.652 /// The element value may be undefined, in which case the destination
639 /// The element type may be any type, not just u8.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.
640 memset,658 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.
642 /// Result type is always void.669 /// Result type is always void.
643 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the670 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
644 /// src ptr, `rhs` is the length.671 /// If the length is compile-time known (due to the destination or
645 /// The element type may be any type, not just u8.672 /// source being a pointer-to-array), then it is guaranteed to be
673 /// greater than zero.
646 memcpy,674 memcpy,
647675
648 /// Uses the `ty_pl` field with payload `Cmpxchg`.676 /// Uses the `ty_pl` field with payload `Cmpxchg`.
...@@ -1226,12 +1254,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1226,12 +1254,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1226 .dbg_var_ptr,1254 .dbg_var_ptr,
1227 .dbg_var_val,1255 .dbg_var_val,
1228 .store,1256 .store,
1257 .store_safe,
1229 .fence,1258 .fence,
1230 .atomic_store_unordered,1259 .atomic_store_unordered,
1231 .atomic_store_monotonic,1260 .atomic_store_monotonic,
1232 .atomic_store_release,1261 .atomic_store_release,
1233 .atomic_store_seq_cst,1262 .atomic_store_seq_cst,
1234 .memset,1263 .memset,
1264 .memset_safe,
1235 .memcpy,1265 .memcpy,
1236 .set_union_tag,1266 .set_union_tag,
1237 .prefetch,1267 .prefetch,
...@@ -1406,11 +1436,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {...@@ -1406,11 +1436,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {
1406 .ret,1436 .ret,
1407 .ret_load,1437 .ret_load,
1408 .store,1438 .store,
1439 .store_safe,
1409 .unreach,1440 .unreach,
1410 .optional_payload_ptr_set,1441 .optional_payload_ptr_set,
1411 .errunion_payload_ptr_set,1442 .errunion_payload_ptr_set,
1412 .set_union_tag,1443 .set_union_tag,
1413 .memset,1444 .memset,
1445 .memset_safe,
1414 .memcpy,1446 .memcpy,
1415 .cmpxchg_weak,1447 .cmpxchg_weak,
1416 .cmpxchg_strong,1448 .cmpxchg_strong,
src/AstGen.zig+6-8
...@@ -8453,18 +8453,16 @@ fn builtinCall(...@@ -8453,18 +8453,16 @@ fn builtinCall(
8453 return rvalue(gz, ri, result, node);8453 return rvalue(gz, ri, result, node);
8454 },8454 },
8455 .memcpy => {8455 .memcpy => {
8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{8456 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
8457 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),8457 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8458 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),8458 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
8459 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8460 });8459 });
8461 return rvalue(gz, ri, .void_value, node);8460 return rvalue(gz, ri, .void_value, node);
8462 },8461 },
8463 .memset => {8462 .memset => {
8464 _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{8463 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
8465 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),8464 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8466 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),8465 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
8467 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
8468 });8466 });
8469 return rvalue(gz, ri, .void_value, node);8467 return rvalue(gz, ri, .void_value, node);
8470 },8468 },
src/BuiltinFn.zig+2-2
...@@ -615,14 +615,14 @@ pub const list = list: {...@@ -615,14 +615,14 @@ pub const list = list: {
615 "@memcpy",615 "@memcpy",
616 .{616 .{
617 .tag = .memcpy,617 .tag = .memcpy,
618 .param_count = 3,618 .param_count = 2,
619 },619 },
620 },620 },
621 .{621 .{
622 "@memset",622 "@memset",
623 .{623 .{
624 .tag = .memset,624 .tag = .memset,
625 .param_count = 3,625 .param_count = 2,
626 },626 },
627 },627 },
628 .{628 .{
src/Liveness.zig+8-17
...@@ -299,11 +299,15 @@ pub fn categorizeOperand(...@@ -299,11 +299,15 @@ pub fn categorizeOperand(
299 },299 },
300300
301 .store,301 .store,
302 .store_safe,
302 .atomic_store_unordered,303 .atomic_store_unordered,
303 .atomic_store_monotonic,304 .atomic_store_monotonic,
304 .atomic_store_release,305 .atomic_store_release,
305 .atomic_store_seq_cst,306 .atomic_store_seq_cst,
306 .set_union_tag,307 .set_union_tag,
308 .memset,
309 .memset_safe,
310 .memcpy,
307 => {311 => {
308 const o = air_datas[inst].bin_op;312 const o = air_datas[inst].bin_op;
309 if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);313 if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
...@@ -597,16 +601,6 @@ pub fn categorizeOperand(...@@ -597,16 +601,6 @@ pub fn categorizeOperand(
597 if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write);601 if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write);
598 return .write;602 return .write;
599 },603 },
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
611 .br => {605 .br => {
612 const br = air_datas[inst].br;606 const br = air_datas[inst].br;
...@@ -972,6 +966,7 @@ fn analyzeInst(...@@ -972,6 +966,7 @@ fn analyzeInst(
972 .bool_and,966 .bool_and,
973 .bool_or,967 .bool_or,
974 .store,968 .store,
969 .store_safe,
975 .array_elem_val,970 .array_elem_val,
976 .slice_elem_val,971 .slice_elem_val,
977 .ptr_elem_val,972 .ptr_elem_val,
...@@ -987,6 +982,9 @@ fn analyzeInst(...@@ -987,6 +982,9 @@ fn analyzeInst(
987 .set_union_tag,982 .set_union_tag,
988 .min,983 .min,
989 .max,984 .max,
985 .memset,
986 .memset_safe,
987 .memcpy,
990 => {988 => {
991 const o = inst_datas[inst].bin_op;989 const o = inst_datas[inst].bin_op;
992 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });990 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });
...@@ -1234,13 +1232,6 @@ fn analyzeInst(...@@ -1234,13 +1232,6 @@ fn analyzeInst(
1234 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;1232 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
1235 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.operand, .none });1233 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.operand, .none });
1236 },1234 },
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
1245 .br => return analyzeInstBr(a, pass, data, inst),1236 .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 {...@@ -239,6 +239,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
239 .bool_and,239 .bool_and,
240 .bool_or,240 .bool_or,
241 .store,241 .store,
242 .store_safe,
242 .array_elem_val,243 .array_elem_val,
243 .slice_elem_val,244 .slice_elem_val,
244 .ptr_elem_val,245 .ptr_elem_val,
...@@ -254,6 +255,9 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -254,6 +255,9 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
254 .set_union_tag,255 .set_union_tag,
255 .min,256 .min,
256 .max,257 .max,
258 .memset,
259 .memset_safe,
260 .memcpy,
257 => {261 => {
258 const bin_op = data[inst].bin_op;262 const bin_op = data[inst].bin_op;
259 try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });263 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 {...@@ -306,13 +310,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
306 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;310 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;
307 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });311 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });
308 },312 },
309 .memset,
310 .memcpy,
311 => {
312 const pl_op = data[inst].pl_op;
313 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
314 try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
315 },
316 .cmpxchg_strong,313 .cmpxchg_strong,
317 .cmpxchg_weak,314 .cmpxchg_weak,
318 => {315 => {
src/Sema.zig+303-75
...@@ -2500,7 +2500,7 @@ fn coerceResultPtr(...@@ -2500,7 +2500,7 @@ fn coerceResultPtr(
25002500
2501 // The last one is always `store`.2501 // The last one is always `store`.
2502 const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];2502 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) {
2504 // no store instruction is generated for zero sized types2504 // no store instruction is generated for zero sized types
2505 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);2505 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);
2506 } else {2506 } else {
...@@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
3386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;3386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3387 const src = inst_data.src();3387 const src = inst_data.src();
3388 const object = try sema.resolveInst(inst_data.operand);3388 const object = try sema.resolveInst(inst_data.operand);
3389 const object_ty = sema.typeOf(object);
3390
3391 const is_pointer_to = object_ty.isSinglePointer();
33923389
3393 const array_ty = if (is_pointer_to)3390 return indexablePtrLen(sema, block, src, object);
3394 object_ty.childType()3391}
3395 else
3396 object_ty;
33973392
3393fn indexablePtrLen(
3394 sema: *Sema,
3395 block: *Block,
3396 src: LazySrcLoc,
3397 object: Air.Inst.Ref,
3398) CompileError!Air.Inst.Ref {
3399 const object_ty = sema.typeOf(object);
3400 const is_pointer_to = object_ty.isSinglePointer();
3401 const array_ty = if (is_pointer_to) object_ty.childType() else object_ty;
3398 try checkIndexable(sema, block, src, array_ty);3402 try checkIndexable(sema, block, src, array_ty);
3403 return sema.fieldVal(block, src, object, "len", src);
3404}
33993405
3406fn indexablePtrLenOrNone(
3407 sema: *Sema,
3408 block: *Block,
3409 src: LazySrcLoc,
3410 object: Air.Inst.Ref,
3411) CompileError!Air.Inst.Ref {
3412 const object_ty = sema.typeOf(object);
3413 const array_ty = t: {
3414 const ptr_size = object_ty.ptrSizeOrNull() orelse break :t object_ty;
3415 break :t switch (ptr_size) {
3416 .Many => return .none,
3417 .One => object_ty.childType(),
3418 else => object_ty,
3419 };
3420 };
3421 try checkIndexable(sema, block, src, array_ty);
3400 return sema.fieldVal(block, src, object, "len", src);3422 return sema.fieldVal(block, src, object, "len", src);
3401}3423}
34023424
...@@ -3502,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -3502,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
3502 const candidate = block.instructions.items[search_index];3524 const candidate = block.instructions.items[search_index];
3503 switch (air_tags[candidate]) {3525 switch (air_tags[candidate]) {
3504 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,3526 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3505 .store => break candidate,3527 .store, .store_safe => break candidate,
3506 else => break :ct,3528 else => break :ct,
3507 }3529 }
3508 };3530 };
...@@ -3728,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3728,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3728 const candidate = block.instructions.items[search_index];3750 const candidate = block.instructions.items[search_index];
3729 switch (air_tags[candidate]) {3751 switch (air_tags[candidate]) {
3730 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,3752 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3731 .store => break candidate,3753 .store, .store_safe => break candidate,
3732 else => break :ct,3754 else => break :ct,
3733 }3755 }
3734 };3756 };
...@@ -3838,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3838,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3838 assert(replacement_block.instructions.items.len > 0);3860 assert(replacement_block.instructions.items.len > 0);
3839 break :result sub_ptr;3861 break :result sub_ptr;
3840 },3862 },
3841 .store => result: {3863 .store, .store_safe => result: {
3842 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;3864 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;
3843 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);3865 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);
3844 break :result .void_value;3866 break :result .void_value;
...@@ -4220,7 +4242,10 @@ fn validateUnionInit(...@@ -4220,7 +4242,10 @@ fn validateUnionInit(
4220 while (block_index > 0) : (block_index -= 1) {4242 while (block_index > 0) : (block_index -= 1) {
4221 const store_inst = block.instructions.items[block_index];4243 const store_inst = block.instructions.items[block_index];
4222 if (store_inst == field_ptr_air_inst) break;4244 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 }
4224 const bin_op = air_datas[store_inst].bin_op;4249 const bin_op = air_datas[store_inst].bin_op;
4225 var lhs = bin_op.lhs;4250 var lhs = bin_op.lhs;
4226 if (Air.refToIndex(lhs)) |lhs_index| {4251 if (Air.refToIndex(lhs)) |lhs_index| {
...@@ -4432,7 +4457,10 @@ fn validateStructInit(...@@ -4432,7 +4457,10 @@ fn validateStructInit(
4432 struct_is_comptime = false;4457 struct_is_comptime = false;
4433 continue :field;4458 continue :field;
4434 }4459 }
4435 if (air_tags[store_inst] != .store) continue;4460 switch (air_tags[store_inst]) {
4461 .store, .store_safe => {},
4462 else => continue,
4463 }
4436 const bin_op = air_datas[store_inst].bin_op;4464 const bin_op = air_datas[store_inst].bin_op;
4437 var lhs = bin_op.lhs;4465 var lhs = bin_op.lhs;
4438 {4466 {
...@@ -4660,7 +4688,10 @@ fn zirValidateArrayInit(...@@ -4660,7 +4688,10 @@ fn zirValidateArrayInit(
4660 array_is_comptime = false;4688 array_is_comptime = false;
4661 continue :outer;4689 continue :outer;
4662 }4690 }
4663 if (air_tags[store_inst] != .store) continue;4691 switch (air_tags[store_inst]) {
4692 .store, .store_safe => {},
4693 else => continue,
4694 }
4664 const bin_op = air_datas[store_inst].bin_op;4695 const bin_op = air_datas[store_inst].bin_op;
4665 var lhs = bin_op.lhs;4696 var lhs = bin_op.lhs;
4666 {4697 {
...@@ -5003,7 +5034,12 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v...@@ -5003,7 +5034,12 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
50035034
5004 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };5035 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };
5005 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };5036 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;
5007 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);5043 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
5008}5044}
50095045
...@@ -9861,8 +9897,11 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -9861,8 +9897,11 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
9861 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;9897 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
9862 const array_ptr = try sema.resolveInst(extra.lhs);9898 const array_ptr = try sema.resolveInst(extra.lhs);
9863 const start = try sema.resolveInst(extra.start);9899 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);
9866}9905}
98679906
9868fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9907fn 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...@@ -9875,8 +9914,11 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
9875 const array_ptr = try sema.resolveInst(extra.lhs);9914 const array_ptr = try sema.resolveInst(extra.lhs);
9876 const start = try sema.resolveInst(extra.start);9915 const start = try sema.resolveInst(extra.start);
9877 const end = try sema.resolveInst(extra.end);9916 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);
9880}9922}
98819923
9882fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9924fn 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...@@ -9891,8 +9933,11 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
9891 const start = try sema.resolveInst(extra.start);9933 const start = try sema.resolveInst(extra.start);
9892 const end = try sema.resolveInst(extra.end);9934 const end = try sema.resolveInst(extra.end);
9893 const sentinel = try sema.resolveInst(extra.sentinel);9935 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);
9896}9941}
98979942
9898fn zirSwitchCapture(9943fn zirSwitchCapture(
...@@ -21748,90 +21793,270 @@ fn analyzeMinMax(...@@ -21748,90 +21793,270 @@ fn analyzeMinMax(
21748 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);21793 return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs);
21749}21794}
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
21751fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {21819fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
21752 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;21820 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;
21754 const src = inst_data.src();21822 const src = inst_data.src();
21755 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };21823 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
21756 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };21824 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 };21825 const dest_ptr = try sema.resolveInst(extra.lhs);
21758 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);21826 const src_ptr = try sema.resolveInst(extra.rhs);
2175921827 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
21760 // TODO AstGen's coerced_ty cannot handle volatile here21828 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
21761 var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data;21829 const target = sema.mod.getTarget();
21762 dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr();21830
21763 const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info);21831 if (dest_len == .none and src_len == .none) {
21764 const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src);21832 const msg = msg: {
2176521833 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{});
21766 const uncasted_src_ptr = try sema.resolveInst(extra.source);21834 errdefer msg.destroy(sema.gpa);
21767 var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data;21835 try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{
21768 src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr();21836 sema.typeOf(dest_ptr).fmt(sema.mod),
21769 const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info);21837 });
21770 const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src);21838 try sema.errNote(block, src_src, msg, "source type {} provides no length", .{
21771 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);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
21773 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {21879 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
21774 if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;21880 if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;
21775 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| {21881 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| {
21776 if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src;21882 const len_u64 = (try len_val.?.getUnsignedIntAdvanced(target, sema)).?;
21777 if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {21883 const len = try sema.usizeCast(block, dest_src, len_u64);
21778 _ = len_val;21884 for (0..len) |i| {
21779 return sema.fail(block, src, "TODO: Sema.zirMemcpy at comptime", .{});21885 const elem_index = try sema.addIntUnsigned(Type.usize, i);
21780 } else break :rs len_src;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;
21781 } else break :rs src_src;21916 } else break :rs src_src;
21782 } else dest_src;21917 } 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
21784 try sema.requireRuntimeBlock(block, src, runtime_src);21962 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
21785 _ = try block.addInst(.{21991 _ = try block.addInst(.{
21786 .tag = .memcpy,21992 .tag = .memcpy,
21787 .data = .{ .pl_op = .{21993 .data = .{ .bin_op = .{
21788 .operand = dest_ptr,21994 .lhs = new_dest_ptr,
21789 .payload = try sema.addExtra(Air.Bin{21995 .rhs = new_src_ptr,
21790 .lhs = src_ptr,
21791 .rhs = len,
21792 }),
21793 } },21996 } },
21794 });21997 });
21795}21998}
2179621999
21797fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {22000fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
21798 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;22001 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;
21800 const src = inst_data.src();22003 const src = inst_data.src();
21801 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };22004 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
21802 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };22005 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 };22006 const dest_ptr = try sema.resolveInst(extra.lhs);
21804 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);22007 const uncoerced_elem = try sema.resolveInst(extra.rhs);
2180522008 const dest_ptr_ty = sema.typeOf(dest_ptr);
21806 // TODO AstGen's coerced_ty cannot handle volatile here22009 try checkIndexable(sema, block, dest_src, dest_ptr_ty);
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);
2181122010
21812 const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src);22011 const dest_elem_ty = dest_ptr_ty.elemType2();
21813 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);22012 const target = sema.mod.getTarget();
2181422013
21815 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: {22014 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
21816 if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src;22025 if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src;
21817 if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {22026 if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| {
21818 if (try sema.resolveMaybeUndefVal(value)) |val| {22027 for (0..len) |i| {
21819 _ = len_val;22028 const elem_index = try sema.addIntUnsigned(Type.usize, i);
21820 _ = val;22029 const elem_ptr = try sema.elemPtr(
21821 return sema.fail(block, src, "TODO: Sema.zirMemset at comptime", .{});22030 block,
21822 } else break :rs value_src;22031 src,
21823 } else break :rs len_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;
21824 } else dest_src;22050 } else dest_src;
2182522051
22052 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
22053
21826 try sema.requireRuntimeBlock(block, src, runtime_src);22054 try sema.requireRuntimeBlock(block, src, runtime_src);
21827 _ = try block.addInst(.{22055 _ = try block.addInst(.{
21828 .tag = .memset,22056 .tag = if (block.wantSafety()) .memset_safe else .memset,
21829 .data = .{ .pl_op = .{22057 .data = .{ .bin_op = .{
21830 .operand = dest_ptr,22058 .lhs = dest_ptr,
21831 .payload = try sema.addExtra(Air.Bin{22059 .rhs = elem,
21832 .lhs = value,
21833 .rhs = len,
21834 }),
21835 } },22060 } },
21836 });22061 });
21837}22062}
...@@ -22948,6 +23173,8 @@ pub const PanicId = enum {...@@ -22948,6 +23173,8 @@ pub const PanicId = enum {
22948 index_out_of_bounds,23173 index_out_of_bounds,
22949 start_index_greater_than_end,23174 start_index_greater_than_end,
22950 for_len_mismatch,23175 for_len_mismatch,
23176 memcpy_len_mismatch,
23177 memcpy_alias,
22951};23178};
2295223179
22953fn addSafetyCheck(23180fn addSafetyCheck(
...@@ -26521,7 +26748,8 @@ fn storePtr(...@@ -26521,7 +26748,8 @@ fn storePtr(
26521 ptr: Air.Inst.Ref,26748 ptr: Air.Inst.Ref,
26522 uncasted_operand: Air.Inst.Ref,26749 uncasted_operand: Air.Inst.Ref,
26523) CompileError!void {26750) 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);
26525}26753}
2652626754
26527fn storePtr2(26755fn storePtr2(
...@@ -28768,10 +28996,10 @@ fn analyzeSlice(...@@ -28768,10 +28996,10 @@ fn analyzeSlice(
28768 uncasted_end_opt: Air.Inst.Ref,28996 uncasted_end_opt: Air.Inst.Ref,
28769 sentinel_opt: Air.Inst.Ref,28997 sentinel_opt: Air.Inst.Ref,
28770 sentinel_src: LazySrcLoc,28998 sentinel_src: LazySrcLoc,
28999 ptr_src: LazySrcLoc,
29000 start_src: LazySrcLoc,
29001 end_src: LazySrcLoc,
28771) CompileError!Air.Inst.Ref {29002) 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 };
28775 // Slice expressions can operate on a variable whose type is an array. This requires29003 // Slice expressions can operate on a variable whose type is an array. This requires
28776 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.29004 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
28777 const ptr_ptr_ty = sema.typeOf(ptr_ptr);29005 const ptr_ptr_ty = sema.typeOf(ptr_ptr);
src/Zir.zig+2-14
...@@ -922,10 +922,10 @@ pub const Inst = struct {...@@ -922,10 +922,10 @@ pub const Inst = struct {
922 /// Uses the `pl_node` union field with payload `FieldParentPtr`.922 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
923 field_parent_ptr,923 field_parent_ptr,
924 /// Implements the `@memcpy` builtin.924 /// Implements the `@memcpy` builtin.
925 /// Uses the `pl_node` union field with payload `Memcpy`.925 /// Uses the `pl_node` union field with payload `Bin`.
926 memcpy,926 memcpy,
927 /// Implements the `@memset` builtin.927 /// Implements the `@memset` builtin.
928 /// Uses the `pl_node` union field with payload `Memset`.928 /// Uses the `pl_node` union field with payload `Bin`.
929 memset,929 memset,
930 /// Implements the `@min` builtin.930 /// Implements the `@min` builtin.
931 /// Uses the `pl_node` union field with payload `Bin`931 /// Uses the `pl_node` union field with payload `Bin`
...@@ -3501,18 +3501,6 @@ pub const Inst = struct {...@@ -3501,18 +3501,6 @@ pub const Inst = struct {
3501 field_ptr: Ref,3501 field_ptr: Ref,
3502 };3502 };
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
3516 pub const Shuffle = struct {3504 pub const Shuffle = struct {
3517 elem_type: Ref,3505 elem_type: Ref,
3518 a: Ref,3506 a: Ref,
src/arch/aarch64/CodeGen.zig+16-4
...@@ -764,7 +764,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -764,7 +764,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
764 .ptrtoint => try self.airPtrToInt(inst),764 .ptrtoint => try self.airPtrToInt(inst),
765 .ret => try self.airRet(inst),765 .ret => try self.airRet(inst),
766 .ret_load => try self.airRetLoad(inst),766 .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),
768 .struct_field_ptr=> try self.airStructFieldPtr(inst),769 .struct_field_ptr=> try self.airStructFieldPtr(inst),
769 .struct_field_val=> try self.airStructFieldVal(inst),770 .struct_field_val=> try self.airStructFieldVal(inst),
770 .array_to_slice => try self.airArrayToSlice(inst),771 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -775,7 +776,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -775,7 +776,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
775 .atomic_rmw => try self.airAtomicRmw(inst),776 .atomic_rmw => try self.airAtomicRmw(inst),
776 .atomic_load => try self.airAtomicLoad(inst),777 .atomic_load => try self.airAtomicLoad(inst),
777 .memcpy => try self.airMemcpy(inst),778 .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),
779 .set_union_tag => try self.airSetUnionTag(inst),781 .set_union_tag => try self.airSetUnionTag(inst),
780 .get_union_tag => try self.airGetUnionTag(inst),782 .get_union_tag => try self.airGetUnionTag(inst),
781 .clz => try self.airClz(inst),783 .clz => try self.airClz(inst),
...@@ -4035,7 +4037,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4035,7 +4037,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4035 }4037 }
4036}4038}
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 }
4039 const bin_op = self.air.instructions.items(.data)[inst].bin_op;4046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4040 const ptr = try self.resolveInst(bin_op.lhs);4047 const ptr = try self.resolveInst(bin_op.lhs);
4041 const value = try self.resolveInst(bin_op.rhs);4048 const value = try self.resolveInst(bin_op.rhs);
...@@ -5975,8 +5982,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr...@@ -5975,8 +5982,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
5975 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});5982 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
5976}5983}
59775984
5978fn airMemset(self: *Self, inst: Air.Inst.Index) !void {5985fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
5979 _ = inst;5986 _ = 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 }
5980 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});5992 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
5981}5993}
59825994
src/arch/arm/CodeGen.zig+16-4
...@@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
748 .ptrtoint => try self.airPtrToInt(inst),748 .ptrtoint => try self.airPtrToInt(inst),
749 .ret => try self.airRet(inst),749 .ret => try self.airRet(inst),
750 .ret_load => try self.airRetLoad(inst),750 .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),
752 .struct_field_ptr=> try self.airStructFieldPtr(inst),753 .struct_field_ptr=> try self.airStructFieldPtr(inst),
753 .struct_field_val=> try self.airStructFieldVal(inst),754 .struct_field_val=> try self.airStructFieldVal(inst),
754 .array_to_slice => try self.airArrayToSlice(inst),755 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -759,7 +760,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -759,7 +760,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
759 .atomic_rmw => try self.airAtomicRmw(inst),760 .atomic_rmw => try self.airAtomicRmw(inst),
760 .atomic_load => try self.airAtomicLoad(inst),761 .atomic_load => try self.airAtomicLoad(inst),
761 .memcpy => try self.airMemcpy(inst),762 .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),
763 .set_union_tag => try self.airSetUnionTag(inst),765 .set_union_tag => try self.airSetUnionTag(inst),
764 .get_union_tag => try self.airGetUnionTag(inst),766 .get_union_tag => try self.airGetUnionTag(inst),
765 .clz => try self.airClz(inst),767 .clz => try self.airClz(inst),
...@@ -2835,7 +2837,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2835,7 +2837,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2835 }2837 }
2836}2838}
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 }
2839 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2846 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2840 const ptr = try self.resolveInst(bin_op.lhs);2847 const ptr = try self.resolveInst(bin_op.lhs);
2841 const value = try self.resolveInst(bin_op.rhs);2848 const value = try self.resolveInst(bin_op.rhs);
...@@ -5921,7 +5928,12 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr...@@ -5921,7 +5928,12 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
5921 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});5928 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
5922}5929}
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 }
5925 _ = inst;5937 _ = inst;
5926 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});5938 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
5927}5939}
src/arch/riscv64/CodeGen.zig+16-4
...@@ -578,7 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -578,7 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
578 .ptrtoint => try self.airPtrToInt(inst),578 .ptrtoint => try self.airPtrToInt(inst),
579 .ret => try self.airRet(inst),579 .ret => try self.airRet(inst),
580 .ret_load => try self.airRetLoad(inst),580 .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),
582 .struct_field_ptr=> try self.airStructFieldPtr(inst),583 .struct_field_ptr=> try self.airStructFieldPtr(inst),
583 .struct_field_val=> try self.airStructFieldVal(inst),584 .struct_field_val=> try self.airStructFieldVal(inst),
584 .array_to_slice => try self.airArrayToSlice(inst),585 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -589,7 +590,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -589,7 +590,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
589 .atomic_rmw => try self.airAtomicRmw(inst),590 .atomic_rmw => try self.airAtomicRmw(inst),
590 .atomic_load => try self.airAtomicLoad(inst),591 .atomic_load => try self.airAtomicLoad(inst),
591 .memcpy => try self.airMemcpy(inst),592 .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),
593 .set_union_tag => try self.airSetUnionTag(inst),595 .set_union_tag => try self.airSetUnionTag(inst),
594 .get_union_tag => try self.airGetUnionTag(inst),596 .get_union_tag => try self.airGetUnionTag(inst),
595 .clz => try self.airClz(inst),597 .clz => try self.airClz(inst),
...@@ -1572,7 +1574,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1572,7 +1574,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1572 }1574 }
1573}1575}
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 }
1576 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1583 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1577 const ptr = try self.resolveInst(bin_op.lhs);1584 const ptr = try self.resolveInst(bin_op.lhs);
1578 const value = try self.resolveInst(bin_op.rhs);1585 const value = try self.resolveInst(bin_op.rhs);
...@@ -2421,8 +2428,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr...@@ -2421,8 +2428,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
2421 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});2428 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
2422}2429}
24232430
2424fn airMemset(self: *Self, inst: Air.Inst.Index) !void {2431fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2425 _ = inst;2432 _ = 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 }
2426 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});2438 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
2427}2439}
24282440
src/arch/sparc64/CodeGen.zig+16-4
...@@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
593 .ptrtoint => try self.airPtrToInt(inst),593 .ptrtoint => try self.airPtrToInt(inst),
594 .ret => try self.airRet(inst),594 .ret => try self.airRet(inst),
595 .ret_load => try self.airRetLoad(inst),595 .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),
597 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),598 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
598 .struct_field_val=> try self.airStructFieldVal(inst),599 .struct_field_val=> try self.airStructFieldVal(inst),
599 .array_to_slice => try self.airArrayToSlice(inst),600 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -605,7 +606,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -605,7 +606,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
605 .atomic_rmw => try self.airAtomicRmw(inst),606 .atomic_rmw => try self.airAtomicRmw(inst),
606 .atomic_load => try self.airAtomicLoad(inst),607 .atomic_load => try self.airAtomicLoad(inst),
607 .memcpy => @panic("TODO try self.airMemcpy(inst)"),608 .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),
609 .set_union_tag => try self.airSetUnionTag(inst),611 .set_union_tag => try self.airSetUnionTag(inst),
610 .get_union_tag => try self.airGetUnionTag(inst),612 .get_union_tag => try self.airGetUnionTag(inst),
611 .clz => try self.airClz(inst),613 .clz => try self.airClz(inst),
...@@ -1764,7 +1766,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -1764,7 +1766,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
1764 return self.finishAirBookkeeping();1766 return self.finishAirBookkeeping();
1765}1767}
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 }
1768 const pl_op = self.air.instructions.items(.data)[inst].pl_op;1775 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1769 const extra = self.air.extraData(Air.Bin, pl_op.payload);1776 const extra = self.air.extraData(Air.Bin, pl_op.payload);
17701777
...@@ -2401,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {...@@ -2401,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
2401 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2408 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2402}2409}
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 }
2405 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2417 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2406 const ptr = try self.resolveInst(bin_op.lhs);2418 const ptr = try self.resolveInst(bin_op.lhs);
2407 const value = try self.resolveInst(bin_op.rhs);2419 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 {...@@ -1883,7 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18831883
1884 .load => func.airLoad(inst),1884 .load => func.airLoad(inst),
1885 .loop => func.airLoop(inst),1885 .loop => func.airLoop(inst),
1886 .memset => func.airMemset(inst),1886 .memset => func.airMemset(inst, false),
1887 .memset_safe => func.airMemset(inst, true),
1887 .not => func.airNot(inst),1888 .not => func.airNot(inst),
1888 .optional_payload => func.airOptionalPayload(inst),1889 .optional_payload => func.airOptionalPayload(inst),
1889 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),1890 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),
...@@ -1913,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1913,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1913 .slice_ptr => func.airSlicePtr(inst),1914 .slice_ptr => func.airSlicePtr(inst),
1914 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),1915 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),
1915 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),1916 .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
1918 .set_union_tag => func.airSetUnionTag(inst),1920 .set_union_tag => func.airSetUnionTag(inst),
1919 .struct_field_ptr => func.airStructFieldPtr(inst),1921 .struct_field_ptr => func.airStructFieldPtr(inst),
...@@ -2221,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2221,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2221 func.finishAir(inst, value, &.{});2223 func.finishAir(inst, value, &.{});
2222}2224}
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 }
2225 const bin_op = func.air.instructions.items(.data)[inst].bin_op;2232 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
22262233
2227 const lhs = try func.resolveInst(bin_op.lhs);2234 const lhs = try func.resolveInst(bin_op.lhs);
...@@ -4148,9 +4155,7 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4148,9 +4155,7 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4148 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4155 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
41494156
4150 const operand = try func.resolveInst(ty_op.operand);4157 const operand = try func.resolveInst(ty_op.operand);
4151 const len = try func.load(operand, Type.usize, func.ptrSize());4158 func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand});
4152 const result = try len.toLocal(func, Type.usize);
4153 func.finishAir(inst, result, &.{ty_op.operand});
4154}4159}
41554160
4156fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4161fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -4208,9 +4213,17 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4208,9 +4213,17 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4208fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4213fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4209 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4214 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4210 const operand = try func.resolveInst(ty_op.operand);4215 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 {
4211 const ptr = try func.load(operand, Type.usize, 0);4220 const ptr = try func.load(operand, Type.usize, 0);
4212 const result = try ptr.toLocal(func, Type.usize);4221 return ptr.toLocal(func, Type.usize);
4213 func.finishAir(inst, result, &.{ty_op.operand});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);
4214}4227}
42154228
4216fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4229fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -4274,8 +4287,10 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4274,8 +4287,10 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4274fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4287fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4275 const un_op = func.air.instructions.items(.data)[inst].un_op;4288 const un_op = func.air.instructions.items(.data)[inst].un_op;
4276 const operand = try func.resolveInst(un_op);4289 const operand = try func.resolveInst(un_op);
42774290 const ptr_ty = func.air.typeOf(un_op);
4278 const result = switch (operand) {4291 const result = if (ptr_ty.isSlice())
4292 try func.slicePtr(operand)
4293 else switch (operand) {
4279 // for stack offset, return a pointer to this offset.4294 // for stack offset, return a pointer to this offset.
4280 .stack_offset => try func.buildPointerOffset(operand, 0, .new),4295 .stack_offset => try func.buildPointerOffset(operand, 0, .new),
4281 else => func.reuseOperand(un_op, operand),4296 else => func.reuseOperand(un_op, operand),
...@@ -4375,16 +4390,25 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -4375,16 +4390,25 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4375 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });4390 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4376}4391}
43774392
4378fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4393fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4379 const pl_op = func.air.instructions.items(.data)[inst].pl_op;4394 if (safety) {
4380 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;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);4401 const ptr = try func.resolveInst(bin_op.lhs);
4383 const value = try func.resolveInst(bin_op.lhs);4402 const ptr_ty = func.air.typeOf(bin_op.lhs);
4384 const len = try func.resolveInst(bin_op.rhs);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 };
4385 try func.memset(ptr, len, value);4409 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 });
4388}4412}
43894413
4390/// Sets a region of memory at `ptr` to the value of `value`4414/// 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 {...@@ -5155,15 +5179,30 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5155 func.finishAir(inst, result, &.{extra.field_ptr});5179 func.finishAir(inst, result, &.{extra.field_ptr});
5156}5180}
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
5158fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5190fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5159 const pl_op = func.air.instructions.items(.data)[inst].pl_op;5191 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5160 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;5192 const dst = try func.resolveInst(bin_op.lhs);
5161 const dst = try func.resolveInst(pl_op.operand);5193 const dst_ty = func.air.typeOf(bin_op.lhs);
5162 const src = try func.resolveInst(bin_op.lhs);5194 const src = try func.resolveInst(bin_op.rhs);
5163 const len = try func.resolveInst(bin_op.rhs);5195 const src_ty = func.air.typeOf(bin_op.rhs);
5164 try func.memcpy(dst, src, len);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 });
5167}5206}
51685207
5169fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5208fn 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 {...@@ -1035,7 +1035,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1035 .ptrtoint => try self.airPtrToInt(inst),1035 .ptrtoint => try self.airPtrToInt(inst),
1036 .ret => try self.airRet(inst),1036 .ret => try self.airRet(inst),
1037 .ret_load => try self.airRetLoad(inst),1037 .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),
1039 .struct_field_ptr=> try self.airStructFieldPtr(inst),1040 .struct_field_ptr=> try self.airStructFieldPtr(inst),
1040 .struct_field_val=> try self.airStructFieldVal(inst),1041 .struct_field_val=> try self.airStructFieldVal(inst),
1041 .array_to_slice => try self.airArrayToSlice(inst),1042 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -1046,7 +1047,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1046,7 +1047,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1046 .atomic_rmw => try self.airAtomicRmw(inst),1047 .atomic_rmw => try self.airAtomicRmw(inst),
1047 .atomic_load => try self.airAtomicLoad(inst),1048 .atomic_load => try self.airAtomicLoad(inst),
1048 .memcpy => try self.airMemcpy(inst),1049 .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),
1050 .set_union_tag => try self.airSetUnionTag(inst),1052 .set_union_tag => try self.airSetUnionTag(inst),
1051 .get_union_tag => try self.airGetUnionTag(inst),1053 .get_union_tag => try self.airGetUnionTag(inst),
1052 .clz => try self.airClz(inst),1054 .clz => try self.airClz(inst),
...@@ -3935,7 +3937,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3935,7 +3937,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3935 }3937 }
3936}3938}
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 }
3939 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3946 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3940 const ptr = try self.resolveInst(bin_op.lhs);3947 const ptr = try self.resolveInst(bin_op.lhs);
3941 const ptr_ty = self.air.typeOf(bin_op.lhs);3948 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...@@ -7678,6 +7685,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
7678fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {7685fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
7679 const un_op = self.air.instructions.items(.data)[inst].un_op;7686 const un_op = self.air.instructions.items(.data)[inst].un_op;
7680 const result = result: {7687 const result = result: {
7688 // TODO: handle case where the operand is a slice not a raw pointer
7681 const src_mcv = try self.resolveInst(un_op);7689 const src_mcv = try self.resolveInst(un_op);
7682 if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv;7690 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...@@ -8148,64 +8156,164 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
8148 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });8156 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
8149}8157}
81508158
8151fn airMemset(self: *Self, inst: Air.Inst.Index) !void {8159fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
8152 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8160 if (safety) {
8153 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;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);
8156 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {8170 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
8157 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8171 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8158 else => null,8172 else => null,
8159 };8173 };
8160 defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);8174 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);
8163 const src_val_lock: ?RegisterLock = switch (src_val) {8178 const src_val_lock: ?RegisterLock = switch (src_val) {
8164 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8179 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8165 else => null,8180 else => null,
8166 };8181 };
8167 defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock);8182 defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock);
81688183
8169 const len = try self.resolveInst(extra.rhs);8184 const elem_abi_size = @intCast(u31, elem_ty.abiSize(self.target.*));
8170 const len_lock: ?RegisterLock = switch (len) {8185
8171 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8186 if (elem_abi_size == 1) {
8172 else => null,8187 const ptr = switch (dst_ptr_ty.ptrSize()) {
8173 };8188 // TODO: this only handles slices stored in the stack
8174 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);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 });
8179}8282}
81808283
8181fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {8284fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
8182 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8285 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8183 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
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);
8186 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {8289 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
8187 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8290 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8188 else => null,8291 else => null,
8189 };8292 };
8190 defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);8293 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);
8193 const src_ptr_lock: ?RegisterLock = switch (src_ptr) {8296 const src_ptr_lock: ?RegisterLock = switch (src_ptr) {
8194 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8297 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8195 else => null,8298 else => null,
8196 };8299 };
8197 defer if (src_ptr_lock) |lock| self.register_manager.unlockReg(lock);8300 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 };
8200 const len_lock: ?RegisterLock = switch (len) {8307 const len_lock: ?RegisterLock = switch (len) {
8201 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8308 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8202 else => null,8309 else => null,
8203 };8310 };
8204 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);8311 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
8206 try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{});8314 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 });
8209}8317}
82108318
8211fn airTagName(self: *Self, inst: Air.Inst.Index) !void {8319fn 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,...@@ -2924,7 +2924,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
2924 .load => try airLoad(f, inst),2924 .load => try airLoad(f, inst),
2925 .ret => try airRet(f, inst, false),2925 .ret => try airRet(f, inst, false),
2926 .ret_load => try airRet(f, inst, true),2926 .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),
2928 .loop => try airLoop(f, inst),2929 .loop => try airLoop(f, inst),
2929 .cond_br => try airCondBr(f, inst),2930 .cond_br => try airCondBr(f, inst),
2930 .br => try airBr(f, inst),2931 .br => try airBr(f, inst),
...@@ -2935,7 +2936,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2935,7 +2936,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
2935 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),2936 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
2936 .atomic_rmw => try airAtomicRmw(f, inst),2937 .atomic_rmw => try airAtomicRmw(f, inst),
2937 .atomic_load => try airAtomicLoad(f, inst),2938 .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),
2939 .memcpy => try airMemcpy(f, inst),2941 .memcpy => try airMemcpy(f, inst),
2940 .set_union_tag => try airSetUnionTag(f, inst),2942 .set_union_tag => try airSetUnionTag(f, inst),
2941 .get_union_tag => try airGetUnionTag(f, inst),2943 .get_union_tag => try airGetUnionTag(f, inst),
...@@ -3574,19 +3576,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3574,19 +3576,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
3574 return local;3576 return local;
3575}3577}
35763578
3577fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {3579fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !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 {
3590 // *a = b;3580 // *a = b;
3591 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3581 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 {...@@ -3597,18 +3587,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3597 const ptr_val = try f.resolveInst(bin_op.lhs);3587 const ptr_val = try f.resolveInst(bin_op.lhs);
3598 const src_ty = f.air.typeOf(bin_op.rhs);3588 const src_ty = f.air.typeOf(bin_op.rhs);
35993589
3600 // TODO Sema should emit a different instruction when the store should3590 const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
3601 // possibly do the safety 0xaa bytes for undefined.3591
3602 const src_val_is_undefined =3592 if (val_is_undef) {
3603 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;3593 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3604 if (src_val_is_undefined) {3594 if (safety and ptr_info.host_size == 0) {
3605 if (ptr_info.host_size == 0) {3595 const writer = f.object.writer();
3606 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3596 try writer.writeAll("memset(");
3607 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);3597 try f.writeCValue(writer, ptr_val, .FunctionArgument);
3608 } else if (!f.wantSafety()) {3598 try writer.writeAll(", 0xaa, sizeof(");
3609 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3599 try f.renderType(writer, ptr_info.pointee_type);
3610 return .none;3600 try writer.writeAll("));\n");
3611 }3601 }
3602 return .none;
3612 }3603 }
36133604
3614 const target = f.object.dg.module.getTarget();3605 const target = f.object.dg.module.getTarget();
...@@ -3844,8 +3835,8 @@ fn airCmpOp(...@@ -3844,8 +3835,8 @@ fn airCmpOp(
3844 data: anytype,3835 data: anytype,
3845 operator: std.math.CompareOperator,3836 operator: std.math.CompareOperator,
3846) !CValue {3837) !CValue {
3847 const operand_ty = f.air.typeOf(data.lhs);3838 const lhs_ty = f.air.typeOf(data.lhs);
3848 const scalar_ty = operand_ty.scalarType();3839 const scalar_ty = lhs_ty.scalarType();
38493840
3850 const target = f.object.dg.module.getTarget();3841 const target = f.object.dg.module.getTarget();
3851 const scalar_bits = scalar_ty.bitSize(target);3842 const scalar_bits = scalar_ty.bitSize(target);
...@@ -3866,17 +3857,21 @@ fn airCmpOp(...@@ -3866,17 +3857,21 @@ fn airCmpOp(
3866 const rhs = try f.resolveInst(data.rhs);3857 const rhs = try f.resolveInst(data.rhs);
3867 try reap(f, inst, &.{ data.lhs, data.rhs });3858 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();
3869 const writer = f.object.writer();3862 const writer = f.object.writer();
3870 const local = try f.allocLocal(inst, inst_ty);3863 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);
3872 try f.writeCValue(writer, local, .Other);3865 try f.writeCValue(writer, local, .Other);
3873 try v.elem(f, writer);3866 try v.elem(f, writer);
3874 try writer.writeAll(" = ");3867 try writer.writeAll(" = ");
3868 if (need_cast) try writer.writeAll("(void*)");
3875 try f.writeCValue(writer, lhs, .Other);3869 try f.writeCValue(writer, lhs, .Other);
3876 try v.elem(f, writer);3870 try v.elem(f, writer);
3877 try writer.writeByte(' ');3871 try writer.writeByte(' ');
3878 try writer.writeAll(compareOperatorC(operator));3872 try writer.writeAll(compareOperatorC(operator));
3879 try writer.writeByte(' ');3873 try writer.writeByte(' ');
3874 if (need_cast) try writer.writeAll("(void*)");
3880 try f.writeCValue(writer, rhs, .Other);3875 try f.writeCValue(writer, rhs, .Other);
3881 try v.elem(f, writer);3876 try v.elem(f, writer);
3882 try writer.writeAll(";\n");3877 try writer.writeAll(";\n");
...@@ -5784,6 +5779,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5784,6 +5779,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
5784 const un_op = f.air.instructions.items(.data)[inst].un_op;5779 const un_op = f.air.instructions.items(.data)[inst].un_op;
57855780
5786 const operand = try f.resolveInst(un_op);5781 const operand = try f.resolveInst(un_op);
5782 const operand_ty = f.air.typeOf(un_op);
5787 try reap(f, inst, &.{un_op});5783 try reap(f, inst, &.{un_op});
5788 const inst_ty = f.air.typeOfIndex(inst);5784 const inst_ty = f.air.typeOfIndex(inst);
5789 const writer = f.object.writer();5785 const writer = f.object.writer();
...@@ -5793,7 +5789,11 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5793,7 +5789,11 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
5793 try writer.writeAll(" = (");5789 try writer.writeAll(" = (");
5794 try f.renderType(writer, inst_ty);5790 try f.renderType(writer, inst_ty);
5795 try writer.writeByte(')');5791 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 }
5797 try writer.writeAll(";\n");5797 try writer.writeAll(";\n");
5798 return local;5798 return local;
5799}5799}
...@@ -6186,19 +6186,66 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa...@@ -6186,19 +6186,66 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
6186 return .none;6186 return .none;
6187}6187}
61886188
6189fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {6189fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void {
6190 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6190 if (ptr_ty.isSlice()) {
6191 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;6191 try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" });
6192 const dest_ty = f.air.typeOf(pl_op.operand);6192 } else {
6193 const dest_ptr = try f.resolveInst(pl_op.operand);6193 try f.writeCValue(writer, ptr, .FunctionArgument);
6194 const value = try f.resolveInst(extra.lhs);6194 }
6195 const len = try f.resolveInst(extra.rhs);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;
6197 const writer = f.object.writer();6206 const writer = f.object.writer();
6198 if (dest_ty.isVolatilePtr()) {6207
6199 var u8_ptr_pl = dest_ty.ptrInfo();6208 if (val_is_undef) {
6200 u8_ptr_pl.data.pointee_type = Type.u8;6209 if (!safety) {
6201 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);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
6202 const index = try f.allocLocal(inst, Type.usize);6249 const index = try f.allocLocal(inst, Type.usize);
62036250
6204 try writer.writeAll("for (");6251 try writer.writeAll("for (");
...@@ -6208,56 +6255,95 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6208,56 +6255,95 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
6208 try writer.writeAll("; ");6255 try writer.writeAll("; ");
6209 try f.writeCValue(writer, index, .Other);6256 try f.writeCValue(writer, index, .Other);
6210 try writer.writeAll(" != ");6257 try writer.writeAll(" != ");
6211 try f.writeCValue(writer, len, .Other);6258 switch (dest_ty.ptrSize()) {
6212 try writer.writeAll("; ");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("; ++");
6213 try f.writeCValue(writer, index, .Other);6269 try f.writeCValue(writer, index, .Other);
6214 try writer.writeAll(" += ");
6215 try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);
6216 try writer.writeAll(") ((");6270 try writer.writeAll(") ((");
6217 try f.renderType(writer, u8_ptr_ty);6271 try f.renderType(writer, elem_ptr_ty);
6218 try writer.writeByte(')');6272 try writer.writeByte(')');
6219 try f.writeCValue(writer, dest_ptr, .FunctionArgument);6273 try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
6220 try writer.writeAll(")[");6274 try writer.writeAll(")[");
6221 try f.writeCValue(writer, index, .Other);6275 try f.writeCValue(writer, index, .Other);
6222 try writer.writeAll("] = ");6276 try writer.writeAll("] = ");
6223 try f.writeCValue(writer, value, .FunctionArgument);6277 try f.writeCValue(writer, value, .FunctionArgument);
6224 try writer.writeAll(";\n");6278 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 });
6227 try freeLocal(f, inst, index.new_local, 0);6281 try freeLocal(f, inst, index.new_local, 0);
62286282
6229 return .none;6283 return .none;
6230 }6284 }
62316285
6232 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6233 try writer.writeAll("memset(");6286 try writer.writeAll("memset(");
6234 try f.writeCValue(writer, dest_ptr, .FunctionArgument);6287 switch (dest_ty.ptrSize()) {
6235 try writer.writeAll(", ");6288 .Slice => {
6236 try f.writeCValue(writer, value, .FunctionArgument);6289 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
6237 try writer.writeAll(", ");6290 try writer.writeAll(", ");
6238 try f.writeCValue(writer, len, .FunctionArgument);6291 try f.writeCValue(writer, value, .FunctionArgument);
6239 try writer.writeAll(");\n");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 });
6241 return .none;6308 return .none;
6242}6309}
62436310
6244fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {6311fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
6245 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6312 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
6246 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;6313 const dest_ptr = try f.resolveInst(bin_op.lhs);
6247 const dest_ptr = try f.resolveInst(pl_op.operand);6314 const src_ptr = try f.resolveInst(bin_op.rhs);
6248 const src_ptr = try f.resolveInst(extra.lhs);6315 const dest_ty = f.air.typeOf(bin_op.lhs);
6249 const len = try f.resolveInst(extra.rhs);6316 const src_ty = f.air.typeOf(bin_op.rhs);
6250 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });6317 const target = f.object.dg.module.getTarget();
6251 const writer = f.object.writer();6318 const writer = f.object.writer();
62526319
6253 try writer.writeAll("memcpy(");6320 try writer.writeAll("memcpy(");
6254 try f.writeCValue(writer, dest_ptr, .FunctionArgument);6321 try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
6255 try writer.writeAll(", ");6322 try writer.writeAll(", ");
6256 try f.writeCValue(writer, src_ptr, .FunctionArgument);6323 try writeSliceOrPtr(f, writer, src_ptr, src_ty);
6257 try writer.writeAll(", ");6324 try writer.writeAll(", ");
6258 try f.writeCValue(writer, len, .FunctionArgument);6325 switch (dest_ty.ptrSize()) {
6259 try writer.writeAll(");\n");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 });
6261 return .none;6347 return .none;
6262}6348}
62636349
src/codegen/llvm.zig+180-71
...@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {...@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {
4649 .not => try self.airNot(inst),4649 .not => try self.airNot(inst),
4650 .ret => try self.airRet(inst),4650 .ret => try self.airRet(inst),
4651 .ret_load => try self.airRetLoad(inst),4651 .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),
4653 .assembly => try self.airAssembly(inst),4654 .assembly => try self.airAssembly(inst),
4654 .slice_ptr => try self.airSliceField(inst, 0),4655 .slice_ptr => try self.airSliceField(inst, 0),
4655 .slice_len => try self.airSliceField(inst, 1),4656 .slice_len => try self.airSliceField(inst, 1),
...@@ -4672,7 +4673,8 @@ pub const FuncGen = struct {...@@ -4672,7 +4673,8 @@ pub const FuncGen = struct {
4672 .fence => try self.airFence(inst),4673 .fence => try self.airFence(inst),
4673 .atomic_rmw => try self.airAtomicRmw(inst),4674 .atomic_rmw => try self.airAtomicRmw(inst),
4674 .atomic_load => try self.airAtomicLoad(inst),4675 .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),
4676 .memcpy => try self.airMemcpy(inst),4678 .memcpy => try self.airMemcpy(inst),
4677 .set_union_tag => try self.airSetUnionTag(inst),4679 .set_union_tag => try self.airSetUnionTag(inst),
4678 .get_union_tag => try self.airGetUnionTag(inst),4680 .get_union_tag => try self.airGetUnionTag(inst),
...@@ -5776,6 +5778,36 @@ pub const FuncGen = struct {...@@ -5776,6 +5778,36 @@ pub const FuncGen = struct {
5776 return result;5778 return result;
5777 }5779 }
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
5779 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {5811 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
5780 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5812 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5781 const operand = try self.resolveInst(ty_op.operand);5813 const operand = try self.resolveInst(ty_op.operand);
...@@ -7261,39 +7293,53 @@ pub const FuncGen = struct {...@@ -7261,39 +7293,53 @@ pub const FuncGen = struct {
7261 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7293 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7262 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7294 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7263 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7295 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);
7265 const offset = try self.resolveInst(bin_op.rhs);7297 const offset = try self.resolveInst(bin_op.rhs);
7266 const ptr_ty = self.air.typeOf(bin_op.lhs);7298 const ptr_ty = self.air.typeOf(bin_op.lhs);
7267 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());7299 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7268 if (ptr_ty.ptrSize() == .One) {7300 switch (ptr_ty.ptrSize()) {
7269 // It's a pointer to an array, so according to LLVM we need an extra GEP index.7301 .One => {
7270 const indices: [2]*llvm.Value = .{7302 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7271 self.context.intType(32).constNull(), offset,7303 const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
7272 };7304 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7273 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");7305 },
7274 } else {7306 .C, .Many => {
7275 const indices: [1]*llvm.Value = .{offset};7307 const indices: [1]*llvm.Value = .{offset};
7276 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");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 },
7277 }7315 }
7278 }7316 }
72797317
7280 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7318 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7281 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7319 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7282 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7320 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);
7284 const offset = try self.resolveInst(bin_op.rhs);7322 const offset = try self.resolveInst(bin_op.rhs);
7285 const negative_offset = self.builder.buildNeg(offset, "");7323 const negative_offset = self.builder.buildNeg(offset, "");
7286 const ptr_ty = self.air.typeOf(bin_op.lhs);7324 const ptr_ty = self.air.typeOf(bin_op.lhs);
7287 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());7325 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7288 if (ptr_ty.ptrSize() == .One) {7326 switch (ptr_ty.ptrSize()) {
7289 // It's a pointer to an array, so according to LLVM we need an extra GEP index.7327 .One => {
7290 const indices: [2]*llvm.Value = .{7328 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7291 self.context.intType(32).constNull(), negative_offset,7329 const indices: [2]*llvm.Value = .{
7292 };7330 self.context.intType(32).constNull(), negative_offset,
7293 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");7331 };
7294 } else {7332 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7295 const indices: [1]*llvm.Value = .{negative_offset};7333 },
7296 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");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 },
7297 }7343 }
7298 }7344 }
72997345
...@@ -7887,8 +7933,10 @@ pub const FuncGen = struct {...@@ -7887,8 +7933,10 @@ pub const FuncGen = struct {
7887 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7933 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7888 const un_op = self.air.instructions.items(.data)[inst].un_op;7934 const un_op = self.air.instructions.items(.data)[inst].un_op;
7889 const operand = try self.resolveInst(un_op);7935 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);
7890 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));7938 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, "");
7892 }7940 }
78937941
7894 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7942 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
...@@ -8082,48 +8130,36 @@ pub const FuncGen = struct {...@@ -8082,48 +8130,36 @@ pub const FuncGen = struct {
8082 return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());8130 return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());
8083 }8131 }
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 {
8086 const bin_op = self.air.instructions.items(.data)[inst].bin_op;8134 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8087 const dest_ptr = try self.resolveInst(bin_op.lhs);8135 const dest_ptr = try self.resolveInst(bin_op.lhs);
8088 const ptr_ty = self.air.typeOf(bin_op.lhs);8136 const ptr_ty = self.air.typeOf(bin_op.lhs);
8089 const operand_ty = ptr_ty.childType();8137 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.
8093 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;8139 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
8094 if (val_is_undef) {8140 if (val_is_undef) {
8095 {8141 // Even if safety is disabled, we still emit a memset to undefined since it conveys
8096 // TODO let's handle this in AIR rather than by having each backend8142 // extra information to LLVM. However, safety makes the difference between using
8097 // check the optimization mode of the compilation because the plan is8143 // 0xaa or actual undefined for the fill byte.
8098 // to support setting the optimization mode at finer grained scopes8144 const u8_llvm_ty = self.context.intType(8);
8099 // which happens in Sema. Codegen should not be aware of this logic.8145 const fill_byte = if (safety)
8100 // I think this comment is basically the same as the other TODO comment just8146 u8_llvm_ty.constInt(0xaa, .False)
8101 // above but I'm leaving them both here to make it look super messy and8147 else
8102 // thereby bait contributors (or let's be honest, probably myself) into8148 u8_llvm_ty.getUndef();
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 }
8112 const target = self.dg.module.getTarget();8149 const target = self.dg.module.getTarget();
8113 const operand_size = operand_ty.abiSize(target);8150 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);
8117 const usize_llvm_ty = try self.dg.lowerType(Type.usize);8151 const usize_llvm_ty = try self.dg.lowerType(Type.usize);
8118 const len = usize_llvm_ty.constInt(operand_size, .False);8152 const len = usize_llvm_ty.constInt(operand_size, .False);
8119 _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());8153 const dest_ptr_align = ptr_ty.ptrAlignment(target);
8120 if (self.dg.module.comp.bin_file.options.valgrind) {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) {
8121 self.valgrindMarkUndef(dest_ptr, len);8156 self.valgrindMarkUndef(dest_ptr, len);
8122 }8157 }
8123 } else {8158 return null;
8124 const src_operand = try self.resolveInst(bin_op.rhs);
8125 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
8126 }8159 }
8160
8161 const src_operand = try self.resolveInst(bin_op.rhs);
8162 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
8127 return null;8163 return null;
8128 }8164 }
81298165
...@@ -8373,34 +8409,107 @@ pub const FuncGen = struct {...@@ -8373,34 +8409,107 @@ pub const FuncGen = struct {
8373 return null;8409 return null;
8374 }8410 }
83758411
8376 fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8412 fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value {
8377 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8413 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8378 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;8414 const dest_slice = try self.resolveInst(bin_op.lhs);
8379 const dest_ptr = try self.resolveInst(pl_op.operand);8415 const ptr_ty = self.air.typeOf(bin_op.lhs);
8380 const ptr_ty = self.air.typeOf(pl_op.operand);8416 const elem_ty = self.air.typeOf(bin_op.rhs);
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;
8386 const target = self.dg.module.getTarget();8417 const target = self.dg.module.getTarget();
8418 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
8387 const dest_ptr_align = ptr_ty.ptrAlignment(target);8419 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) {8443 if (elem_abi_size == 1) {
8391 self.valgrindMarkUndef(dest_ptr, len);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;
8392 }8449 }
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
8393 return null;8501 return null;
8394 }8502 }
83958503
8396 fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8504 fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8397 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8505 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8398 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;8506 const dest_slice = try self.resolveInst(bin_op.lhs);
8399 const dest_ptr = try self.resolveInst(pl_op.operand);8507 const dest_ptr_ty = self.air.typeOf(bin_op.lhs);
8400 const dest_ptr_ty = self.air.typeOf(pl_op.operand);8508 const src_slice = try self.resolveInst(bin_op.rhs);
8401 const src_ptr = try self.resolveInst(extra.lhs);8509 const src_ptr_ty = self.air.typeOf(bin_op.rhs);
8402 const src_ptr_ty = self.air.typeOf(extra.lhs);8510 const src_ptr = self.sliceOrArrayPtr(src_slice, src_ptr_ty);
8403 const len = try self.resolveInst(extra.rhs);8511 const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
8512 const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
8404 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();8513 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
8405 const target = self.dg.module.getTarget();8514 const target = self.dg.module.getTarget();
8406 _ = self.builder.buildMemCpy(8515 _ = self.builder.buildMemCpy(
src/print_air.zig+4-24
...@@ -140,6 +140,7 @@ const Writer = struct {...@@ -140,6 +140,7 @@ const Writer = struct {
140 .bool_and,140 .bool_and,
141 .bool_or,141 .bool_or,
142 .store,142 .store,
143 .store_safe,
143 .array_elem_val,144 .array_elem_val,
144 .slice_elem_val,145 .slice_elem_val,
145 .ptr_elem_val,146 .ptr_elem_val,
...@@ -169,6 +170,9 @@ const Writer = struct {...@@ -169,6 +170,9 @@ const Writer = struct {
169 .cmp_gte_optimized,170 .cmp_gte_optimized,
170 .cmp_gt_optimized,171 .cmp_gt_optimized,
171 .cmp_neq_optimized,172 .cmp_neq_optimized,
173 .memcpy,
174 .memset,
175 .memset_safe,
172 => try w.writeBinOp(s, inst),176 => try w.writeBinOp(s, inst),
173177
174 .is_null,178 .is_null,
...@@ -315,8 +319,6 @@ const Writer = struct {...@@ -315,8 +319,6 @@ const Writer = struct {
315 .atomic_store_release => try w.writeAtomicStore(s, inst, .Release),319 .atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
316 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),320 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),
317 .atomic_rmw => try w.writeAtomicRmw(s, inst),321 .atomic_rmw => try w.writeAtomicRmw(s, inst),
318 .memcpy => try w.writeMemcpy(s, inst),
319 .memset => try w.writeMemset(s, inst),
320 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),322 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
321 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),323 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
322 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),324 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
...@@ -591,17 +593,6 @@ const Writer = struct {...@@ -591,17 +593,6 @@ const Writer = struct {
591 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });593 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
592 }594 }
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
605 fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {596 fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
606 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;597 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
607 const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;598 const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
...@@ -610,17 +601,6 @@ const Writer = struct {...@@ -610,17 +601,6 @@ const Writer = struct {
610 try s.print(", {d}", .{extra.field_index});601 try s.print(", {d}", .{extra.field_index});
611 }602 }
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
624 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {604 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
625 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;605 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
626 const val = w.air.values[ty_pl.payload];606 const val = w.air.values[ty_pl.payload];
src/print_zir.zig+2-28
...@@ -277,8 +277,6 @@ const Writer = struct {...@@ -277,8 +277,6 @@ const Writer = struct {
277 .atomic_load => try self.writeAtomicLoad(stream, inst),277 .atomic_load => try self.writeAtomicLoad(stream, inst),
278 .atomic_store => try self.writeAtomicStore(stream, inst),278 .atomic_store => try self.writeAtomicStore(stream, inst),
279 .atomic_rmw => try self.writeAtomicRmw(stream, inst),279 .atomic_rmw => try self.writeAtomicRmw(stream, inst),
280 .memcpy => try self.writeMemcpy(stream, inst),
281 .memset => try self.writeMemset(stream, inst),
282 .shuffle => try self.writeShuffle(stream, inst),280 .shuffle => try self.writeShuffle(stream, inst),
283 .mul_add => try self.writeMulAdd(stream, inst),281 .mul_add => try self.writeMulAdd(stream, inst),
284 .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),282 .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
...@@ -346,6 +344,8 @@ const Writer = struct {...@@ -346,6 +344,8 @@ const Writer = struct {
346 .vector_type,344 .vector_type,
347 .max,345 .max,
348 .min,346 .min,
347 .memcpy,
348 .memset,
349 .elem_ptr_node,349 .elem_ptr_node,
350 .elem_val_node,350 .elem_val_node,
351 .elem_ptr,351 .elem_ptr,
...@@ -1000,32 +1000,6 @@ const Writer = struct {...@@ -1000,32 +1000,6 @@ const Writer = struct {
1000 try self.writeSrc(stream, inst_data.src());1000 try self.writeSrc(stream, inst_data.src());
1001 }1001 }
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
1029 fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1003 fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1030 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1004 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1031 const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);1005 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 {...@@ -3843,9 +3843,14 @@ pub const Type = extern union {
3843 };3843 };
3844 }3844 }
38453845
3846 /// Asserts the `Type` is a pointer.3846 /// Asserts `ty` is a pointer.
3847 pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size {3847 pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size {
3848 return switch (self.tag()) {3848 return ptrSizeOrNull(ty).?;
3849 }
3850
3851 /// Returns `null` if `ty` is not a pointer.
3852 pub fn ptrSizeOrNull(ty: Type) ?std.builtin.Type.Pointer.Size {
3853 return switch (ty.tag()) {
3849 .const_slice,3854 .const_slice,
3850 .mut_slice,3855 .mut_slice,
3851 .const_slice_u8,3856 .const_slice_u8,
...@@ -3870,9 +3875,9 @@ pub const Type = extern union {...@@ -3870,9 +3875,9 @@ pub const Type = extern union {
3870 .inferred_alloc_mut,3875 .inferred_alloc_mut,
3871 => .One,3876 => .One,
38723877
3873 .pointer => self.castTag(.pointer).?.data.size,3878 .pointer => ty.castTag(.pointer).?.data.size,
38743879
3875 else => unreachable,3880 else => null,
3876 };3881 };
3877 }3882 }
38783883
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 {...@@ -353,22 +353,90 @@ fn f2(x: bool) []const u8 {
353 return (if (x) &fA else &fB)();353 return (if (x) &fA else &fB)();
354}354}
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
356test "memcpy and memset intrinsics" {425test "memcpy and memset intrinsics" {
357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
359 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO428 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
360429
361 try testMemcpyMemset();430 try testMemcpyMemset();
362 // TODO add comptime test coverage431 try comptime testMemcpyMemset();
363 //comptime try testMemcpyMemset();
364}432}
365433
366fn testMemcpyMemset() !void {434fn testMemcpyMemset() !void {
367 var foo: [20]u8 = undefined;435 var foo: [20]u8 = undefined;
368 var bar: [20]u8 = undefined;436 var bar: [20]u8 = undefined;
369437
370 @memset(&foo, 'A', foo.len);438 @memset(&foo, 'A');
371 @memcpy(&bar, &foo, bar.len);439 @memcpy(&bar, &foo);
372440
373 try expect(bar[0] == 'A');441 try expect(bar[0] == 'A');
374 try expect(bar[11] == 'A');442 try expect(bar[11] == 'A');
test/behavior/bugs/718.zig+1-1
...@@ -14,7 +14,7 @@ test "zero keys with @memset" {...@@ -14,7 +14,7 @@ test "zero keys with @memset" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO15 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);
18 try expect(!keys.up);18 try expect(!keys.up);
19 try expect(!keys.down);19 try expect(!keys.down);
20 try expect(!keys.left);20 try expect(!keys.left);
test/behavior/builtin_functions_returning_void_or_noreturn.zig+2-2
...@@ -17,8 +17,8 @@ test {...@@ -17,8 +17,8 @@ test {
17 try testing.expectEqual(void, @TypeOf(@breakpoint()));17 try testing.expectEqual(void, @TypeOf(@breakpoint()));
18 try testing.expectEqual({}, @export(x, .{ .name = "x" }));18 try testing.expectEqual({}, @export(x, .{ .name = "x" }));
19 try testing.expectEqual({}, @fence(.Acquire));19 try testing.expectEqual({}, @fence(.Acquire));
20 try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0));20 try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1)[0..0], @intToPtr([*]u8, 1)[0..0]));
21 try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0));21 try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1)[0..0], undefined));
22 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));22 try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
23 try testing.expectEqual({}, @prefetch(&val, .{}));23 try testing.expectEqual({}, @prefetch(&val, .{}));
24 try testing.expectEqual({}, @setAlignStack(16));24 try testing.expectEqual({}, @setAlignStack(16));
test/behavior/struct.zig+2-2
...@@ -91,7 +91,7 @@ test "structs" {...@@ -91,7 +91,7 @@ test "structs" {
91 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO91 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9292
93 var foo: StructFoo = undefined;93 var foo: StructFoo = undefined;
94 @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));94 @memset(@ptrCast([*]u8, &foo)[0..@sizeOf(StructFoo)], 0);
95 foo.a += 1;95 foo.a += 1;
96 foo.b = foo.a == 1;96 foo.b = foo.a == 1;
97 try testFoo(foo);97 try testFoo(foo);
...@@ -498,7 +498,7 @@ test "packed struct fields are ordered from LSB to MSB" {...@@ -498,7 +498,7 @@ test "packed struct fields are ordered from LSB to MSB" {
498498
499 var all: u64 = 0x7765443322221111;499 var all: u64 = 0x7765443322221111;
500 var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined;500 var bytes: [8]u8 align(@alignOf(Bitfields)) = undefined;
501 @memcpy(&bytes, @ptrCast([*]u8, &all), 8);501 @memcpy(bytes[0..8], @ptrCast([*]u8, &all));
502 var bitfields = @ptrCast(*Bitfields, &bytes).*;502 var bitfields = @ptrCast(*Bitfields, &bytes).*;
503503
504 try expect(bitfields.f1 == 0x1111);504 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 {...@@ -2,18 +2,35 @@ pub export fn entry() void {
2 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };2 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
3 var slice: []u8 = &buf;3 var slice: []u8 = &buf;
4 const a: u32 = 1234;4 const a: u32 = 1234;
5 @memcpy(slice, @ptrCast([*]const u8, &a), 4);5 @memcpy(slice.ptr, @ptrCast([*]const u8, &a));
6}6}
7pub export fn entry1() void {7pub export fn entry1() void {
8 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };8 var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
9 var ptr: *u8 = &buf[0];9 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);
11}21}
1222
13// error23// error
14// backend=stage224// backend=stage2
15// target=native25// target=native
16//26//
17// :5:13: error: expected type '[*]u8', found '[]u8'27// :5:5: error: unknown @memcpy length
18// :10:13: error: expected type '[*]u8', found '*u8'28// :5:18: note: destination type [*]u8 provides no length
19// :10:13: note: a single pointer cannot cast into a many pointer29// :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) {...@@ -15,7 +15,7 @@ const E = enum(u32) {
1515
16pub fn main() !void {16pub fn main() !void {
17 var e: E = undefined;17 var e: E = undefined;
18 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));18 @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55);
19 var n = @tagName(e);19 var n = @tagName(e);
20 _ = n;20 _ = n;
21 return error.TestFailed;21 return error.TestFailed;
test/cases/safety/@tagName on corrupted union value.zig +1-1
...@@ -15,7 +15,7 @@ const U = union(enum(u32)) {...@@ -15,7 +15,7 @@ const U = union(enum(u32)) {
1515
16pub fn main() !void {16pub fn main() !void {
17 var u: U = undefined;17 var u: U = undefined;
18 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));18 @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55);
19 var t: @typeInfo(U).Union.tag_type.? = u;19 var t: @typeInfo(U).Union.tag_type.? = u;
20 var n = @tagName(t);20 var n = @tagName(t);
21 _ = n;21 _ = 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) {...@@ -15,7 +15,7 @@ const E = enum(u32) {
1515
16pub fn main() !void {16pub fn main() !void {
17 var e: E = undefined;17 var e: E = undefined;
18 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));18 @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55);
19 switch (e) {19 switch (e) {
20 .X, .Y => @breakpoint(),20 .X, .Y => @breakpoint(),
21 }21 }
test/cases/safety/switch on corrupted union value.zig +1-1
...@@ -15,7 +15,7 @@ const U = union(enum(u32)) {...@@ -15,7 +15,7 @@ const U = union(enum(u32)) {
1515
16pub fn main() !void {16pub fn main() !void {
17 var u: U = undefined;17 var u: U = undefined;
18 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));18 @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55);
19 switch (u) {19 switch (u) {
20 .X, .Y => @breakpoint(),20 .X, .Y => @breakpoint(),
21 }21 }