| author | |
| committer | |
| log | d2278f21560014c635afe4bc247a9096ecb845de |
| tree | e8bca82dcc8d09f6f287054b4c2ba171c190bf37 |
| parent | 0ff5d7b24e2017199566d1bab75254fa5ef68611 |
| parent | 05d284c842a5ba21cd836c2b212daa24227a9177 |
| signature |
Stage2 garbage collect decls12 files changed, 1043 insertions(+), 445 deletions(-)
lib/std/comptime_string_map.zig+24-24| ... | ... | @@ -17,18 +17,18 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type { |
| 17 | 17 | }; |
| 18 | 18 | var sorted_kvs: [kvs.len]KV = undefined; |
| 19 | 19 | const lenAsc = (struct { |
| 20 | fn lenAsc(a: KV, b: KV) bool { | |
| 20 | fn lenAsc(context: void, a: KV, b: KV) bool { | |
| 21 | 21 | return a.key.len < b.key.len; |
| 22 | 22 | } |
| 23 | 23 | }).lenAsc; |
| 24 | 24 | for (kvs) |kv, i| { |
| 25 | 25 | if (V != void) { |
| 26 | sorted_kvs[i] = .{.key = kv.@"0", .value = kv.@"1"}; | |
| 26 | sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" }; | |
| 27 | 27 | } else { |
| 28 | sorted_kvs[i] = .{.key = kv.@"0", .value = {}}; | |
| 28 | sorted_kvs[i] = .{ .key = kv.@"0", .value = {} }; | |
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | std.sort.sort(KV, &sorted_kvs, lenAsc); | |
| 31 | std.sort.sort(KV, &sorted_kvs, {}, lenAsc); | |
| 32 | 32 | const min_len = sorted_kvs[0].key.len; |
| 33 | 33 | const max_len = sorted_kvs[sorted_kvs.len - 1].key.len; |
| 34 | 34 | var len_indexes: [max_len + 1]usize = undefined; |
| ... | ... | @@ -83,11 +83,11 @@ const TestEnum = enum { |
| 83 | 83 | |
| 84 | 84 | test "ComptimeStringMap list literal of list literals" { |
| 85 | 85 | const map = ComptimeStringMap(TestEnum, .{ |
| 86 | .{"these", .D}, | |
| 87 | .{"have", .A}, | |
| 88 | .{"nothing", .B}, | |
| 89 | .{"incommon", .C}, | |
| 90 | .{"samelen", .E}, | |
| 86 | .{ "these", .D }, | |
| 87 | .{ "have", .A }, | |
| 88 | .{ "nothing", .B }, | |
| 89 | .{ "incommon", .C }, | |
| 90 | .{ "samelen", .E }, | |
| 91 | 91 | }); |
| 92 | 92 | |
| 93 | 93 | testMap(map); |
| ... | ... | @@ -99,11 +99,11 @@ test "ComptimeStringMap array of structs" { |
| 99 | 99 | @"1": TestEnum, |
| 100 | 100 | }; |
| 101 | 101 | const map = ComptimeStringMap(TestEnum, [_]KV{ |
| 102 | .{.@"0" = "these", .@"1" = .D}, | |
| 103 | .{.@"0" = "have", .@"1" = .A}, | |
| 104 | .{.@"0" = "nothing", .@"1" = .B}, | |
| 105 | .{.@"0" = "incommon", .@"1" = .C}, | |
| 106 | .{.@"0" = "samelen", .@"1" = .E}, | |
| 102 | .{ .@"0" = "these", .@"1" = .D }, | |
| 103 | .{ .@"0" = "have", .@"1" = .A }, | |
| 104 | .{ .@"0" = "nothing", .@"1" = .B }, | |
| 105 | .{ .@"0" = "incommon", .@"1" = .C }, | |
| 106 | .{ .@"0" = "samelen", .@"1" = .E }, | |
| 107 | 107 | }); |
| 108 | 108 | |
| 109 | 109 | testMap(map); |
| ... | ... | @@ -115,11 +115,11 @@ test "ComptimeStringMap slice of structs" { |
| 115 | 115 | @"1": TestEnum, |
| 116 | 116 | }; |
| 117 | 117 | const slice: []const KV = &[_]KV{ |
| 118 | .{.@"0" = "these", .@"1" = .D}, | |
| 119 | .{.@"0" = "have", .@"1" = .A}, | |
| 120 | .{.@"0" = "nothing", .@"1" = .B}, | |
| 121 | .{.@"0" = "incommon", .@"1" = .C}, | |
| 122 | .{.@"0" = "samelen", .@"1" = .E}, | |
| 118 | .{ .@"0" = "these", .@"1" = .D }, | |
| 119 | .{ .@"0" = "have", .@"1" = .A }, | |
| 120 | .{ .@"0" = "nothing", .@"1" = .B }, | |
| 121 | .{ .@"0" = "incommon", .@"1" = .C }, | |
| 122 | .{ .@"0" = "samelen", .@"1" = .E }, | |
| 123 | 123 | }; |
| 124 | 124 | const map = ComptimeStringMap(TestEnum, slice); |
| 125 | 125 | |
| ... | ... | @@ -142,11 +142,11 @@ test "ComptimeStringMap void value type, slice of structs" { |
| 142 | 142 | @"0": []const u8, |
| 143 | 143 | }; |
| 144 | 144 | const slice: []const KV = &[_]KV{ |
| 145 | .{.@"0" = "these"}, | |
| 146 | .{.@"0" = "have"}, | |
| 147 | .{.@"0" = "nothing"}, | |
| 148 | .{.@"0" = "incommon"}, | |
| 149 | .{.@"0" = "samelen"}, | |
| 145 | .{ .@"0" = "these" }, | |
| 146 | .{ .@"0" = "have" }, | |
| 147 | .{ .@"0" = "nothing" }, | |
| 148 | .{ .@"0" = "incommon" }, | |
| 149 | .{ .@"0" = "samelen" }, | |
| 150 | 150 | }; |
| 151 | 151 | const map = ComptimeStringMap(void, slice); |
| 152 | 152 |
lib/std/debug.zig+3-3| ... | ... | @@ -1003,7 +1003,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI |
| 1003 | 1003 | // Even though lld emits symbols in ascending order, this debug code |
| 1004 | 1004 | // should work for programs linked in any valid way. |
| 1005 | 1005 | // This sort is so that we can binary search later. |
| 1006 | std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); | |
| 1006 | std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressLessThan); | |
| 1007 | 1007 | |
| 1008 | 1008 | return ModuleDebugInfo{ |
| 1009 | 1009 | .base_address = undefined, |
| ... | ... | @@ -1058,7 +1058,7 @@ const MachoSymbol = struct { |
| 1058 | 1058 | return self.nlist.n_value; |
| 1059 | 1059 | } |
| 1060 | 1060 | |
| 1061 | fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool { | |
| 1061 | fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool { | |
| 1062 | 1062 | return lhs.address() < rhs.address(); |
| 1063 | 1063 | } |
| 1064 | 1064 | }; |
| ... | ... | @@ -1300,7 +1300,7 @@ pub const DebugInfo = struct { |
| 1300 | 1300 | fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking }) |
| 1301 | 1301 | else |
| 1302 | 1302 | fs.openSelfExe(.{ .intended_io_mode = .blocking }); |
| 1303 | ||
| 1303 | ||
| 1304 | 1304 | const elf_file = copy catch |err| switch (err) { |
| 1305 | 1305 | error.FileNotFound => return error.MissingDebugInfo, |
| 1306 | 1306 | else => return err, |
lib/std/http/headers.zig+2-2| ... | ... | @@ -58,7 +58,7 @@ const HeaderEntry = struct { |
| 58 | 58 | self.never_index = never_index orelse never_index_default(self.name); |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | fn compare(a: HeaderEntry, b: HeaderEntry) bool { | |
| 61 | fn compare(context: void, a: HeaderEntry, b: HeaderEntry) bool { | |
| 62 | 62 | if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) { |
| 63 | 63 | // Things beginning with a colon *must* be before others |
| 64 | 64 | const a_is_colon = a.name[0] == ':'; |
| ... | ... | @@ -342,7 +342,7 @@ pub const Headers = struct { |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | 344 | pub fn sort(self: *Self) void { |
| 345 | std.sort.sort(HeaderEntry, self.data.items, HeaderEntry.compare); | |
| 345 | std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare); | |
| 346 | 346 | self.rebuild_index(); |
| 347 | 347 | } |
| 348 | 348 |
lib/std/net.zig+2-2| ... | ... | @@ -836,7 +836,7 @@ fn linuxLookupName( |
| 836 | 836 | key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT; |
| 837 | 837 | addr.sortkey = key; |
| 838 | 838 | } |
| 839 | std.sort.sort(LookupAddr, addrs.span(), addrCmpLessThan); | |
| 839 | std.sort.sort(LookupAddr, addrs.span(), {}, addrCmpLessThan); | |
| 840 | 840 | } |
| 841 | 841 | |
| 842 | 842 | const Policy = struct { |
| ... | ... | @@ -953,7 +953,7 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool { |
| 953 | 953 | } |
| 954 | 954 | |
| 955 | 955 | // Parameters `b` and `a` swapped to make this descending. |
| 956 | fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool { | |
| 956 | fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool { | |
| 957 | 957 | return a.sortkey < b.sortkey; |
| 958 | 958 | } |
| 959 | 959 |
lib/std/sort.zig+351-219| ... | ... | @@ -5,7 +5,13 @@ const mem = std.mem; |
| 5 | 5 | const math = std.math; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | |
| 8 | pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compareFn: fn (lhs: T, rhs: T) math.Order) ?usize { | |
| 8 | pub fn binarySearch( | |
| 9 | comptime T: type, | |
| 10 | key: T, | |
| 11 | items: []const T, | |
| 12 | context: var, | |
| 13 | comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order, | |
| 14 | ) ?usize { | |
| 9 | 15 | var left: usize = 0; |
| 10 | 16 | var right: usize = items.len; |
| 11 | 17 | |
| ... | ... | @@ -13,7 +19,7 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare |
| 13 | 19 | // Avoid overflowing in the midpoint calculation |
| 14 | 20 | const mid = left + (right - left) / 2; |
| 15 | 21 | // Compare the key with the midpoint element |
| 16 | switch (compareFn(key, items[mid])) { | |
| 22 | switch (compareFn(context, key, items[mid])) { | |
| 17 | 23 | .eq => return mid, |
| 18 | 24 | .gt => left = mid + 1, |
| 19 | 25 | .lt => right = mid, |
| ... | ... | @@ -23,56 +29,61 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare |
| 23 | 29 | return null; |
| 24 | 30 | } |
| 25 | 31 | |
| 26 | test "std.sort.binarySearch" { | |
| 32 | test "binarySearch" { | |
| 27 | 33 | const S = struct { |
| 28 | fn order_u32(lhs: u32, rhs: u32) math.Order { | |
| 34 | fn order_u32(context: void, lhs: u32, rhs: u32) math.Order { | |
| 29 | 35 | return math.order(lhs, rhs); |
| 30 | 36 | } |
| 31 | fn order_i32(lhs: i32, rhs: i32) math.Order { | |
| 37 | fn order_i32(context: void, lhs: i32, rhs: i32) math.Order { | |
| 32 | 38 | return math.order(lhs, rhs); |
| 33 | 39 | } |
| 34 | 40 | }; |
| 35 | 41 | testing.expectEqual( |
| 36 | 42 | @as(?usize, null), |
| 37 | binarySearch(u32, 1, &[_]u32{}, S.order_u32), | |
| 43 | binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32), | |
| 38 | 44 | ); |
| 39 | 45 | testing.expectEqual( |
| 40 | 46 | @as(?usize, 0), |
| 41 | binarySearch(u32, 1, &[_]u32{1}, S.order_u32), | |
| 47 | binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32), | |
| 42 | 48 | ); |
| 43 | 49 | testing.expectEqual( |
| 44 | 50 | @as(?usize, null), |
| 45 | binarySearch(u32, 1, &[_]u32{0}, S.order_u32), | |
| 51 | binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32), | |
| 46 | 52 | ); |
| 47 | 53 | testing.expectEqual( |
| 48 | 54 | @as(?usize, null), |
| 49 | binarySearch(u32, 0, &[_]u32{1}, S.order_u32), | |
| 55 | binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32), | |
| 50 | 56 | ); |
| 51 | 57 | testing.expectEqual( |
| 52 | 58 | @as(?usize, 4), |
| 53 | binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, S.order_u32), | |
| 59 | binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32), | |
| 54 | 60 | ); |
| 55 | 61 | testing.expectEqual( |
| 56 | 62 | @as(?usize, 0), |
| 57 | binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, S.order_u32), | |
| 63 | binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32), | |
| 58 | 64 | ); |
| 59 | 65 | testing.expectEqual( |
| 60 | 66 | @as(?usize, 1), |
| 61 | binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, S.order_i32), | |
| 67 | binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32), | |
| 62 | 68 | ); |
| 63 | 69 | testing.expectEqual( |
| 64 | 70 | @as(?usize, 3), |
| 65 | binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, S.order_i32), | |
| 71 | binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32), | |
| 66 | 72 | ); |
| 67 | 73 | } |
| 68 | 74 | |
| 69 | 75 | /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). |
| 70 | pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void { | |
| 76 | pub fn insertionSort( | |
| 77 | comptime T: type, | |
| 78 | items: []T, | |
| 79 | context: var, | |
| 80 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 81 | ) void { | |
| 71 | 82 | var i: usize = 1; |
| 72 | 83 | while (i < items.len) : (i += 1) { |
| 73 | 84 | const x = items[i]; |
| 74 | 85 | var j: usize = i; |
| 75 | while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) { | |
| 86 | while (j > 0 and lessThan(context, x, items[j - 1])) : (j -= 1) { | |
| 76 | 87 | items[j] = items[j - 1]; |
| 77 | 88 | } |
| 78 | 89 | items[j] = x; |
| ... | ... | @@ -168,20 +179,25 @@ const Pull = struct { |
| 168 | 179 | |
| 169 | 180 | /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). |
| 170 | 181 | /// Currently implemented as block sort. |
| 171 | pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void { | |
| 182 | pub fn sort( | |
| 183 | comptime T: type, | |
| 184 | items: []T, | |
| 185 | context: var, | |
| 186 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 187 | ) void { | |
| 172 | 188 | // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c |
| 173 | 189 | var cache: [512]T = undefined; |
| 174 | 190 | |
| 175 | 191 | if (items.len < 4) { |
| 176 | 192 | if (items.len == 3) { |
| 177 | 193 | // hard coded insertion sort |
| 178 | if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 179 | if (lessThan(items[2], items[1])) { | |
| 194 | if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 195 | if (lessThan(context, items[2], items[1])) { | |
| 180 | 196 | mem.swap(T, &items[1], &items[2]); |
| 181 | if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 197 | if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 182 | 198 | } |
| 183 | 199 | } else if (items.len == 2) { |
| 184 | if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 200 | if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]); | |
| 185 | 201 | } |
| 186 | 202 | return; |
| 187 | 203 | } |
| ... | ... | @@ -197,75 +213,75 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 197 | 213 | const sliced_items = items[range.start..]; |
| 198 | 214 | switch (range.length()) { |
| 199 | 215 | 8 => { |
| 200 | swap(T, sliced_items, lessThan, &order, 0, 1); | |
| 201 | swap(T, sliced_items, lessThan, &order, 2, 3); | |
| 202 | swap(T, sliced_items, lessThan, &order, 4, 5); | |
| 203 | swap(T, sliced_items, lessThan, &order, 6, 7); | |
| 204 | swap(T, sliced_items, lessThan, &order, 0, 2); | |
| 205 | swap(T, sliced_items, lessThan, &order, 1, 3); | |
| 206 | swap(T, sliced_items, lessThan, &order, 4, 6); | |
| 207 | swap(T, sliced_items, lessThan, &order, 5, 7); | |
| 208 | swap(T, sliced_items, lessThan, &order, 1, 2); | |
| 209 | swap(T, sliced_items, lessThan, &order, 5, 6); | |
| 210 | swap(T, sliced_items, lessThan, &order, 0, 4); | |
| 211 | swap(T, sliced_items, lessThan, &order, 3, 7); | |
| 212 | swap(T, sliced_items, lessThan, &order, 1, 5); | |
| 213 | swap(T, sliced_items, lessThan, &order, 2, 6); | |
| 214 | swap(T, sliced_items, lessThan, &order, 1, 4); | |
| 215 | swap(T, sliced_items, lessThan, &order, 3, 6); | |
| 216 | swap(T, sliced_items, lessThan, &order, 2, 4); | |
| 217 | swap(T, sliced_items, lessThan, &order, 3, 5); | |
| 218 | swap(T, sliced_items, lessThan, &order, 3, 4); | |
| 216 | swap(T, sliced_items, context, lessThan, &order, 0, 1); | |
| 217 | swap(T, sliced_items, context, lessThan, &order, 2, 3); | |
| 218 | swap(T, sliced_items, context, lessThan, &order, 4, 5); | |
| 219 | swap(T, sliced_items, context, lessThan, &order, 6, 7); | |
| 220 | swap(T, sliced_items, context, lessThan, &order, 0, 2); | |
| 221 | swap(T, sliced_items, context, lessThan, &order, 1, 3); | |
| 222 | swap(T, sliced_items, context, lessThan, &order, 4, 6); | |
| 223 | swap(T, sliced_items, context, lessThan, &order, 5, 7); | |
| 224 | swap(T, sliced_items, context, lessThan, &order, 1, 2); | |
| 225 | swap(T, sliced_items, context, lessThan, &order, 5, 6); | |
| 226 | swap(T, sliced_items, context, lessThan, &order, 0, 4); | |
| 227 | swap(T, sliced_items, context, lessThan, &order, 3, 7); | |
| 228 | swap(T, sliced_items, context, lessThan, &order, 1, 5); | |
| 229 | swap(T, sliced_items, context, lessThan, &order, 2, 6); | |
| 230 | swap(T, sliced_items, context, lessThan, &order, 1, 4); | |
| 231 | swap(T, sliced_items, context, lessThan, &order, 3, 6); | |
| 232 | swap(T, sliced_items, context, lessThan, &order, 2, 4); | |
| 233 | swap(T, sliced_items, context, lessThan, &order, 3, 5); | |
| 234 | swap(T, sliced_items, context, lessThan, &order, 3, 4); | |
| 219 | 235 | }, |
| 220 | 236 | 7 => { |
| 221 | swap(T, sliced_items, lessThan, &order, 1, 2); | |
| 222 | swap(T, sliced_items, lessThan, &order, 3, 4); | |
| 223 | swap(T, sliced_items, lessThan, &order, 5, 6); | |
| 224 | swap(T, sliced_items, lessThan, &order, 0, 2); | |
| 225 | swap(T, sliced_items, lessThan, &order, 3, 5); | |
| 226 | swap(T, sliced_items, lessThan, &order, 4, 6); | |
| 227 | swap(T, sliced_items, lessThan, &order, 0, 1); | |
| 228 | swap(T, sliced_items, lessThan, &order, 4, 5); | |
| 229 | swap(T, sliced_items, lessThan, &order, 2, 6); | |
| 230 | swap(T, sliced_items, lessThan, &order, 0, 4); | |
| 231 | swap(T, sliced_items, lessThan, &order, 1, 5); | |
| 232 | swap(T, sliced_items, lessThan, &order, 0, 3); | |
| 233 | swap(T, sliced_items, lessThan, &order, 2, 5); | |
| 234 | swap(T, sliced_items, lessThan, &order, 1, 3); | |
| 235 | swap(T, sliced_items, lessThan, &order, 2, 4); | |
| 236 | swap(T, sliced_items, lessThan, &order, 2, 3); | |
| 237 | swap(T, sliced_items, context, lessThan, &order, 1, 2); | |
| 238 | swap(T, sliced_items, context, lessThan, &order, 3, 4); | |
| 239 | swap(T, sliced_items, context, lessThan, &order, 5, 6); | |
| 240 | swap(T, sliced_items, context, lessThan, &order, 0, 2); | |
| 241 | swap(T, sliced_items, context, lessThan, &order, 3, 5); | |
| 242 | swap(T, sliced_items, context, lessThan, &order, 4, 6); | |
| 243 | swap(T, sliced_items, context, lessThan, &order, 0, 1); | |
| 244 | swap(T, sliced_items, context, lessThan, &order, 4, 5); | |
| 245 | swap(T, sliced_items, context, lessThan, &order, 2, 6); | |
| 246 | swap(T, sliced_items, context, lessThan, &order, 0, 4); | |
| 247 | swap(T, sliced_items, context, lessThan, &order, 1, 5); | |
| 248 | swap(T, sliced_items, context, lessThan, &order, 0, 3); | |
| 249 | swap(T, sliced_items, context, lessThan, &order, 2, 5); | |
| 250 | swap(T, sliced_items, context, lessThan, &order, 1, 3); | |
| 251 | swap(T, sliced_items, context, lessThan, &order, 2, 4); | |
| 252 | swap(T, sliced_items, context, lessThan, &order, 2, 3); | |
| 237 | 253 | }, |
| 238 | 254 | 6 => { |
| 239 | swap(T, sliced_items, lessThan, &order, 1, 2); | |
| 240 | swap(T, sliced_items, lessThan, &order, 4, 5); | |
| 241 | swap(T, sliced_items, lessThan, &order, 0, 2); | |
| 242 | swap(T, sliced_items, lessThan, &order, 3, 5); | |
| 243 | swap(T, sliced_items, lessThan, &order, 0, 1); | |
| 244 | swap(T, sliced_items, lessThan, &order, 3, 4); | |
| 245 | swap(T, sliced_items, lessThan, &order, 2, 5); | |
| 246 | swap(T, sliced_items, lessThan, &order, 0, 3); | |
| 247 | swap(T, sliced_items, lessThan, &order, 1, 4); | |
| 248 | swap(T, sliced_items, lessThan, &order, 2, 4); | |
| 249 | swap(T, sliced_items, lessThan, &order, 1, 3); | |
| 250 | swap(T, sliced_items, lessThan, &order, 2, 3); | |
| 255 | swap(T, sliced_items, context, lessThan, &order, 1, 2); | |
| 256 | swap(T, sliced_items, context, lessThan, &order, 4, 5); | |
| 257 | swap(T, sliced_items, context, lessThan, &order, 0, 2); | |
| 258 | swap(T, sliced_items, context, lessThan, &order, 3, 5); | |
| 259 | swap(T, sliced_items, context, lessThan, &order, 0, 1); | |
| 260 | swap(T, sliced_items, context, lessThan, &order, 3, 4); | |
| 261 | swap(T, sliced_items, context, lessThan, &order, 2, 5); | |
| 262 | swap(T, sliced_items, context, lessThan, &order, 0, 3); | |
| 263 | swap(T, sliced_items, context, lessThan, &order, 1, 4); | |
| 264 | swap(T, sliced_items, context, lessThan, &order, 2, 4); | |
| 265 | swap(T, sliced_items, context, lessThan, &order, 1, 3); | |
| 266 | swap(T, sliced_items, context, lessThan, &order, 2, 3); | |
| 251 | 267 | }, |
| 252 | 268 | 5 => { |
| 253 | swap(T, sliced_items, lessThan, &order, 0, 1); | |
| 254 | swap(T, sliced_items, lessThan, &order, 3, 4); | |
| 255 | swap(T, sliced_items, lessThan, &order, 2, 4); | |
| 256 | swap(T, sliced_items, lessThan, &order, 2, 3); | |
| 257 | swap(T, sliced_items, lessThan, &order, 1, 4); | |
| 258 | swap(T, sliced_items, lessThan, &order, 0, 3); | |
| 259 | swap(T, sliced_items, lessThan, &order, 0, 2); | |
| 260 | swap(T, sliced_items, lessThan, &order, 1, 3); | |
| 261 | swap(T, sliced_items, lessThan, &order, 1, 2); | |
| 269 | swap(T, sliced_items, context, lessThan, &order, 0, 1); | |
| 270 | swap(T, sliced_items, context, lessThan, &order, 3, 4); | |
| 271 | swap(T, sliced_items, context, lessThan, &order, 2, 4); | |
| 272 | swap(T, sliced_items, context, lessThan, &order, 2, 3); | |
| 273 | swap(T, sliced_items, context, lessThan, &order, 1, 4); | |
| 274 | swap(T, sliced_items, context, lessThan, &order, 0, 3); | |
| 275 | swap(T, sliced_items, context, lessThan, &order, 0, 2); | |
| 276 | swap(T, sliced_items, context, lessThan, &order, 1, 3); | |
| 277 | swap(T, sliced_items, context, lessThan, &order, 1, 2); | |
| 262 | 278 | }, |
| 263 | 279 | 4 => { |
| 264 | swap(T, sliced_items, lessThan, &order, 0, 1); | |
| 265 | swap(T, sliced_items, lessThan, &order, 2, 3); | |
| 266 | swap(T, sliced_items, lessThan, &order, 0, 2); | |
| 267 | swap(T, sliced_items, lessThan, &order, 1, 3); | |
| 268 | swap(T, sliced_items, lessThan, &order, 1, 2); | |
| 280 | swap(T, sliced_items, context, lessThan, &order, 0, 1); | |
| 281 | swap(T, sliced_items, context, lessThan, &order, 2, 3); | |
| 282 | swap(T, sliced_items, context, lessThan, &order, 0, 2); | |
| 283 | swap(T, sliced_items, context, lessThan, &order, 1, 3); | |
| 284 | swap(T, sliced_items, context, lessThan, &order, 1, 2); | |
| 269 | 285 | }, |
| 270 | 286 | else => {}, |
| 271 | 287 | } |
| ... | ... | @@ -288,16 +304,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 288 | 304 | var A2 = iterator.nextRange(); |
| 289 | 305 | var B2 = iterator.nextRange(); |
| 290 | 306 | |
| 291 | if (lessThan(items[B1.end - 1], items[A1.start])) { | |
| 307 | if (lessThan(context, items[B1.end - 1], items[A1.start])) { | |
| 292 | 308 | // the two ranges are in reverse order, so copy them in reverse order into the cache |
| 293 | 309 | mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]); |
| 294 | 310 | mem.copy(T, cache[0..], items[B1.start..B1.end]); |
| 295 | } else if (lessThan(items[B1.start], items[A1.end - 1])) { | |
| 311 | } else if (lessThan(context, items[B1.start], items[A1.end - 1])) { | |
| 296 | 312 | // these two ranges weren't already in order, so merge them into the cache |
| 297 | mergeInto(T, items, A1, B1, lessThan, cache[0..]); | |
| 313 | mergeInto(T, items, A1, B1, context, lessThan, cache[0..]); | |
| 298 | 314 | } else { |
| 299 | 315 | // if A1, B1, A2, and B2 are all in order, skip doing anything else |
| 300 | if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue; | |
| 316 | if (!lessThan(context, items[B2.start], items[A2.end - 1]) and !lessThan(context, items[A2.start], items[B1.end - 1])) continue; | |
| 301 | 317 | |
| 302 | 318 | // copy A1 and B1 into the cache in the same order |
| 303 | 319 | mem.copy(T, cache[0..], items[A1.start..A1.end]); |
| ... | ... | @@ -306,13 +322,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 306 | 322 | A1 = Range.init(A1.start, B1.end); |
| 307 | 323 | |
| 308 | 324 | // merge A2 and B2 into the cache |
| 309 | if (lessThan(items[B2.end - 1], items[A2.start])) { | |
| 325 | if (lessThan(context, items[B2.end - 1], items[A2.start])) { | |
| 310 | 326 | // the two ranges are in reverse order, so copy them in reverse order into the cache |
| 311 | 327 | mem.copy(T, cache[A1.length() + B2.length() ..], items[A2.start..A2.end]); |
| 312 | 328 | mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]); |
| 313 | } else if (lessThan(items[B2.start], items[A2.end - 1])) { | |
| 329 | } else if (lessThan(context, items[B2.start], items[A2.end - 1])) { | |
| 314 | 330 | // these two ranges weren't already in order, so merge them into the cache |
| 315 | mergeInto(T, items, A2, B2, lessThan, cache[A1.length()..]); | |
| 331 | mergeInto(T, items, A2, B2, context, lessThan, cache[A1.length()..]); | |
| 316 | 332 | } else { |
| 317 | 333 | // copy A2 and B2 into the cache in the same order |
| 318 | 334 | mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]); |
| ... | ... | @@ -324,13 +340,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 324 | 340 | const A3 = Range.init(0, A1.length()); |
| 325 | 341 | const B3 = Range.init(A1.length(), A1.length() + A2.length()); |
| 326 | 342 | |
| 327 | if (lessThan(cache[B3.end - 1], cache[A3.start])) { | |
| 343 | if (lessThan(context, cache[B3.end - 1], cache[A3.start])) { | |
| 328 | 344 | // the two ranges are in reverse order, so copy them in reverse order into the items |
| 329 | 345 | mem.copy(T, items[A1.start + A2.length() ..], cache[A3.start..A3.end]); |
| 330 | 346 | mem.copy(T, items[A1.start..], cache[B3.start..B3.end]); |
| 331 | } else if (lessThan(cache[B3.start], cache[A3.end - 1])) { | |
| 347 | } else if (lessThan(context, cache[B3.start], cache[A3.end - 1])) { | |
| 332 | 348 | // these two ranges weren't already in order, so merge them back into the items |
| 333 | mergeInto(T, cache[0..], A3, B3, lessThan, items[A1.start..]); | |
| 349 | mergeInto(T, cache[0..], A3, B3, context, lessThan, items[A1.start..]); | |
| 334 | 350 | } else { |
| 335 | 351 | // copy A3 and B3 into the items in the same order |
| 336 | 352 | mem.copy(T, items[A1.start..], cache[A3.start..A3.end]); |
| ... | ... | @@ -347,13 +363,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 347 | 363 | var A = iterator.nextRange(); |
| 348 | 364 | var B = iterator.nextRange(); |
| 349 | 365 | |
| 350 | if (lessThan(items[B.end - 1], items[A.start])) { | |
| 366 | if (lessThan(context, items[B.end - 1], items[A.start])) { | |
| 351 | 367 | // the two ranges are in reverse order, so a simple rotation should fix it |
| 352 | 368 | mem.rotate(T, items[A.start..B.end], A.length()); |
| 353 | } else if (lessThan(items[B.start], items[A.end - 1])) { | |
| 369 | } else if (lessThan(context, items[B.start], items[A.end - 1])) { | |
| 354 | 370 | // these two ranges weren't already in order, so we'll need to merge them! |
| 355 | 371 | mem.copy(T, cache[0..], items[A.start..A.end]); |
| 356 | mergeExternal(T, items, A, B, lessThan, cache[0..]); | |
| 372 | mergeExternal(T, items, A, B, context, lessThan, cache[0..]); | |
| 357 | 373 | } |
| 358 | 374 | } |
| 359 | 375 | } |
| ... | ... | @@ -435,7 +451,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 435 | 451 | last = index; |
| 436 | 452 | count += 1; |
| 437 | 453 | }) { |
| 438 | index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), lessThan, find - count); | |
| 454 | index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), context, lessThan, find - count); | |
| 439 | 455 | if (index == A.end) break; |
| 440 | 456 | } |
| 441 | 457 | index = last; |
| ... | ... | @@ -493,7 +509,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 493 | 509 | last = index - 1; |
| 494 | 510 | count += 1; |
| 495 | 511 | }) { |
| 496 | index = findFirstBackward(T, items, items[last], Range.init(B.start, last), lessThan, find - count); | |
| 512 | index = findFirstBackward(T, items, items[last], Range.init(B.start, last), context, lessThan, find - count); | |
| 497 | 513 | if (index == B.start) break; |
| 498 | 514 | } |
| 499 | 515 | index = last; |
| ... | ... | @@ -558,7 +574,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 558 | 574 | index = pull[pull_index].from; |
| 559 | 575 | count = 1; |
| 560 | 576 | while (count < length) : (count += 1) { |
| 561 | index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), lessThan, length - count); | |
| 577 | index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), context, lessThan, length - count); | |
| 562 | 578 | const range = Range.init(index + 1, pull[pull_index].from + 1); |
| 563 | 579 | mem.rotate(T, items[range.start..range.end], range.length() - count); |
| 564 | 580 | pull[pull_index].from = index + count; |
| ... | ... | @@ -568,7 +584,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 568 | 584 | index = pull[pull_index].from + 1; |
| 569 | 585 | count = 1; |
| 570 | 586 | while (count < length) : (count += 1) { |
| 571 | index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), lessThan, length - count); | |
| 587 | index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), context, lessThan, length - count); | |
| 572 | 588 | const range = Range.init(pull[pull_index].from, index - 1); |
| 573 | 589 | mem.rotate(T, items[range.start..range.end], count); |
| 574 | 590 | pull[pull_index].from = index - 1 - count; |
| ... | ... | @@ -615,10 +631,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 615 | 631 | } |
| 616 | 632 | } |
| 617 | 633 | |
| 618 | if (lessThan(items[B.end - 1], items[A.start])) { | |
| 634 | if (lessThan(context, items[B.end - 1], items[A.start])) { | |
| 619 | 635 | // the two ranges are in reverse order, so a simple rotation should fix it |
| 620 | 636 | mem.rotate(T, items[A.start..B.end], A.length()); |
| 621 | } else if (lessThan(items[A.end], items[A.end - 1])) { | |
| 637 | } else if (lessThan(context, items[A.end], items[A.end - 1])) { | |
| 622 | 638 | // these two ranges weren't already in order, so we'll need to merge them! |
| 623 | 639 | var findA: usize = undefined; |
| 624 | 640 | |
| ... | ... | @@ -656,16 +672,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 656 | 672 | while (true) { |
| 657 | 673 | // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block, |
| 658 | 674 | // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks. |
| 659 | if ((lastB.length() > 0 and !lessThan(items[lastB.end - 1], items[indexA])) or blockB.length() == 0) { | |
| 675 | if ((lastB.length() > 0 and !lessThan(context, items[lastB.end - 1], items[indexA])) or blockB.length() == 0) { | |
| 660 | 676 | // figure out where to split the previous B block, and rotate it at the split |
| 661 | const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan); | |
| 677 | const B_split = binaryFirst(T, items, items[indexA], lastB, context, lessThan); | |
| 662 | 678 | const B_remaining = lastB.end - B_split; |
| 663 | 679 | |
| 664 | 680 | // swap the minimum A block to the beginning of the rolling A blocks |
| 665 | 681 | var minA = blockA.start; |
| 666 | 682 | findA = minA + block_size; |
| 667 | 683 | while (findA < blockA.end) : (findA += block_size) { |
| 668 | if (lessThan(items[findA], items[minA])) { | |
| 684 | if (lessThan(context, items[findA], items[minA])) { | |
| 669 | 685 | minA = findA; |
| 670 | 686 | } |
| 671 | 687 | } |
| ... | ... | @@ -681,11 +697,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 681 | 697 | // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace) |
| 682 | 698 | |
| 683 | 699 | if (lastA.length() <= cache.len) { |
| 684 | mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]); | |
| 700 | mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, cache[0..]); | |
| 685 | 701 | } else if (buffer2.length() > 0) { |
| 686 | mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, buffer2); | |
| 702 | mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, buffer2); | |
| 687 | 703 | } else { |
| 688 | mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan); | |
| 704 | mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan); | |
| 689 | 705 | } |
| 690 | 706 | |
| 691 | 707 | if (buffer2.length() > 0 or block_size <= cache.len) { |
| ... | ... | @@ -741,11 +757,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 741 | 757 | |
| 742 | 758 | // merge the last A block with the remaining B values |
| 743 | 759 | if (lastA.length() <= cache.len) { |
| 744 | mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]); | |
| 760 | mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, cache[0..]); | |
| 745 | 761 | } else if (buffer2.length() > 0) { |
| 746 | mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, buffer2); | |
| 762 | mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, buffer2); | |
| 747 | 763 | } else { |
| 748 | mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), lessThan); | |
| 764 | mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan); | |
| 749 | 765 | } |
| 750 | 766 | } |
| 751 | 767 | } |
| ... | ... | @@ -755,7 +771,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 755 | 771 | |
| 756 | 772 | // while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort, |
| 757 | 773 | // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here |
| 758 | insertionSort(T, items[buffer2.start..buffer2.end], lessThan); | |
| 774 | insertionSort(T, items[buffer2.start..buffer2.end], context, lessThan); | |
| 759 | 775 | |
| 760 | 776 | pull_index = 0; |
| 761 | 777 | while (pull_index < 2) : (pull_index += 1) { |
| ... | ... | @@ -764,7 +780,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 764 | 780 | // the values were pulled out to the left, so redistribute them back to the right |
| 765 | 781 | var buffer = Range.init(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count); |
| 766 | 782 | while (buffer.length() > 0) { |
| 767 | index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), lessThan, unique); | |
| 783 | index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), context, lessThan, unique); | |
| 768 | 784 | const amount = index - buffer.end; |
| 769 | 785 | mem.rotate(T, items[buffer.start..index], buffer.length()); |
| 770 | 786 | buffer.start += (amount + 1); |
| ... | ... | @@ -775,7 +791,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 775 | 791 | // the values were pulled out to the right, so redistribute them back to the left |
| 776 | 792 | var buffer = Range.init(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end); |
| 777 | 793 | while (buffer.length() > 0) { |
| 778 | index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), lessThan, unique); | |
| 794 | index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), context, lessThan, unique); | |
| 779 | 795 | const amount = buffer.start - index; |
| 780 | 796 | mem.rotate(T, items[index..buffer.end], amount); |
| 781 | 797 | buffer.start -= amount; |
| ... | ... | @@ -792,7 +808,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo |
| 792 | 808 | } |
| 793 | 809 | |
| 794 | 810 | // merge operation without a buffer |
| 795 | fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessThan: fn (T, T) bool) void { | |
| 811 | fn mergeInPlace( | |
| 812 | comptime T: type, | |
| 813 | items: []T, | |
| 814 | A_arg: Range, | |
| 815 | B_arg: Range, | |
| 816 | context: var, | |
| 817 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 818 | ) void { | |
| 796 | 819 | if (A_arg.length() == 0 or B_arg.length() == 0) return; |
| 797 | 820 | |
| 798 | 821 | // this just repeatedly binary searches into B and rotates A into position. |
| ... | ... | @@ -818,7 +841,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh |
| 818 | 841 | |
| 819 | 842 | while (true) { |
| 820 | 843 | // find the first place in B where the first item in A needs to be inserted |
| 821 | const mid = binaryFirst(T, items, items[A.start], B, lessThan); | |
| 844 | const mid = binaryFirst(T, items, items[A.start], B, context, lessThan); | |
| 822 | 845 | |
| 823 | 846 | // rotate A into place |
| 824 | 847 | const amount = mid - A.end; |
| ... | ... | @@ -828,13 +851,21 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh |
| 828 | 851 | // calculate the new A and B ranges |
| 829 | 852 | B.start = mid; |
| 830 | 853 | A = Range.init(A.start + amount, B.start); |
| 831 | A.start = binaryLast(T, items, items[A.start], A, lessThan); | |
| 854 | A.start = binaryLast(T, items, items[A.start], A, context, lessThan); | |
| 832 | 855 | if (A.length() == 0) break; |
| 833 | 856 | } |
| 834 | 857 | } |
| 835 | 858 | |
| 836 | 859 | // merge operation using an internal buffer |
| 837 | fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, buffer: Range) void { | |
| 860 | fn mergeInternal( | |
| 861 | comptime T: type, | |
| 862 | items: []T, | |
| 863 | A: Range, | |
| 864 | B: Range, | |
| 865 | context: var, | |
| 866 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 867 | buffer: Range, | |
| 868 | ) void { | |
| 838 | 869 | // whenever we find a value to add to the final array, swap it with the value that's already in that spot |
| 839 | 870 | // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order |
| 840 | 871 | var A_count: usize = 0; |
| ... | ... | @@ -843,7 +874,7 @@ fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn |
| 843 | 874 | |
| 844 | 875 | if (B.length() > 0 and A.length() > 0) { |
| 845 | 876 | while (true) { |
| 846 | if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) { | |
| 877 | if (!lessThan(context, items[B.start + B_count], items[buffer.start + A_count])) { | |
| 847 | 878 | mem.swap(T, &items[A.start + insert], &items[buffer.start + A_count]); |
| 848 | 879 | A_count += 1; |
| 849 | 880 | insert += 1; |
| ... | ... | @@ -870,63 +901,102 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s |
| 870 | 901 | |
| 871 | 902 | // combine a linear search with a binary search to reduce the number of comparisons in situations |
| 872 | 903 | // where have some idea as to how many unique values there are and where the next value might be |
| 873 | fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { | |
| 904 | fn findFirstForward( | |
| 905 | comptime T: type, | |
| 906 | items: []T, | |
| 907 | value: T, | |
| 908 | range: Range, | |
| 909 | context: var, | |
| 910 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 911 | unique: usize, | |
| 912 | ) usize { | |
| 874 | 913 | if (range.length() == 0) return range.start; |
| 875 | 914 | const skip = math.max(range.length() / unique, @as(usize, 1)); |
| 876 | 915 | |
| 877 | 916 | var index = range.start + skip; |
| 878 | while (lessThan(items[index - 1], value)) : (index += skip) { | |
| 917 | while (lessThan(context, items[index - 1], value)) : (index += skip) { | |
| 879 | 918 | if (index >= range.end - skip) { |
| 880 | return binaryFirst(T, items, value, Range.init(index, range.end), lessThan); | |
| 919 | return binaryFirst(T, items, value, Range.init(index, range.end), context, lessThan); | |
| 881 | 920 | } |
| 882 | 921 | } |
| 883 | 922 | |
| 884 | return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); | |
| 923 | return binaryFirst(T, items, value, Range.init(index - skip, index), context, lessThan); | |
| 885 | 924 | } |
| 886 | 925 | |
| 887 | fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { | |
| 926 | fn findFirstBackward( | |
| 927 | comptime T: type, | |
| 928 | items: []T, | |
| 929 | value: T, | |
| 930 | range: Range, | |
| 931 | context: var, | |
| 932 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 933 | unique: usize, | |
| 934 | ) usize { | |
| 888 | 935 | if (range.length() == 0) return range.start; |
| 889 | 936 | const skip = math.max(range.length() / unique, @as(usize, 1)); |
| 890 | 937 | |
| 891 | 938 | var index = range.end - skip; |
| 892 | while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) { | |
| 939 | while (index > range.start and !lessThan(context, items[index - 1], value)) : (index -= skip) { | |
| 893 | 940 | if (index < range.start + skip) { |
| 894 | return binaryFirst(T, items, value, Range.init(range.start, index), lessThan); | |
| 941 | return binaryFirst(T, items, value, Range.init(range.start, index), context, lessThan); | |
| 895 | 942 | } |
| 896 | 943 | } |
| 897 | 944 | |
| 898 | return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); | |
| 945 | return binaryFirst(T, items, value, Range.init(index, index + skip), context, lessThan); | |
| 899 | 946 | } |
| 900 | 947 | |
| 901 | fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { | |
| 948 | fn findLastForward( | |
| 949 | comptime T: type, | |
| 950 | items: []T, | |
| 951 | value: T, | |
| 952 | range: Range, | |
| 953 | context: var, | |
| 954 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 955 | unique: usize, | |
| 956 | ) usize { | |
| 902 | 957 | if (range.length() == 0) return range.start; |
| 903 | 958 | const skip = math.max(range.length() / unique, @as(usize, 1)); |
| 904 | 959 | |
| 905 | 960 | var index = range.start + skip; |
| 906 | while (!lessThan(value, items[index - 1])) : (index += skip) { | |
| 961 | while (!lessThan(context, value, items[index - 1])) : (index += skip) { | |
| 907 | 962 | if (index >= range.end - skip) { |
| 908 | return binaryLast(T, items, value, Range.init(index, range.end), lessThan); | |
| 963 | return binaryLast(T, items, value, Range.init(index, range.end), context, lessThan); | |
| 909 | 964 | } |
| 910 | 965 | } |
| 911 | 966 | |
| 912 | return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); | |
| 967 | return binaryLast(T, items, value, Range.init(index - skip, index), context, lessThan); | |
| 913 | 968 | } |
| 914 | 969 | |
| 915 | fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { | |
| 970 | fn findLastBackward( | |
| 971 | comptime T: type, | |
| 972 | items: []T, | |
| 973 | value: T, | |
| 974 | range: Range, | |
| 975 | context: var, | |
| 976 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 977 | unique: usize, | |
| 978 | ) usize { | |
| 916 | 979 | if (range.length() == 0) return range.start; |
| 917 | 980 | const skip = math.max(range.length() / unique, @as(usize, 1)); |
| 918 | 981 | |
| 919 | 982 | var index = range.end - skip; |
| 920 | while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) { | |
| 983 | while (index > range.start and lessThan(context, value, items[index - 1])) : (index -= skip) { | |
| 921 | 984 | if (index < range.start + skip) { |
| 922 | return binaryLast(T, items, value, Range.init(range.start, index), lessThan); | |
| 985 | return binaryLast(T, items, value, Range.init(range.start, index), context, lessThan); | |
| 923 | 986 | } |
| 924 | 987 | } |
| 925 | 988 | |
| 926 | return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); | |
| 989 | return binaryLast(T, items, value, Range.init(index, index + skip), context, lessThan); | |
| 927 | 990 | } |
| 928 | 991 | |
| 929 | fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { | |
| 992 | fn binaryFirst( | |
| 993 | comptime T: type, | |
| 994 | items: []T, | |
| 995 | value: T, | |
| 996 | range: Range, | |
| 997 | context: var, | |
| 998 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 999 | ) usize { | |
| 930 | 1000 | var curr = range.start; |
| 931 | 1001 | var size = range.length(); |
| 932 | 1002 | if (range.start >= range.end) return range.end; |
| ... | ... | @@ -935,14 +1005,21 @@ fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: f |
| 935 | 1005 | |
| 936 | 1006 | size /= 2; |
| 937 | 1007 | const mid = items[curr + size]; |
| 938 | if (lessThan(mid, value)) { | |
| 1008 | if (lessThan(context, mid, value)) { | |
| 939 | 1009 | curr += size + offset; |
| 940 | 1010 | } |
| 941 | 1011 | } |
| 942 | 1012 | return curr; |
| 943 | 1013 | } |
| 944 | 1014 | |
| 945 | fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { | |
| 1015 | fn binaryLast( | |
| 1016 | comptime T: type, | |
| 1017 | items: []T, | |
| 1018 | value: T, | |
| 1019 | range: Range, | |
| 1020 | context: var, | |
| 1021 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 1022 | ) usize { | |
| 946 | 1023 | var curr = range.start; |
| 947 | 1024 | var size = range.length(); |
| 948 | 1025 | if (range.start >= range.end) return range.end; |
| ... | ... | @@ -951,14 +1028,22 @@ fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn |
| 951 | 1028 | |
| 952 | 1029 | size /= 2; |
| 953 | 1030 | const mid = items[curr + size]; |
| 954 | if (!lessThan(value, mid)) { | |
| 1031 | if (!lessThan(context, value, mid)) { | |
| 955 | 1032 | curr += size + offset; |
| 956 | 1033 | } |
| 957 | 1034 | } |
| 958 | 1035 | return curr; |
| 959 | 1036 | } |
| 960 | 1037 | |
| 961 | fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void { | |
| 1038 | fn mergeInto( | |
| 1039 | comptime T: type, | |
| 1040 | from: []T, | |
| 1041 | A: Range, | |
| 1042 | B: Range, | |
| 1043 | context: var, | |
| 1044 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 1045 | into: []T, | |
| 1046 | ) void { | |
| 962 | 1047 | var A_index: usize = A.start; |
| 963 | 1048 | var B_index: usize = B.start; |
| 964 | 1049 | const A_last = A.end; |
| ... | ... | @@ -966,7 +1051,7 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T |
| 966 | 1051 | var insert_index: usize = 0; |
| 967 | 1052 | |
| 968 | 1053 | while (true) { |
| 969 | if (!lessThan(from[B_index], from[A_index])) { | |
| 1054 | if (!lessThan(context, from[B_index], from[A_index])) { | |
| 970 | 1055 | into[insert_index] = from[A_index]; |
| 971 | 1056 | A_index += 1; |
| 972 | 1057 | insert_index += 1; |
| ... | ... | @@ -988,7 +1073,15 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T |
| 988 | 1073 | } |
| 989 | 1074 | } |
| 990 | 1075 | |
| 991 | fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, cache: []T) void { | |
| 1076 | fn mergeExternal( | |
| 1077 | comptime T: type, | |
| 1078 | items: []T, | |
| 1079 | A: Range, | |
| 1080 | B: Range, | |
| 1081 | context: var, | |
| 1082 | comptime lessThan: fn (@TypeOf(context), T, T) bool, | |
| 1083 | cache: []T, | |
| 1084 | ) void { | |
| 992 | 1085 | // A fits into the cache, so use that instead of the internal buffer |
| 993 | 1086 | var A_index: usize = 0; |
| 994 | 1087 | var B_index: usize = B.start; |
| ... | ... | @@ -998,7 +1091,7 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn |
| 998 | 1091 | |
| 999 | 1092 | if (B.length() > 0 and A.length() > 0) { |
| 1000 | 1093 | while (true) { |
| 1001 | if (!lessThan(items[B_index], cache[A_index])) { | |
| 1094 | if (!lessThan(context, items[B_index], cache[A_index])) { | |
| 1002 | 1095 | items[insert_index] = cache[A_index]; |
| 1003 | 1096 | A_index += 1; |
| 1004 | 1097 | insert_index += 1; |
| ... | ... | @@ -1016,17 +1109,25 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn |
| 1016 | 1109 | mem.copy(T, items[insert_index..], cache[A_index..A_last]); |
| 1017 | 1110 | } |
| 1018 | 1111 | |
| 1019 | fn swap(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool, order: *[8]u8, x: usize, y: usize) void { | |
| 1020 | if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { | |
| 1112 | fn swap( | |
| 1113 | comptime T: type, | |
| 1114 | items: []T, | |
| 1115 | context: var, | |
| 1116 | comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool, | |
| 1117 | order: *[8]u8, | |
| 1118 | x: usize, | |
| 1119 | y: usize, | |
| 1120 | ) void { | |
| 1121 | if (lessThan(context, items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(context, items[x], items[y]))) { | |
| 1021 | 1122 | mem.swap(T, &items[x], &items[y]); |
| 1022 | 1123 | mem.swap(u8, &(order.*)[x], &(order.*)[y]); |
| 1023 | 1124 | } |
| 1024 | 1125 | } |
| 1025 | 1126 | |
| 1026 | // Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`. | |
| 1027 | pub fn asc(comptime T: type) fn (T, T) bool { | |
| 1127 | /// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`. | |
| 1128 | pub fn asc(comptime T: type) fn (void, T, T) bool { | |
| 1028 | 1129 | const impl = struct { |
| 1029 | fn inner(a: T, b: T) bool { | |
| 1130 | fn inner(context: void, a: T, b: T) bool { | |
| 1030 | 1131 | return a < b; |
| 1031 | 1132 | } |
| 1032 | 1133 | }; |
| ... | ... | @@ -1034,9 +1135,10 @@ pub fn asc(comptime T: type) fn (T, T) bool { |
| 1034 | 1135 | return impl.inner; |
| 1035 | 1136 | } |
| 1036 | 1137 | |
| 1037 | pub fn desc(comptime T: type) fn (T, T) bool { | |
| 1138 | /// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`. | |
| 1139 | pub fn desc(comptime T: type) fn (void, T, T) bool { | |
| 1038 | 1140 | const impl = struct { |
| 1039 | fn inner(a: T, b: T) bool { | |
| 1141 | fn inner(context: void, a: T, b: T) bool { | |
| 1040 | 1142 | return a > b; |
| 1041 | 1143 | } |
| 1042 | 1144 | }; |
| ... | ... | @@ -1085,7 +1187,7 @@ fn testStableSort() void { |
| 1085 | 1187 | }, |
| 1086 | 1188 | }; |
| 1087 | 1189 | for (cases) |*case| { |
| 1088 | insertionSort(IdAndValue, (case.*)[0..], cmpByValue); | |
| 1190 | insertionSort(IdAndValue, (case.*)[0..], {}, cmpByValue); | |
| 1089 | 1191 | for (case.*) |item, i| { |
| 1090 | 1192 | testing.expect(item.id == expected[i].id); |
| 1091 | 1193 | testing.expect(item.value == expected[i].value); |
| ... | ... | @@ -1096,11 +1198,16 @@ const IdAndValue = struct { |
| 1096 | 1198 | id: usize, |
| 1097 | 1199 | value: i32, |
| 1098 | 1200 | }; |
| 1099 | fn cmpByValue(a: IdAndValue, b: IdAndValue) bool { | |
| 1100 | return asc(i32)(a.value, b.value); | |
| 1201 | fn cmpByValue(context: void, a: IdAndValue, b: IdAndValue) bool { | |
| 1202 | return asc_i32(context, a.value, b.value); | |
| 1101 | 1203 | } |
| 1102 | 1204 | |
| 1103 | test "std.sort" { | |
| 1205 | const asc_u8 = asc(u8); | |
| 1206 | const asc_i32 = asc(i32); | |
| 1207 | const desc_u8 = desc(u8); | |
| 1208 | const desc_i32 = desc(i32); | |
| 1209 | ||
| 1210 | test "sort" { | |
| 1104 | 1211 | const u8cases = [_][]const []const u8{ |
| 1105 | 1212 | &[_][]const u8{ |
| 1106 | 1213 | "", |
| ... | ... | @@ -1132,7 +1239,7 @@ test "std.sort" { |
| 1132 | 1239 | var buf: [8]u8 = undefined; |
| 1133 | 1240 | const slice = buf[0..case[0].len]; |
| 1134 | 1241 | mem.copy(u8, slice, case[0]); |
| 1135 | sort(u8, slice, asc(u8)); | |
| 1242 | sort(u8, slice, {}, asc_u8); | |
| 1136 | 1243 | testing.expect(mem.eql(u8, slice, case[1])); |
| 1137 | 1244 | } |
| 1138 | 1245 | |
| ... | ... | @@ -1167,12 +1274,12 @@ test "std.sort" { |
| 1167 | 1274 | var buf: [8]i32 = undefined; |
| 1168 | 1275 | const slice = buf[0..case[0].len]; |
| 1169 | 1276 | mem.copy(i32, slice, case[0]); |
| 1170 | sort(i32, slice, asc(i32)); | |
| 1277 | sort(i32, slice, {}, asc_i32); | |
| 1171 | 1278 | testing.expect(mem.eql(i32, slice, case[1])); |
| 1172 | 1279 | } |
| 1173 | 1280 | } |
| 1174 | 1281 | |
| 1175 | test "std.sort descending" { | |
| 1282 | test "sort descending" { | |
| 1176 | 1283 | const rev_cases = [_][]const []const i32{ |
| 1177 | 1284 | &[_][]const i32{ |
| 1178 | 1285 | &[_]i32{}, |
| ... | ... | @@ -1204,14 +1311,14 @@ test "std.sort descending" { |
| 1204 | 1311 | var buf: [8]i32 = undefined; |
| 1205 | 1312 | const slice = buf[0..case[0].len]; |
| 1206 | 1313 | mem.copy(i32, slice, case[0]); |
| 1207 | sort(i32, slice, desc(i32)); | |
| 1314 | sort(i32, slice, {}, desc_i32); | |
| 1208 | 1315 | testing.expect(mem.eql(i32, slice, case[1])); |
| 1209 | 1316 | } |
| 1210 | 1317 | } |
| 1211 | 1318 | |
| 1212 | 1319 | test "another sort case" { |
| 1213 | 1320 | var arr = [_]i32{ 5, 3, 1, 2, 4 }; |
| 1214 | sort(i32, arr[0..], asc(i32)); | |
| 1321 | sort(i32, arr[0..], {}, asc_i32); | |
| 1215 | 1322 | |
| 1216 | 1323 | testing.expect(mem.eql(i32, &arr, &[_]i32{ 1, 2, 3, 4, 5 })); |
| 1217 | 1324 | } |
| ... | ... | @@ -1236,7 +1343,7 @@ fn fuzzTest(rng: *std.rand.Random) !void { |
| 1236 | 1343 | item.id = index; |
| 1237 | 1344 | item.value = rng.intRangeLessThan(i32, 0, 100); |
| 1238 | 1345 | } |
| 1239 | sort(IdAndValue, array, cmpByValue); | |
| 1346 | sort(IdAndValue, array, {}, cmpByValue); | |
| 1240 | 1347 | |
| 1241 | 1348 | var index: usize = 1; |
| 1242 | 1349 | while (index < array.len) : (index += 1) { |
| ... | ... | @@ -1248,7 +1355,12 @@ fn fuzzTest(rng: *std.rand.Random) !void { |
| 1248 | 1355 | } |
| 1249 | 1356 | } |
| 1250 | 1357 | |
| 1251 | pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize { | |
| 1358 | pub fn argMin( | |
| 1359 | comptime T: type, | |
| 1360 | items: []const T, | |
| 1361 | context: var, | |
| 1362 | comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool, | |
| 1363 | ) ?usize { | |
| 1252 | 1364 | if (items.len == 0) { |
| 1253 | 1365 | return null; |
| 1254 | 1366 | } |
| ... | ... | @@ -1256,7 +1368,7 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) |
| 1256 | 1368 | var smallest = items[0]; |
| 1257 | 1369 | var smallest_index: usize = 0; |
| 1258 | 1370 | for (items[1..]) |item, i| { |
| 1259 | if (lessThan(item, smallest)) { | |
| 1371 | if (lessThan(context, item, smallest)) { | |
| 1260 | 1372 | smallest = item; |
| 1261 | 1373 | smallest_index = i + 1; |
| 1262 | 1374 | } |
| ... | ... | @@ -1265,32 +1377,42 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) |
| 1265 | 1377 | return smallest_index; |
| 1266 | 1378 | } |
| 1267 | 1379 | |
| 1268 | test "std.sort.argMin" { | |
| 1269 | testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, asc(i32))); | |
| 1270 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, asc(i32))); | |
| 1271 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); | |
| 1272 | testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); | |
| 1273 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); | |
| 1274 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); | |
| 1275 | testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); | |
| 1380 | test "argMin" { | |
| 1381 | testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, {}, asc_i32)); | |
| 1382 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, {}, asc_i32)); | |
| 1383 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32)); | |
| 1384 | testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32)); | |
| 1385 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32)); | |
| 1386 | testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32)); | |
| 1387 | testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32)); | |
| 1276 | 1388 | } |
| 1277 | 1389 | |
| 1278 | pub fn min(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T { | |
| 1279 | const i = argMin(T, items, lessThan) orelse return null; | |
| 1390 | pub fn min( | |
| 1391 | comptime T: type, | |
| 1392 | items: []const T, | |
| 1393 | context: var, | |
| 1394 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 1395 | ) ?T { | |
| 1396 | const i = argMin(T, items, context, lessThan) orelse return null; | |
| 1280 | 1397 | return items[i]; |
| 1281 | 1398 | } |
| 1282 | 1399 | |
| 1283 | test "std.sort.min" { | |
| 1284 | testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, asc(i32))); | |
| 1285 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, asc(i32))); | |
| 1286 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); | |
| 1287 | testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); | |
| 1288 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); | |
| 1289 | testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); | |
| 1290 | testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); | |
| 1400 | test "min" { | |
| 1401 | testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, {}, asc_i32)); | |
| 1402 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, {}, asc_i32)); | |
| 1403 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32)); | |
| 1404 | testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32)); | |
| 1405 | testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32)); | |
| 1406 | testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32)); | |
| 1407 | testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32)); | |
| 1291 | 1408 | } |
| 1292 | 1409 | |
| 1293 | pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize { | |
| 1410 | pub fn argMax( | |
| 1411 | comptime T: type, | |
| 1412 | items: []const T, | |
| 1413 | context: var, | |
| 1414 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 1415 | ) ?usize { | |
| 1294 | 1416 | if (items.len == 0) { |
| 1295 | 1417 | return null; |
| 1296 | 1418 | } |
| ... | ... | @@ -1298,7 +1420,7 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) |
| 1298 | 1420 | var biggest = items[0]; |
| 1299 | 1421 | var biggest_index: usize = 0; |
| 1300 | 1422 | for (items[1..]) |item, i| { |
| 1301 | if (lessThan(biggest, item)) { | |
| 1423 | if (lessThan(context, biggest, item)) { | |
| 1302 | 1424 | biggest = item; |
| 1303 | 1425 | biggest_index = i + 1; |
| 1304 | 1426 | } |
| ... | ... | @@ -1307,35 +1429,45 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) |
| 1307 | 1429 | return biggest_index; |
| 1308 | 1430 | } |
| 1309 | 1431 | |
| 1310 | test "std.sort.argMax" { | |
| 1311 | testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, asc(i32))); | |
| 1312 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, asc(i32))); | |
| 1313 | testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); | |
| 1314 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); | |
| 1315 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); | |
| 1316 | testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); | |
| 1317 | testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); | |
| 1432 | test "argMax" { | |
| 1433 | testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, {}, asc_i32)); | |
| 1434 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, {}, asc_i32)); | |
| 1435 | testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32)); | |
| 1436 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32)); | |
| 1437 | testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32)); | |
| 1438 | testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32)); | |
| 1439 | testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32)); | |
| 1318 | 1440 | } |
| 1319 | 1441 | |
| 1320 | pub fn max(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T { | |
| 1321 | const i = argMax(T, items, lessThan) orelse return null; | |
| 1442 | pub fn max( | |
| 1443 | comptime T: type, | |
| 1444 | items: []const T, | |
| 1445 | context: var, | |
| 1446 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 1447 | ) ?T { | |
| 1448 | const i = argMax(T, items, context, lessThan) orelse return null; | |
| 1322 | 1449 | return items[i]; |
| 1323 | 1450 | } |
| 1324 | 1451 | |
| 1325 | test "std.sort.max" { | |
| 1326 | testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, asc(i32))); | |
| 1327 | testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, asc(i32))); | |
| 1328 | testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); | |
| 1329 | testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); | |
| 1330 | testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); | |
| 1331 | testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); | |
| 1332 | testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); | |
| 1452 | test "max" { | |
| 1453 | testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, {}, asc_i32)); | |
| 1454 | testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, {}, asc_i32)); | |
| 1455 | testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32)); | |
| 1456 | testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32)); | |
| 1457 | testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32)); | |
| 1458 | testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32)); | |
| 1459 | testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32)); | |
| 1333 | 1460 | } |
| 1334 | 1461 | |
| 1335 | pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) bool { | |
| 1462 | pub fn isSorted( | |
| 1463 | comptime T: type, | |
| 1464 | items: []const T, | |
| 1465 | context: var, | |
| 1466 | comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool, | |
| 1467 | ) bool { | |
| 1336 | 1468 | var i: usize = 1; |
| 1337 | 1469 | while (i < items.len) : (i += 1) { |
| 1338 | if (lessThan(items[i], items[i - 1])) { | |
| 1470 | if (lessThan(context, items[i], items[i - 1])) { | |
| 1339 | 1471 | return false; |
| 1340 | 1472 | } |
| 1341 | 1473 | } |
| ... | ... | @@ -1343,29 +1475,29 @@ pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T |
| 1343 | 1475 | return true; |
| 1344 | 1476 | } |
| 1345 | 1477 | |
| 1346 | test "std.sort.isSorted" { | |
| 1347 | testing.expect(isSorted(i32, &[_]i32{}, asc(i32))); | |
| 1348 | testing.expect(isSorted(i32, &[_]i32{10}, asc(i32))); | |
| 1349 | testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); | |
| 1350 | testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, asc(i32))); | |
| 1478 | test "isSorted" { | |
| 1479 | testing.expect(isSorted(i32, &[_]i32{}, {}, asc_i32)); | |
| 1480 | testing.expect(isSorted(i32, &[_]i32{10}, {}, asc_i32)); | |
| 1481 | testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32)); | |
| 1482 | testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, {}, asc_i32)); | |
| 1351 | 1483 | |
| 1352 | testing.expect(isSorted(i32, &[_]i32{}, desc(i32))); | |
| 1353 | testing.expect(isSorted(i32, &[_]i32{-20}, desc(i32))); | |
| 1354 | testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, desc(i32))); | |
| 1355 | testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, desc(i32))); | |
| 1484 | testing.expect(isSorted(i32, &[_]i32{}, {}, desc_i32)); | |
| 1485 | testing.expect(isSorted(i32, &[_]i32{-20}, {}, desc_i32)); | |
| 1486 | testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, {}, desc_i32)); | |
| 1487 | testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, {}, desc_i32)); | |
| 1356 | 1488 | |
| 1357 | testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); | |
| 1358 | testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, desc(i32))); | |
| 1489 | testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32)); | |
| 1490 | testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, desc_i32)); | |
| 1359 | 1491 | |
| 1360 | testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, asc(i32))); | |
| 1361 | testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, desc(i32))); | |
| 1492 | testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, {}, asc_i32)); | |
| 1493 | testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, desc_i32)); | |
| 1362 | 1494 | |
| 1363 | testing.expect(isSorted(u8, "abcd", asc(u8))); | |
| 1364 | testing.expect(isSorted(u8, "zyxw", desc(u8))); | |
| 1495 | testing.expect(isSorted(u8, "abcd", {}, asc_u8)); | |
| 1496 | testing.expect(isSorted(u8, "zyxw", {}, desc_u8)); | |
| 1365 | 1497 | |
| 1366 | testing.expectEqual(false, isSorted(u8, "abcd", desc(u8))); | |
| 1367 | testing.expectEqual(false, isSorted(u8, "zyxw", asc(u8))); | |
| 1498 | testing.expectEqual(false, isSorted(u8, "abcd", {}, desc_u8)); | |
| 1499 | testing.expectEqual(false, isSorted(u8, "zyxw", {}, asc_u8)); | |
| 1368 | 1500 | |
| 1369 | testing.expect(isSorted(u8, "ffff", asc(u8))); | |
| 1370 | testing.expect(isSorted(u8, "ffff", desc(u8))); | |
| 1501 | testing.expect(isSorted(u8, "ffff", {}, asc_u8)); | |
| 1502 | testing.expect(isSorted(u8, "ffff", {}, desc_u8)); | |
| 1371 | 1503 | } |
src-self-hosted/Module.zig+57-12| ... | ... | @@ -576,6 +576,8 @@ pub fn update(self: *Module) !void { |
| 576 | 576 | // TODO Use the cache hash file system to detect which source files changed. |
| 577 | 577 | // Here we simulate a full cache miss. |
| 578 | 578 | // Analyze the root source file now. |
| 579 | // Source files could have been loaded for any reason; to force a refresh we unload now. | |
| 580 | self.root_scope.unload(self.allocator); | |
| 579 | 581 | self.analyzeRoot(self.root_scope) catch |err| switch (err) { |
| 580 | 582 | error.AnalysisFail => { |
| 581 | 583 | assert(self.totalErrorCount() != 0); |
| ... | ... | @@ -594,8 +596,11 @@ pub fn update(self: *Module) !void { |
| 594 | 596 | try self.deleteDecl(decl); |
| 595 | 597 | } |
| 596 | 598 | |
| 597 | // Unload all the source files from memory. | |
| 598 | self.root_scope.unload(self.allocator); | |
| 599 | // If there are any errors, we anticipate the source files being loaded | |
| 600 | // to report error messages. Otherwise we unload all source files to save memory. | |
| 601 | if (self.totalErrorCount() == 0) { | |
| 602 | self.root_scope.unload(self.allocator); | |
| 603 | } | |
| 599 | 604 | |
| 600 | 605 | try self.bin_file.flush(); |
| 601 | 606 | self.link_error_flags = self.bin_file.error_flags; |
| ... | ... | @@ -668,8 +673,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 668 | 673 | assert(errors.items.len == self.totalErrorCount()); |
| 669 | 674 | |
| 670 | 675 | return AllErrors{ |
| 671 | .arena = arena.state, | |
| 672 | 676 | .list = try arena.allocator.dupe(AllErrors.Message, errors.items), |
| 677 | .arena = arena.state, | |
| 673 | 678 | }; |
| 674 | 679 | } |
| 675 | 680 | |
| ... | ... | @@ -878,11 +883,11 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 878 | 883 | const decl = kv.value; |
| 879 | 884 | deleted_decls.removeAssertDiscard(decl); |
| 880 | 885 | const new_contents_hash = Decl.hashSimpleName(src_decl.contents); |
| 886 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); | |
| 881 | 887 | if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { |
| 882 | //std.debug.warn("noticed '{}' source changed\n", .{src_decl.name}); | |
| 883 | decl.analysis = .outdated; | |
| 888 | //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash }); | |
| 889 | try self.markOutdatedDecl(decl); | |
| 884 | 890 | decl.contents_hash = new_contents_hash; |
| 885 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | |
| 886 | 891 | } |
| 887 | 892 | } else if (src_decl.cast(zir.Inst.Export)) |export_inst| { |
| 888 | 893 | try exports_to_resolve.append(&export_inst.base); |
| ... | ... | @@ -905,6 +910,8 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 905 | 910 | } |
| 906 | 911 | |
| 907 | 912 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 913 | try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len); | |
| 914 | ||
| 908 | 915 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 909 | 916 | const name_hash = decl.fullyQualifiedNameHash(); |
| 910 | 917 | self.decl_table.removeAssertDiscard(name_hash); |
| ... | ... | @@ -916,17 +923,20 @@ fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 916 | 923 | // another reference to it may turn up. |
| 917 | 924 | assert(!dep.deletion_flag); |
| 918 | 925 | dep.deletion_flag = true; |
| 919 | try self.deletion_set.append(self.allocator, dep); | |
| 926 | self.deletion_set.appendAssumeCapacity(dep); | |
| 920 | 927 | } |
| 921 | 928 | } |
| 922 | 929 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. |
| 923 | 930 | for (decl.dependants.items) |dep| { |
| 924 | 931 | dep.removeDependency(decl); |
| 925 | 932 | if (dep.analysis != .outdated) { |
| 926 | dep.analysis = .outdated; | |
| 927 | try self.work_queue.writeItem(.{ .re_analyze_decl = dep }); | |
| 933 | // TODO Move this failure possibility to the top of the function. | |
| 934 | try self.markOutdatedDecl(dep); | |
| 928 | 935 | } |
| 929 | 936 | } |
| 937 | if (self.failed_decls.remove(decl)) |entry| { | |
| 938 | entry.value.destroy(self.allocator); | |
| 939 | } | |
| 930 | 940 | self.deleteDeclExports(decl); |
| 931 | 941 | self.bin_file.freeDecl(decl); |
| 932 | 942 | decl.destroy(self.allocator); |
| ... | ... | @@ -1083,20 +1093,31 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi |
| 1083 | 1093 | .codegen_failure_retryable, |
| 1084 | 1094 | .complete, |
| 1085 | 1095 | => if (dep.generation != self.generation) { |
| 1086 | dep.analysis = .outdated; | |
| 1087 | try self.work_queue.writeItem(.{ .re_analyze_decl = dep }); | |
| 1096 | try self.markOutdatedDecl(dep); | |
| 1088 | 1097 | }, |
| 1089 | 1098 | } |
| 1090 | 1099 | } |
| 1091 | 1100 | } |
| 1092 | 1101 | } |
| 1093 | 1102 | |
| 1103 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | |
| 1104 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | |
| 1105 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | |
| 1106 | if (self.failed_decls.remove(decl)) |entry| { | |
| 1107 | entry.value.destroy(self.allocator); | |
| 1108 | } | |
| 1109 | decl.analysis = .outdated; | |
| 1110 | } | |
| 1111 | ||
| 1094 | 1112 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { |
| 1095 | 1113 | const hash = Decl.hashSimpleName(old_inst.name); |
| 1096 | 1114 | if (self.decl_table.get(hash)) |kv| { |
| 1097 | 1115 | const decl = kv.value; |
| 1098 | 1116 | try self.reAnalyzeDecl(decl, old_inst); |
| 1099 | 1117 | return decl; |
| 1118 | } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| { | |
| 1119 | // This is just a named reference to another decl. | |
| 1120 | return self.analyzeDeclVal(scope, decl_val); | |
| 1100 | 1121 | } else { |
| 1101 | 1122 | const new_decl = blk: { |
| 1102 | 1123 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); |
| ... | ... | @@ -1442,7 +1463,9 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 1442 | 1463 | switch (old_inst.tag) { |
| 1443 | 1464 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), |
| 1444 | 1465 | .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), |
| 1466 | .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?), | |
| 1445 | 1467 | .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?), |
| 1468 | .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?), | |
| 1446 | 1469 | .str => { |
| 1447 | 1470 | const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes; |
| 1448 | 1471 | // The bytes references memory inside the ZIR module, which can get deallocated |
| ... | ... | @@ -1480,6 +1503,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 1480 | 1503 | } |
| 1481 | 1504 | } |
| 1482 | 1505 | |
| 1506 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { | |
| 1507 | return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); | |
| 1508 | } | |
| 1509 | ||
| 1483 | 1510 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { |
| 1484 | 1511 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 1485 | 1512 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); |
| ... | ... | @@ -1501,6 +1528,24 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn |
| 1501 | 1528 | return self.analyzeDeclRef(scope, inst.base.src, decl); |
| 1502 | 1529 | } |
| 1503 | 1530 | |
| 1531 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { | |
| 1532 | const decl_name = inst.positionals.name; | |
| 1533 | // This will need to get more fleshed out when there are proper structs & namespaces. | |
| 1534 | const zir_module = scope.namespace(); | |
| 1535 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse | |
| 1536 | return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); | |
| 1537 | ||
| 1538 | const decl = try self.resolveCompleteDecl(scope, src_decl); | |
| 1539 | ||
| 1540 | return decl; | |
| 1541 | } | |
| 1542 | ||
| 1543 | fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { | |
| 1544 | const decl = try self.analyzeDeclVal(scope, inst); | |
| 1545 | const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 1546 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | |
| 1547 | } | |
| 1548 | ||
| 1504 | 1549 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { |
| 1505 | 1550 | const decl_tv = try decl.typedValue(); |
| 1506 | 1551 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); |
| ... | ... | @@ -1621,7 +1666,7 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn |
| 1621 | 1666 | } |
| 1622 | 1667 | |
| 1623 | 1668 | fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { |
| 1624 | return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType()); | |
| 1669 | return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); | |
| 1625 | 1670 | } |
| 1626 | 1671 | |
| 1627 | 1672 | fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst { |
src-self-hosted/main.zig+15-1| ... | ... | @@ -407,7 +407,21 @@ fn buildOutputType( |
| 407 | 407 | std.debug.warn("-fno-emit-bin not supported yet", .{}); |
| 408 | 408 | process.exit(1); |
| 409 | 409 | }, |
| 410 | .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }), | |
| 410 | .yes_default_path => switch (output_mode) { | |
| 411 | .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }), | |
| 412 | .Lib => blk: { | |
| 413 | const suffix = switch (link_mode orelse .Static) { | |
| 414 | .Static => target_info.target.staticLibSuffix(), | |
| 415 | .Dynamic => target_info.target.dynamicLibSuffix(), | |
| 416 | }; | |
| 417 | break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{ | |
| 418 | target_info.target.libPrefix(), | |
| 419 | root_name, | |
| 420 | suffix, | |
| 421 | }); | |
| 422 | }, | |
| 423 | .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }), | |
| 424 | }, | |
| 411 | 425 | .yes => |p| p, |
| 412 | 426 | }; |
| 413 | 427 |
src-self-hosted/test.zig+111-29| ... | ... | @@ -27,9 +27,32 @@ pub const TestContext = struct { |
| 27 | 27 | |
| 28 | 28 | pub const ZIRTransformCase = struct { |
| 29 | 29 | name: []const u8, |
| 30 | src: [:0]const u8, | |
| 31 | expected_zir: []const u8, | |
| 32 | 30 | cross_target: std.zig.CrossTarget, |
| 31 | updates: std.ArrayList(Update), | |
| 32 | ||
| 33 | pub const Update = struct { | |
| 34 | expected: Expected, | |
| 35 | src: [:0]const u8, | |
| 36 | }; | |
| 37 | ||
| 38 | pub const Expected = union(enum) { | |
| 39 | zir: []const u8, | |
| 40 | errors: []const []const u8, | |
| 41 | }; | |
| 42 | ||
| 43 | pub fn addZIR(case: *ZIRTransformCase, src: [:0]const u8, zir_text: []const u8) void { | |
| 44 | case.updates.append(.{ | |
| 45 | .src = src, | |
| 46 | .expected = .{ .zir = zir_text }, | |
| 47 | }) catch unreachable; | |
| 48 | } | |
| 49 | ||
| 50 | pub fn addError(case: *ZIRTransformCase, src: [:0]const u8, errors: []const []const u8) void { | |
| 51 | case.updates.append(.{ | |
| 52 | .src = src, | |
| 53 | .expected = .{ .errors = errors }, | |
| 54 | }) catch unreachable; | |
| 55 | } | |
| 33 | 56 | }; |
| 34 | 57 | |
| 35 | 58 | pub fn addZIRCompareOutput( |
| ... | ... | @@ -52,14 +75,32 @@ pub const TestContext = struct { |
| 52 | 75 | src: [:0]const u8, |
| 53 | 76 | expected_zir: []const u8, |
| 54 | 77 | ) void { |
| 55 | ctx.zir_transform_cases.append(.{ | |
| 78 | const case = ctx.zir_transform_cases.addOne() catch unreachable; | |
| 79 | case.* = .{ | |
| 56 | 80 | .name = name, |
| 57 | .src = src, | |
| 58 | .expected_zir = expected_zir, | |
| 59 | 81 | .cross_target = cross_target, |
| 82 | .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator), | |
| 83 | }; | |
| 84 | case.updates.append(.{ | |
| 85 | .src = src, | |
| 86 | .expected = .{ .zir = expected_zir }, | |
| 60 | 87 | }) catch unreachable; |
| 61 | 88 | } |
| 62 | 89 | |
| 90 | pub fn addZIRMulti( | |
| 91 | ctx: *TestContext, | |
| 92 | name: []const u8, | |
| 93 | cross_target: std.zig.CrossTarget, | |
| 94 | ) *ZIRTransformCase { | |
| 95 | const case = ctx.zir_transform_cases.addOne() catch unreachable; | |
| 96 | case.* = .{ | |
| 97 | .name = name, | |
| 98 | .cross_target = cross_target, | |
| 99 | .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator), | |
| 100 | }; | |
| 101 | return case; | |
| 102 | } | |
| 103 | ||
| 63 | 104 | fn init(self: *TestContext) !void { |
| 64 | 105 | self.* = .{ |
| 65 | 106 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator), |
| ... | ... | @@ -178,13 +219,11 @@ pub const TestContext = struct { |
| 178 | 219 | var tmp = std.testing.tmpDir(.{}); |
| 179 | 220 | defer tmp.cleanup(); |
| 180 | 221 | |
| 181 | var prg_node = root_node.start(case.name, 3); | |
| 182 | prg_node.activate(); | |
| 183 | defer prg_node.end(); | |
| 222 | var update_node = root_node.start(case.name, case.updates.items.len); | |
| 223 | update_node.activate(); | |
| 224 | defer update_node.end(); | |
| 184 | 225 | |
| 185 | 226 | const tmp_src_path = "test-case.zir"; |
| 186 | try tmp.dir.writeFile(tmp_src_path, case.src); | |
| 187 | ||
| 188 | 227 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 189 | 228 | defer root_pkg.destroy(); |
| 190 | 229 | |
| ... | ... | @@ -198,25 +237,68 @@ pub const TestContext = struct { |
| 198 | 237 | }); |
| 199 | 238 | defer module.deinit(); |
| 200 | 239 | |
| 201 | var module_node = prg_node.start("parse/analysis/codegen", null); | |
| 202 | module_node.activate(); | |
| 203 | try module.update(); | |
| 204 | module_node.end(); | |
| 205 | ||
| 206 | var emit_node = prg_node.start("emit", null); | |
| 207 | emit_node.activate(); | |
| 208 | var new_zir_module = try zir.emit(allocator, module); | |
| 209 | defer new_zir_module.deinit(allocator); | |
| 210 | emit_node.end(); | |
| 211 | ||
| 212 | var write_node = prg_node.start("write", null); | |
| 213 | write_node.activate(); | |
| 214 | var out_zir = std.ArrayList(u8).init(allocator); | |
| 215 | defer out_zir.deinit(); | |
| 216 | try new_zir_module.writeToStream(allocator, out_zir.outStream()); | |
| 217 | write_node.end(); | |
| 218 | ||
| 219 | std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items); | |
| 240 | for (case.updates.items) |update| { | |
| 241 | var prg_node = update_node.start("", 3); | |
| 242 | prg_node.activate(); | |
| 243 | defer prg_node.end(); | |
| 244 | ||
| 245 | try tmp.dir.writeFile(tmp_src_path, update.src); | |
| 246 | ||
| 247 | var module_node = prg_node.start("parse/analysis/codegen", null); | |
| 248 | module_node.activate(); | |
| 249 | try module.update(); | |
| 250 | module_node.end(); | |
| 251 | ||
| 252 | switch (update.expected) { | |
| 253 | .zir => |expected_zir| { | |
| 254 | var emit_node = prg_node.start("emit", null); | |
| 255 | emit_node.activate(); | |
| 256 | var new_zir_module = try zir.emit(allocator, module); | |
| 257 | defer new_zir_module.deinit(allocator); | |
| 258 | emit_node.end(); | |
| 259 | ||
| 260 | var write_node = prg_node.start("write", null); | |
| 261 | write_node.activate(); | |
| 262 | var out_zir = std.ArrayList(u8).init(allocator); | |
| 263 | defer out_zir.deinit(); | |
| 264 | try new_zir_module.writeToStream(allocator, out_zir.outStream()); | |
| 265 | write_node.end(); | |
| 266 | ||
| 267 | std.testing.expectEqualSlices(u8, expected_zir, out_zir.items); | |
| 268 | }, | |
| 269 | .errors => |expected_errors| { | |
| 270 | var all_errors = try module.getAllErrorsAlloc(); | |
| 271 | defer all_errors.deinit(module.allocator); | |
| 272 | for (expected_errors) |expected_error| { | |
| 273 | for (all_errors.list) |full_err_msg| { | |
| 274 | const text = try std.fmt.allocPrint(allocator, ":{}:{}: error: {}", .{ | |
| 275 | full_err_msg.line + 1, | |
| 276 | full_err_msg.column + 1, | |
| 277 | full_err_msg.msg, | |
| 278 | }); | |
| 279 | defer allocator.free(text); | |
| 280 | if (std.mem.eql(u8, text, expected_error)) { | |
| 281 | break; | |
| 282 | } | |
| 283 | } else { | |
| 284 | std.debug.warn( | |
| 285 | "{}\nExpected this error:\n================\n{}\n================\nBut found these errors:\n================\n", | |
| 286 | .{ case.name, expected_error }, | |
| 287 | ); | |
| 288 | for (all_errors.list) |full_err_msg| { | |
| 289 | std.debug.warn(":{}:{}: error: {}\n", .{ | |
| 290 | full_err_msg.line + 1, | |
| 291 | full_err_msg.column + 1, | |
| 292 | full_err_msg.msg, | |
| 293 | }); | |
| 294 | } | |
| 295 | std.debug.warn("================\nTest failed\n", .{}); | |
| 296 | std.process.exit(1); | |
| 297 | } | |
| 298 | } | |
| 299 | }, | |
| 300 | } | |
| 301 | } | |
| 220 | 302 | } |
| 221 | 303 | }; |
| 222 | 304 |
src-self-hosted/type.zig+22| ... | ... | @@ -51,6 +51,7 @@ pub const Type = extern union { |
| 51 | 51 | .comptime_float => return .ComptimeFloat, |
| 52 | 52 | .noreturn => return .NoReturn, |
| 53 | 53 | .@"null" => return .Null, |
| 54 | .@"undefined" => return .Undefined, | |
| 54 | 55 | |
| 55 | 56 | .fn_noreturn_no_args => return .Fn, |
| 56 | 57 | .fn_naked_noreturn_no_args => return .Fn, |
| ... | ... | @@ -201,6 +202,7 @@ pub const Type = extern union { |
| 201 | 202 | => return out_stream.writeAll(@tagName(t)), |
| 202 | 203 | |
| 203 | 204 | .@"null" => return out_stream.writeAll("@TypeOf(null)"), |
| 205 | .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), | |
| 204 | 206 | |
| 205 | 207 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 206 | 208 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| ... | ... | @@ -265,6 +267,7 @@ pub const Type = extern union { |
| 265 | 267 | .comptime_float => return Value.initTag(.comptime_float_type), |
| 266 | 268 | .noreturn => return Value.initTag(.noreturn_type), |
| 267 | 269 | .@"null" => return Value.initTag(.null_type), |
| 270 | .@"undefined" => return Value.initTag(.undefined_type), | |
| 268 | 271 | .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), |
| 269 | 272 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 270 | 273 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), |
| ... | ... | @@ -318,6 +321,7 @@ pub const Type = extern union { |
| 318 | 321 | .comptime_float, |
| 319 | 322 | .noreturn, |
| 320 | 323 | .@"null", |
| 324 | .@"undefined", | |
| 321 | 325 | => false, |
| 322 | 326 | }; |
| 323 | 327 | } |
| ... | ... | @@ -378,6 +382,7 @@ pub const Type = extern union { |
| 378 | 382 | .comptime_float, |
| 379 | 383 | .noreturn, |
| 380 | 384 | .@"null", |
| 385 | .@"undefined", | |
| 381 | 386 | => unreachable, |
| 382 | 387 | }; |
| 383 | 388 | } |
| ... | ... | @@ -410,6 +415,7 @@ pub const Type = extern union { |
| 410 | 415 | .comptime_float, |
| 411 | 416 | .noreturn, |
| 412 | 417 | .@"null", |
| 418 | .@"undefined", | |
| 413 | 419 | .array, |
| 414 | 420 | .array_u8_sentinel_0, |
| 415 | 421 | .const_slice_u8, |
| ... | ... | @@ -454,6 +460,7 @@ pub const Type = extern union { |
| 454 | 460 | .comptime_float, |
| 455 | 461 | .noreturn, |
| 456 | 462 | .@"null", |
| 463 | .@"undefined", | |
| 457 | 464 | .array, |
| 458 | 465 | .array_u8_sentinel_0, |
| 459 | 466 | .single_const_pointer, |
| ... | ... | @@ -498,6 +505,7 @@ pub const Type = extern union { |
| 498 | 505 | .comptime_float, |
| 499 | 506 | .noreturn, |
| 500 | 507 | .@"null", |
| 508 | .@"undefined", | |
| 501 | 509 | .array, |
| 502 | 510 | .array_u8_sentinel_0, |
| 503 | 511 | .fn_noreturn_no_args, |
| ... | ... | @@ -543,6 +551,7 @@ pub const Type = extern union { |
| 543 | 551 | .comptime_float, |
| 544 | 552 | .noreturn, |
| 545 | 553 | .@"null", |
| 554 | .@"undefined", | |
| 546 | 555 | .fn_noreturn_no_args, |
| 547 | 556 | .fn_naked_noreturn_no_args, |
| 548 | 557 | .fn_ccc_void_no_args, |
| ... | ... | @@ -586,6 +595,7 @@ pub const Type = extern union { |
| 586 | 595 | .comptime_float, |
| 587 | 596 | .noreturn, |
| 588 | 597 | .@"null", |
| 598 | .@"undefined", | |
| 589 | 599 | .fn_noreturn_no_args, |
| 590 | 600 | .fn_naked_noreturn_no_args, |
| 591 | 601 | .fn_ccc_void_no_args, |
| ... | ... | @@ -630,6 +640,7 @@ pub const Type = extern union { |
| 630 | 640 | .comptime_float, |
| 631 | 641 | .noreturn, |
| 632 | 642 | .@"null", |
| 643 | .@"undefined", | |
| 633 | 644 | .fn_noreturn_no_args, |
| 634 | 645 | .fn_naked_noreturn_no_args, |
| 635 | 646 | .fn_ccc_void_no_args, |
| ... | ... | @@ -662,6 +673,7 @@ pub const Type = extern union { |
| 662 | 673 | .comptime_float, |
| 663 | 674 | .noreturn, |
| 664 | 675 | .@"null", |
| 676 | .@"undefined", | |
| 665 | 677 | .fn_noreturn_no_args, |
| 666 | 678 | .fn_naked_noreturn_no_args, |
| 667 | 679 | .fn_ccc_void_no_args, |
| ... | ... | @@ -707,6 +719,7 @@ pub const Type = extern union { |
| 707 | 719 | .comptime_float, |
| 708 | 720 | .noreturn, |
| 709 | 721 | .@"null", |
| 722 | .@"undefined", | |
| 710 | 723 | .fn_noreturn_no_args, |
| 711 | 724 | .fn_naked_noreturn_no_args, |
| 712 | 725 | .fn_ccc_void_no_args, |
| ... | ... | @@ -781,6 +794,7 @@ pub const Type = extern union { |
| 781 | 794 | .comptime_float, |
| 782 | 795 | .noreturn, |
| 783 | 796 | .@"null", |
| 797 | .@"undefined", | |
| 784 | 798 | .array, |
| 785 | 799 | .single_const_pointer, |
| 786 | 800 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -826,6 +840,7 @@ pub const Type = extern union { |
| 826 | 840 | .comptime_float, |
| 827 | 841 | .noreturn, |
| 828 | 842 | .@"null", |
| 843 | .@"undefined", | |
| 829 | 844 | .array, |
| 830 | 845 | .single_const_pointer, |
| 831 | 846 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -870,6 +885,7 @@ pub const Type = extern union { |
| 870 | 885 | .comptime_float, |
| 871 | 886 | .noreturn, |
| 872 | 887 | .@"null", |
| 888 | .@"undefined", | |
| 873 | 889 | .array, |
| 874 | 890 | .single_const_pointer, |
| 875 | 891 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -914,6 +930,7 @@ pub const Type = extern union { |
| 914 | 930 | .comptime_float, |
| 915 | 931 | .noreturn, |
| 916 | 932 | .@"null", |
| 933 | .@"undefined", | |
| 917 | 934 | .array, |
| 918 | 935 | .single_const_pointer, |
| 919 | 936 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -958,6 +975,7 @@ pub const Type = extern union { |
| 958 | 975 | .comptime_float, |
| 959 | 976 | .noreturn, |
| 960 | 977 | .@"null", |
| 978 | .@"undefined", | |
| 961 | 979 | .array, |
| 962 | 980 | .single_const_pointer, |
| 963 | 981 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -1013,6 +1031,7 @@ pub const Type = extern union { |
| 1013 | 1031 | .anyerror, |
| 1014 | 1032 | .noreturn, |
| 1015 | 1033 | .@"null", |
| 1034 | .@"undefined", | |
| 1016 | 1035 | .fn_noreturn_no_args, |
| 1017 | 1036 | .fn_naked_noreturn_no_args, |
| 1018 | 1037 | .fn_ccc_void_no_args, |
| ... | ... | @@ -1062,6 +1081,7 @@ pub const Type = extern union { |
| 1062 | 1081 | .void, |
| 1063 | 1082 | .noreturn, |
| 1064 | 1083 | .@"null", |
| 1084 | .@"undefined", | |
| 1065 | 1085 | => return true, |
| 1066 | 1086 | |
| 1067 | 1087 | .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, |
| ... | ... | @@ -1115,6 +1135,7 @@ pub const Type = extern union { |
| 1115 | 1135 | .void, |
| 1116 | 1136 | .noreturn, |
| 1117 | 1137 | .@"null", |
| 1138 | .@"undefined", | |
| 1118 | 1139 | .int_unsigned, |
| 1119 | 1140 | .int_signed, |
| 1120 | 1141 | .array, |
| ... | ... | @@ -1157,6 +1178,7 @@ pub const Type = extern union { |
| 1157 | 1178 | comptime_float, |
| 1158 | 1179 | noreturn, |
| 1159 | 1180 | @"null", |
| 1181 | @"undefined", | |
| 1160 | 1182 | fn_noreturn_no_args, |
| 1161 | 1183 | fn_naked_noreturn_no_args, |
| 1162 | 1184 | fn_ccc_void_no_args, |
src-self-hosted/value.zig+12| ... | ... | @@ -47,6 +47,7 @@ pub const Value = extern union { |
| 47 | 47 | comptime_float_type, |
| 48 | 48 | noreturn_type, |
| 49 | 49 | null_type, |
| 50 | undefined_type, | |
| 50 | 51 | fn_noreturn_no_args_type, |
| 51 | 52 | fn_naked_noreturn_no_args_type, |
| 52 | 53 | fn_ccc_void_no_args_type, |
| ... | ... | @@ -141,6 +142,7 @@ pub const Value = extern union { |
| 141 | 142 | .comptime_float_type => return out_stream.writeAll("comptime_float"), |
| 142 | 143 | .noreturn_type => return out_stream.writeAll("noreturn"), |
| 143 | 144 | .null_type => return out_stream.writeAll("@TypeOf(null)"), |
| 145 | .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"), | |
| 144 | 146 | .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), |
| 145 | 147 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 146 | 148 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), |
| ... | ... | @@ -225,6 +227,7 @@ pub const Value = extern union { |
| 225 | 227 | .comptime_float_type => Type.initTag(.comptime_float), |
| 226 | 228 | .noreturn_type => Type.initTag(.noreturn), |
| 227 | 229 | .null_type => Type.initTag(.@"null"), |
| 230 | .undefined_type => Type.initTag(.@"undefined"), | |
| 228 | 231 | .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), |
| 229 | 232 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 230 | 233 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), |
| ... | ... | @@ -281,6 +284,7 @@ pub const Value = extern union { |
| 281 | 284 | .comptime_float_type, |
| 282 | 285 | .noreturn_type, |
| 283 | 286 | .null_type, |
| 287 | .undefined_type, | |
| 284 | 288 | .fn_noreturn_no_args_type, |
| 285 | 289 | .fn_naked_noreturn_no_args_type, |
| 286 | 290 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -339,6 +343,7 @@ pub const Value = extern union { |
| 339 | 343 | .comptime_float_type, |
| 340 | 344 | .noreturn_type, |
| 341 | 345 | .null_type, |
| 346 | .undefined_type, | |
| 342 | 347 | .fn_noreturn_no_args_type, |
| 343 | 348 | .fn_naked_noreturn_no_args_type, |
| 344 | 349 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -398,6 +403,7 @@ pub const Value = extern union { |
| 398 | 403 | .comptime_float_type, |
| 399 | 404 | .noreturn_type, |
| 400 | 405 | .null_type, |
| 406 | .undefined_type, | |
| 401 | 407 | .fn_noreturn_no_args_type, |
| 402 | 408 | .fn_naked_noreturn_no_args_type, |
| 403 | 409 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -462,6 +468,7 @@ pub const Value = extern union { |
| 462 | 468 | .comptime_float_type, |
| 463 | 469 | .noreturn_type, |
| 464 | 470 | .null_type, |
| 471 | .undefined_type, | |
| 465 | 472 | .fn_noreturn_no_args_type, |
| 466 | 473 | .fn_naked_noreturn_no_args_type, |
| 467 | 474 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -555,6 +562,7 @@ pub const Value = extern union { |
| 555 | 562 | .comptime_float_type, |
| 556 | 563 | .noreturn_type, |
| 557 | 564 | .null_type, |
| 565 | .undefined_type, | |
| 558 | 566 | .fn_noreturn_no_args_type, |
| 559 | 567 | .fn_naked_noreturn_no_args_type, |
| 560 | 568 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -610,6 +618,7 @@ pub const Value = extern union { |
| 610 | 618 | .comptime_float_type, |
| 611 | 619 | .noreturn_type, |
| 612 | 620 | .null_type, |
| 621 | .undefined_type, | |
| 613 | 622 | .fn_noreturn_no_args_type, |
| 614 | 623 | .fn_naked_noreturn_no_args_type, |
| 615 | 624 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -710,6 +719,7 @@ pub const Value = extern union { |
| 710 | 719 | .comptime_float_type, |
| 711 | 720 | .noreturn_type, |
| 712 | 721 | .null_type, |
| 722 | .undefined_type, | |
| 713 | 723 | .fn_noreturn_no_args_type, |
| 714 | 724 | .fn_naked_noreturn_no_args_type, |
| 715 | 725 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -771,6 +781,7 @@ pub const Value = extern union { |
| 771 | 781 | .comptime_float_type, |
| 772 | 782 | .noreturn_type, |
| 773 | 783 | .null_type, |
| 784 | .undefined_type, | |
| 774 | 785 | .fn_noreturn_no_args_type, |
| 775 | 786 | .fn_naked_noreturn_no_args_type, |
| 776 | 787 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -849,6 +860,7 @@ pub const Value = extern union { |
| 849 | 860 | .comptime_float_type, |
| 850 | 861 | .noreturn_type, |
| 851 | 862 | .null_type, |
| 863 | .undefined_type, | |
| 852 | 864 | .fn_noreturn_no_args_type, |
| 853 | 865 | .fn_naked_noreturn_no_args_type, |
| 854 | 866 | .fn_ccc_void_no_args_type, |
src-self-hosted/zir.zig+302-141| ... | ... | @@ -27,9 +27,12 @@ pub const Inst = struct { |
| 27 | 27 | pub const Tag = enum { |
| 28 | 28 | breakpoint, |
| 29 | 29 | call, |
| 30 | /// Represents a reference to a global decl by name. | |
| 31 | /// The syntax `@foo` is equivalent to `declref("foo")`. | |
| 30 | compileerror, | |
| 31 | /// Represents a pointer to a global decl by name. | |
| 32 | 32 | declref, |
| 33 | /// The syntax `@foo` is equivalent to `declval("foo")`. | |
| 34 | /// declval is equivalent to declref followed by deref. | |
| 35 | declval, | |
| 33 | 36 | str, |
| 34 | 37 | int, |
| 35 | 38 | ptrtoint, |
| ... | ... | @@ -59,6 +62,8 @@ pub const Inst = struct { |
| 59 | 62 | .breakpoint => Breakpoint, |
| 60 | 63 | .call => Call, |
| 61 | 64 | .declref => DeclRef, |
| 65 | .declval => DeclVal, | |
| 66 | .compileerror => CompileError, | |
| 62 | 67 | .str => Str, |
| 63 | 68 | .int => Int, |
| 64 | 69 | .ptrtoint => PtrToInt, |
| ... | ... | @@ -122,6 +127,26 @@ pub const Inst = struct { |
| 122 | 127 | kw_args: struct {}, |
| 123 | 128 | }; |
| 124 | 129 | |
| 130 | pub const DeclVal = struct { | |
| 131 | pub const base_tag = Tag.declval; | |
| 132 | base: Inst, | |
| 133 | ||
| 134 | positionals: struct { | |
| 135 | name: []const u8, | |
| 136 | }, | |
| 137 | kw_args: struct {}, | |
| 138 | }; | |
| 139 | ||
| 140 | pub const CompileError = struct { | |
| 141 | pub const base_tag = Tag.compileerror; | |
| 142 | base: Inst, | |
| 143 | ||
| 144 | positionals: struct { | |
| 145 | msg: []const u8, | |
| 146 | }, | |
| 147 | kw_args: struct {}, | |
| 148 | }; | |
| 149 | ||
| 125 | 150 | pub const Str = struct { |
| 126 | 151 | pub const base_tag = Tag.str; |
| 127 | 152 | base: Inst, |
| ... | ... | @@ -254,11 +279,11 @@ pub const Inst = struct { |
| 254 | 279 | base: Inst, |
| 255 | 280 | |
| 256 | 281 | positionals: struct { |
| 257 | tag: BuiltinType, | |
| 282 | tag: Builtin, | |
| 258 | 283 | }, |
| 259 | 284 | kw_args: struct {}, |
| 260 | 285 | |
| 261 | pub const BuiltinType = enum { | |
| 286 | pub const Builtin = enum { | |
| 262 | 287 | isize, |
| 263 | 288 | usize, |
| 264 | 289 | c_short, |
| ... | ... | @@ -282,32 +307,42 @@ pub const Inst = struct { |
| 282 | 307 | anyerror, |
| 283 | 308 | comptime_int, |
| 284 | 309 | comptime_float, |
| 310 | @"true", | |
| 311 | @"false", | |
| 312 | @"null", | |
| 313 | @"undefined", | |
| 314 | void_value, | |
| 285 | 315 | |
| 286 | pub fn toType(self: BuiltinType) Type { | |
| 316 | pub fn toTypedValue(self: Builtin) TypedValue { | |
| 287 | 317 | return switch (self) { |
| 288 | .isize => Type.initTag(.isize), | |
| 289 | .usize => Type.initTag(.usize), | |
| 290 | .c_short => Type.initTag(.c_short), | |
| 291 | .c_ushort => Type.initTag(.c_ushort), | |
| 292 | .c_int => Type.initTag(.c_int), | |
| 293 | .c_uint => Type.initTag(.c_uint), | |
| 294 | .c_long => Type.initTag(.c_long), | |
| 295 | .c_ulong => Type.initTag(.c_ulong), | |
| 296 | .c_longlong => Type.initTag(.c_longlong), | |
| 297 | .c_ulonglong => Type.initTag(.c_ulonglong), | |
| 298 | .c_longdouble => Type.initTag(.c_longdouble), | |
| 299 | .c_void => Type.initTag(.c_void), | |
| 300 | .f16 => Type.initTag(.f16), | |
| 301 | .f32 => Type.initTag(.f32), | |
| 302 | .f64 => Type.initTag(.f64), | |
| 303 | .f128 => Type.initTag(.f128), | |
| 304 | .bool => Type.initTag(.bool), | |
| 305 | .void => Type.initTag(.void), | |
| 306 | .noreturn => Type.initTag(.noreturn), | |
| 307 | .type => Type.initTag(.type), | |
| 308 | .anyerror => Type.initTag(.anyerror), | |
| 309 | .comptime_int => Type.initTag(.comptime_int), | |
| 310 | .comptime_float => Type.initTag(.comptime_float), | |
| 318 | .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) }, | |
| 319 | .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) }, | |
| 320 | .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) }, | |
| 321 | .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) }, | |
| 322 | .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) }, | |
| 323 | .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) }, | |
| 324 | .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) }, | |
| 325 | .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) }, | |
| 326 | .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) }, | |
| 327 | .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) }, | |
| 328 | .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) }, | |
| 329 | .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) }, | |
| 330 | .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) }, | |
| 331 | .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) }, | |
| 332 | .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) }, | |
| 333 | .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) }, | |
| 334 | .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) }, | |
| 335 | .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) }, | |
| 336 | .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) }, | |
| 337 | .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) }, | |
| 338 | .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) }, | |
| 339 | .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) }, | |
| 340 | .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) }, | |
| 341 | .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) }, | |
| 342 | .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) }, | |
| 343 | .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) }, | |
| 344 | .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) }, | |
| 345 | .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) }, | |
| 311 | 346 | }; |
| 312 | 347 | } |
| 313 | 348 | }; |
| ... | ... | @@ -440,7 +475,7 @@ pub const Module = struct { |
| 440 | 475 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| 441 | 476 | } |
| 442 | 477 | |
| 443 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body }); | |
| 478 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize }); | |
| 444 | 479 | |
| 445 | 480 | /// TODO Look into making a table to speed this up. |
| 446 | 481 | pub fn findDecl(self: Module, name: []const u8) ?*Inst { |
| ... | ... | @@ -462,17 +497,17 @@ pub const Module = struct { |
| 462 | 497 | try inst_table.ensureCapacity(self.decls.len); |
| 463 | 498 | |
| 464 | 499 | for (self.decls) |decl, decl_i| { |
| 465 | try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null }); | |
| 500 | try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null }); | |
| 466 | 501 | |
| 467 | 502 | if (decl.cast(Inst.Fn)) |fn_inst| { |
| 468 | 503 | for (fn_inst.positionals.body.instructions) |inst, inst_i| { |
| 469 | try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body }); | |
| 504 | try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i }); | |
| 470 | 505 | } |
| 471 | 506 | } |
| 472 | 507 | } |
| 473 | 508 | |
| 474 | 509 | for (self.decls) |decl, i| { |
| 475 | try stream.print("@{} ", .{i}); | |
| 510 | try stream.print("@{} ", .{decl.name}); | |
| 476 | 511 | try self.writeInstToStream(stream, decl, &inst_table); |
| 477 | 512 | try stream.writeByte('\n'); |
| 478 | 513 | } |
| ... | ... | @@ -489,6 +524,8 @@ pub const Module = struct { |
| 489 | 524 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), |
| 490 | 525 | .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table), |
| 491 | 526 | .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table), |
| 527 | .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table), | |
| 528 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table), | |
| 492 | 529 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), |
| 493 | 530 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), |
| 494 | 531 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), |
| ... | ... | @@ -587,9 +624,18 @@ pub const Module = struct { |
| 587 | 624 | } |
| 588 | 625 | |
| 589 | 626 | fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void { |
| 590 | const info = inst_table.getValue(inst).?; | |
| 591 | const prefix = if (info.fn_body == null) "@" else "%"; | |
| 592 | try stream.print("{}{}", .{ prefix, info.index }); | |
| 627 | if (inst_table.getValue(inst)) |info| { | |
| 628 | if (info.index) |i| { | |
| 629 | try stream.print("%{}", .{info.index}); | |
| 630 | } else { | |
| 631 | try stream.print("@{}", .{info.inst.name}); | |
| 632 | } | |
| 633 | } else if (inst.cast(Inst.DeclVal)) |decl_val| { | |
| 634 | try stream.print("@{}", .{decl_val.positionals.name}); | |
| 635 | } else { | |
| 636 | //try stream.print("?", .{}); | |
| 637 | unreachable; | |
| 638 | } | |
| 593 | 639 | } |
| 594 | 640 | }; |
| 595 | 641 | |
| ... | ... | @@ -884,6 +930,7 @@ const Parser = struct { |
| 884 | 930 | try requireEatBytes(self, ")"); |
| 885 | 931 | |
| 886 | 932 | inst_specific.base.contents = self.source[contents_start..self.i]; |
| 933 | //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents }); | |
| 887 | 934 | |
| 888 | 935 | return &inst_specific.base; |
| 889 | 936 | } |
| ... | ... | @@ -964,47 +1011,17 @@ const Parser = struct { |
| 964 | 1011 | self.i = src; |
| 965 | 1012 | return self.fail("unrecognized identifier: {}", .{bad_name}); |
| 966 | 1013 | } else { |
| 967 | const name_array = try self.arena.allocator.create(Inst.Str); | |
| 968 | name_array.* = .{ | |
| 969 | .base = .{ | |
| 970 | .name = try self.generateName(), | |
| 971 | .src = src, | |
| 972 | .tag = Inst.Str.base_tag, | |
| 973 | }, | |
| 974 | .positionals = .{ .bytes = ident }, | |
| 975 | .kw_args = .{}, | |
| 976 | }; | |
| 977 | const name = try self.arena.allocator.create(Inst.Ref); | |
| 978 | name.* = .{ | |
| 979 | .base = .{ | |
| 980 | .name = try self.generateName(), | |
| 981 | .src = src, | |
| 982 | .tag = Inst.Ref.base_tag, | |
| 983 | }, | |
| 984 | .positionals = .{ .operand = &name_array.base }, | |
| 985 | .kw_args = .{}, | |
| 986 | }; | |
| 987 | const declref = try self.arena.allocator.create(Inst.DeclRef); | |
| 988 | declref.* = .{ | |
| 989 | .base = .{ | |
| 990 | .name = try self.generateName(), | |
| 991 | .src = src, | |
| 992 | .tag = Inst.DeclRef.base_tag, | |
| 993 | }, | |
| 994 | .positionals = .{ .name = &name.base }, | |
| 995 | .kw_args = .{}, | |
| 996 | }; | |
| 997 | const deref = try self.arena.allocator.create(Inst.Deref); | |
| 998 | deref.* = .{ | |
| 1014 | const declval = try self.arena.allocator.create(Inst.DeclVal); | |
| 1015 | declval.* = .{ | |
| 999 | 1016 | .base = .{ |
| 1000 | 1017 | .name = try self.generateName(), |
| 1001 | 1018 | .src = src, |
| 1002 | .tag = Inst.Deref.base_tag, | |
| 1019 | .tag = Inst.DeclVal.base_tag, | |
| 1003 | 1020 | }, |
| 1004 | .positionals = .{ .ptr = &declref.base }, | |
| 1021 | .positionals = .{ .name = ident }, | |
| 1005 | 1022 | .kw_args = .{}, |
| 1006 | 1023 | }; |
| 1007 | return &deref.base; | |
| 1024 | return &declval.base; | |
| 1008 | 1025 | } |
| 1009 | 1026 | }; |
| 1010 | 1027 | if (local_ref) { |
| ... | ... | @@ -1025,12 +1042,15 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { |
| 1025 | 1042 | var ctx: EmitZIR = .{ |
| 1026 | 1043 | .allocator = allocator, |
| 1027 | 1044 | .decls = .{}, |
| 1028 | .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator), | |
| 1029 | 1045 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 1030 | 1046 | .old_module = &old_module, |
| 1047 | .next_auto_name = 0, | |
| 1048 | .names = std.StringHashMap(void).init(allocator), | |
| 1049 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator), | |
| 1031 | 1050 | }; |
| 1032 | 1051 | defer ctx.decls.deinit(allocator); |
| 1033 | defer ctx.decl_table.deinit(); | |
| 1052 | defer ctx.names.deinit(); | |
| 1053 | defer ctx.primitive_table.deinit(); | |
| 1034 | 1054 | errdefer ctx.arena.deinit(); |
| 1035 | 1055 | |
| 1036 | 1056 | try ctx.emit(); |
| ... | ... | @@ -1046,47 +1066,90 @@ const EmitZIR = struct { |
| 1046 | 1066 | arena: std.heap.ArenaAllocator, |
| 1047 | 1067 | old_module: *const IrModule, |
| 1048 | 1068 | decls: std.ArrayListUnmanaged(*Inst), |
| 1049 | decl_table: std.AutoHashMap(*ir.Inst, *Inst), | |
| 1069 | names: std.StringHashMap(void), | |
| 1070 | next_auto_name: usize, | |
| 1071 | primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst), | |
| 1050 | 1072 | |
| 1051 | 1073 | fn emit(self: *EmitZIR) !void { |
| 1052 | var it = self.old_module.decl_exports.iterator(); | |
| 1053 | while (it.next()) |kv| { | |
| 1054 | const decl = kv.key; | |
| 1055 | const exports = kv.value; | |
| 1056 | const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value); | |
| 1057 | for (exports) |module_export| { | |
| 1058 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); | |
| 1059 | const export_inst = try self.arena.allocator.create(Inst.Export); | |
| 1060 | export_inst.* = .{ | |
| 1061 | .base = .{ | |
| 1062 | .name = try self.autoName(), | |
| 1063 | .src = module_export.src, | |
| 1064 | .tag = Inst.Export.base_tag, | |
| 1065 | }, | |
| 1066 | .positionals = .{ | |
| 1067 | .symbol_name = symbol_name, | |
| 1068 | .value = export_value, | |
| 1069 | }, | |
| 1070 | .kw_args = .{}, | |
| 1071 | }; | |
| 1072 | try self.decls.append(self.allocator, &export_inst.base); | |
| 1074 | // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced | |
| 1075 | // by the hash table. | |
| 1076 | var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); | |
| 1077 | defer src_decls.deinit(); | |
| 1078 | try src_decls.ensureCapacity(self.old_module.decl_table.size); | |
| 1079 | try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size); | |
| 1080 | try self.names.ensureCapacity(self.old_module.decl_table.size); | |
| 1081 | ||
| 1082 | var decl_it = self.old_module.decl_table.iterator(); | |
| 1083 | while (decl_it.next()) |kv| { | |
| 1084 | const decl = kv.value; | |
| 1085 | src_decls.appendAssumeCapacity(decl); | |
| 1086 | self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); | |
| 1087 | } | |
| 1088 | std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { | |
| 1089 | fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { | |
| 1090 | return a.src < b.src; | |
| 1091 | } | |
| 1092 | }).lessThan); | |
| 1093 | ||
| 1094 | // Emit all the decls. | |
| 1095 | for (src_decls.items) |ir_decl| { | |
| 1096 | if (self.old_module.export_owners.getValue(ir_decl)) |exports| { | |
| 1097 | for (exports) |module_export| { | |
| 1098 | const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name)); | |
| 1099 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); | |
| 1100 | const export_inst = try self.arena.allocator.create(Inst.Export); | |
| 1101 | export_inst.* = .{ | |
| 1102 | .base = .{ | |
| 1103 | .name = try self.autoName(), | |
| 1104 | .src = module_export.src, | |
| 1105 | .tag = Inst.Export.base_tag, | |
| 1106 | }, | |
| 1107 | .positionals = .{ | |
| 1108 | .symbol_name = symbol_name, | |
| 1109 | .value = declval, | |
| 1110 | }, | |
| 1111 | .kw_args = .{}, | |
| 1112 | }; | |
| 1113 | try self.decls.append(self.allocator, &export_inst.base); | |
| 1114 | } | |
| 1115 | } else { | |
| 1116 | const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value); | |
| 1117 | new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); | |
| 1073 | 1118 | } |
| 1074 | 1119 | } |
| 1075 | 1120 | } |
| 1076 | 1121 | |
| 1077 | fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst { | |
| 1122 | fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst { | |
| 1078 | 1123 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| 1079 | if (self.decl_table.getValue(inst)) |decl| { | |
| 1080 | return decl; | |
| 1081 | } | |
| 1082 | const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); | |
| 1083 | try self.decl_table.putNoClobber(inst, new_decl); | |
| 1124 | const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: { | |
| 1125 | const owner_decl = func_pl.func.owner_decl; | |
| 1126 | break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); | |
| 1127 | } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: { | |
| 1128 | break :blk try self.emitDeclRef(inst.src, declref.decl); | |
| 1129 | } else blk: { | |
| 1130 | break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); | |
| 1131 | }; | |
| 1132 | try inst_table.putNoClobber(inst, new_decl); | |
| 1084 | 1133 | return new_decl; |
| 1085 | 1134 | } else { |
| 1086 | 1135 | return inst_table.getValue(inst).?; |
| 1087 | 1136 | } |
| 1088 | 1137 | } |
| 1089 | 1138 | |
| 1139 | fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst { | |
| 1140 | const declval = try self.arena.allocator.create(Inst.DeclVal); | |
| 1141 | declval.* = .{ | |
| 1142 | .base = .{ | |
| 1143 | .name = try self.autoName(), | |
| 1144 | .src = src, | |
| 1145 | .tag = Inst.DeclVal.base_tag, | |
| 1146 | }, | |
| 1147 | .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) }, | |
| 1148 | .kw_args = .{}, | |
| 1149 | }; | |
| 1150 | return &declval.base; | |
| 1151 | } | |
| 1152 | ||
| 1090 | 1153 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { |
| 1091 | 1154 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); |
| 1092 | 1155 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| ... | ... | @@ -1105,8 +1168,31 @@ const EmitZIR = struct { |
| 1105 | 1168 | return &int_inst.base; |
| 1106 | 1169 | } |
| 1107 | 1170 | |
| 1171 | fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst { | |
| 1172 | const declval = try self.emitDeclVal(src, mem.spanZ(decl.name)); | |
| 1173 | const ref_inst = try self.arena.allocator.create(Inst.Ref); | |
| 1174 | ref_inst.* = .{ | |
| 1175 | .base = .{ | |
| 1176 | .name = try self.autoName(), | |
| 1177 | .src = src, | |
| 1178 | .tag = Inst.Ref.base_tag, | |
| 1179 | }, | |
| 1180 | .positionals = .{ | |
| 1181 | .operand = declval, | |
| 1182 | }, | |
| 1183 | .kw_args = .{}, | |
| 1184 | }; | |
| 1185 | try self.decls.append(self.allocator, &ref_inst.base); | |
| 1186 | ||
| 1187 | return &ref_inst.base; | |
| 1188 | } | |
| 1189 | ||
| 1108 | 1190 | fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst { |
| 1109 | 1191 | const allocator = &self.arena.allocator; |
| 1192 | if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| { | |
| 1193 | const decl = decl_ref.decl; | |
| 1194 | return self.emitDeclRef(src, decl); | |
| 1195 | } | |
| 1110 | 1196 | switch (typed_value.ty.zigTypeTag()) { |
| 1111 | 1197 | .Pointer => { |
| 1112 | 1198 | const ptr_elem_type = typed_value.ty.elemType(); |
| ... | ... | @@ -1142,7 +1228,6 @@ const EmitZIR = struct { |
| 1142 | 1228 | }, |
| 1143 | 1229 | .kw_args = .{}, |
| 1144 | 1230 | }; |
| 1145 | try self.decls.append(self.allocator, &as_inst.base); | |
| 1146 | 1231 | |
| 1147 | 1232 | return &as_inst.base; |
| 1148 | 1233 | }, |
| ... | ... | @@ -1159,7 +1244,44 @@ const EmitZIR = struct { |
| 1159 | 1244 | var instructions = std.ArrayList(*Inst).init(self.allocator); |
| 1160 | 1245 | defer instructions.deinit(); |
| 1161 | 1246 | |
| 1162 | try self.emitBody(module_fn.analysis.success, &inst_table, &instructions); | |
| 1247 | switch (module_fn.analysis) { | |
| 1248 | .queued => unreachable, | |
| 1249 | .in_progress => unreachable, | |
| 1250 | .success => |body| { | |
| 1251 | try self.emitBody(body, &inst_table, &instructions); | |
| 1252 | }, | |
| 1253 | .sema_failure => { | |
| 1254 | const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?; | |
| 1255 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | |
| 1256 | fail_inst.* = .{ | |
| 1257 | .base = .{ | |
| 1258 | .name = try self.autoName(), | |
| 1259 | .src = src, | |
| 1260 | .tag = Inst.CompileError.base_tag, | |
| 1261 | }, | |
| 1262 | .positionals = .{ | |
| 1263 | .msg = try self.arena.allocator.dupe(u8, err_msg.msg), | |
| 1264 | }, | |
| 1265 | .kw_args = .{}, | |
| 1266 | }; | |
| 1267 | try instructions.append(&fail_inst.base); | |
| 1268 | }, | |
| 1269 | .dependency_failure => { | |
| 1270 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | |
| 1271 | fail_inst.* = .{ | |
| 1272 | .base = .{ | |
| 1273 | .name = try self.autoName(), | |
| 1274 | .src = src, | |
| 1275 | .tag = Inst.CompileError.base_tag, | |
| 1276 | }, | |
| 1277 | .positionals = .{ | |
| 1278 | .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"), | |
| 1279 | }, | |
| 1280 | .kw_args = .{}, | |
| 1281 | }; | |
| 1282 | try instructions.append(&fail_inst.base); | |
| 1283 | }, | |
| 1284 | } | |
| 1163 | 1285 | |
| 1164 | 1286 | const fn_type = try self.emitType(src, module_fn.fn_type); |
| 1165 | 1287 | |
| ... | ... | @@ -1182,6 +1304,33 @@ const EmitZIR = struct { |
| 1182 | 1304 | try self.decls.append(self.allocator, &fn_inst.base); |
| 1183 | 1305 | return &fn_inst.base; |
| 1184 | 1306 | }, |
| 1307 | .Array => { | |
| 1308 | // TODO more checks to make sure this can be emitted as a string literal | |
| 1309 | //const array_elem_type = ptr_elem_type.elemType(); | |
| 1310 | //if (array_elem_type.eql(Type.initTag(.u8)) and | |
| 1311 | // ptr_elem_type.hasSentinel(Value.initTag(.zero))) | |
| 1312 | //{ | |
| 1313 | //} | |
| 1314 | const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { | |
| 1315 | error.AnalysisFail => unreachable, | |
| 1316 | else => |e| return e, | |
| 1317 | }; | |
| 1318 | const str_inst = try self.arena.allocator.create(Inst.Str); | |
| 1319 | str_inst.* = .{ | |
| 1320 | .base = .{ | |
| 1321 | .name = try self.autoName(), | |
| 1322 | .src = src, | |
| 1323 | .tag = Inst.Str.base_tag, | |
| 1324 | }, | |
| 1325 | .positionals = .{ | |
| 1326 | .bytes = bytes, | |
| 1327 | }, | |
| 1328 | .kw_args = .{}, | |
| 1329 | }; | |
| 1330 | try self.decls.append(self.allocator, &str_inst.base); | |
| 1331 | return &str_inst.base; | |
| 1332 | }, | |
| 1333 | .Void => return self.emitPrimitive(src, .void_value), | |
| 1185 | 1334 | else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), |
| 1186 | 1335 | } |
| 1187 | 1336 | } |
| ... | ... | @@ -1395,30 +1544,30 @@ const EmitZIR = struct { |
| 1395 | 1544 | |
| 1396 | 1545 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { |
| 1397 | 1546 | switch (ty.tag()) { |
| 1398 | .isize => return self.emitPrimitiveType(src, .isize), | |
| 1399 | .usize => return self.emitPrimitiveType(src, .usize), | |
| 1400 | .c_short => return self.emitPrimitiveType(src, .c_short), | |
| 1401 | .c_ushort => return self.emitPrimitiveType(src, .c_ushort), | |
| 1402 | .c_int => return self.emitPrimitiveType(src, .c_int), | |
| 1403 | .c_uint => return self.emitPrimitiveType(src, .c_uint), | |
| 1404 | .c_long => return self.emitPrimitiveType(src, .c_long), | |
| 1405 | .c_ulong => return self.emitPrimitiveType(src, .c_ulong), | |
| 1406 | .c_longlong => return self.emitPrimitiveType(src, .c_longlong), | |
| 1407 | .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong), | |
| 1408 | .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble), | |
| 1409 | .c_void => return self.emitPrimitiveType(src, .c_void), | |
| 1410 | .f16 => return self.emitPrimitiveType(src, .f16), | |
| 1411 | .f32 => return self.emitPrimitiveType(src, .f32), | |
| 1412 | .f64 => return self.emitPrimitiveType(src, .f64), | |
| 1413 | .f128 => return self.emitPrimitiveType(src, .f128), | |
| 1414 | .anyerror => return self.emitPrimitiveType(src, .anyerror), | |
| 1547 | .isize => return self.emitPrimitive(src, .isize), | |
| 1548 | .usize => return self.emitPrimitive(src, .usize), | |
| 1549 | .c_short => return self.emitPrimitive(src, .c_short), | |
| 1550 | .c_ushort => return self.emitPrimitive(src, .c_ushort), | |
| 1551 | .c_int => return self.emitPrimitive(src, .c_int), | |
| 1552 | .c_uint => return self.emitPrimitive(src, .c_uint), | |
| 1553 | .c_long => return self.emitPrimitive(src, .c_long), | |
| 1554 | .c_ulong => return self.emitPrimitive(src, .c_ulong), | |
| 1555 | .c_longlong => return self.emitPrimitive(src, .c_longlong), | |
| 1556 | .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong), | |
| 1557 | .c_longdouble => return self.emitPrimitive(src, .c_longdouble), | |
| 1558 | .c_void => return self.emitPrimitive(src, .c_void), | |
| 1559 | .f16 => return self.emitPrimitive(src, .f16), | |
| 1560 | .f32 => return self.emitPrimitive(src, .f32), | |
| 1561 | .f64 => return self.emitPrimitive(src, .f64), | |
| 1562 | .f128 => return self.emitPrimitive(src, .f128), | |
| 1563 | .anyerror => return self.emitPrimitive(src, .anyerror), | |
| 1415 | 1564 | else => switch (ty.zigTypeTag()) { |
| 1416 | .Bool => return self.emitPrimitiveType(src, .bool), | |
| 1417 | .Void => return self.emitPrimitiveType(src, .void), | |
| 1418 | .NoReturn => return self.emitPrimitiveType(src, .noreturn), | |
| 1419 | .Type => return self.emitPrimitiveType(src, .type), | |
| 1420 | .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int), | |
| 1421 | .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float), | |
| 1565 | .Bool => return self.emitPrimitive(src, .bool), | |
| 1566 | .Void => return self.emitPrimitive(src, .void), | |
| 1567 | .NoReturn => return self.emitPrimitive(src, .noreturn), | |
| 1568 | .Type => return self.emitPrimitive(src, .type), | |
| 1569 | .ComptimeInt => return self.emitPrimitive(src, .comptime_int), | |
| 1570 | .ComptimeFloat => return self.emitPrimitive(src, .comptime_float), | |
| 1422 | 1571 | .Fn => { |
| 1423 | 1572 | const param_types = try self.allocator.alloc(Type, ty.fnParamLen()); |
| 1424 | 1573 | defer self.allocator.free(param_types); |
| ... | ... | @@ -1453,24 +1602,36 @@ const EmitZIR = struct { |
| 1453 | 1602 | } |
| 1454 | 1603 | |
| 1455 | 1604 | fn autoName(self: *EmitZIR) ![]u8 { |
| 1456 | return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len}); | |
| 1605 | while (true) { | |
| 1606 | const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name}); | |
| 1607 | self.next_auto_name += 1; | |
| 1608 | const gop = try self.names.getOrPut(proposed_name); | |
| 1609 | if (!gop.found_existing) { | |
| 1610 | gop.kv.value = {}; | |
| 1611 | return proposed_name; | |
| 1612 | } | |
| 1613 | } | |
| 1457 | 1614 | } |
| 1458 | 1615 | |
| 1459 | fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst { | |
| 1460 | const primitive_inst = try self.arena.allocator.create(Inst.Primitive); | |
| 1461 | primitive_inst.* = .{ | |
| 1462 | .base = .{ | |
| 1463 | .name = try self.autoName(), | |
| 1464 | .src = src, | |
| 1465 | .tag = Inst.Primitive.base_tag, | |
| 1466 | }, | |
| 1467 | .positionals = .{ | |
| 1468 | .tag = tag, | |
| 1469 | }, | |
| 1470 | .kw_args = .{}, | |
| 1471 | }; | |
| 1472 | try self.decls.append(self.allocator, &primitive_inst.base); | |
| 1473 | return &primitive_inst.base; | |
| 1616 | fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst { | |
| 1617 | const gop = try self.primitive_table.getOrPut(tag); | |
| 1618 | if (!gop.found_existing) { | |
| 1619 | const primitive_inst = try self.arena.allocator.create(Inst.Primitive); | |
| 1620 | primitive_inst.* = .{ | |
| 1621 | .base = .{ | |
| 1622 | .name = try self.autoName(), | |
| 1623 | .src = src, | |
| 1624 | .tag = Inst.Primitive.base_tag, | |
| 1625 | }, | |
| 1626 | .positionals = .{ | |
| 1627 | .tag = tag, | |
| 1628 | }, | |
| 1629 | .kw_args = .{}, | |
| 1630 | }; | |
| 1631 | try self.decls.append(self.allocator, &primitive_inst.base); | |
| 1632 | gop.kv.value = &primitive_inst.base; | |
| 1633 | } | |
| 1634 | return gop.kv.value; | |
| 1474 | 1635 | } |
| 1475 | 1636 | |
| 1476 | 1637 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst { |
test/stage2/zir.zig+142-12| ... | ... | @@ -21,14 +21,17 @@ pub fn addCases(ctx: *TestContext) void { |
| 21 | 21 | \\ %11 = return() |
| 22 | 22 | \\}) |
| 23 | 23 | , |
| 24 | \\@0 = primitive(void) | |
| 25 | \\@1 = fntype([], @0, cc=C) | |
| 26 | \\@2 = fn(@1, { | |
| 24 | \\@void = primitive(void) | |
| 25 | \\@fnty = fntype([], @void, cc=C) | |
| 26 | \\@9 = str("entry") | |
| 27 | \\@10 = ref(@9) | |
| 28 | \\@unnamed$6 = str("entry") | |
| 29 | \\@unnamed$7 = ref(@unnamed$6) | |
| 30 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 31 | \\@unnamed$10 = fntype([], @void, cc=C) | |
| 32 | \\@entry = fn(@unnamed$10, { | |
| 27 | 33 | \\ %0 = return() |
| 28 | 34 | \\}) |
| 29 | \\@3 = str("entry") | |
| 30 | \\@4 = ref(@3) | |
| 31 | \\@5 = export(@4, @2) | |
| 32 | 35 | \\ |
| 33 | 36 | ); |
| 34 | 37 | ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, |
| ... | ... | @@ -68,17 +71,144 @@ pub fn addCases(ctx: *TestContext) void { |
| 68 | 71 | \\@10 = ref(@9) |
| 69 | 72 | \\@11 = export(@10, @entry) |
| 70 | 73 | , |
| 71 | \\@0 = primitive(void) | |
| 72 | \\@1 = fntype([], @0, cc=C) | |
| 73 | \\@2 = fn(@1, { | |
| 74 | \\@void = primitive(void) | |
| 75 | \\@fnty = fntype([], @void, cc=C) | |
| 76 | \\@0 = int(0) | |
| 77 | \\@1 = int(1) | |
| 78 | \\@2 = int(2) | |
| 79 | \\@3 = int(3) | |
| 80 | \\@unnamed$7 = fntype([], @void, cc=C) | |
| 81 | \\@entry = fn(@unnamed$7, { | |
| 74 | 82 | \\ %0 = return() |
| 75 | 83 | \\}) |
| 76 | \\@3 = str("entry") | |
| 77 | \\@4 = ref(@3) | |
| 78 | \\@5 = export(@4, @2) | |
| 84 | \\@a = str("2\x08\x01\n") | |
| 85 | \\@9 = str("entry") | |
| 86 | \\@10 = ref(@9) | |
| 87 | \\@unnamed$14 = str("entry") | |
| 88 | \\@unnamed$15 = ref(@unnamed$14) | |
| 89 | \\@unnamed$16 = export(@unnamed$15, @entry) | |
| 79 | 90 | \\ |
| 80 | 91 | ); |
| 81 | 92 | |
| 93 | { | |
| 94 | var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64); | |
| 95 | case.addZIR( | |
| 96 | \\@void = primitive(void) | |
| 97 | \\@fnty = fntype([], @void, cc=C) | |
| 98 | \\ | |
| 99 | \\@9 = str("entry") | |
| 100 | \\@10 = ref(@9) | |
| 101 | \\@11 = export(@10, @entry) | |
| 102 | \\ | |
| 103 | \\@entry = fn(@fnty, { | |
| 104 | \\ %0 = call(@a, []) | |
| 105 | \\ %1 = return() | |
| 106 | \\}) | |
| 107 | \\ | |
| 108 | \\@a = fn(@fnty, { | |
| 109 | \\ %0 = call(@b, []) | |
| 110 | \\ %1 = return() | |
| 111 | \\}) | |
| 112 | \\ | |
| 113 | \\@b = fn(@fnty, { | |
| 114 | \\ %0 = call(@a, []) | |
| 115 | \\ %1 = return() | |
| 116 | \\}) | |
| 117 | , | |
| 118 | \\@void = primitive(void) | |
| 119 | \\@fnty = fntype([], @void, cc=C) | |
| 120 | \\@9 = str("entry") | |
| 121 | \\@10 = ref(@9) | |
| 122 | \\@unnamed$6 = str("entry") | |
| 123 | \\@unnamed$7 = ref(@unnamed$6) | |
| 124 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 125 | \\@unnamed$12 = fntype([], @void, cc=C) | |
| 126 | \\@entry = fn(@unnamed$12, { | |
| 127 | \\ %0 = call(@a, [], modifier=auto) | |
| 128 | \\ %1 = return() | |
| 129 | \\}) | |
| 130 | \\@unnamed$17 = fntype([], @void, cc=C) | |
| 131 | \\@a = fn(@unnamed$17, { | |
| 132 | \\ %0 = call(@b, [], modifier=auto) | |
| 133 | \\ %1 = return() | |
| 134 | \\}) | |
| 135 | \\@unnamed$22 = fntype([], @void, cc=C) | |
| 136 | \\@b = fn(@unnamed$22, { | |
| 137 | \\ %0 = call(@a, [], modifier=auto) | |
| 138 | \\ %1 = return() | |
| 139 | \\}) | |
| 140 | \\ | |
| 141 | ); | |
| 142 | // Now we introduce a compile error | |
| 143 | case.addError( | |
| 144 | \\@void = primitive(void) | |
| 145 | \\@fnty = fntype([], @void, cc=C) | |
| 146 | \\ | |
| 147 | \\@9 = str("entry") | |
| 148 | \\@10 = ref(@9) | |
| 149 | \\@11 = export(@10, @entry) | |
| 150 | \\ | |
| 151 | \\@entry = fn(@fnty, { | |
| 152 | \\ %0 = call(@a, []) | |
| 153 | \\ %1 = return() | |
| 154 | \\}) | |
| 155 | \\ | |
| 156 | \\@a = fn(@fnty, { | |
| 157 | \\ %0 = call(@b, []) | |
| 158 | \\ %1 = return() | |
| 159 | \\}) | |
| 160 | \\ | |
| 161 | \\@b = fn(@fnty, { | |
| 162 | \\ %9 = compileerror("message") | |
| 163 | \\ %0 = call(@a, []) | |
| 164 | \\ %1 = return() | |
| 165 | \\}) | |
| 166 | , | |
| 167 | &[_][]const u8{ | |
| 168 | ":19:21: error: message", | |
| 169 | }, | |
| 170 | ); | |
| 171 | // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are | |
| 172 | // referencing either of them. This tests that the cycle is detected, and the error | |
| 173 | // goes away. | |
| 174 | case.addZIR( | |
| 175 | \\@void = primitive(void) | |
| 176 | \\@fnty = fntype([], @void, cc=C) | |
| 177 | \\ | |
| 178 | \\@9 = str("entry") | |
| 179 | \\@10 = ref(@9) | |
| 180 | \\@11 = export(@10, @entry) | |
| 181 | \\ | |
| 182 | \\@entry = fn(@fnty, { | |
| 183 | \\ %1 = return() | |
| 184 | \\}) | |
| 185 | \\ | |
| 186 | \\@a = fn(@fnty, { | |
| 187 | \\ %0 = call(@b, []) | |
| 188 | \\ %1 = return() | |
| 189 | \\}) | |
| 190 | \\ | |
| 191 | \\@b = fn(@fnty, { | |
| 192 | \\ %9 = compileerror("message") | |
| 193 | \\ %0 = call(@a, []) | |
| 194 | \\ %1 = return() | |
| 195 | \\}) | |
| 196 | , | |
| 197 | \\@void = primitive(void) | |
| 198 | \\@fnty = fntype([], @void, cc=C) | |
| 199 | \\@9 = str("entry") | |
| 200 | \\@10 = ref(@9) | |
| 201 | \\@unnamed$6 = str("entry") | |
| 202 | \\@unnamed$7 = ref(@unnamed$6) | |
| 203 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 204 | \\@unnamed$10 = fntype([], @void, cc=C) | |
| 205 | \\@entry = fn(@unnamed$10, { | |
| 206 | \\ %0 = return() | |
| 207 | \\}) | |
| 208 | \\ | |
| 209 | ); | |
| 210 | } | |
| 211 | ||
| 82 | 212 | if (std.Target.current.os.tag != .linux or |
| 83 | 213 | std.Target.current.cpu.arch != .x86_64) |
| 84 | 214 | { |