| author | |
| committer | |
| log | 3c66850e4296ce2e0f9e0d25bc537aa489f4603e |
| tree | ae4b78d3e1ee15253ee353a8c9d972a1034f6fc6 |
| parent | d0311e28b397d173f0d60c403985047ec952a172 |
| parent | badad16f88ac7e1eb84eadf76e13b4dc346d4ced |
| signature |
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 | 8681 | {#header_close#} |
| 8682 | 8682 | |
| 8683 | 8683 | {#header_open|@memcpy#} |
| 8684 | <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize) void{#endsyntax#}</pre> | |
| 8685 | <p> | |
| 8686 | This function copies bytes from one region of memory to another. {#syntax#}dest{#endsyntax#} and | |
| 8687 | {#syntax#}source{#endsyntax#} are both pointers and must not overlap. | |
| 8688 | </p> | |
| 8689 | <p> | |
| 8690 | This function is a low level intrinsic with no safety mechanisms. Most code | |
| 8691 | should not use this function, instead using something like this: | |
| 8692 | </p> | |
| 8693 | <pre>{#syntax#}for (dest, source[0..byte_count]) |*d, s| d.* = s;{#endsyntax#}</pre> | |
| 8694 | <p> | |
| 8695 | The optimizer is intelligent enough to turn the above snippet into a memcpy. | |
| 8696 | </p> | |
| 8697 | <p>There is also a standard library function for this:</p> | |
| 8698 | <pre>{#syntax#}const mem = @import("std").mem; | |
| 8699 | mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre> | |
| 8684 | <pre>{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}</pre> | |
| 8685 | <p>This function copies bytes from one region of memory to another.</p> | |
| 8686 | <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or | |
| 8687 | a mutable many-item {#link|pointer|Pointers#}. It may have any | |
| 8688 | alignment, and it may have any element type.</p> | |
| 8689 | <p>Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a | |
| 8690 | mutable pointer to an array, or a mutable many-item | |
| 8691 | {#link|pointer|Pointers#}. It may have any alignment, and it may have any | |
| 8692 | element type.</p> | |
| 8693 | <p>The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#} | |
| 8694 | into the {#syntax#}dest{#endsyntax#} element type. The element types may have | |
| 8695 | different ABI size, however, that may incur a performance penalty.</p> | |
| 8696 | <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and | |
| 8697 | {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided, | |
| 8698 | they must be equal.</p> | |
| 8699 | <p>Finally, the two memory regions must not overlap.</p> | |
| 8700 | 8700 | {#header_close#} |
| 8701 | 8701 | |
| 8702 | 8702 | {#header_open|@memset#} |
| 8703 | <pre>{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize) void{#endsyntax#}</pre> | |
| 8704 | <p> | |
| 8705 | This function sets a region of memory to {#syntax#}c{#endsyntax#}. {#syntax#}dest{#endsyntax#} is a pointer. | |
| 8706 | </p> | |
| 8707 | <p> | |
| 8708 | This function is a low level intrinsic with no safety mechanisms. Most | |
| 8709 | code should not use this function, instead using something like this: | |
| 8710 | </p> | |
| 8711 | <pre>{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}</pre> | |
| 8712 | <p> | |
| 8713 | The optimizer is intelligent enough to turn the above snippet into a memset. | |
| 8714 | </p> | |
| 8715 | <p>There is also a standard library function for this:</p> | |
| 8716 | <pre>{#syntax#}const mem = @import("std").mem; | |
| 8717 | mem.set(u8, dest, c);{#endsyntax#}</pre> | |
| 8703 | <pre>{#syntax#}@memset(dest, elem) void{#endsyntax#}</pre> | |
| 8704 | <p>This function sets all the elements of a memory region to {#syntax#}elem{#endsyntax#}.</p> | |
| 8705 | <p>{#syntax#}dest{#endsyntax#} must be a mutable slice or a mutable pointer to an array. | |
| 8706 | It may have any alignment, and it may have any element type.</p> | |
| 8707 | <p>{#syntax#}elem{#endsyntax#} is coerced to the element type of {#syntax#}dest{#endsyntax#}.</p> | |
| 8718 | 8708 | {#header_close#} |
| 8719 | 8709 | |
| 8720 | 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 | 121 | _ = model; |
| 122 | 122 | var sl = spinlocks.get(@ptrToInt(src)); |
| 123 | 123 | defer sl.release(); |
| 124 | @memcpy(dest, src, size); | |
| 124 | @memcpy(dest[0..size], src); | |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | 127 | fn __atomic_store(size: u32, dest: [*]u8, src: [*]u8, model: i32) callconv(.C) void { |
| 128 | 128 | _ = model; |
| 129 | 129 | var sl = spinlocks.get(@ptrToInt(dest)); |
| 130 | 130 | defer sl.release(); |
| 131 | @memcpy(dest, src, size); | |
| 131 | @memcpy(dest[0..size], src); | |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | fn __atomic_exchange(size: u32, ptr: [*]u8, val: [*]u8, old: [*]u8, model: i32) callconv(.C) void { |
| 135 | 135 | _ = model; |
| 136 | 136 | var sl = spinlocks.get(@ptrToInt(ptr)); |
| 137 | 137 | defer sl.release(); |
| 138 | @memcpy(old, ptr, size); | |
| 139 | @memcpy(ptr, val, size); | |
| 138 | @memcpy(old[0..size], ptr); | |
| 139 | @memcpy(ptr[0..size], val); | |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | fn __atomic_compare_exchange( |
| ... | ... | @@ -155,10 +155,10 @@ fn __atomic_compare_exchange( |
| 155 | 155 | if (expected[i] != b) break; |
| 156 | 156 | } else { |
| 157 | 157 | // The two objects, ptr and expected, are equal |
| 158 | @memcpy(ptr, desired, size); | |
| 158 | @memcpy(ptr[0..size], desired); | |
| 159 | 159 | return 1; |
| 160 | 160 | } |
| 161 | @memcpy(expected, ptr, size); | |
| 161 | @memcpy(expected[0..size], ptr); | |
| 162 | 162 | return 0; |
| 163 | 163 | } |
| 164 | 164 |
lib/compiler_rt/emutls.zig+2-2| ... | ... | @@ -139,10 +139,10 @@ const ObjectArray = struct { |
| 139 | 139 | |
| 140 | 140 | if (control.default_value) |value| { |
| 141 | 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 | 143 | } else { |
| 144 | 144 | // no default: return zeroed memory. |
| 145 | @memset(data, 0, size); | |
| 145 | @memset(data[0..size], 0); | |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | 148 | self.slots[index] = @ptrCast(*anyopaque, data); |
lib/std/array_hash_map.zig+2-2| ... | ... | @@ -1893,7 +1893,7 @@ const IndexHeader = struct { |
| 1893 | 1893 | const index_size = hash_map.capacityIndexSize(new_bit_index); |
| 1894 | 1894 | const nbytes = @sizeOf(IndexHeader) + index_size * len; |
| 1895 | 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 | 1897 | const result = @ptrCast(*IndexHeader, bytes.ptr); |
| 1898 | 1898 | result.* = .{ |
| 1899 | 1899 | .bit_index = new_bit_index, |
| ... | ... | @@ -1914,7 +1914,7 @@ const IndexHeader = struct { |
| 1914 | 1914 | const index_size = hash_map.capacityIndexSize(header.bit_index); |
| 1915 | 1915 | const ptr = @ptrCast([*]align(@alignOf(IndexHeader)) u8, header); |
| 1916 | 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 | } |
| 1919 | 1919 | |
| 1920 | 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 | 121 | |
| 122 | 122 | const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len); |
| 123 | 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 | 125 | self.clearAndFree(); |
| 126 | 126 | return new_memory; |
| 127 | 127 | } |
| ... | ... | @@ -281,11 +281,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 281 | 281 | const new_len = old_len + items.len; |
| 282 | 282 | assert(new_len <= self.capacity); |
| 283 | 283 | self.items.len = new_len; |
| 284 | @memcpy( | |
| 285 | @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len), | |
| 286 | @ptrCast([*]const u8, items.ptr), | |
| 287 | items.len * @sizeOf(T), | |
| 288 | ); | |
| 284 | @memcpy(self.items[old_len..][0..items.len], items); | |
| 289 | 285 | } |
| 290 | 286 | |
| 291 | 287 | pub const Writer = if (T != u8) |
| ... | ... | @@ -601,7 +597,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 601 | 597 | |
| 602 | 598 | const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len); |
| 603 | 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 | 601 | self.clearAndFree(allocator); |
| 606 | 602 | return new_memory; |
| 607 | 603 | } |
| ... | ... | @@ -740,11 +736,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 740 | 736 | const new_len = old_len + items.len; |
| 741 | 737 | assert(new_len <= self.capacity); |
| 742 | 738 | self.items.len = new_len; |
| 743 | @memcpy( | |
| 744 | @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len), | |
| 745 | @ptrCast([*]const u8, items.ptr), | |
| 746 | items.len * @sizeOf(T), | |
| 747 | ); | |
| 739 | @memcpy(self.items[old_len..][0..items.len], items); | |
| 748 | 740 | } |
| 749 | 741 | |
| 750 | 742 | pub const WriterContext = struct { |
lib/std/builtin.zig+2| ... | ... | @@ -1002,6 +1002,8 @@ pub const panic_messages = struct { |
| 1002 | 1002 | pub const index_out_of_bounds = "index out of bounds"; |
| 1003 | 1003 | pub const start_index_greater_than_end = "start index is larger than end index"; |
| 1004 | 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 | }; |
| 1006 | 1008 | |
| 1007 | 1009 | pub noinline fn returnError(st: *StackTrace) void { |
lib/std/c/darwin.zig+1-1| ... | ... | @@ -3670,7 +3670,7 @@ pub const MachTask = extern struct { |
| 3670 | 3670 | else => |err| return unexpectedKernError(err), |
| 3671 | 3671 | } |
| 3672 | 3672 | |
| 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 | 3674 | _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read); |
| 3675 | 3675 | |
| 3676 | 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 | 209 | acc |= (computed_tag[j] ^ tag[j]); |
| 210 | 210 | } |
| 211 | 211 | if (acc != 0) { |
| 212 | @memset(m.ptr, undefined, m.len); | |
| 212 | @memset(m, undefined); | |
| 213 | 213 | return error.AuthenticationFailed; |
| 214 | 214 | } |
| 215 | 215 | } |
| ... | ... | @@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type { |
| 390 | 390 | acc |= (computed_tag[j] ^ tag[j]); |
| 391 | 391 | } |
| 392 | 392 | if (acc != 0) { |
| 393 | @memset(m.ptr, undefined, m.len); | |
| 393 | @memset(m, undefined); | |
| 394 | 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 | 91 | acc |= (computed_tag[p] ^ tag[p]); |
| 92 | 92 | } |
| 93 | 93 | if (acc != 0) { |
| 94 | @memset(m.ptr, undefined, m.len); | |
| 94 | @memset(m, undefined); | |
| 95 | 95 | return error.AuthenticationFailed; |
| 96 | 96 | } |
| 97 | 97 |
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 | 531 | const pub_key = subject.pubKey(); |
| 532 | 532 | if (pub_key.len > main_cert_pub_key_buf.len) |
| 533 | 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 | 535 | main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len); |
| 536 | 536 | } else { |
| 537 | 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 | 135 | /// Sets a slice to zeroes. |
| 136 | 136 | /// Prevents the store from being optimized out. |
| 137 | 137 | pub fn secureZero(comptime T: type, s: []T) void { |
| 138 | // NOTE: We do not use a volatile slice cast here since LLVM cannot | |
| 139 | // see that it can be replaced by a memset. | |
| 138 | // TODO: implement `@memset` for non-byte-sized element type in the llvm backend | |
| 139 | //@memset(@as([]volatile T, s), 0); | |
| 140 | 140 | const ptr = @ptrCast([*]volatile u8, s.ptr); |
| 141 | 141 | const length = s.len * @sizeOf(T); |
| 142 | @memset(ptr, 0, length); | |
| 142 | @memset(ptr[0..length], 0); | |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | 145 | test "crypto.utils.timingSafeEql" { |
lib/std/fifo.zig+4-4| ... | ... | @@ -104,7 +104,7 @@ pub fn LinearFifo( |
| 104 | 104 | } |
| 105 | 105 | { // set unused area to undefined |
| 106 | 106 | const unused = mem.sliceAsBytes(self.buf[self.count..]); |
| 107 | @memset(unused.ptr, undefined, unused.len); | |
| 107 | @memset(unused, undefined); | |
| 108 | 108 | } |
| 109 | 109 | } |
| 110 | 110 | |
| ... | ... | @@ -182,12 +182,12 @@ pub fn LinearFifo( |
| 182 | 182 | const slice = self.readableSliceMut(0); |
| 183 | 183 | if (slice.len >= count) { |
| 184 | 184 | const unused = mem.sliceAsBytes(slice[0..count]); |
| 185 | @memset(unused.ptr, undefined, unused.len); | |
| 185 | @memset(unused, undefined); | |
| 186 | 186 | } else { |
| 187 | 187 | const unused = mem.sliceAsBytes(slice[0..]); |
| 188 | @memset(unused.ptr, undefined, unused.len); | |
| 188 | @memset(unused, undefined); | |
| 189 | 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 | 193 | if (autoalign and self.count == count) { |
lib/std/hash/murmur.zig+8-14| ... | ... | @@ -99,9 +99,8 @@ pub const Murmur2_64 = struct { |
| 99 | 99 | |
| 100 | 100 | pub fn hashWithSeed(str: []const u8, seed: u64) u64 { |
| 101 | 101 | const m: u64 = 0xc6a4a7935bd1e995; |
| 102 | const len = @as(u64, str.len); | |
| 103 | var h1: u64 = seed ^ (len *% m); | |
| 104 | for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| { | |
| 102 | var h1: u64 = seed ^ (@as(u64, str.len) *% m); | |
| 103 | for (@ptrCast([*]align(1) const u64, str.ptr)[0 .. str.len / 8]) |v| { | |
| 105 | 104 | var k1: u64 = v; |
| 106 | 105 | if (native_endian == .Big) |
| 107 | 106 | k1 = @byteSwap(k1); |
| ... | ... | @@ -111,11 +110,11 @@ pub const Murmur2_64 = struct { |
| 111 | 110 | h1 ^= k1; |
| 112 | 111 | h1 *%= m; |
| 113 | 112 | } |
| 114 | const rest = len & 7; | |
| 115 | const offset = len - rest; | |
| 113 | const rest = str.len & 7; | |
| 114 | const offset = str.len - rest; | |
| 116 | 115 | if (rest > 0) { |
| 117 | 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 | 118 | if (native_endian == .Big) |
| 120 | 119 | k1 = @byteSwap(k1); |
| 121 | 120 | h1 ^= k1; |
| ... | ... | @@ -282,13 +281,8 @@ pub const Murmur3_32 = struct { |
| 282 | 281 | |
| 283 | 282 | fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 { |
| 284 | 283 | const hashbytes = hashbits / 8; |
| 285 | var key: [256]u8 = undefined; | |
| 286 | var hashes: [hashbytes * 256]u8 = undefined; | |
| 287 | var final: [hashbytes]u8 = undefined; | |
| 288 | ||
| 289 | @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key))); | |
| 290 | @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes))); | |
| 291 | @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final))); | |
| 284 | var key: [256]u8 = [1]u8{0} ** 256; | |
| 285 | var hashes: [hashbytes * 256]u8 = [1]u8{0} ** (hashbytes * 256); | |
| 292 | 286 | |
| 293 | 287 | var i: u32 = 0; |
| 294 | 288 | while (i < 256) : (i += 1) { |
| ... | ... | @@ -297,7 +291,7 @@ fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 { |
| 297 | 291 | var h = hash_fn(key[0..i], 256 - i); |
| 298 | 292 | if (native_endian == .Big) |
| 299 | 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 | } |
| 302 | 296 | |
| 303 | 297 | return @truncate(u32, hash_fn(&hashes, 0)); |
lib/std/hash_map.zig+1-1| ... | ... | @@ -1449,7 +1449,7 @@ pub fn HashMapUnmanaged( |
| 1449 | 1449 | } |
| 1450 | 1450 | |
| 1451 | 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 | } |
| 1454 | 1454 | |
| 1455 | 1455 | // 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 | 759 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); |
| 760 | 760 | if (new_size_class <= size_class) { |
| 761 | 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 | 764 | if (config.verbose_log) { |
| 765 | 765 | log.info("small resize {d} bytes at {*} to {d}", .{ |
| ... | ... | @@ -911,7 +911,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 911 | 911 | self.empty_buckets = bucket; |
| 912 | 912 | } |
| 913 | 913 | } else { |
| 914 | @memset(old_mem.ptr, undefined, old_mem.len); | |
| 914 | @memset(old_mem, undefined); | |
| 915 | 915 | } |
| 916 | 916 | if (config.safety) { |
| 917 | 917 | assert(self.small_allocations.remove(@ptrToInt(old_mem.ptr))); |
| ... | ... | @@ -1011,7 +1011,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 1011 | 1011 | }; |
| 1012 | 1012 | self.buckets[bucket_index] = ptr; |
| 1013 | 1013 | // 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 | 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 | 2756 | |
| 2757 | 2757 | var buffer1 = try testing.allocator.alloc(u8, 16); |
| 2758 | 2758 | defer testing.allocator.free(buffer1); |
| 2759 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2759 | @memset(buffer1, 0xaa); | |
| 2760 | 2760 | |
| 2761 | 2761 | // writeTwosComplement: |
| 2762 | 2762 | // (1) should not write beyond buffer[0..abi_size] |
| ... | ... | @@ -2773,7 +2773,7 @@ test "big int conversion read twos complement with padding" { |
| 2773 | 2773 | a.toConst().writeTwosComplement(buffer1[0..16], .Big); |
| 2774 | 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 })); |
| 2775 | 2775 | |
| 2776 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2776 | @memset(buffer1, 0xaa); | |
| 2777 | 2777 | try a.set(-0x01_02030405_06070809_0a0b0c0d); |
| 2778 | 2778 | bit_count = 12 * 8 + 2; |
| 2779 | 2779 | |
| ... | ... | @@ -2794,7 +2794,7 @@ test "big int write twos complement +/- zero" { |
| 2794 | 2794 | |
| 2795 | 2795 | var buffer1 = try testing.allocator.alloc(u8, 16); |
| 2796 | 2796 | defer testing.allocator.free(buffer1); |
| 2797 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2797 | @memset(buffer1, 0xaa); | |
| 2798 | 2798 | |
| 2799 | 2799 | // Test zero |
| 2800 | 2800 | |
| ... | ... | @@ -2807,7 +2807,7 @@ test "big int write twos complement +/- zero" { |
| 2807 | 2807 | m.toConst().writeTwosComplement(buffer1[0..16], .Big); |
| 2808 | 2808 | try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16)))); |
| 2809 | 2809 | |
| 2810 | @memset(buffer1.ptr, 0xaa, buffer1.len); | |
| 2810 | @memset(buffer1, 0xaa); | |
| 2811 | 2811 | m.positive = false; |
| 2812 | 2812 | |
| 2813 | 2813 | // Test negative zero |
lib/std/mem/Allocator.zig+5-4| ... | ... | @@ -215,7 +215,7 @@ pub fn allocAdvancedWithRetAddr( |
| 215 | 215 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; |
| 216 | 216 | const byte_ptr = self.rawAlloc(byte_count, log2a(a), return_address) orelse return Error.OutOfMemory; |
| 217 | 217 | // TODO: https://github.com/ziglang/zig/issues/4298 |
| 218 | @memset(byte_ptr, undefined, byte_count); | |
| 218 | @memset(byte_ptr[0..byte_count], undefined); | |
| 219 | 219 | const byte_slice = byte_ptr[0..byte_count]; |
| 220 | 220 | return mem.bytesAsSlice(T, @alignCast(a, byte_slice)); |
| 221 | 221 | } |
| ... | ... | @@ -282,9 +282,10 @@ pub fn reallocAdvanced( |
| 282 | 282 | |
| 283 | 283 | const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse |
| 284 | 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 | 287 | // 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 | 289 | self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address); |
| 289 | 290 | |
| 290 | 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 | 300 | if (bytes_len == 0) return; |
| 300 | 301 | const non_const_ptr = @constCast(bytes.ptr); |
| 301 | 302 | // 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 | 304 | self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress()); |
| 304 | 305 | } |
| 305 | 306 |
lib/std/multi_array_list.zig+1-2| ... | ... | @@ -360,11 +360,10 @@ pub fn MultiArrayList(comptime T: type) type { |
| 360 | 360 | if (@sizeOf(field_info.type) != 0) { |
| 361 | 361 | const field = @intToEnum(Field, i); |
| 362 | 362 | const dest_slice = self_slice.items(field)[new_len..]; |
| 363 | const byte_count = dest_slice.len * @sizeOf(field_info.type); | |
| 364 | 363 | // We use memset here for more efficient codegen in safety-checked, |
| 365 | 364 | // valgrind-enabled builds. Otherwise the valgrind client request |
| 366 | 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 | 369 | self.len = new_len; |
lib/std/net.zig+3-3| ... | ... | @@ -1020,7 +1020,7 @@ fn linuxLookupName( |
| 1020 | 1020 | for (addrs.items, 0..) |*addr, i| { |
| 1021 | 1021 | var key: i32 = 0; |
| 1022 | 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 | 1024 | var da6 = os.sockaddr.in6{ |
| 1025 | 1025 | .family = os.AF.INET6, |
| 1026 | 1026 | .scope_id = addr.addr.in6.sa.scope_id, |
| ... | ... | @@ -1029,7 +1029,7 @@ fn linuxLookupName( |
| 1029 | 1029 | .addr = [1]u8{0} ** 16, |
| 1030 | 1030 | }; |
| 1031 | 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 | 1033 | var da4 = os.sockaddr.in{ |
| 1034 | 1034 | .family = os.AF.INET, |
| 1035 | 1035 | .port = 65535, |
| ... | ... | @@ -1577,7 +1577,7 @@ fn resMSendRc( |
| 1577 | 1577 | |
| 1578 | 1578 | // Get local address and open/bind a socket |
| 1579 | 1579 | var sa: Address = undefined; |
| 1580 | @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address)); | |
| 1580 | @memset(@ptrCast([*]u8, &sa)[0..@sizeOf(Address)], 0); | |
| 1581 | 1581 | sa.any.family = family; |
| 1582 | 1582 | try os.bind(fd, &sa.any, sl); |
| 1583 | 1583 |
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 | 5217 | .macos, .ios, .watchos, .tvos => { |
| 5218 | 5218 | // On macOS, we can use F.GETPATH fcntl command to query the OS for |
| 5219 | 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 | 5221 | switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) { |
| 5222 | 5222 | .SUCCESS => {}, |
| 5223 | 5223 | .BADF => return error.FileNotFound, |
| ... | ... | @@ -5308,7 +5308,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 5308 | 5308 | if (comptime builtin.os.version_range.semver.max.order(.{ .major = 6, .minor = 0 }) == .lt) { |
| 5309 | 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 | 5312 | switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) { |
| 5313 | 5313 | .SUCCESS => {}, |
| 5314 | 5314 | .BADF => return error.FileNotFound, |
| ... | ... | @@ -5322,7 +5322,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 5322 | 5322 | if (comptime builtin.os.version_range.semver.max.order(.{ .major = 10, .minor = 0 }) == .lt) { |
| 5323 | 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 | 5326 | switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) { |
| 5327 | 5327 | .SUCCESS => {}, |
| 5328 | 5328 | .ACCES => return error.AccessDenied, |
| ... | ... | @@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 { |
| 5548 | 5548 | var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined; |
| 5549 | 5549 | // >= rather than > to make room for the null byte |
| 5550 | 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 | 5552 | path_with_null[file_path.len] = 0; |
| 5553 | 5553 | return path_with_null; |
| 5554 | 5554 | } |
| ... | ... | @@ -5720,7 +5720,7 @@ pub fn res_mkquery( |
| 5720 | 5720 | |
| 5721 | 5721 | // Construct query template - ID will be filled later |
| 5722 | 5722 | var q: [280]u8 = undefined; |
| 5723 | @memset(&q, 0, n); | |
| 5723 | @memset(q[0..n], 0); | |
| 5724 | 5724 | q[2] = @as(u8, op) * 8 + 1; |
| 5725 | 5725 | q[5] = 1; |
| 5726 | 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 | 1184 | .mask = undefined, |
| 1185 | 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 | } |
| 1189 | 1189 | |
| 1190 | 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 | 1200 | if (oact) |old| { |
| 1201 | 1201 | old.handler.handler = oldksa.handler; |
| 1202 | 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 | } |
| 1205 | 1205 | |
| 1206 | 1206 | return 0; |
| ... | ... | @@ -1515,7 +1515,7 @@ pub fn sched_yield() usize { |
| 1515 | 1515 | pub fn sched_getaffinity(pid: pid_t, size: usize, set: *cpu_set_t) usize { |
| 1516 | 1516 | const rc = syscall3(.sched_getaffinity, @bitCast(usize, @as(isize, pid)), size, @ptrToInt(set)); |
| 1517 | 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 | 1519 | return 0; |
| 1520 | 1520 | } |
| 1521 | 1521 |
lib/std/os/windows.zig+3-3| ... | ... | @@ -755,9 +755,9 @@ pub fn CreateSymbolicLink( |
| 755 | 755 | }; |
| 756 | 756 | |
| 757 | 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 | 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 | 761 | _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null); |
| 762 | 762 | } |
| 763 | 763 | |
| ... | ... | @@ -1179,7 +1179,7 @@ pub fn GetFinalPathNameByHandle( |
| 1179 | 1179 | var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]); |
| 1180 | 1180 | input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT); |
| 1181 | 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)); | |
| 1183 | 1183 | |
| 1184 | 1184 | DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) { |
| 1185 | 1185 | error.AccessDenied => unreachable, |
lib/std/zig/c_builtins.zig+5-5| ... | ... | @@ -152,7 +152,7 @@ pub inline fn __builtin___memset_chk( |
| 152 | 152 | |
| 153 | 153 | pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque { |
| 154 | 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 | 156 | return dst; |
| 157 | 157 | } |
| 158 | 158 | |
| ... | ... | @@ -171,10 +171,10 @@ pub inline fn __builtin_memcpy( |
| 171 | 171 | noalias src: ?*const anyopaque, |
| 172 | 172 | len: usize, |
| 173 | 173 | ) ?*anyopaque { |
| 174 | const dst_cast = @ptrCast([*c]u8, dst); | |
| 175 | const src_cast = @ptrCast([*c]const u8, src); | |
| 176 | ||
| 177 | @memcpy(dst_cast, src_cast, len); | |
| 174 | if (len > 0) @memcpy( | |
| 175 | @ptrCast([*]u8, dst.?)[0..len], | |
| 176 | @ptrCast([*]const u8, src.?), | |
| 177 | ); | |
| 178 | 178 | return dst; |
| 179 | 179 | } |
| 180 | 180 |
src/Air.zig+40-8| ... | ... | @@ -138,12 +138,14 @@ pub const Inst = struct { |
| 138 | 138 | /// The offset is in element type units, not bytes. |
| 139 | 139 | /// Wrapping is undefined behavior. |
| 140 | 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 | 142 | /// Uses the `ty_pl` field. Payload is `Bin`. |
| 142 | 143 | ptr_add, |
| 143 | 144 | /// Subtract an offset from a pointer, returning a new pointer. |
| 144 | 145 | /// The offset is in element type units, not bytes. |
| 145 | 146 | /// Wrapping is undefined behavior. |
| 146 | 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 | 149 | /// Uses the `ty_pl` field. Payload is `Bin`. |
| 148 | 150 | ptr_sub, |
| 149 | 151 | /// Given two operands which can be floats, integers, or vectors, returns the |
| ... | ... | @@ -462,6 +464,7 @@ pub const Inst = struct { |
| 462 | 464 | /// Uses the `ty_op` field. |
| 463 | 465 | load, |
| 464 | 466 | /// Converts a pointer to its address. Result type is always `usize`. |
| 467 | /// Pointer type size may be any, including slice. | |
| 465 | 468 | /// Uses the `un_op` field. |
| 466 | 469 | ptrtoint, |
| 467 | 470 | /// Given a boolean, returns 0 or 1. |
| ... | ... | @@ -484,7 +487,16 @@ pub const Inst = struct { |
| 484 | 487 | /// Write a value to a pointer. LHS is pointer, RHS is value. |
| 485 | 488 | /// Result type is always void. |
| 486 | 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 | 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 | 500 | /// Indicates the program counter will never get to this instruction. |
| 489 | 501 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 490 | 502 | unreach, |
| ... | ... | @@ -632,17 +644,33 @@ pub const Inst = struct { |
| 632 | 644 | /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`. |
| 633 | 645 | select, |
| 634 | 646 | |
| 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 | 650 | /// Result type is always void. |
| 637 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | |
| 638 | /// value, `rhs` is the length. | |
| 639 | /// The element type may be any type, not just u8. | |
| 651 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value. | |
| 652 | /// The element value may be undefined, in which case the destination | |
| 653 | /// memory region has undefined bytes after this instruction is | |
| 654 | /// evaluated. In such case ignoring this instruction is legal | |
| 655 | /// lowering. | |
| 656 | /// If the length is compile-time known (due to the destination being a | |
| 657 | /// pointer-to-array), then it is guaranteed to be greater than zero. | |
| 640 | 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 | 669 | /// Result type is always void. |
| 643 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | |
| 644 | /// src ptr, `rhs` is the length. | |
| 645 | /// The element type may be any type, not just u8. | |
| 670 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. | |
| 671 | /// If the length is compile-time known (due to the destination or | |
| 672 | /// source being a pointer-to-array), then it is guaranteed to be | |
| 673 | /// greater than zero. | |
| 646 | 674 | memcpy, |
| 647 | 675 | |
| 648 | 676 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| ... | ... | @@ -1226,12 +1254,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1226 | 1254 | .dbg_var_ptr, |
| 1227 | 1255 | .dbg_var_val, |
| 1228 | 1256 | .store, |
| 1257 | .store_safe, | |
| 1229 | 1258 | .fence, |
| 1230 | 1259 | .atomic_store_unordered, |
| 1231 | 1260 | .atomic_store_monotonic, |
| 1232 | 1261 | .atomic_store_release, |
| 1233 | 1262 | .atomic_store_seq_cst, |
| 1234 | 1263 | .memset, |
| 1264 | .memset_safe, | |
| 1235 | 1265 | .memcpy, |
| 1236 | 1266 | .set_union_tag, |
| 1237 | 1267 | .prefetch, |
| ... | ... | @@ -1406,11 +1436,13 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool { |
| 1406 | 1436 | .ret, |
| 1407 | 1437 | .ret_load, |
| 1408 | 1438 | .store, |
| 1439 | .store_safe, | |
| 1409 | 1440 | .unreach, |
| 1410 | 1441 | .optional_payload_ptr_set, |
| 1411 | 1442 | .errunion_payload_ptr_set, |
| 1412 | 1443 | .set_union_tag, |
| 1413 | 1444 | .memset, |
| 1445 | .memset_safe, | |
| 1414 | 1446 | .memcpy, |
| 1415 | 1447 | .cmpxchg_weak, |
| 1416 | 1448 | .cmpxchg_strong, |
src/AstGen.zig+6-8| ... | ... | @@ -8453,18 +8453,16 @@ fn builtinCall( |
| 8453 | 8453 | return rvalue(gz, ri, result, node); |
| 8454 | 8454 | }, |
| 8455 | 8455 | .memcpy => { |
| 8456 | _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ | |
| 8457 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), | |
| 8458 | .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]), | |
| 8459 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), | |
| 8456 | _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ | |
| 8457 | .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), | |
| 8458 | .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), | |
| 8460 | 8459 | }); |
| 8461 | 8460 | return rvalue(gz, ri, .void_value, node); |
| 8462 | 8461 | }, |
| 8463 | 8462 | .memset => { |
| 8464 | _ = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ | |
| 8465 | .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]), | |
| 8466 | .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]), | |
| 8467 | .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]), | |
| 8463 | _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{ | |
| 8464 | .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), | |
| 8465 | .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), | |
| 8468 | 8466 | }); |
| 8469 | 8467 | return rvalue(gz, ri, .void_value, node); |
| 8470 | 8468 | }, |
src/BuiltinFn.zig+2-2| ... | ... | @@ -615,14 +615,14 @@ pub const list = list: { |
| 615 | 615 | "@memcpy", |
| 616 | 616 | .{ |
| 617 | 617 | .tag = .memcpy, |
| 618 | .param_count = 3, | |
| 618 | .param_count = 2, | |
| 619 | 619 | }, |
| 620 | 620 | }, |
| 621 | 621 | .{ |
| 622 | 622 | "@memset", |
| 623 | 623 | .{ |
| 624 | 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 | 299 | }, |
| 300 | 300 | |
| 301 | 301 | .store, |
| 302 | .store_safe, | |
| 302 | 303 | .atomic_store_unordered, |
| 303 | 304 | .atomic_store_monotonic, |
| 304 | 305 | .atomic_store_release, |
| 305 | 306 | .atomic_store_seq_cst, |
| 306 | 307 | .set_union_tag, |
| 308 | .memset, | |
| 309 | .memset_safe, | |
| 310 | .memcpy, | |
| 307 | 311 | => { |
| 308 | 312 | const o = air_datas[inst].bin_op; |
| 309 | 313 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| ... | ... | @@ -597,16 +601,6 @@ pub fn categorizeOperand( |
| 597 | 601 | if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); |
| 598 | 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 | }, | |
| 610 | 604 | |
| 611 | 605 | .br => { |
| 612 | 606 | const br = air_datas[inst].br; |
| ... | ... | @@ -972,6 +966,7 @@ fn analyzeInst( |
| 972 | 966 | .bool_and, |
| 973 | 967 | .bool_or, |
| 974 | 968 | .store, |
| 969 | .store_safe, | |
| 975 | 970 | .array_elem_val, |
| 976 | 971 | .slice_elem_val, |
| 977 | 972 | .ptr_elem_val, |
| ... | ... | @@ -987,6 +982,9 @@ fn analyzeInst( |
| 987 | 982 | .set_union_tag, |
| 988 | 983 | .min, |
| 989 | 984 | .max, |
| 985 | .memset, | |
| 986 | .memset_safe, | |
| 987 | .memcpy, | |
| 990 | 988 | => { |
| 991 | 989 | const o = inst_datas[inst].bin_op; |
| 992 | 990 | return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none }); |
| ... | ... | @@ -1234,13 +1232,6 @@ fn analyzeInst( |
| 1234 | 1232 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 1235 | 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 | }, | |
| 1244 | 1235 | |
| 1245 | 1236 | .br => return analyzeInstBr(a, pass, data, inst), |
| 1246 | 1237 |
src/Liveness/Verify.zig+4-7| ... | ... | @@ -239,6 +239,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 239 | 239 | .bool_and, |
| 240 | 240 | .bool_or, |
| 241 | 241 | .store, |
| 242 | .store_safe, | |
| 242 | 243 | .array_elem_val, |
| 243 | 244 | .slice_elem_val, |
| 244 | 245 | .ptr_elem_val, |
| ... | ... | @@ -254,6 +255,9 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 254 | 255 | .set_union_tag, |
| 255 | 256 | .min, |
| 256 | 257 | .max, |
| 258 | .memset, | |
| 259 | .memset_safe, | |
| 260 | .memcpy, | |
| 257 | 261 | => { |
| 258 | 262 | const bin_op = data[inst].bin_op; |
| 259 | 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 | 310 | const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data; |
| 307 | 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 | 313 | .cmpxchg_strong, |
| 317 | 314 | .cmpxchg_weak, |
| 318 | 315 | => { |
src/Sema.zig+303-75| ... | ... | @@ -2500,7 +2500,7 @@ fn coerceResultPtr( |
| 2500 | 2500 | |
| 2501 | 2501 | // The last one is always `store`. |
| 2502 | 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 | 2504 | // no store instruction is generated for zero sized types |
| 2505 | 2505 | assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null); |
| 2506 | 2506 | } else { |
| ... | ... | @@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 3386 | 3386 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 3387 | 3387 | const src = inst_data.src(); |
| 3388 | 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(); | |
| 3392 | 3389 | |
| 3393 | const array_ty = if (is_pointer_to) | |
| 3394 | object_ty.childType() | |
| 3395 | else | |
| 3396 | object_ty; | |
| 3390 | return indexablePtrLen(sema, block, src, object); | |
| 3391 | } | |
| 3397 | 3392 | |
| 3393 | fn 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 | 3402 | try checkIndexable(sema, block, src, array_ty); |
| 3403 | return sema.fieldVal(block, src, object, "len", src); | |
| 3404 | } | |
| 3399 | 3405 | |
| 3406 | fn 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 | 3422 | return sema.fieldVal(block, src, object, "len", src); |
| 3401 | 3423 | } |
| 3402 | 3424 | |
| ... | ... | @@ -3502,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3502 | 3524 | const candidate = block.instructions.items[search_index]; |
| 3503 | 3525 | switch (air_tags[candidate]) { |
| 3504 | 3526 | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 3505 | .store => break candidate, | |
| 3527 | .store, .store_safe => break candidate, | |
| 3506 | 3528 | else => break :ct, |
| 3507 | 3529 | } |
| 3508 | 3530 | }; |
| ... | ... | @@ -3728,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3728 | 3750 | const candidate = block.instructions.items[search_index]; |
| 3729 | 3751 | switch (air_tags[candidate]) { |
| 3730 | 3752 | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 3731 | .store => break candidate, | |
| 3753 | .store, .store_safe => break candidate, | |
| 3732 | 3754 | else => break :ct, |
| 3733 | 3755 | } |
| 3734 | 3756 | }; |
| ... | ... | @@ -3838,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3838 | 3860 | assert(replacement_block.instructions.items.len > 0); |
| 3839 | 3861 | break :result sub_ptr; |
| 3840 | 3862 | }, |
| 3841 | .store => result: { | |
| 3863 | .store, .store_safe => result: { | |
| 3842 | 3864 | const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op; |
| 3843 | 3865 | try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast); |
| 3844 | 3866 | break :result .void_value; |
| ... | ... | @@ -4220,7 +4242,10 @@ fn validateUnionInit( |
| 4220 | 4242 | while (block_index > 0) : (block_index -= 1) { |
| 4221 | 4243 | const store_inst = block.instructions.items[block_index]; |
| 4222 | 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 | 4249 | const bin_op = air_datas[store_inst].bin_op; |
| 4225 | 4250 | var lhs = bin_op.lhs; |
| 4226 | 4251 | if (Air.refToIndex(lhs)) |lhs_index| { |
| ... | ... | @@ -4432,7 +4457,10 @@ fn validateStructInit( |
| 4432 | 4457 | struct_is_comptime = false; |
| 4433 | 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 | 4464 | const bin_op = air_datas[store_inst].bin_op; |
| 4437 | 4465 | var lhs = bin_op.lhs; |
| 4438 | 4466 | { |
| ... | ... | @@ -4660,7 +4688,10 @@ fn zirValidateArrayInit( |
| 4660 | 4688 | array_is_comptime = false; |
| 4661 | 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 | 4695 | const bin_op = air_datas[store_inst].bin_op; |
| 4665 | 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 | 5034 | |
| 5004 | 5035 | const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node }; |
| 5005 | 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 | 5043 | return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); |
| 5008 | 5044 | } |
| 5009 | 5045 | |
| ... | ... | @@ -9861,8 +9897,11 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 9861 | 9897 | const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; |
| 9862 | 9898 | const array_ptr = try sema.resolveInst(extra.lhs); |
| 9863 | 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 }; | |
| 9864 | 9903 | |
| 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 | } |
| 9867 | 9906 | |
| 9868 | 9907 | fn 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 | 9914 | const array_ptr = try sema.resolveInst(extra.lhs); |
| 9876 | 9915 | const start = try sema.resolveInst(extra.start); |
| 9877 | 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 }; | |
| 9878 | 9920 | |
| 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 | } |
| 9881 | 9923 | |
| 9882 | 9924 | fn 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 | 9933 | const start = try sema.resolveInst(extra.start); |
| 9892 | 9934 | const end = try sema.resolveInst(extra.end); |
| 9893 | 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 }; | |
| 9894 | 9939 | |
| 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 | } |
| 9897 | 9942 | |
| 9898 | 9943 | fn zirSwitchCapture( |
| ... | ... | @@ -21748,90 +21793,270 @@ fn analyzeMinMax( |
| 21748 | 21793 | return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); |
| 21749 | 21794 | } |
| 21750 | 21795 | |
| 21796 | fn 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 | ||
| 21751 | 21819 | fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 21752 | 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 | 21822 | const src = inst_data.src(); |
| 21755 | 21823 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 21756 | 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 }; | |
| 21758 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 21759 | ||
| 21760 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 21761 | var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 21762 | dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 21763 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info); | |
| 21764 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | |
| 21765 | ||
| 21766 | const uncasted_src_ptr = try sema.resolveInst(extra.source); | |
| 21767 | var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; | |
| 21768 | src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); | |
| 21769 | const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); | |
| 21770 | const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); | |
| 21771 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | |
| 21825 | const dest_ptr = try sema.resolveInst(extra.lhs); | |
| 21826 | const src_ptr = try sema.resolveInst(extra.rhs); | |
| 21827 | const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); | |
| 21828 | const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); | |
| 21829 | const target = sema.mod.getTarget(); | |
| 21830 | ||
| 21831 | if (dest_len == .none and src_len == .none) { | |
| 21832 | const msg = msg: { | |
| 21833 | const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); | |
| 21834 | errdefer msg.destroy(sema.gpa); | |
| 21835 | try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{ | |
| 21836 | sema.typeOf(dest_ptr).fmt(sema.mod), | |
| 21837 | }); | |
| 21838 | try sema.errNote(block, src_src, msg, "source type {} provides no length", .{ | |
| 21839 | sema.typeOf(src_ptr).fmt(sema.mod), | |
| 21840 | }); | |
| 21841 | break :msg msg; | |
| 21842 | }; | |
| 21843 | return sema.failWithOwnedErrorMsg(msg); | |
| 21844 | } | |
| 21845 | ||
| 21846 | var len_val: ?Value = null; | |
| 21847 | ||
| 21848 | if (dest_len != .none and src_len != .none) check: { | |
| 21849 | // If we can check at compile-time, no need for runtime safety. | |
| 21850 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { | |
| 21851 | len_val = dest_len_val; | |
| 21852 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { | |
| 21853 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { | |
| 21854 | const msg = msg: { | |
| 21855 | const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); | |
| 21856 | errdefer msg.destroy(sema.gpa); | |
| 21857 | try sema.errNote(block, dest_src, msg, "length {} here", .{ | |
| 21858 | dest_len_val.fmtValue(Type.usize, sema.mod), | |
| 21859 | }); | |
| 21860 | try sema.errNote(block, src_src, msg, "length {} here", .{ | |
| 21861 | src_len_val.fmtValue(Type.usize, sema.mod), | |
| 21862 | }); | |
| 21863 | break :msg msg; | |
| 21864 | }; | |
| 21865 | return sema.failWithOwnedErrorMsg(msg); | |
| 21866 | } | |
| 21867 | break :check; | |
| 21868 | } | |
| 21869 | } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { | |
| 21870 | len_val = src_len_val; | |
| 21871 | } | |
| 21872 | ||
| 21873 | if (block.wantSafety()) { | |
| 21874 | const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); | |
| 21875 | try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); | |
| 21876 | } | |
| 21877 | } | |
| 21772 | 21878 | |
| 21773 | 21879 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { |
| 21774 | 21880 | if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src; |
| 21775 | if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| { | |
| 21776 | if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src; | |
| 21777 | if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| { | |
| 21778 | _ = len_val; | |
| 21779 | return sema.fail(block, src, "TODO: Sema.zirMemcpy at comptime", .{}); | |
| 21780 | } else break :rs len_src; | |
| 21881 | if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| { | |
| 21882 | const len_u64 = (try len_val.?.getUnsignedIntAdvanced(target, sema)).?; | |
| 21883 | const len = try sema.usizeCast(block, dest_src, len_u64); | |
| 21884 | for (0..len) |i| { | |
| 21885 | const elem_index = try sema.addIntUnsigned(Type.usize, i); | |
| 21886 | const dest_elem_ptr = try sema.elemPtr( | |
| 21887 | block, | |
| 21888 | src, | |
| 21889 | dest_ptr, | |
| 21890 | elem_index, | |
| 21891 | src, | |
| 21892 | true, // init | |
| 21893 | false, // oob_safety | |
| 21894 | ); | |
| 21895 | const src_elem_ptr = try sema.elemPtr( | |
| 21896 | block, | |
| 21897 | src, | |
| 21898 | src_ptr, | |
| 21899 | elem_index, | |
| 21900 | src, | |
| 21901 | false, // init | |
| 21902 | false, // oob_safety | |
| 21903 | ); | |
| 21904 | const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src); | |
| 21905 | try sema.storePtr2( | |
| 21906 | block, | |
| 21907 | src, | |
| 21908 | dest_elem_ptr, | |
| 21909 | dest_src, | |
| 21910 | uncoerced_elem, | |
| 21911 | src_src, | |
| 21912 | .store, | |
| 21913 | ); | |
| 21914 | } | |
| 21915 | return; | |
| 21781 | 21916 | } else break :rs src_src; |
| 21782 | 21917 | } else dest_src; |
| 21783 | 21918 | |
| 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 | 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 | 21991 | _ = try block.addInst(.{ |
| 21786 | 21992 | .tag = .memcpy, |
| 21787 | .data = .{ .pl_op = .{ | |
| 21788 | .operand = dest_ptr, | |
| 21789 | .payload = try sema.addExtra(Air.Bin{ | |
| 21790 | .lhs = src_ptr, | |
| 21791 | .rhs = len, | |
| 21792 | }), | |
| 21993 | .data = .{ .bin_op = .{ | |
| 21994 | .lhs = new_dest_ptr, | |
| 21995 | .rhs = new_src_ptr, | |
| 21793 | 21996 | } }, |
| 21794 | 21997 | }); |
| 21795 | 21998 | } |
| 21796 | 21999 | |
| 21797 | 22000 | fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 21798 | 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 | 22003 | const src = inst_data.src(); |
| 21801 | 22004 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 21802 | 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 }; | |
| 21804 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 21805 | ||
| 21806 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 21807 | var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 21808 | ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 21809 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); | |
| 21810 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | |
| 22006 | const dest_ptr = try sema.resolveInst(extra.lhs); | |
| 22007 | const uncoerced_elem = try sema.resolveInst(extra.rhs); | |
| 22008 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 22009 | try checkIndexable(sema, block, dest_src, dest_ptr_ty); | |
| 21811 | 22010 | |
| 21812 | const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); | |
| 21813 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | |
| 22011 | const dest_elem_ty = dest_ptr_ty.elemType2(); | |
| 22012 | const target = sema.mod.getTarget(); | |
| 21814 | 22013 | |
| 21815 | 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 | 22025 | if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src; |
| 21817 | if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| { | |
| 21818 | if (try sema.resolveMaybeUndefVal(value)) |val| { | |
| 21819 | _ = len_val; | |
| 21820 | _ = val; | |
| 21821 | return sema.fail(block, src, "TODO: Sema.zirMemset at comptime", .{}); | |
| 21822 | } else break :rs value_src; | |
| 21823 | } else break :rs len_src; | |
| 22026 | if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| { | |
| 22027 | for (0..len) |i| { | |
| 22028 | const elem_index = try sema.addIntUnsigned(Type.usize, i); | |
| 22029 | const elem_ptr = try sema.elemPtr( | |
| 22030 | block, | |
| 22031 | src, | |
| 22032 | dest_ptr, | |
| 22033 | elem_index, | |
| 22034 | src, | |
| 22035 | true, // init | |
| 22036 | false, // oob_safety | |
| 22037 | ); | |
| 22038 | try sema.storePtr2( | |
| 22039 | block, | |
| 22040 | src, | |
| 22041 | elem_ptr, | |
| 22042 | dest_src, | |
| 22043 | uncoerced_elem, | |
| 22044 | value_src, | |
| 22045 | .store, | |
| 22046 | ); | |
| 22047 | } | |
| 22048 | return; | |
| 22049 | } else break :rs value_src; | |
| 21824 | 22050 | } else dest_src; |
| 21825 | 22051 | |
| 22052 | const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); | |
| 22053 | ||
| 21826 | 22054 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 21827 | 22055 | _ = try block.addInst(.{ |
| 21828 | .tag = .memset, | |
| 21829 | .data = .{ .pl_op = .{ | |
| 21830 | .operand = dest_ptr, | |
| 21831 | .payload = try sema.addExtra(Air.Bin{ | |
| 21832 | .lhs = value, | |
| 21833 | .rhs = len, | |
| 21834 | }), | |
| 22056 | .tag = if (block.wantSafety()) .memset_safe else .memset, | |
| 22057 | .data = .{ .bin_op = .{ | |
| 22058 | .lhs = dest_ptr, | |
| 22059 | .rhs = elem, | |
| 21835 | 22060 | } }, |
| 21836 | 22061 | }); |
| 21837 | 22062 | } |
| ... | ... | @@ -22948,6 +23173,8 @@ pub const PanicId = enum { |
| 22948 | 23173 | index_out_of_bounds, |
| 22949 | 23174 | start_index_greater_than_end, |
| 22950 | 23175 | for_len_mismatch, |
| 23176 | memcpy_len_mismatch, | |
| 23177 | memcpy_alias, | |
| 22951 | 23178 | }; |
| 22952 | 23179 | |
| 22953 | 23180 | fn addSafetyCheck( |
| ... | ... | @@ -26521,7 +26748,8 @@ fn storePtr( |
| 26521 | 26748 | ptr: Air.Inst.Ref, |
| 26522 | 26749 | uncasted_operand: Air.Inst.Ref, |
| 26523 | 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 | } |
| 26526 | 26754 | |
| 26527 | 26755 | fn storePtr2( |
| ... | ... | @@ -28768,10 +28996,10 @@ fn analyzeSlice( |
| 28768 | 28996 | uncasted_end_opt: Air.Inst.Ref, |
| 28769 | 28997 | sentinel_opt: Air.Inst.Ref, |
| 28770 | 28998 | sentinel_src: LazySrcLoc, |
| 28999 | ptr_src: LazySrcLoc, | |
| 29000 | start_src: LazySrcLoc, | |
| 29001 | end_src: LazySrcLoc, | |
| 28771 | 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 | 29003 | // Slice expressions can operate on a variable whose type is an array. This requires |
| 28776 | 29004 | // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. |
| 28777 | 29005 | const ptr_ptr_ty = sema.typeOf(ptr_ptr); |
src/Zir.zig+2-14| ... | ... | @@ -922,10 +922,10 @@ pub const Inst = struct { |
| 922 | 922 | /// Uses the `pl_node` union field with payload `FieldParentPtr`. |
| 923 | 923 | field_parent_ptr, |
| 924 | 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 | 926 | memcpy, |
| 927 | 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 | 929 | memset, |
| 930 | 930 | /// Implements the `@min` builtin. |
| 931 | 931 | /// Uses the `pl_node` union field with payload `Bin` |
| ... | ... | @@ -3501,18 +3501,6 @@ pub const Inst = struct { |
| 3501 | 3501 | field_ptr: Ref, |
| 3502 | 3502 | }; |
| 3503 | 3503 | |
| 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 | 3504 | pub const Shuffle = struct { |
| 3517 | 3505 | elem_type: Ref, |
| 3518 | 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 | 764 | .ptrtoint => try self.airPtrToInt(inst), |
| 765 | 765 | .ret => try self.airRet(inst), |
| 766 | 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 | 769 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 769 | 770 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 770 | 771 | .array_to_slice => try self.airArrayToSlice(inst), |
| ... | ... | @@ -775,7 +776,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 775 | 776 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 776 | 777 | .atomic_load => try self.airAtomicLoad(inst), |
| 777 | 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 | 781 | .set_union_tag => try self.airSetUnionTag(inst), |
| 780 | 782 | .get_union_tag => try self.airGetUnionTag(inst), |
| 781 | 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 | 4037 | } |
| 4036 | 4038 | } |
| 4037 | 4039 | |
| 4038 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | |
| 4040 | fn 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 | 4046 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 4040 | 4047 | const ptr = try self.resolveInst(bin_op.lhs); |
| 4041 | 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 | 5982 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 5976 | 5983 | } |
| 5977 | 5984 | |
| 5978 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 5985 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | |
| 5979 | 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 | 5992 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 5981 | 5993 | } |
| 5982 | 5994 |
src/arch/arm/CodeGen.zig+16-4| ... | ... | @@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 748 | 748 | .ptrtoint => try self.airPtrToInt(inst), |
| 749 | 749 | .ret => try self.airRet(inst), |
| 750 | 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 | 753 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 753 | 754 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 754 | 755 | .array_to_slice => try self.airArrayToSlice(inst), |
| ... | ... | @@ -759,7 +760,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 759 | 760 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 760 | 761 | .atomic_load => try self.airAtomicLoad(inst), |
| 761 | 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 | 765 | .set_union_tag => try self.airSetUnionTag(inst), |
| 764 | 766 | .get_union_tag => try self.airGetUnionTag(inst), |
| 765 | 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 | 2837 | } |
| 2836 | 2838 | } |
| 2837 | 2839 | |
| 2838 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | |
| 2840 | fn 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 | 2846 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2840 | 2847 | const ptr = try self.resolveInst(bin_op.lhs); |
| 2841 | 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 | 5928 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 5922 | 5929 | } |
| 5923 | 5930 | |
| 5924 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 5931 | fn 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 | 5937 | _ = inst; |
| 5926 | 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 | 578 | .ptrtoint => try self.airPtrToInt(inst), |
| 579 | 579 | .ret => try self.airRet(inst), |
| 580 | 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 | 583 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 583 | 584 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 584 | 585 | .array_to_slice => try self.airArrayToSlice(inst), |
| ... | ... | @@ -589,7 +590,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 589 | 590 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 590 | 591 | .atomic_load => try self.airAtomicLoad(inst), |
| 591 | 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 | 595 | .set_union_tag => try self.airSetUnionTag(inst), |
| 594 | 596 | .get_union_tag => try self.airGetUnionTag(inst), |
| 595 | 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 | 1574 | } |
| 1573 | 1575 | } |
| 1574 | 1576 | |
| 1575 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | |
| 1577 | fn 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 | 1583 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1577 | 1584 | const ptr = try self.resolveInst(bin_op.lhs); |
| 1578 | 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 | 2428 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 2422 | 2429 | } |
| 2423 | 2430 | |
| 2424 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 2431 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | |
| 2425 | 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 | 2438 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 2427 | 2439 | } |
| 2428 | 2440 |
src/arch/sparc64/CodeGen.zig+16-4| ... | ... | @@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 593 | 593 | .ptrtoint => try self.airPtrToInt(inst), |
| 594 | 594 | .ret => try self.airRet(inst), |
| 595 | 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 | 598 | .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), |
| 598 | 599 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 599 | 600 | .array_to_slice => try self.airArrayToSlice(inst), |
| ... | ... | @@ -605,7 +606,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 605 | 606 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 606 | 607 | .atomic_load => try self.airAtomicLoad(inst), |
| 607 | 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 | 611 | .set_union_tag => try self.airSetUnionTag(inst), |
| 610 | 612 | .get_union_tag => try self.airGetUnionTag(inst), |
| 611 | 613 | .clz => try self.airClz(inst), |
| ... | ... | @@ -1764,7 +1766,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 1764 | 1766 | return self.finishAirBookkeeping(); |
| 1765 | 1767 | } |
| 1766 | 1768 | |
| 1767 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 1769 | fn 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 | 1775 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1769 | 1776 | const extra = self.air.extraData(Air.Bin, pl_op.payload); |
| 1770 | 1777 | |
| ... | ... | @@ -2401,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 2401 | 2408 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2402 | 2409 | } |
| 2403 | 2410 | |
| 2404 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | |
| 2411 | fn 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 | 2417 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2406 | 2418 | const ptr = try self.resolveInst(bin_op.lhs); |
| 2407 | 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 | 1883 | |
| 1884 | 1884 | .load => func.airLoad(inst), |
| 1885 | 1885 | .loop => func.airLoop(inst), |
| 1886 | .memset => func.airMemset(inst), | |
| 1886 | .memset => func.airMemset(inst, false), | |
| 1887 | .memset_safe => func.airMemset(inst, true), | |
| 1887 | 1888 | .not => func.airNot(inst), |
| 1888 | 1889 | .optional_payload => func.airOptionalPayload(inst), |
| 1889 | 1890 | .optional_payload_ptr => func.airOptionalPayloadPtr(inst), |
| ... | ... | @@ -1913,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1913 | 1914 | .slice_ptr => func.airSlicePtr(inst), |
| 1914 | 1915 | .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()), |
| 1915 | 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), | |
| 1917 | 1919 | |
| 1918 | 1920 | .set_union_tag => func.airSetUnionTag(inst), |
| 1919 | 1921 | .struct_field_ptr => func.airStructFieldPtr(inst), |
| ... | ... | @@ -2221,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2221 | 2223 | func.finishAir(inst, value, &.{}); |
| 2222 | 2224 | } |
| 2223 | 2225 | |
| 2224 | fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2226 | fn 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 | 2232 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; |
| 2226 | 2233 | |
| 2227 | 2234 | const lhs = try func.resolveInst(bin_op.lhs); |
| ... | ... | @@ -4148,9 +4155,7 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4148 | 4155 | const ty_op = func.air.instructions.items(.data)[inst].ty_op; |
| 4149 | 4156 | |
| 4150 | 4157 | const operand = try func.resolveInst(ty_op.operand); |
| 4151 | const len = try func.load(operand, Type.usize, func.ptrSize()); | |
| 4152 | const result = try len.toLocal(func, Type.usize); | |
| 4153 | func.finishAir(inst, result, &.{ty_op.operand}); | |
| 4158 | func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand}); | |
| 4154 | 4159 | } |
| 4155 | 4160 | |
| 4156 | 4161 | fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -4208,9 +4213,17 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4208 | 4213 | fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4209 | 4214 | const ty_op = func.air.instructions.items(.data)[inst].ty_op; |
| 4210 | 4215 | const operand = try func.resolveInst(ty_op.operand); |
| 4216 | func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand}); | |
| 4217 | } | |
| 4218 | ||
| 4219 | fn slicePtr(func: *CodeGen, operand: WValue) InnerError!WValue { | |
| 4211 | 4220 | const ptr = try func.load(operand, Type.usize, 0); |
| 4212 | const result = try ptr.toLocal(func, Type.usize); | |
| 4213 | func.finishAir(inst, result, &.{ty_op.operand}); | |
| 4221 | return ptr.toLocal(func, Type.usize); | |
| 4222 | } | |
| 4223 | ||
| 4224 | fn 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 | } |
| 4215 | 4228 | |
| 4216 | 4229 | fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -4274,8 +4287,10 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4274 | 4287 | fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4275 | 4288 | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 4276 | 4289 | const operand = try func.resolveInst(un_op); |
| 4277 | ||
| 4278 | const result = switch (operand) { | |
| 4290 | const ptr_ty = func.air.typeOf(un_op); | |
| 4291 | const result = if (ptr_ty.isSlice()) | |
| 4292 | try func.slicePtr(operand) | |
| 4293 | else switch (operand) { | |
| 4279 | 4294 | // for stack offset, return a pointer to this offset. |
| 4280 | 4295 | .stack_offset => try func.buildPointerOffset(operand, 0, .new), |
| 4281 | 4296 | else => func.reuseOperand(un_op, operand), |
| ... | ... | @@ -4375,16 +4390,25 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 4375 | 4390 | func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 4376 | 4391 | } |
| 4377 | 4392 | |
| 4378 | fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4379 | const pl_op = func.air.instructions.items(.data)[inst].pl_op; | |
| 4380 | const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; | |
| 4393 | fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | |
| 4394 | if (safety) { | |
| 4395 | // TODO if the value is undef, write 0xaa bytes to dest | |
| 4396 | } else { | |
| 4397 | // TODO if the value is undef, don't lower this instruction | |
| 4398 | } | |
| 4399 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; | |
| 4381 | 4400 | |
| 4382 | const ptr = try func.resolveInst(pl_op.operand); | |
| 4383 | const value = try func.resolveInst(bin_op.lhs); | |
| 4384 | const len = try func.resolveInst(bin_op.rhs); | |
| 4401 | const ptr = try func.resolveInst(bin_op.lhs); | |
| 4402 | const ptr_ty = func.air.typeOf(bin_op.lhs); | |
| 4403 | const value = try func.resolveInst(bin_op.rhs); | |
| 4404 | const len = switch (ptr_ty.ptrSize()) { | |
| 4405 | .Slice => try func.sliceLen(ptr), | |
| 4406 | .One => @as(WValue, .{ .imm32 = @intCast(u32, ptr_ty.childType().arrayLen()) }), | |
| 4407 | .C, .Many => unreachable, | |
| 4408 | }; | |
| 4385 | 4409 | try func.memset(ptr, len, value); |
| 4386 | 4410 | |
| 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 | } |
| 4389 | 4413 | |
| 4390 | 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 | 5179 | func.finishAir(inst, result, &.{extra.field_ptr}); |
| 5156 | 5180 | } |
| 5157 | 5181 | |
| 5182 | fn 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 | ||
| 5158 | 5190 | fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5159 | const pl_op = func.air.instructions.items(.data)[inst].pl_op; | |
| 5160 | const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; | |
| 5161 | const dst = try func.resolveInst(pl_op.operand); | |
| 5162 | const src = try func.resolveInst(bin_op.lhs); | |
| 5163 | const len = try func.resolveInst(bin_op.rhs); | |
| 5164 | try func.memcpy(dst, src, len); | |
| 5191 | const bin_op = func.air.instructions.items(.data)[inst].bin_op; | |
| 5192 | const dst = try func.resolveInst(bin_op.lhs); | |
| 5193 | const dst_ty = func.air.typeOf(bin_op.lhs); | |
| 5194 | const src = try func.resolveInst(bin_op.rhs); | |
| 5195 | const src_ty = func.air.typeOf(bin_op.rhs); | |
| 5196 | const len = switch (dst_ty.ptrSize()) { | |
| 5197 | .Slice => try func.sliceLen(dst), | |
| 5198 | .One => @as(WValue, .{ .imm64 = dst_ty.childType().arrayLen() }), | |
| 5199 | .C, .Many => unreachable, | |
| 5200 | }; | |
| 5201 | const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty); | |
| 5202 | const src_ptr = try func.sliceOrArrayPtr(src, src_ty); | |
| 5203 | try func.memcpy(dst_ptr, src_ptr, len); | |
| 5165 | 5204 | |
| 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 | } |
| 5168 | 5207 | |
| 5169 | 5208 | fn 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 | 1035 | .ptrtoint => try self.airPtrToInt(inst), |
| 1036 | 1036 | .ret => try self.airRet(inst), |
| 1037 | 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 | 1040 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 1040 | 1041 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 1041 | 1042 | .array_to_slice => try self.airArrayToSlice(inst), |
| ... | ... | @@ -1046,7 +1047,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1046 | 1047 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 1047 | 1048 | .atomic_load => try self.airAtomicLoad(inst), |
| 1048 | 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 | 1052 | .set_union_tag => try self.airSetUnionTag(inst), |
| 1051 | 1053 | .get_union_tag => try self.airGetUnionTag(inst), |
| 1052 | 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 | 3937 | } |
| 3936 | 3938 | } |
| 3937 | 3939 | |
| 3938 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | |
| 3940 | fn 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 | 3946 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3940 | 3947 | const ptr = try self.resolveInst(bin_op.lhs); |
| 3941 | 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 | 7685 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 7679 | 7686 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 7680 | 7687 | const result = result: { |
| 7688 | // TODO: handle case where the operand is a slice not a raw pointer | |
| 7681 | 7689 | const src_mcv = try self.resolveInst(un_op); |
| 7682 | 7690 | if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv; |
| 7683 | 7691 | |
| ... | ... | @@ -8148,64 +8156,164 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr |
| 8148 | 8156 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 8149 | 8157 | } |
| 8150 | 8158 | |
| 8151 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 8152 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 8153 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 8159 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | |
| 8160 | if (safety) { | |
| 8161 | // TODO if the value is undef, write 0xaa bytes to dest | |
| 8162 | } else { | |
| 8163 | // TODO if the value is undef, don't lower this instruction | |
| 8164 | } | |
| 8154 | 8165 | |
| 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 | 8170 | const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { |
| 8157 | 8171 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8158 | 8172 | else => null, |
| 8159 | 8173 | }; |
| 8160 | 8174 | defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8161 | 8175 | |
| 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 | 8178 | const src_val_lock: ?RegisterLock = switch (src_val) { |
| 8164 | 8179 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8165 | 8180 | else => null, |
| 8166 | 8181 | }; |
| 8167 | 8182 | defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock); |
| 8168 | 8183 | |
| 8169 | const len = try self.resolveInst(extra.rhs); | |
| 8170 | const len_lock: ?RegisterLock = switch (len) { | |
| 8171 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | |
| 8172 | else => null, | |
| 8173 | }; | |
| 8174 | defer if (len_lock) |lock| self.register_manager.unlockReg(lock); | |
| 8184 | const elem_abi_size = @intCast(u31, elem_ty.abiSize(self.target.*)); | |
| 8185 | ||
| 8186 | if (elem_abi_size == 1) { | |
| 8187 | const ptr = switch (dst_ptr_ty.ptrSize()) { | |
| 8188 | // TODO: this only handles slices stored in the stack | |
| 8189 | .Slice => @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 0 }), | |
| 8190 | .One => dst_ptr, | |
| 8191 | .C, .Many => unreachable, | |
| 8192 | }; | |
| 8193 | const len = switch (dst_ptr_ty.ptrSize()) { | |
| 8194 | // TODO: this only handles slices stored in the stack | |
| 8195 | .Slice => @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 8 }), | |
| 8196 | .One => @as(MCValue, .{ .immediate = dst_ptr_ty.childType().arrayLen() }), | |
| 8197 | .C, .Many => unreachable, | |
| 8198 | }; | |
| 8199 | const len_lock: ?RegisterLock = switch (len) { | |
| 8200 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | |
| 8201 | else => null, | |
| 8202 | }; | |
| 8203 | defer if (len_lock) |lock| self.register_manager.unlockReg(lock); | |
| 8204 | ||
| 8205 | try self.genInlineMemset(ptr, src_val, len, .{}); | |
| 8206 | return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 8207 | } | |
| 8208 | ||
| 8209 | // Store the first element, and then rely on memcpy copying forwards. | |
| 8210 | // Length zero requires a runtime check - so we handle arrays specially | |
| 8211 | // here to elide it. | |
| 8212 | switch (dst_ptr_ty.ptrSize()) { | |
| 8213 | .Slice => { | |
| 8214 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 8215 | const slice_ptr_ty = dst_ptr_ty.slicePtrFieldType(&buf); | |
| 8216 | ||
| 8217 | // TODO: this only handles slices stored in the stack | |
| 8218 | const ptr = @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 0 }); | |
| 8219 | const len = @as(MCValue, .{ .stack_offset = dst_ptr.stack_offset - 8 }); | |
| 8220 | ||
| 8221 | // Used to store the number of elements for comparison. | |
| 8222 | // After comparison, updated to store number of bytes needed to copy. | |
| 8223 | const len_reg = try self.register_manager.allocReg(null, gp); | |
| 8224 | const len_mcv: MCValue = .{ .register = len_reg }; | |
| 8225 | const len_lock = self.register_manager.lockRegAssumeUnused(len_reg); | |
| 8226 | defer self.register_manager.unlockReg(len_lock); | |
| 8227 | ||
| 8228 | try self.asmRegisterMemory(.mov, len_reg, Memory.sib(.qword, .{ | |
| 8229 | .base = .rbp, | |
| 8230 | .disp = -len.stack_offset, | |
| 8231 | })); | |
| 8232 | ||
| 8233 | const skip_reloc = try self.asmJccReloc(undefined, .z); | |
| 8234 | try self.store(ptr, src_val, slice_ptr_ty, elem_ty); | |
| 8235 | ||
| 8236 | const second_elem_ptr_reg = try self.register_manager.allocReg(null, gp); | |
| 8237 | const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg }; | |
| 8238 | const second_elem_ptr_lock = self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg); | |
| 8239 | defer self.register_manager.unlockReg(second_elem_ptr_lock); | |
| 8240 | ||
| 8241 | try self.asmRegisterMemory( | |
| 8242 | .lea, | |
| 8243 | second_elem_ptr_reg, | |
| 8244 | Memory.sib(.qword, .{ | |
| 8245 | .base = try self.copyToTmpRegister(Type.usize, ptr), | |
| 8246 | .disp = elem_abi_size, | |
| 8247 | }), | |
| 8248 | ); | |
| 8249 | ||
| 8250 | try self.genBinOpMir(.sub, Type.usize, len_mcv, .{ .immediate = 1 }); | |
| 8251 | try self.asmRegisterRegisterImmediate(.imul, len_reg, len_reg, Immediate.u(elem_abi_size)); | |
| 8252 | try self.genInlineMemcpy(second_elem_ptr_mcv, ptr, len_mcv, .{}); | |
| 8175 | 8253 | |
| 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); | |
| 8177 | 8265 | |
| 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 | } |
| 8180 | 8283 | |
| 8181 | 8284 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 8182 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 8183 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 8285 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 8184 | 8286 | |
| 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 | 8289 | const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { |
| 8187 | 8290 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8188 | 8291 | else => null, |
| 8189 | 8292 | }; |
| 8190 | 8293 | defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8191 | 8294 | |
| 8192 | const src_ptr = try self.resolveInst(extra.lhs); | |
| 8295 | const src_ptr = try self.resolveInst(bin_op.rhs); | |
| 8193 | 8296 | const src_ptr_lock: ?RegisterLock = switch (src_ptr) { |
| 8194 | 8297 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8195 | 8298 | else => null, |
| 8196 | 8299 | }; |
| 8197 | 8300 | defer if (src_ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8198 | 8301 | |
| 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 | 8307 | const len_lock: ?RegisterLock = switch (len) { |
| 8201 | 8308 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8202 | 8309 | else => null, |
| 8203 | 8310 | }; |
| 8204 | 8311 | defer if (len_lock) |lock| self.register_manager.unlockReg(lock); |
| 8205 | 8312 | |
| 8313 | // TODO: dst_ptr and src_ptr could be slices rather than raw pointers | |
| 8206 | 8314 | try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{}); |
| 8207 | 8315 | |
| 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 | } |
| 8210 | 8318 | |
| 8211 | 8319 | fn 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 | 2924 | .load => try airLoad(f, inst), |
| 2925 | 2925 | .ret => try airRet(f, inst, false), |
| 2926 | 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 | 2929 | .loop => try airLoop(f, inst), |
| 2929 | 2930 | .cond_br => try airCondBr(f, inst), |
| 2930 | 2931 | .br => try airBr(f, inst), |
| ... | ... | @@ -2935,7 +2936,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2935 | 2936 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), |
| 2936 | 2937 | .atomic_rmw => try airAtomicRmw(f, inst), |
| 2937 | 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 | 2941 | .memcpy => try airMemcpy(f, inst), |
| 2940 | 2942 | .set_union_tag => try airSetUnionTag(f, inst), |
| 2941 | 2943 | .get_union_tag => try airGetUnionTag(f, inst), |
| ... | ... | @@ -3574,19 +3576,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3574 | 3576 | return local; |
| 3575 | 3577 | } |
| 3576 | 3578 | |
| 3577 | fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { | |
| 3578 | if (f.wantSafety()) { | |
| 3579 | const writer = f.object.writer(); | |
| 3580 | try writer.writeAll("memset("); | |
| 3581 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 3582 | try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); | |
| 3583 | try f.renderType(writer, lhs_child_ty); | |
| 3584 | try writer.writeAll("));\n"); | |
| 3585 | } | |
| 3586 | return .none; | |
| 3587 | } | |
| 3588 | ||
| 3589 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 3579 | fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { | |
| 3590 | 3580 | // *a = b; |
| 3591 | 3581 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 3592 | 3582 | |
| ... | ... | @@ -3597,18 +3587,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3597 | 3587 | const ptr_val = try f.resolveInst(bin_op.lhs); |
| 3598 | 3588 | const src_ty = f.air.typeOf(bin_op.rhs); |
| 3599 | 3589 | |
| 3600 | // TODO Sema should emit a different instruction when the store should | |
| 3601 | // possibly do the safety 0xaa bytes for undefined. | |
| 3602 | const src_val_is_undefined = | |
| 3603 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; | |
| 3604 | if (src_val_is_undefined) { | |
| 3605 | if (ptr_info.host_size == 0) { | |
| 3606 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3607 | return try storeUndefined(f, ptr_info.pointee_type, ptr_val); | |
| 3608 | } else if (!f.wantSafety()) { | |
| 3609 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3610 | return .none; | |
| 3590 | const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; | |
| 3591 | ||
| 3592 | if (val_is_undef) { | |
| 3593 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3594 | if (safety and ptr_info.host_size == 0) { | |
| 3595 | const writer = f.object.writer(); | |
| 3596 | try writer.writeAll("memset("); | |
| 3597 | try f.writeCValue(writer, ptr_val, .FunctionArgument); | |
| 3598 | try writer.writeAll(", 0xaa, sizeof("); | |
| 3599 | try f.renderType(writer, ptr_info.pointee_type); | |
| 3600 | try writer.writeAll("));\n"); | |
| 3611 | 3601 | } |
| 3602 | return .none; | |
| 3612 | 3603 | } |
| 3613 | 3604 | |
| 3614 | 3605 | const target = f.object.dg.module.getTarget(); |
| ... | ... | @@ -3844,8 +3835,8 @@ fn airCmpOp( |
| 3844 | 3835 | data: anytype, |
| 3845 | 3836 | operator: std.math.CompareOperator, |
| 3846 | 3837 | ) !CValue { |
| 3847 | const operand_ty = f.air.typeOf(data.lhs); | |
| 3848 | const scalar_ty = operand_ty.scalarType(); | |
| 3838 | const lhs_ty = f.air.typeOf(data.lhs); | |
| 3839 | const scalar_ty = lhs_ty.scalarType(); | |
| 3849 | 3840 | |
| 3850 | 3841 | const target = f.object.dg.module.getTarget(); |
| 3851 | 3842 | const scalar_bits = scalar_ty.bitSize(target); |
| ... | ... | @@ -3866,17 +3857,21 @@ fn airCmpOp( |
| 3866 | 3857 | const rhs = try f.resolveInst(data.rhs); |
| 3867 | 3858 | try reap(f, inst, &.{ data.lhs, data.rhs }); |
| 3868 | 3859 | |
| 3860 | const rhs_ty = f.air.typeOf(data.rhs); | |
| 3861 | const need_cast = lhs_ty.isSinglePointer() != rhs_ty.isSinglePointer(); | |
| 3869 | 3862 | const writer = f.object.writer(); |
| 3870 | 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 | 3865 | try f.writeCValue(writer, local, .Other); |
| 3873 | 3866 | try v.elem(f, writer); |
| 3874 | 3867 | try writer.writeAll(" = "); |
| 3868 | if (need_cast) try writer.writeAll("(void*)"); | |
| 3875 | 3869 | try f.writeCValue(writer, lhs, .Other); |
| 3876 | 3870 | try v.elem(f, writer); |
| 3877 | 3871 | try writer.writeByte(' '); |
| 3878 | 3872 | try writer.writeAll(compareOperatorC(operator)); |
| 3879 | 3873 | try writer.writeByte(' '); |
| 3874 | if (need_cast) try writer.writeAll("(void*)"); | |
| 3880 | 3875 | try f.writeCValue(writer, rhs, .Other); |
| 3881 | 3876 | try v.elem(f, writer); |
| 3882 | 3877 | try writer.writeAll(";\n"); |
| ... | ... | @@ -5784,6 +5779,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5784 | 5779 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 5785 | 5780 | |
| 5786 | 5781 | const operand = try f.resolveInst(un_op); |
| 5782 | const operand_ty = f.air.typeOf(un_op); | |
| 5787 | 5783 | try reap(f, inst, &.{un_op}); |
| 5788 | 5784 | const inst_ty = f.air.typeOfIndex(inst); |
| 5789 | 5785 | const writer = f.object.writer(); |
| ... | ... | @@ -5793,7 +5789,11 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5793 | 5789 | try writer.writeAll(" = ("); |
| 5794 | 5790 | try f.renderType(writer, inst_ty); |
| 5795 | 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 | 5797 | try writer.writeAll(";\n"); |
| 5798 | 5798 | return local; |
| 5799 | 5799 | } |
| ... | ... | @@ -6186,19 +6186,66 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa |
| 6186 | 6186 | return .none; |
| 6187 | 6187 | } |
| 6188 | 6188 | |
| 6189 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 6190 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 6191 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | |
| 6192 | const dest_ty = f.air.typeOf(pl_op.operand); | |
| 6193 | const dest_ptr = try f.resolveInst(pl_op.operand); | |
| 6194 | const value = try f.resolveInst(extra.lhs); | |
| 6195 | const len = try f.resolveInst(extra.rhs); | |
| 6189 | fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void { | |
| 6190 | if (ptr_ty.isSlice()) { | |
| 6191 | try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" }); | |
| 6192 | } else { | |
| 6193 | try f.writeCValue(writer, ptr, .FunctionArgument); | |
| 6194 | } | |
| 6195 | } | |
| 6196 | 6196 | |
| 6197 | fn 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 | 6206 | const writer = f.object.writer(); |
| 6198 | if (dest_ty.isVolatilePtr()) { | |
| 6199 | var u8_ptr_pl = dest_ty.ptrInfo(); | |
| 6200 | u8_ptr_pl.data.pointee_type = Type.u8; | |
| 6201 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | |
| 6207 | ||
| 6208 | if (val_is_undef) { | |
| 6209 | if (!safety) { | |
| 6210 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6211 | return .none; | |
| 6212 | } | |
| 6213 | ||
| 6214 | try writer.writeAll("memset("); | |
| 6215 | switch (dest_ty.ptrSize()) { | |
| 6216 | .Slice => { | |
| 6217 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); | |
| 6218 | try writer.writeAll(", 0xaa, "); | |
| 6219 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6220 | if (elem_abi_size > 1) { | |
| 6221 | try writer.print(" * {d});\n", .{elem_abi_size}); | |
| 6222 | } else { | |
| 6223 | try writer.writeAll(");\n"); | |
| 6224 | } | |
| 6225 | }, | |
| 6226 | .One => { | |
| 6227 | const array_ty = dest_ty.childType(); | |
| 6228 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6229 | ||
| 6230 | try f.writeCValue(writer, dest_slice, .FunctionArgument); | |
| 6231 | try writer.print(", 0xaa, {d});\n", .{len}); | |
| 6232 | }, | |
| 6233 | .Many, .C => unreachable, | |
| 6234 | } | |
| 6235 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6236 | return .none; | |
| 6237 | } | |
| 6238 | ||
| 6239 | if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) { | |
| 6240 | // For the assignment in this loop, the array pointer needs to get | |
| 6241 | // casted to a regular pointer, otherwise an error like this occurs: | |
| 6242 | // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable | |
| 6243 | var elem_ptr_ty_pl: Type.Payload.ElemType = .{ | |
| 6244 | .base = .{ .tag = .c_mut_pointer }, | |
| 6245 | .data = elem_ty, | |
| 6246 | }; | |
| 6247 | const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base); | |
| 6248 | ||
| 6202 | 6249 | const index = try f.allocLocal(inst, Type.usize); |
| 6203 | 6250 | |
| 6204 | 6251 | try writer.writeAll("for ("); |
| ... | ... | @@ -6208,56 +6255,95 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6208 | 6255 | try writer.writeAll("; "); |
| 6209 | 6256 | try f.writeCValue(writer, index, .Other); |
| 6210 | 6257 | try writer.writeAll(" != "); |
| 6211 | try f.writeCValue(writer, len, .Other); | |
| 6212 | try writer.writeAll("; "); | |
| 6258 | switch (dest_ty.ptrSize()) { | |
| 6259 | .Slice => { | |
| 6260 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6261 | }, | |
| 6262 | .One => { | |
| 6263 | const array_ty = dest_ty.childType(); | |
| 6264 | try writer.print("{d}", .{array_ty.arrayLen()}); | |
| 6265 | }, | |
| 6266 | .Many, .C => unreachable, | |
| 6267 | } | |
| 6268 | try writer.writeAll("; ++"); | |
| 6213 | 6269 | try f.writeCValue(writer, index, .Other); |
| 6214 | try writer.writeAll(" += "); | |
| 6215 | try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); | |
| 6216 | 6270 | try writer.writeAll(") (("); |
| 6217 | try f.renderType(writer, u8_ptr_ty); | |
| 6271 | try f.renderType(writer, elem_ptr_ty); | |
| 6218 | 6272 | try writer.writeByte(')'); |
| 6219 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 6273 | try writeSliceOrPtr(f, writer, dest_slice, dest_ty); | |
| 6220 | 6274 | try writer.writeAll(")["); |
| 6221 | 6275 | try f.writeCValue(writer, index, .Other); |
| 6222 | 6276 | try writer.writeAll("] = "); |
| 6223 | 6277 | try f.writeCValue(writer, value, .FunctionArgument); |
| 6224 | 6278 | try writer.writeAll(";\n"); |
| 6225 | 6279 | |
| 6226 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 6280 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6227 | 6281 | try freeLocal(f, inst, index.new_local, 0); |
| 6228 | 6282 | |
| 6229 | 6283 | return .none; |
| 6230 | 6284 | } |
| 6231 | 6285 | |
| 6232 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 6233 | 6286 | try writer.writeAll("memset("); |
| 6234 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 6235 | try writer.writeAll(", "); | |
| 6236 | try f.writeCValue(writer, value, .FunctionArgument); | |
| 6237 | try writer.writeAll(", "); | |
| 6238 | try f.writeCValue(writer, len, .FunctionArgument); | |
| 6239 | try writer.writeAll(");\n"); | |
| 6287 | switch (dest_ty.ptrSize()) { | |
| 6288 | .Slice => { | |
| 6289 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); | |
| 6290 | try writer.writeAll(", "); | |
| 6291 | try f.writeCValue(writer, value, .FunctionArgument); | |
| 6292 | try writer.writeAll(", "); | |
| 6293 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6294 | try writer.writeAll(");\n"); | |
| 6295 | }, | |
| 6296 | .One => { | |
| 6297 | const array_ty = dest_ty.childType(); | |
| 6298 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6240 | 6299 | |
| 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 | 6308 | return .none; |
| 6242 | 6309 | } |
| 6243 | 6310 | |
| 6244 | 6311 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6245 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 6246 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | |
| 6247 | const dest_ptr = try f.resolveInst(pl_op.operand); | |
| 6248 | const src_ptr = try f.resolveInst(extra.lhs); | |
| 6249 | const len = try f.resolveInst(extra.rhs); | |
| 6250 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 6312 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 6313 | const dest_ptr = try f.resolveInst(bin_op.lhs); | |
| 6314 | const src_ptr = try f.resolveInst(bin_op.rhs); | |
| 6315 | const dest_ty = f.air.typeOf(bin_op.lhs); | |
| 6316 | const src_ty = f.air.typeOf(bin_op.rhs); | |
| 6317 | const target = f.object.dg.module.getTarget(); | |
| 6251 | 6318 | const writer = f.object.writer(); |
| 6252 | 6319 | |
| 6253 | 6320 | try writer.writeAll("memcpy("); |
| 6254 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 6321 | try writeSliceOrPtr(f, writer, dest_ptr, dest_ty); | |
| 6255 | 6322 | try writer.writeAll(", "); |
| 6256 | try f.writeCValue(writer, src_ptr, .FunctionArgument); | |
| 6323 | try writeSliceOrPtr(f, writer, src_ptr, src_ty); | |
| 6257 | 6324 | try writer.writeAll(", "); |
| 6258 | try f.writeCValue(writer, len, .FunctionArgument); | |
| 6259 | try writer.writeAll(");\n"); | |
| 6325 | switch (dest_ty.ptrSize()) { | |
| 6326 | .Slice => { | |
| 6327 | const elem_ty = dest_ty.childType(); | |
| 6328 | const elem_abi_size = elem_ty.abiSize(target); | |
| 6329 | try f.writeCValueMember(writer, dest_ptr, .{ .identifier = "len" }); | |
| 6330 | if (elem_abi_size > 1) { | |
| 6331 | try writer.print(" * {d});\n", .{elem_abi_size}); | |
| 6332 | } else { | |
| 6333 | try writer.writeAll(");\n"); | |
| 6334 | } | |
| 6335 | }, | |
| 6336 | .One => { | |
| 6337 | const array_ty = dest_ty.childType(); | |
| 6338 | const elem_ty = array_ty.childType(); | |
| 6339 | const elem_abi_size = elem_ty.abiSize(target); | |
| 6340 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6341 | try writer.print("{d});\n", .{len}); | |
| 6342 | }, | |
| 6343 | .Many, .C => unreachable, | |
| 6344 | } | |
| 6260 | 6345 | |
| 6346 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6261 | 6347 | return .none; |
| 6262 | 6348 | } |
| 6263 | 6349 |
src/codegen/llvm.zig+180-71| ... | ... | @@ -4649,7 +4649,8 @@ pub const FuncGen = struct { |
| 4649 | 4649 | .not => try self.airNot(inst), |
| 4650 | 4650 | .ret => try self.airRet(inst), |
| 4651 | 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 | 4654 | .assembly => try self.airAssembly(inst), |
| 4654 | 4655 | .slice_ptr => try self.airSliceField(inst, 0), |
| 4655 | 4656 | .slice_len => try self.airSliceField(inst, 1), |
| ... | ... | @@ -4672,7 +4673,8 @@ pub const FuncGen = struct { |
| 4672 | 4673 | .fence => try self.airFence(inst), |
| 4673 | 4674 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 4674 | 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 | 4678 | .memcpy => try self.airMemcpy(inst), |
| 4677 | 4679 | .set_union_tag => try self.airSetUnionTag(inst), |
| 4678 | 4680 | .get_union_tag => try self.airGetUnionTag(inst), |
| ... | ... | @@ -5776,6 +5778,36 @@ pub const FuncGen = struct { |
| 5776 | 5778 | return result; |
| 5777 | 5779 | } |
| 5778 | 5780 | |
| 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 | 5811 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value { |
| 5780 | 5812 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5781 | 5813 | const operand = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -7261,39 +7293,53 @@ pub const FuncGen = struct { |
| 7261 | 7293 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7262 | 7294 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 7263 | 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 | 7297 | const offset = try self.resolveInst(bin_op.rhs); |
| 7266 | 7298 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 7267 | 7299 | const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType()); |
| 7268 | if (ptr_ty.ptrSize() == .One) { | |
| 7269 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | |
| 7270 | const indices: [2]*llvm.Value = .{ | |
| 7271 | self.context.intType(32).constNull(), offset, | |
| 7272 | }; | |
| 7273 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); | |
| 7274 | } else { | |
| 7275 | const indices: [1]*llvm.Value = .{offset}; | |
| 7276 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); | |
| 7300 | switch (ptr_ty.ptrSize()) { | |
| 7301 | .One => { | |
| 7302 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | |
| 7303 | const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset }; | |
| 7304 | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); | |
| 7305 | }, | |
| 7306 | .C, .Many => { | |
| 7307 | const indices: [1]*llvm.Value = .{offset}; | |
| 7308 | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); | |
| 7309 | }, | |
| 7310 | .Slice => { | |
| 7311 | const base = self.builder.buildExtractValue(ptr, 0, ""); | |
| 7312 | const indices: [1]*llvm.Value = .{offset}; | |
| 7313 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, ""); | |
| 7314 | }, | |
| 7277 | 7315 | } |
| 7278 | 7316 | } |
| 7279 | 7317 | |
| 7280 | 7318 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7281 | 7319 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 7282 | 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 | 7322 | const offset = try self.resolveInst(bin_op.rhs); |
| 7285 | 7323 | const negative_offset = self.builder.buildNeg(offset, ""); |
| 7286 | 7324 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 7287 | 7325 | const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType()); |
| 7288 | if (ptr_ty.ptrSize() == .One) { | |
| 7289 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | |
| 7290 | const indices: [2]*llvm.Value = .{ | |
| 7291 | self.context.intType(32).constNull(), negative_offset, | |
| 7292 | }; | |
| 7293 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); | |
| 7294 | } else { | |
| 7295 | const indices: [1]*llvm.Value = .{negative_offset}; | |
| 7296 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, ""); | |
| 7326 | switch (ptr_ty.ptrSize()) { | |
| 7327 | .One => { | |
| 7328 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | |
| 7329 | const indices: [2]*llvm.Value = .{ | |
| 7330 | self.context.intType(32).constNull(), negative_offset, | |
| 7331 | }; | |
| 7332 | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); | |
| 7333 | }, | |
| 7334 | .C, .Many => { | |
| 7335 | const indices: [1]*llvm.Value = .{negative_offset}; | |
| 7336 | return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, ""); | |
| 7337 | }, | |
| 7338 | .Slice => { | |
| 7339 | const base = self.builder.buildExtractValue(ptr, 0, ""); | |
| 7340 | const indices: [1]*llvm.Value = .{negative_offset}; | |
| 7341 | return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, ""); | |
| 7342 | }, | |
| 7297 | 7343 | } |
| 7298 | 7344 | } |
| 7299 | 7345 | |
| ... | ... | @@ -7887,8 +7933,10 @@ pub const FuncGen = struct { |
| 7887 | 7933 | fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7888 | 7934 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 7889 | 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 | 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 | } |
| 7893 | 7941 | |
| 7894 | 7942 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| ... | ... | @@ -8082,48 +8130,36 @@ pub const FuncGen = struct { |
| 8082 | 8130 | return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget()); |
| 8083 | 8131 | } |
| 8084 | 8132 | |
| 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 | 8134 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8087 | 8135 | const dest_ptr = try self.resolveInst(bin_op.lhs); |
| 8088 | 8136 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 8089 | 8137 | const operand_ty = ptr_ty.childType(); |
| 8090 | 8138 | |
| 8091 | // TODO Sema should emit a different instruction when the store should | |
| 8092 | // possibly do the safety 0xaa bytes for undefined. | |
| 8093 | 8139 | const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; |
| 8094 | 8140 | if (val_is_undef) { |
| 8095 | { | |
| 8096 | // TODO let's handle this in AIR rather than by having each backend | |
| 8097 | // check the optimization mode of the compilation because the plan is | |
| 8098 | // to support setting the optimization mode at finer grained scopes | |
| 8099 | // which happens in Sema. Codegen should not be aware of this logic. | |
| 8100 | // I think this comment is basically the same as the other TODO comment just | |
| 8101 | // above but I'm leaving them both here to make it look super messy and | |
| 8102 | // thereby bait contributors (or let's be honest, probably myself) into | |
| 8103 | // fixing this instead of letting it rot. | |
| 8104 | const safety = switch (self.dg.module.comp.bin_file.options.optimize_mode) { | |
| 8105 | .ReleaseSmall, .ReleaseFast => false, | |
| 8106 | .Debug, .ReleaseSafe => true, | |
| 8107 | }; | |
| 8108 | if (!safety) { | |
| 8109 | return null; | |
| 8110 | } | |
| 8111 | } | |
| 8141 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | |
| 8142 | // extra information to LLVM. However, safety makes the difference between using | |
| 8143 | // 0xaa or actual undefined for the fill byte. | |
| 8144 | const u8_llvm_ty = self.context.intType(8); | |
| 8145 | const fill_byte = if (safety) | |
| 8146 | u8_llvm_ty.constInt(0xaa, .False) | |
| 8147 | else | |
| 8148 | u8_llvm_ty.getUndef(); | |
| 8112 | 8149 | const target = self.dg.module.getTarget(); |
| 8113 | 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 | 8151 | const usize_llvm_ty = try self.dg.lowerType(Type.usize); |
| 8118 | 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()); | |
| 8120 | if (self.dg.module.comp.bin_file.options.valgrind) { | |
| 8153 | const dest_ptr_align = ptr_ty.ptrAlignment(target); | |
| 8154 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | |
| 8155 | if (safety and self.dg.module.comp.bin_file.options.valgrind) { | |
| 8121 | 8156 | self.valgrindMarkUndef(dest_ptr, len); |
| 8122 | 8157 | } |
| 8123 | } else { | |
| 8124 | const src_operand = try self.resolveInst(bin_op.rhs); | |
| 8125 | try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic); | |
| 8158 | return null; | |
| 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 | 8163 | return null; |
| 8128 | 8164 | } |
| 8129 | 8165 | |
| ... | ... | @@ -8373,34 +8409,107 @@ pub const FuncGen = struct { |
| 8373 | 8409 | return null; |
| 8374 | 8410 | } |
| 8375 | 8411 | |
| 8376 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | |
| 8377 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 8378 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 8379 | const dest_ptr = try self.resolveInst(pl_op.operand); | |
| 8380 | const ptr_ty = self.air.typeOf(pl_op.operand); | |
| 8381 | const value = try self.resolveInst(extra.lhs); | |
| 8382 | const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false; | |
| 8383 | const len = try self.resolveInst(extra.rhs); | |
| 8384 | const u8_llvm_ty = self.context.intType(8); | |
| 8385 | const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value; | |
| 8412 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value { | |
| 8413 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 8414 | const dest_slice = try self.resolveInst(bin_op.lhs); | |
| 8415 | const ptr_ty = self.air.typeOf(bin_op.lhs); | |
| 8416 | const elem_ty = self.air.typeOf(bin_op.rhs); | |
| 8386 | 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 | 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); | |
| 8389 | 8442 | |
| 8390 | if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) { | |
| 8391 | self.valgrindMarkUndef(dest_ptr, len); | |
| 8443 | if (elem_abi_size == 1) { | |
| 8444 | // In this case we can take advantage of LLVM's intrinsic. | |
| 8445 | const fill_byte = self.builder.buildBitCast(value, u8_llvm_ty, ""); | |
| 8446 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | |
| 8447 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | |
| 8448 | return null; | |
| 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 | 8501 | return null; |
| 8394 | 8502 | } |
| 8395 | 8503 | |
| 8396 | 8504 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8397 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 8398 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 8399 | const dest_ptr = try self.resolveInst(pl_op.operand); | |
| 8400 | const dest_ptr_ty = self.air.typeOf(pl_op.operand); | |
| 8401 | const src_ptr = try self.resolveInst(extra.lhs); | |
| 8402 | const src_ptr_ty = self.air.typeOf(extra.lhs); | |
| 8403 | const len = try self.resolveInst(extra.rhs); | |
| 8505 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 8506 | const dest_slice = try self.resolveInst(bin_op.lhs); | |
| 8507 | const dest_ptr_ty = self.air.typeOf(bin_op.lhs); | |
| 8508 | const src_slice = try self.resolveInst(bin_op.rhs); | |
| 8509 | const src_ptr_ty = self.air.typeOf(bin_op.rhs); | |
| 8510 | const src_ptr = self.sliceOrArrayPtr(src_slice, src_ptr_ty); | |
| 8511 | const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); | |
| 8512 | const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); | |
| 8404 | 8513 | const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr(); |
| 8405 | 8514 | const target = self.dg.module.getTarget(); |
| 8406 | 8515 | _ = self.builder.buildMemCpy( |
src/print_air.zig+4-24| ... | ... | @@ -140,6 +140,7 @@ const Writer = struct { |
| 140 | 140 | .bool_and, |
| 141 | 141 | .bool_or, |
| 142 | 142 | .store, |
| 143 | .store_safe, | |
| 143 | 144 | .array_elem_val, |
| 144 | 145 | .slice_elem_val, |
| 145 | 146 | .ptr_elem_val, |
| ... | ... | @@ -169,6 +170,9 @@ const Writer = struct { |
| 169 | 170 | .cmp_gte_optimized, |
| 170 | 171 | .cmp_gt_optimized, |
| 171 | 172 | .cmp_neq_optimized, |
| 173 | .memcpy, | |
| 174 | .memset, | |
| 175 | .memset_safe, | |
| 172 | 176 | => try w.writeBinOp(s, inst), |
| 173 | 177 | |
| 174 | 178 | .is_null, |
| ... | ... | @@ -315,8 +319,6 @@ const Writer = struct { |
| 315 | 319 | .atomic_store_release => try w.writeAtomicStore(s, inst, .Release), |
| 316 | 320 | .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst), |
| 317 | 321 | .atomic_rmw => try w.writeAtomicRmw(s, inst), |
| 318 | .memcpy => try w.writeMemcpy(s, inst), | |
| 319 | .memset => try w.writeMemset(s, inst), | |
| 320 | 322 | .field_parent_ptr => try w.writeFieldParentPtr(s, inst), |
| 321 | 323 | .wasm_memory_size => try w.writeWasmMemorySize(s, inst), |
| 322 | 324 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), |
| ... | ... | @@ -591,17 +593,6 @@ const Writer = struct { |
| 591 | 593 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); |
| 592 | 594 | } |
| 593 | 595 | |
| 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 | 596 | fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 606 | 597 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 607 | 598 | const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; |
| ... | ... | @@ -610,17 +601,6 @@ const Writer = struct { |
| 610 | 601 | try s.print(", {d}", .{extra.field_index}); |
| 611 | 602 | } |
| 612 | 603 | |
| 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 | 604 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 625 | 605 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 626 | 606 | const val = w.air.values[ty_pl.payload]; |
src/print_zir.zig+2-28| ... | ... | @@ -277,8 +277,6 @@ const Writer = struct { |
| 277 | 277 | .atomic_load => try self.writeAtomicLoad(stream, inst), |
| 278 | 278 | .atomic_store => try self.writeAtomicStore(stream, inst), |
| 279 | 279 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), |
| 280 | .memcpy => try self.writeMemcpy(stream, inst), | |
| 281 | .memset => try self.writeMemset(stream, inst), | |
| 282 | 280 | .shuffle => try self.writeShuffle(stream, inst), |
| 283 | 281 | .mul_add => try self.writeMulAdd(stream, inst), |
| 284 | 282 | .field_parent_ptr => try self.writeFieldParentPtr(stream, inst), |
| ... | ... | @@ -346,6 +344,8 @@ const Writer = struct { |
| 346 | 344 | .vector_type, |
| 347 | 345 | .max, |
| 348 | 346 | .min, |
| 347 | .memcpy, | |
| 348 | .memset, | |
| 349 | 349 | .elem_ptr_node, |
| 350 | 350 | .elem_val_node, |
| 351 | 351 | .elem_ptr, |
| ... | ... | @@ -1000,32 +1000,6 @@ const Writer = struct { |
| 1000 | 1000 | try self.writeSrc(stream, inst_data.src()); |
| 1001 | 1001 | } |
| 1002 | 1002 | |
| 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 | 1003 | fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 1030 | 1004 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1031 | 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 | 3843 | }; |
| 3844 | 3844 | } |
| 3845 | 3845 | |
| 3846 | /// Asserts the `Type` is a pointer. | |
| 3847 | pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size { | |
| 3848 | return switch (self.tag()) { | |
| 3846 | /// Asserts `ty` is a pointer. | |
| 3847 | pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size { | |
| 3848 | return ptrSizeOrNull(ty).?; | |
| 3849 | } | |
| 3850 | ||
| 3851 | /// Returns `null` if `ty` is not a pointer. | |
| 3852 | pub fn ptrSizeOrNull(ty: Type) ?std.builtin.Type.Pointer.Size { | |
| 3853 | return switch (ty.tag()) { | |
| 3849 | 3854 | .const_slice, |
| 3850 | 3855 | .mut_slice, |
| 3851 | 3856 | .const_slice_u8, |
| ... | ... | @@ -3870,9 +3875,9 @@ pub const Type = extern union { |
| 3870 | 3875 | .inferred_alloc_mut, |
| 3871 | 3876 | => .One, |
| 3872 | 3877 | |
| 3873 | .pointer => self.castTag(.pointer).?.data.size, | |
| 3878 | .pointer => ty.castTag(.pointer).?.data.size, | |
| 3874 | 3879 | |
| 3875 | else => unreachable, | |
| 3880 | else => null, | |
| 3876 | 3881 | }; |
| 3877 | 3882 | } |
| 3878 | 3883 |
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 | 353 | return (if (x) &fA else &fB)(); |
| 354 | 354 | } |
| 355 | 355 | |
| 356 | test "@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 | ||
| 369 | fn 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 | ||
| 388 | test "@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 | ||
| 402 | fn 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 | ||
| 356 | 425 | test "memcpy and memset intrinsics" { |
| 357 | 426 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 358 | 427 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 359 | 428 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 360 | 429 | |
| 361 | 430 | try testMemcpyMemset(); |
| 362 | // TODO add comptime test coverage | |
| 363 | //comptime try testMemcpyMemset(); | |
| 431 | try comptime testMemcpyMemset(); | |
| 364 | 432 | } |
| 365 | 433 | |
| 366 | 434 | fn testMemcpyMemset() !void { |
| 367 | 435 | var foo: [20]u8 = undefined; |
| 368 | 436 | var bar: [20]u8 = undefined; |
| 369 | 437 | |
| 370 | @memset(&foo, 'A', foo.len); | |
| 371 | @memcpy(&bar, &foo, bar.len); | |
| 438 | @memset(&foo, 'A'); | |
| 439 | @memcpy(&bar, &foo); | |
| 372 | 440 | |
| 373 | 441 | try expect(bar[0] == 'A'); |
| 374 | 442 | try expect(bar[11] == 'A'); |
test/behavior/bugs/718.zig+1-1| ... | ... | @@ -14,7 +14,7 @@ test "zero keys with @memset" { |
| 14 | 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 15 | 15 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 16 | 16 | |
| 17 | @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@TypeOf(keys))); | |
| 17 | @memset(@ptrCast([*]u8, &keys)[0..@sizeOf(@TypeOf(keys))], 0); | |
| 18 | 18 | try expect(!keys.up); |
| 19 | 19 | try expect(!keys.down); |
| 20 | 20 | try expect(!keys.left); |
test/behavior/builtin_functions_returning_void_or_noreturn.zig+2-2| ... | ... | @@ -17,8 +17,8 @@ test { |
| 17 | 17 | try testing.expectEqual(void, @TypeOf(@breakpoint())); |
| 18 | 18 | try testing.expectEqual({}, @export(x, .{ .name = "x" })); |
| 19 | 19 | try testing.expectEqual({}, @fence(.Acquire)); |
| 20 | try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1), @intToPtr([*]u8, 1), 0)); | |
| 21 | try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1), undefined, 0)); | |
| 20 | try testing.expectEqual({}, @memcpy(@intToPtr([*]u8, 1)[0..0], @intToPtr([*]u8, 1)[0..0])); | |
| 21 | try testing.expectEqual({}, @memset(@intToPtr([*]u8, 1)[0..0], undefined)); | |
| 22 | 22 | try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); |
| 23 | 23 | try testing.expectEqual({}, @prefetch(&val, .{})); |
| 24 | 24 | try testing.expectEqual({}, @setAlignStack(16)); |
test/behavior/struct.zig+2-2| ... | ... | @@ -91,7 +91,7 @@ test "structs" { |
| 91 | 91 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 92 | 92 | |
| 93 | 93 | var foo: StructFoo = undefined; |
| 94 | @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); | |
| 94 | @memset(@ptrCast([*]u8, &foo)[0..@sizeOf(StructFoo)], 0); | |
| 95 | 95 | foo.a += 1; |
| 96 | 96 | foo.b = foo.a == 1; |
| 97 | 97 | try testFoo(foo); |
| ... | ... | @@ -498,7 +498,7 @@ test "packed struct fields are ordered from LSB to MSB" { |
| 498 | 498 | |
| 499 | 499 | var all: u64 = 0x7765443322221111; |
| 500 | 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 | 502 | var bitfields = @ptrCast(*Bitfields, &bytes).*; |
| 503 | 503 | |
| 504 | 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 | 2 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 3 | 3 | var slice: []u8 = &buf; |
| 4 | 4 | const a: u32 = 1234; |
| 5 | @memcpy(slice, @ptrCast([*]const u8, &a), 4); | |
| 5 | @memcpy(slice.ptr, @ptrCast([*]const u8, &a)); | |
| 6 | 6 | } |
| 7 | 7 | pub export fn entry1() void { |
| 8 | 8 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 9 | 9 | var ptr: *u8 = &buf[0]; |
| 10 | @memcpy(ptr, 0, 4); | |
| 10 | @memcpy(ptr, 0); | |
| 11 | } | |
| 12 | pub 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 | } | |
| 17 | pub 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 | } |
| 12 | 22 | |
| 13 | 23 | // error |
| 14 | 24 | // backend=stage2 |
| 15 | 25 | // target=native |
| 16 | 26 | // |
| 17 | // :5:13: error: expected type '[*]u8', found '[]u8' | |
| 18 | // :10:13: error: expected type '[*]u8', found '*u8' | |
| 19 | // :10:13: note: a single pointer cannot cast into a many pointer | |
| 27 | // :5:5: error: unknown @memcpy length | |
| 28 | // :5:18: note: destination type [*]u8 provides no length | |
| 29 | // :5:24: note: source type [*]align(4) const u8 provides no length | |
| 30 | // :10:13: error: type 'u8' does not support indexing | |
| 31 | // :10:13: note: for loop operand must be an array, slice, tuple, or vector | |
| 32 | // :15:13: error: type '*u8' does not support indexing | |
| 33 | // :15:13: note: for loop operand must be an array, slice, tuple, or vector | |
| 34 | // :20:5: error: non-matching @memcpy lengths | |
| 35 | // :20:13: note: length 6 here | |
| 36 | // :20:20: note: length 5 here |
test/cases/safety/@tagName on corrupted enum value.zig	+1-1| ... | ... | @@ -15,7 +15,7 @@ const E = enum(u32) { |
| 15 | 15 | |
| 16 | 16 | pub fn main() !void { |
| 17 | 17 | var e: E = undefined; |
| 18 | @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); | |
| 18 | @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55); | |
| 19 | 19 | var n = @tagName(e); |
| 20 | 20 | _ = n; |
| 21 | 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 | 15 | |
| 16 | 16 | pub fn main() !void { |
| 17 | 17 | var u: U = undefined; |
| 18 | @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); | |
| 18 | @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55); | |
| 19 | 19 | var t: @typeInfo(U).Union.tag_type.? = u; |
| 20 | 20 | var n = @tagName(t); |
| 21 | 21 | _ = n; |
test/cases/safety/memcpy_alias.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } | |
| 10 | pub 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 | 15 | |
| 16 | 16 | pub fn main() !void { |
| 17 | 17 | var e: E = undefined; |
| 18 | @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); | |
| 18 | @memset(@ptrCast([*]u8, &e)[0..@sizeOf(E)], 0x55); | |
| 19 | 19 | switch (e) { |
| 20 | 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 | 15 | |
| 16 | 16 | pub fn main() !void { |
| 17 | 17 | var u: U = undefined; |
| 18 | @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); | |
| 18 | @memset(@ptrCast([*]u8, &u)[0..@sizeOf(U)], 0x55); | |
| 19 | 19 | switch (u) { |
| 20 | 20 | .X, .Y => @breakpoint(), |
| 21 | 21 | } |