| author | |
| committer | |
| log | a8f0f37adb3bae8ad3d1f344fdaf1f1051551d21 |
| tree | c433b384cc2dd72495c71d7820a69cc2ddd649d7 |
| parent | f9bd049c89e4d2b4d3f51a937ec2114c3cac9176 |
| parent | 6fb105fdd7798dc988de09a7b6709c5168355dfa |
48 files changed, 4504 insertions(+), 1967 deletions(-)
lib/std/builtin.zig-2| ... | @@ -289,8 +289,6 @@ pub const TypeInfo = union(enum) { | ... | @@ -289,8 +289,6 @@ pub const TypeInfo = union(enum) { |
| 289 | /// therefore must be kept in sync with the compiler implementation. | 289 | /// therefore must be kept in sync with the compiler implementation. |
| 290 | pub const Error = struct { | 290 | pub const Error = struct { |
| 291 | name: []const u8, | 291 | name: []const u8, |
| 292 | /// This field is ignored when using @Type(). | ||
| 293 | value: comptime_int, | ||
| 294 | }; | 292 | }; |
| 295 | 293 | ||
| 296 | /// This data structure is used by the Zig language code generation and | 294 | /// This data structure is used by the Zig language code generation and |
lib/std/c/darwin.zig+1| ... | @@ -11,6 +11,7 @@ const macho = std.macho; | ... | @@ -11,6 +11,7 @@ const macho = std.macho; |
| 11 | usingnamespace @import("../os/bits.zig"); | 11 | usingnamespace @import("../os/bits.zig"); |
| 12 | 12 | ||
| 13 | extern "c" fn __error() *c_int; | 13 | extern "c" fn __error() *c_int; |
| 14 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; | ||
| 14 | pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; | 15 | pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; |
| 15 | pub extern "c" fn _dyld_image_count() u32; | 16 | pub extern "c" fn _dyld_image_count() u32; |
| 16 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; | 17 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; |
lib/std/cache_hash.zig+38-32| ... | @@ -4,7 +4,8 @@ | ... | @@ -4,7 +4,8 @@ |
| 4 | // The MIT license requires this copyright notice to be included in all copies | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 7 | const Blake3 = std.crypto.hash.Blake3; | 7 | const crypto = std.crypto; |
| 8 | const Hasher = crypto.auth.siphash.SipHash128(1, 3); // provides enough collision resistance for the CacheHash use cases, while being one of our fastest options right now | ||
| 8 | const fs = std.fs; | 9 | const fs = std.fs; |
| 9 | const base64 = std.base64; | 10 | const base64 = std.base64; |
| 10 | const ArrayList = std.ArrayList; | 11 | const ArrayList = std.ArrayList; |
| ... | @@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator; | ... | @@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator; |
| 16 | 17 | ||
| 17 | const base64_encoder = fs.base64_encoder; | 18 | const base64_encoder = fs.base64_encoder; |
| 18 | const base64_decoder = fs.base64_decoder; | 19 | const base64_decoder = fs.base64_decoder; |
| 19 | /// This is 70 more bits than UUIDs. For an analysis of probability of collisions, see: | 20 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 |
| 20 | /// https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions | 21 | const BIN_DIGEST_LEN = 16; |
| 21 | const BIN_DIGEST_LEN = 24; | ||
| 22 | const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); | 22 | const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); |
| 23 | 23 | ||
| 24 | const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; | 24 | const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; |
| ... | @@ -43,9 +43,13 @@ pub const File = struct { | ... | @@ -43,9 +43,13 @@ pub const File = struct { |
| 43 | } | 43 | } |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | /// CacheHash manages project-local `zig-cache` directories. | ||
| 47 | /// This is not a general-purpose cache. | ||
| 48 | /// It was designed to be fast and simple, not to withstand attacks using specially-crafted input. | ||
| 46 | pub const CacheHash = struct { | 49 | pub const CacheHash = struct { |
| 47 | allocator: *Allocator, | 50 | allocator: *Allocator, |
| 48 | blake3: Blake3, | 51 | hasher_init: Hasher, // initial state, that can be copied |
| 52 | hasher: Hasher, // current state for incremental hashing | ||
| 49 | manifest_dir: fs.Dir, | 53 | manifest_dir: fs.Dir, |
| 50 | manifest_file: ?fs.File, | 54 | manifest_file: ?fs.File, |
| 51 | manifest_dirty: bool, | 55 | manifest_dirty: bool, |
| ... | @@ -54,9 +58,11 @@ pub const CacheHash = struct { | ... | @@ -54,9 +58,11 @@ pub const CacheHash = struct { |
| 54 | 58 | ||
| 55 | /// Be sure to call release after successful initialization. | 59 | /// Be sure to call release after successful initialization. |
| 56 | pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash { | 60 | pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash { |
| 61 | const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length); | ||
| 57 | return CacheHash{ | 62 | return CacheHash{ |
| 58 | .allocator = allocator, | 63 | .allocator = allocator, |
| 59 | .blake3 = Blake3.init(.{}), | 64 | .hasher_init = hasher_init, |
| 65 | .hasher = hasher_init, | ||
| 60 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), | 66 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), |
| 61 | .manifest_file = null, | 67 | .manifest_file = null, |
| 62 | .manifest_dirty = false, | 68 | .manifest_dirty = false, |
| ... | @@ -69,8 +75,8 @@ pub const CacheHash = struct { | ... | @@ -69,8 +75,8 @@ pub const CacheHash = struct { |
| 69 | pub fn addSlice(self: *CacheHash, val: []const u8) void { | 75 | pub fn addSlice(self: *CacheHash, val: []const u8) void { |
| 70 | assert(self.manifest_file == null); | 76 | assert(self.manifest_file == null); |
| 71 | 77 | ||
| 72 | self.blake3.update(val); | 78 | self.hasher.update(val); |
| 73 | self.blake3.update(&[_]u8{0}); | 79 | self.hasher.update(&[_]u8{0}); |
| 74 | } | 80 | } |
| 75 | 81 | ||
| 76 | /// Convert the input value into bytes and record it as a dependency of the | 82 | /// Convert the input value into bytes and record it as a dependency of the |
| ... | @@ -133,12 +139,12 @@ pub const CacheHash = struct { | ... | @@ -133,12 +139,12 @@ pub const CacheHash = struct { |
| 133 | assert(self.manifest_file == null); | 139 | assert(self.manifest_file == null); |
| 134 | 140 | ||
| 135 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; | 141 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 136 | self.blake3.final(&bin_digest); | 142 | self.hasher.final(&bin_digest); |
| 137 | 143 | ||
| 138 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); | 144 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); |
| 139 | 145 | ||
| 140 | self.blake3 = Blake3.init(.{}); | 146 | self.hasher = self.hasher_init; |
| 141 | self.blake3.update(&bin_digest); | 147 | self.hasher.update(&bin_digest); |
| 142 | 148 | ||
| 143 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); | 149 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); |
| 144 | defer self.allocator.free(manifest_file_path); | 150 | defer self.allocator.free(manifest_file_path); |
| ... | @@ -238,7 +244,7 @@ pub const CacheHash = struct { | ... | @@ -238,7 +244,7 @@ pub const CacheHash = struct { |
| 238 | } | 244 | } |
| 239 | 245 | ||
| 240 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; | 246 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 241 | try hashFile(this_file, &actual_digest); | 247 | try hashFile(this_file, &actual_digest, self.hasher_init); |
| 242 | 248 | ||
| 243 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { | 249 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { |
| 244 | cache_hash_file.bin_digest = actual_digest; | 250 | cache_hash_file.bin_digest = actual_digest; |
| ... | @@ -248,7 +254,7 @@ pub const CacheHash = struct { | ... | @@ -248,7 +254,7 @@ pub const CacheHash = struct { |
| 248 | } | 254 | } |
| 249 | 255 | ||
| 250 | if (!any_file_changed) { | 256 | if (!any_file_changed) { |
| 251 | self.blake3.update(&cache_hash_file.bin_digest); | 257 | self.hasher.update(&cache_hash_file.bin_digest); |
| 252 | } | 258 | } |
| 253 | } | 259 | } |
| 254 | 260 | ||
| ... | @@ -256,8 +262,8 @@ pub const CacheHash = struct { | ... | @@ -256,8 +262,8 @@ pub const CacheHash = struct { |
| 256 | // cache miss | 262 | // cache miss |
| 257 | // keep the manifest file open | 263 | // keep the manifest file open |
| 258 | // reset the hash | 264 | // reset the hash |
| 259 | self.blake3 = Blake3.init(.{}); | 265 | self.hasher = self.hasher_init; |
| 260 | self.blake3.update(&bin_digest); | 266 | self.hasher.update(&bin_digest); |
| 261 | 267 | ||
| 262 | // Remove files not in the initial hash | 268 | // Remove files not in the initial hash |
| 263 | for (self.files.items[input_file_count..]) |*file| { | 269 | for (self.files.items[input_file_count..]) |*file| { |
| ... | @@ -266,7 +272,7 @@ pub const CacheHash = struct { | ... | @@ -266,7 +272,7 @@ pub const CacheHash = struct { |
| 266 | self.files.shrink(input_file_count); | 272 | self.files.shrink(input_file_count); |
| 267 | 273 | ||
| 268 | for (self.files.items) |file| { | 274 | for (self.files.items) |file| { |
| 269 | self.blake3.update(&file.bin_digest); | 275 | self.hasher.update(&file.bin_digest); |
| 270 | } | 276 | } |
| 271 | return null; | 277 | return null; |
| 272 | } | 278 | } |
| ... | @@ -304,23 +310,23 @@ pub const CacheHash = struct { | ... | @@ -304,23 +310,23 @@ pub const CacheHash = struct { |
| 304 | 310 | ||
| 305 | // Hash while reading from disk, to keep the contents in the cpu cache while | 311 | // Hash while reading from disk, to keep the contents in the cpu cache while |
| 306 | // doing hashing. | 312 | // doing hashing. |
| 307 | var blake3 = Blake3.init(.{}); | 313 | var hasher = self.hasher_init; |
| 308 | var off: usize = 0; | 314 | var off: usize = 0; |
| 309 | while (true) { | 315 | while (true) { |
| 310 | // give me everything you've got, captain | 316 | // give me everything you've got, captain |
| 311 | const bytes_read = try file.read(contents[off..]); | 317 | const bytes_read = try file.read(contents[off..]); |
| 312 | if (bytes_read == 0) break; | 318 | if (bytes_read == 0) break; |
| 313 | blake3.update(contents[off..][0..bytes_read]); | 319 | hasher.update(contents[off..][0..bytes_read]); |
| 314 | off += bytes_read; | 320 | off += bytes_read; |
| 315 | } | 321 | } |
| 316 | blake3.final(&ch_file.bin_digest); | 322 | hasher.final(&ch_file.bin_digest); |
| 317 | 323 | ||
| 318 | ch_file.contents = contents; | 324 | ch_file.contents = contents; |
| 319 | } else { | 325 | } else { |
| 320 | try hashFile(file, &ch_file.bin_digest); | 326 | try hashFile(file, &ch_file.bin_digest, self.hasher_init); |
| 321 | } | 327 | } |
| 322 | 328 | ||
| 323 | self.blake3.update(&ch_file.bin_digest); | 329 | self.hasher.update(&ch_file.bin_digest); |
| 324 | } | 330 | } |
| 325 | 331 | ||
| 326 | /// Add a file as a dependency of process being cached, after the initial hash has been | 332 | /// Add a file as a dependency of process being cached, after the initial hash has been |
| ... | @@ -382,7 +388,7 @@ pub const CacheHash = struct { | ... | @@ -382,7 +388,7 @@ pub const CacheHash = struct { |
| 382 | // the artifacts to cache. | 388 | // the artifacts to cache. |
| 383 | 389 | ||
| 384 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; | 390 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 385 | self.blake3.final(&bin_digest); | 391 | self.hasher.final(&bin_digest); |
| 386 | 392 | ||
| 387 | var out_digest: [BASE64_DIGEST_LEN]u8 = undefined; | 393 | var out_digest: [BASE64_DIGEST_LEN]u8 = undefined; |
| 388 | base64_encoder.encode(&out_digest, &bin_digest); | 394 | base64_encoder.encode(&out_digest, &bin_digest); |
| ... | @@ -433,17 +439,17 @@ pub const CacheHash = struct { | ... | @@ -433,17 +439,17 @@ pub const CacheHash = struct { |
| 433 | } | 439 | } |
| 434 | }; | 440 | }; |
| 435 | 441 | ||
| 436 | fn hashFile(file: fs.File, bin_digest: []u8) !void { | 442 | fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void { |
| 437 | var blake3 = Blake3.init(.{}); | ||
| 438 | var buf: [1024]u8 = undefined; | 443 | var buf: [1024]u8 = undefined; |
| 439 | 444 | ||
| 445 | var hasher = hasher_init; | ||
| 440 | while (true) { | 446 | while (true) { |
| 441 | const bytes_read = try file.read(&buf); | 447 | const bytes_read = try file.read(&buf); |
| 442 | if (bytes_read == 0) break; | 448 | if (bytes_read == 0) break; |
| 443 | blake3.update(buf[0..bytes_read]); | 449 | hasher.update(buf[0..bytes_read]); |
| 444 | } | 450 | } |
| 445 | 451 | ||
| 446 | blake3.final(bin_digest); | 452 | hasher.final(bin_digest); |
| 447 | } | 453 | } |
| 448 | 454 | ||
| 449 | /// If the wall clock time, rounded to the same precision as the | 455 | /// If the wall clock time, rounded to the same precision as the |
| ... | @@ -507,7 +513,7 @@ test "cache file and then recall it" { | ... | @@ -507,7 +513,7 @@ test "cache file and then recall it" { |
| 507 | _ = try ch.addFile(temp_file, null); | 513 | _ = try ch.addFile(temp_file, null); |
| 508 | 514 | ||
| 509 | // There should be nothing in the cache | 515 | // There should be nothing in the cache |
| 510 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 516 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 511 | 517 | ||
| 512 | digest1 = ch.final(); | 518 | digest1 = ch.final(); |
| 513 | } | 519 | } |
| ... | @@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" { |
| 575 | const temp_file_idx = try ch.addFile(temp_file, 100); | 581 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 576 | 582 | ||
| 577 | // There should be nothing in the cache | 583 | // There should be nothing in the cache |
| 578 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 584 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 579 | 585 | ||
| 580 | testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); | 586 | testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); |
| 581 | 587 | ||
| ... | @@ -592,7 +598,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -592,7 +598,7 @@ test "check that changing a file makes cache fail" { |
| 592 | const temp_file_idx = try ch.addFile(temp_file, 100); | 598 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 593 | 599 | ||
| 594 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 600 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 595 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 601 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 596 | 602 | ||
| 597 | // The cache system does not keep the contents of re-hashed input files. | 603 | // The cache system does not keep the contents of re-hashed input files. |
| 598 | testing.expect(ch.files.items[temp_file_idx].contents == null); | 604 | testing.expect(ch.files.items[temp_file_idx].contents == null); |
| ... | @@ -625,7 +631,7 @@ test "no file inputs" { | ... | @@ -625,7 +631,7 @@ test "no file inputs" { |
| 625 | ch.add("1234"); | 631 | ch.add("1234"); |
| 626 | 632 | ||
| 627 | // There should be nothing in the cache | 633 | // There should be nothing in the cache |
| 628 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 634 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 629 | 635 | ||
| 630 | digest1 = ch.final(); | 636 | digest1 = ch.final(); |
| 631 | } | 637 | } |
| ... | @@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" { | ... | @@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" { |
| 672 | _ = try ch.addFile(temp_file1, null); | 678 | _ = try ch.addFile(temp_file1, null); |
| 673 | 679 | ||
| 674 | // There should be nothing in the cache | 680 | // There should be nothing in the cache |
| 675 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 681 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 676 | 682 | ||
| 677 | _ = try ch.addFilePost(temp_file2); | 683 | _ = try ch.addFilePost(temp_file2); |
| 678 | 684 | ||
| ... | @@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" { | ... | @@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" { |
| 705 | _ = try ch.addFile(temp_file1, null); | 711 | _ = try ch.addFile(temp_file1, null); |
| 706 | 712 | ||
| 707 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 713 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 708 | testing.expectEqual(@as(?[32]u8, null), try ch.hit()); | 714 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| 709 | 715 | ||
| 710 | _ = try ch.addFilePost(temp_file2); | 716 | _ = try ch.addFilePost(temp_file2); |
| 711 | 717 |
lib/std/crypto.zig+2| ... | @@ -18,6 +18,7 @@ pub const hash = struct { | ... | @@ -18,6 +18,7 @@ pub const hash = struct { |
| 18 | /// Authentication (MAC) functions. | 18 | /// Authentication (MAC) functions. |
| 19 | pub const auth = struct { | 19 | pub const auth = struct { |
| 20 | pub const hmac = @import("crypto/hmac.zig"); | 20 | pub const hmac = @import("crypto/hmac.zig"); |
| 21 | pub const siphash = @import("crypto/siphash.zig"); | ||
| 21 | }; | 22 | }; |
| 22 | 23 | ||
| 23 | /// Authenticated Encryption with Associated Data | 24 | /// Authenticated Encryption with Associated Data |
| ... | @@ -80,6 +81,7 @@ test "crypto" { | ... | @@ -80,6 +81,7 @@ test "crypto" { |
| 80 | _ = @import("crypto/sha1.zig"); | 81 | _ = @import("crypto/sha1.zig"); |
| 81 | _ = @import("crypto/sha2.zig"); | 82 | _ = @import("crypto/sha2.zig"); |
| 82 | _ = @import("crypto/sha3.zig"); | 83 | _ = @import("crypto/sha3.zig"); |
| 84 | _ = @import("crypto/siphash.zig"); | ||
| 83 | _ = @import("crypto/25519/curve25519.zig"); | 85 | _ = @import("crypto/25519/curve25519.zig"); |
| 84 | _ = @import("crypto/25519/ed25519.zig"); | 86 | _ = @import("crypto/25519/ed25519.zig"); |
| 85 | _ = @import("crypto/25519/edwards25519.zig"); | 87 | _ = @import("crypto/25519/edwards25519.zig"); |
lib/std/crypto/benchmark.zig+4| ... | @@ -60,6 +60,10 @@ const macs = [_]Crypto{ | ... | @@ -60,6 +60,10 @@ const macs = [_]Crypto{ |
| 60 | Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" }, | 60 | Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" }, |
| 61 | Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" }, | 61 | Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" }, |
| 62 | Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" }, | 62 | Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" }, |
| 63 | Crypto{ .ty = crypto.auth.siphash.SipHash64(2, 4), .name = "siphash-2-4" }, | ||
| 64 | Crypto{ .ty = crypto.auth.siphash.SipHash64(1, 3), .name = "siphash-1-3" }, | ||
| 65 | Crypto{ .ty = crypto.auth.siphash.SipHash128(2, 4), .name = "siphash128-2-4" }, | ||
| 66 | Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" }, | ||
| 63 | }; | 67 | }; |
| 64 | 68 | ||
| 65 | pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { | 69 | pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { |
lib/std/crypto/siphash.zig created+431| ... | @@ -0,0 +1,431 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | // | ||
| 7 | // SipHash is a moderately fast pseudorandom function, returning a 64-bit or 128-bit tag for an arbitrary long input. | ||
| 8 | // | ||
| 9 | // Typical use cases include: | ||
| 10 | // - protection against against DoS attacks for hash tables and bloom filters | ||
| 11 | // - authentication of short-lived messages in online protocols | ||
| 12 | // | ||
| 13 | // https://131002.net/siphash/ | ||
| 14 | const std = @import("../std.zig"); | ||
| 15 | const assert = std.debug.assert; | ||
| 16 | const testing = std.testing; | ||
| 17 | const math = std.math; | ||
| 18 | const mem = std.mem; | ||
| 19 | |||
| 20 | /// SipHash function with 64-bit output. | ||
| 21 | /// | ||
| 22 | /// Recommended parameters are: | ||
| 23 | /// - (c_rounds=4, d_rounds=8) for conservative security; regular hash functions such as BLAKE2 or BLAKE3 are usually a better alternative. | ||
| 24 | /// - (c_rounds=2, d_rounds=4) standard parameters. | ||
| 25 | /// - (c_rounds=1, d_rounds=3) reduced-round function. Faster, no known implications on its practical security level. | ||
| 26 | /// - (c_rounds=1, d_rounds=2) fastest option, but the output may be distinguishable from random data with related keys or non-uniform input - not suitable as a PRF. | ||
| 27 | /// | ||
| 28 | /// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary. | ||
| 29 | /// And due to its small output size, collisions in SipHash64 can be found with an exhaustive search. | ||
| 30 | pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 31 | return SipHash(u64, c_rounds, d_rounds); | ||
| 32 | } | ||
| 33 | |||
| 34 | /// SipHash function with 128-bit output. | ||
| 35 | /// | ||
| 36 | /// Recommended parameters are: | ||
| 37 | /// - (c_rounds=4, d_rounds=8) for conservative security; regular hash functions such as BLAKE2 or BLAKE3 are usually a better alternative. | ||
| 38 | /// - (c_rounds=2, d_rounds=4) standard parameters. | ||
| 39 | /// - (c_rounds=1, d_rounds=4) reduced-round function. Recommended to hash very short, similar strings, when a 128-bit PRF output is still required. | ||
| 40 | /// - (c_rounds=1, d_rounds=3) reduced-round function. Faster, no known implications on its practical security level. | ||
| 41 | /// - (c_rounds=1, d_rounds=2) fastest option, but the output may be distinguishable from random data with related keys or non-uniform input - not suitable as a PRF. | ||
| 42 | /// | ||
| 43 | /// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary. | ||
| 44 | pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 45 | return SipHash(u128, c_rounds, d_rounds); | ||
| 46 | } | ||
| 47 | |||
| 48 | fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 49 | assert(T == u64 or T == u128); | ||
| 50 | assert(c_rounds > 0 and d_rounds > 0); | ||
| 51 | |||
| 52 | return struct { | ||
| 53 | const Self = @This(); | ||
| 54 | const digest_size = 64; | ||
| 55 | const block_size = 64; | ||
| 56 | |||
| 57 | v0: u64, | ||
| 58 | v1: u64, | ||
| 59 | v2: u64, | ||
| 60 | v3: u64, | ||
| 61 | msg_len: u8, | ||
| 62 | |||
| 63 | pub fn init(key: []const u8) Self { | ||
| 64 | assert(key.len >= 16); | ||
| 65 | |||
| 66 | const k0 = mem.readIntLittle(u64, key[0..8]); | ||
| 67 | const k1 = mem.readIntLittle(u64, key[8..16]); | ||
| 68 | |||
| 69 | var d = Self{ | ||
| 70 | .v0 = k0 ^ 0x736f6d6570736575, | ||
| 71 | .v1 = k1 ^ 0x646f72616e646f6d, | ||
| 72 | .v2 = k0 ^ 0x6c7967656e657261, | ||
| 73 | .v3 = k1 ^ 0x7465646279746573, | ||
| 74 | .msg_len = 0, | ||
| 75 | }; | ||
| 76 | |||
| 77 | if (T == u128) { | ||
| 78 | d.v1 ^= 0xee; | ||
| 79 | } | ||
| 80 | |||
| 81 | return d; | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn update(self: *Self, b: []const u8) void { | ||
| 85 | std.debug.assert(b.len % 8 == 0); | ||
| 86 | |||
| 87 | var off: usize = 0; | ||
| 88 | while (off < b.len) : (off += 8) { | ||
| 89 | @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 8]}); | ||
| 90 | } | ||
| 91 | |||
| 92 | self.msg_len +%= @truncate(u8, b.len); | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn final(self: *Self, b: []const u8) T { | ||
| 96 | std.debug.assert(b.len < 8); | ||
| 97 | |||
| 98 | self.msg_len +%= @truncate(u8, b.len); | ||
| 99 | |||
| 100 | var buf = [_]u8{0} ** 8; | ||
| 101 | mem.copy(u8, buf[0..], b[0..]); | ||
| 102 | buf[7] = self.msg_len; | ||
| 103 | self.round(buf[0..]); | ||
| 104 | |||
| 105 | if (T == u128) { | ||
| 106 | self.v2 ^= 0xee; | ||
| 107 | } else { | ||
| 108 | self.v2 ^= 0xff; | ||
| 109 | } | ||
| 110 | |||
| 111 | // TODO this is a workaround, should be able to supply the value without a separate variable | ||
| 112 | const inl = std.builtin.CallOptions{ .modifier = .always_inline }; | ||
| 113 | |||
| 114 | comptime var i: usize = 0; | ||
| 115 | inline while (i < d_rounds) : (i += 1) { | ||
| 116 | @call(inl, sipRound, .{self}); | ||
| 117 | } | ||
| 118 | |||
| 119 | const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; | ||
| 120 | if (T == u64) { | ||
| 121 | return b1; | ||
| 122 | } | ||
| 123 | |||
| 124 | self.v1 ^= 0xdd; | ||
| 125 | |||
| 126 | comptime var j: usize = 0; | ||
| 127 | inline while (j < d_rounds) : (j += 1) { | ||
| 128 | @call(inl, sipRound, .{self}); | ||
| 129 | } | ||
| 130 | |||
| 131 | const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; | ||
| 132 | return (@as(u128, b2) << 64) | b1; | ||
| 133 | } | ||
| 134 | |||
| 135 | fn round(self: *Self, b: []const u8) void { | ||
| 136 | assert(b.len == 8); | ||
| 137 | |||
| 138 | const m = mem.readIntLittle(u64, b[0..8]); | ||
| 139 | self.v3 ^= m; | ||
| 140 | |||
| 141 | // TODO this is a workaround, should be able to supply the value without a separate variable | ||
| 142 | const inl = std.builtin.CallOptions{ .modifier = .always_inline }; | ||
| 143 | comptime var i: usize = 0; | ||
| 144 | inline while (i < c_rounds) : (i += 1) { | ||
| 145 | @call(inl, sipRound, .{self}); | ||
| 146 | } | ||
| 147 | |||
| 148 | self.v0 ^= m; | ||
| 149 | } | ||
| 150 | |||
| 151 | fn sipRound(d: *Self) void { | ||
| 152 | d.v0 +%= d.v1; | ||
| 153 | d.v1 = math.rotl(u64, d.v1, @as(u64, 13)); | ||
| 154 | d.v1 ^= d.v0; | ||
| 155 | d.v0 = math.rotl(u64, d.v0, @as(u64, 32)); | ||
| 156 | d.v2 +%= d.v3; | ||
| 157 | d.v3 = math.rotl(u64, d.v3, @as(u64, 16)); | ||
| 158 | d.v3 ^= d.v2; | ||
| 159 | d.v0 +%= d.v3; | ||
| 160 | d.v3 = math.rotl(u64, d.v3, @as(u64, 21)); | ||
| 161 | d.v3 ^= d.v0; | ||
| 162 | d.v2 +%= d.v1; | ||
| 163 | d.v1 = math.rotl(u64, d.v1, @as(u64, 17)); | ||
| 164 | d.v1 ^= d.v2; | ||
| 165 | d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn hash(msg: []const u8, key: []const u8) T { | ||
| 169 | const aligned_len = msg.len - (msg.len % 8); | ||
| 170 | var c = Self.init(key); | ||
| 171 | @call(.{ .modifier = .always_inline }, c.update, .{msg[0..aligned_len]}); | ||
| 172 | return @call(.{ .modifier = .always_inline }, c.final, .{msg[aligned_len..]}); | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | } | ||
| 176 | |||
| 177 | fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 178 | assert(T == u64 or T == u128); | ||
| 179 | assert(c_rounds > 0 and d_rounds > 0); | ||
| 180 | |||
| 181 | return struct { | ||
| 182 | const State = SipHashStateless(T, c_rounds, d_rounds); | ||
| 183 | const Self = @This(); | ||
| 184 | pub const minimum_key_length = 16; | ||
| 185 | pub const mac_length = @sizeOf(T); | ||
| 186 | pub const block_length = 8; | ||
| 187 | |||
| 188 | state: State, | ||
| 189 | buf: [8]u8, | ||
| 190 | buf_len: usize, | ||
| 191 | |||
| 192 | /// Initialize a state for a SipHash function | ||
| 193 | pub fn init(key: []const u8) Self { | ||
| 194 | return Self{ | ||
| 195 | .state = State.init(key), | ||
| 196 | .buf = undefined, | ||
| 197 | .buf_len = 0, | ||
| 198 | }; | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Add data to the state | ||
| 202 | pub fn update(self: *Self, b: []const u8) void { | ||
| 203 | var off: usize = 0; | ||
| 204 | |||
| 205 | if (self.buf_len != 0 and self.buf_len + b.len >= 8) { | ||
| 206 | off += 8 - self.buf_len; | ||
| 207 | mem.copy(u8, self.buf[self.buf_len..], b[0..off]); | ||
| 208 | self.state.update(self.buf[0..]); | ||
| 209 | self.buf_len = 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | const remain_len = b.len - off; | ||
| 213 | const aligned_len = remain_len - (remain_len % 8); | ||
| 214 | self.state.update(b[off .. off + aligned_len]); | ||
| 215 | |||
| 216 | mem.copy(u8, self.buf[self.buf_len..], b[off + aligned_len ..]); | ||
| 217 | self.buf_len += @intCast(u8, b[off + aligned_len ..].len); | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Return an authentication tag for the current state | ||
| 221 | pub fn final(self: *Self, out: []u8) void { | ||
| 222 | std.debug.assert(out.len >= mac_length); | ||
| 223 | mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len])); | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Return an authentication tag for a message and a key | ||
| 227 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { | ||
| 228 | var ctx = Self.init(key); | ||
| 229 | ctx.update(msg); | ||
| 230 | ctx.final(out[0..]); | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Return an authentication tag for the current state, as an integer | ||
| 234 | pub fn finalInt(self: *Self) T { | ||
| 235 | return self.state.final(self.buf[0..self.buf_len]); | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Return an authentication tag for a message and a key, as an integer | ||
| 239 | pub fn toInt(msg: []const u8, key: []const u8) T { | ||
| 240 | return State.hash(msg, key); | ||
| 241 | } | ||
| 242 | }; | ||
| 243 | } | ||
| 244 | |||
| 245 | // Test vectors from reference implementation. | ||
| 246 | // https://github.com/veorq/SipHash/blob/master/vectors.h | ||
| 247 | const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"; | ||
| 248 | |||
| 249 | test "siphash64-2-4 sanity" { | ||
| 250 | const vectors = [_][8]u8{ | ||
| 251 | "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72".*, // "" | ||
| 252 | "\xfd\x67\xdc\x93\xc5\x39\xf8\x74".*, // "\x00" | ||
| 253 | "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d".*, // "\x00\x01" ... etc | ||
| 254 | "\x2d\x7e\xfb\xd7\x96\x66\x67\x85".*, | ||
| 255 | "\xb7\x87\x71\x27\xe0\x94\x27\xcf".*, | ||
| 256 | "\x8d\xa6\x99\xcd\x64\x55\x76\x18".*, | ||
| 257 | "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb".*, | ||
| 258 | "\x37\xd1\x01\x8b\xf5\x00\x02\xab".*, | ||
| 259 | "\x62\x24\x93\x9a\x79\xf5\xf5\x93".*, | ||
| 260 | "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e".*, | ||
| 261 | "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a".*, | ||
| 262 | "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4".*, | ||
| 263 | "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75".*, | ||
| 264 | "\x90\x3d\x84\xc0\x27\x56\xea\x14".*, | ||
| 265 | "\xee\xf2\x7a\x8e\x90\xca\x23\xf7".*, | ||
| 266 | "\xe5\x45\xbe\x49\x61\xca\x29\xa1".*, | ||
| 267 | "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f".*, | ||
| 268 | "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69".*, | ||
| 269 | "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b".*, | ||
| 270 | "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb".*, | ||
| 271 | "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe".*, | ||
| 272 | "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0".*, | ||
| 273 | "\x88\x3e\xa3\xe3\x95\x67\x53\x93".*, | ||
| 274 | "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8".*, | ||
| 275 | "\x94\xaf\x49\xf6\xc6\x50\xad\xb8".*, | ||
| 276 | "\xea\xb8\x85\x8a\xde\x92\xe1\xbc".*, | ||
| 277 | "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17".*, | ||
| 278 | "\xad\xcf\x6b\x07\x63\x61\x2e\x2f".*, | ||
| 279 | "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde".*, | ||
| 280 | "\x71\x65\x95\x87\x66\x50\xa2\xa6".*, | ||
| 281 | "\x28\xef\x49\x5c\x53\xa3\x87\xad".*, | ||
| 282 | "\x42\xc3\x41\xd8\xfa\x92\xd8\x32".*, | ||
| 283 | "\xce\x7c\xf2\x72\x2f\x51\x27\x71".*, | ||
| 284 | "\xe3\x78\x59\xf9\x46\x23\xf3\xa7".*, | ||
| 285 | "\x38\x12\x05\xbb\x1a\xb0\xe0\x12".*, | ||
| 286 | "\xae\x97\xa1\x0f\xd4\x34\xe0\x15".*, | ||
| 287 | "\xb4\xa3\x15\x08\xbe\xff\x4d\x31".*, | ||
| 288 | "\x81\x39\x62\x29\xf0\x90\x79\x02".*, | ||
| 289 | "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca".*, | ||
| 290 | "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a".*, | ||
| 291 | "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e".*, | ||
| 292 | "\x92\x59\x58\xfc\xd6\x42\x0c\xad".*, | ||
| 293 | "\xa9\x15\xc2\x9b\xc8\x06\x73\x18".*, | ||
| 294 | "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4".*, | ||
| 295 | "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9".*, | ||
| 296 | "\x87\x57\x75\x19\x04\x8f\x53\xa9".*, | ||
| 297 | "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb".*, | ||
| 298 | "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0".*, | ||
| 299 | "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6".*, | ||
| 300 | "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7".*, | ||
| 301 | "\x72\xfe\x52\x97\x5a\x43\x64\xee".*, | ||
| 302 | "\x5a\x16\x45\xb2\x76\xd5\x92\xa1".*, | ||
| 303 | "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a".*, | ||
| 304 | "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81".*, | ||
| 305 | "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f".*, | ||
| 306 | "\x99\x24\xa4\x3c\xc1\x31\x57\x24".*, | ||
| 307 | "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7".*, | ||
| 308 | "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea".*, | ||
| 309 | "\x13\x50\x79\xa3\x23\x1c\xe6\x60".*, | ||
| 310 | "\x93\x2b\x28\x46\xe4\xd7\x06\x66".*, | ||
| 311 | "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c".*, | ||
| 312 | "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f".*, | ||
| 313 | "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5".*, | ||
| 314 | "\x72\x45\x06\xeb\x4c\x32\x8a\x95".*, | ||
| 315 | }; | ||
| 316 | |||
| 317 | const siphash = SipHash64(2, 4); | ||
| 318 | |||
| 319 | var buffer: [64]u8 = undefined; | ||
| 320 | for (vectors) |vector, i| { | ||
| 321 | buffer[i] = @intCast(u8, i); | ||
| 322 | |||
| 323 | var out: [siphash.mac_length]u8 = undefined; | ||
| 324 | siphash.create(&out, buffer[0..i], test_key); | ||
| 325 | testing.expectEqual(out, vector); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | test "siphash128-2-4 sanity" { | ||
| 330 | const vectors = [_][16]u8{ | ||
| 331 | "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93".*, | ||
| 332 | "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45".*, | ||
| 333 | "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4".*, | ||
| 334 | "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51".*, | ||
| 335 | "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79".*, | ||
| 336 | "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27".*, | ||
| 337 | "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e".*, | ||
| 338 | "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39".*, | ||
| 339 | "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4".*, | ||
| 340 | "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed".*, | ||
| 341 | "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba".*, | ||
| 342 | "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18".*, | ||
| 343 | "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25".*, | ||
| 344 | "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7".*, | ||
| 345 | "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02".*, | ||
| 346 | "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9".*, | ||
| 347 | "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77".*, | ||
| 348 | "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40".*, | ||
| 349 | "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23".*, | ||
| 350 | "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1".*, | ||
| 351 | "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb".*, | ||
| 352 | "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12".*, | ||
| 353 | "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae".*, | ||
| 354 | "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c".*, | ||
| 355 | "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad".*, | ||
| 356 | "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f".*, | ||
| 357 | "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66".*, | ||
| 358 | "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94".*, | ||
| 359 | "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4".*, | ||
| 360 | "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7".*, | ||
| 361 | "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87".*, | ||
| 362 | "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35".*, | ||
| 363 | "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68".*, | ||
| 364 | "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf".*, | ||
| 365 | "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde".*, | ||
| 366 | "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8".*, | ||
| 367 | "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11".*, | ||
| 368 | "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b".*, | ||
| 369 | "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5".*, | ||
| 370 | "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9".*, | ||
| 371 | "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8".*, | ||
| 372 | "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb".*, | ||
| 373 | "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b".*, | ||
| 374 | "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89".*, | ||
| 375 | "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42".*, | ||
| 376 | "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c".*, | ||
| 377 | "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02".*, | ||
| 378 | "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b".*, | ||
| 379 | "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16".*, | ||
| 380 | "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03".*, | ||
| 381 | "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f".*, | ||
| 382 | "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38".*, | ||
| 383 | "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c".*, | ||
| 384 | "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e".*, | ||
| 385 | "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87".*, | ||
| 386 | "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda".*, | ||
| 387 | "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36".*, | ||
| 388 | "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e".*, | ||
| 389 | "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d".*, | ||
| 390 | "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59".*, | ||
| 391 | "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40".*, | ||
| 392 | "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a".*, | ||
| 393 | "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd".*, | ||
| 394 | "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c".*, | ||
| 395 | }; | ||
| 396 | |||
| 397 | const siphash = SipHash128(2, 4); | ||
| 398 | |||
| 399 | var buffer: [64]u8 = undefined; | ||
| 400 | for (vectors) |vector, i| { | ||
| 401 | buffer[i] = @intCast(u8, i); | ||
| 402 | |||
| 403 | var out: [siphash.mac_length]u8 = undefined; | ||
| 404 | siphash.create(&out, buffer[0..i], test_key[0..]); | ||
| 405 | testing.expectEqual(out, vector); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | test "iterative non-divisible update" { | ||
| 410 | var buf: [1024]u8 = undefined; | ||
| 411 | for (buf) |*e, i| { | ||
| 412 | e.* = @truncate(u8, i); | ||
| 413 | } | ||
| 414 | |||
| 415 | const key = "0x128dad08f12307"; | ||
| 416 | const Siphash = SipHash64(2, 4); | ||
| 417 | |||
| 418 | var end: usize = 9; | ||
| 419 | while (end < buf.len) : (end += 9) { | ||
| 420 | const non_iterative_hash = Siphash.toInt(buf[0..end], key[0..]); | ||
| 421 | |||
| 422 | var siphash = Siphash.init(key); | ||
| 423 | var i: usize = 0; | ||
| 424 | while (i < end) : (i += 7) { | ||
| 425 | siphash.update(buf[i..std.math.min(i + 7, end)]); | ||
| 426 | } | ||
| 427 | const iterative_hash = siphash.finalInt(); | ||
| 428 | |||
| 429 | std.testing.expectEqual(iterative_hash, non_iterative_hash); | ||
| 430 | } | ||
| 431 | } | ||
lib/std/elf.zig+3| ... | @@ -976,6 +976,9 @@ pub const EM = extern enum(u16) { | ... | @@ -976,6 +976,9 @@ pub const EM = extern enum(u16) { |
| 976 | /// MIPS RS3000 Little-endian | 976 | /// MIPS RS3000 Little-endian |
| 977 | _MIPS_RS3_LE = 10, | 977 | _MIPS_RS3_LE = 10, |
| 978 | 978 | ||
| 979 | /// SPU Mark II | ||
| 980 | _SPU_2 = 13, | ||
| 981 | |||
| 979 | /// Hewlett-Packard PA-RISC | 982 | /// Hewlett-Packard PA-RISC |
| 980 | _PARISC = 15, | 983 | _PARISC = 15, |
| 981 | 984 |
lib/std/fs.zig+19-12| ... | @@ -686,21 +686,28 @@ pub const Dir = struct { | ... | @@ -686,21 +686,28 @@ pub const Dir = struct { |
| 686 | return self.openFileW(path_w.span(), flags); | 686 | return self.openFileW(path_w.span(), flags); |
| 687 | } | 687 | } |
| 688 | 688 | ||
| 689 | var os_flags: u32 = os.O_CLOEXEC; | ||
| 689 | // Use the O_ locking flags if the os supports them | 690 | // Use the O_ locking flags if the os supports them |
| 690 | // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag) | 691 | // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag) |
| 691 | const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; | 692 | const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; |
| 692 | const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) | 693 | if (has_flock_open_flags) { |
| 693 | os.O_NONBLOCK | os.O_SYNC | 694 | const nonblocking_lock_flag = if (flags.lock_nonblocking) |
| 694 | else | 695 | os.O_NONBLOCK | os.O_SYNC |
| 695 | @as(u32, 0); | 696 | else |
| 696 | const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { | 697 | @as(u32, 0); |
| 697 | .None => @as(u32, 0), | 698 | os_flags |= switch (flags.lock) { |
| 698 | .Shared => os.O_SHLOCK | nonblocking_lock_flag, | 699 | .None => @as(u32, 0), |
| 699 | .Exclusive => os.O_EXLOCK | nonblocking_lock_flag, | 700 | .Shared => os.O_SHLOCK | nonblocking_lock_flag, |
| 700 | } else 0; | 701 | .Exclusive => os.O_EXLOCK | nonblocking_lock_flag, |
| 701 | 702 | }; | |
| 702 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 703 | } |
| 703 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) | 704 | if (@hasDecl(os, "O_LARGEFILE")) { |
| 705 | os_flags |= os.O_LARGEFILE; | ||
| 706 | } | ||
| 707 | if (!flags.allow_ctty) { | ||
| 708 | os_flags |= os.O_NOCTTY; | ||
| 709 | } | ||
| 710 | os_flags |= if (flags.write and flags.read) | ||
| 704 | @as(u32, os.O_RDWR) | 711 | @as(u32, os.O_RDWR) |
| 705 | else if (flags.write) | 712 | else if (flags.write) |
| 706 | @as(u32, os.O_WRONLY) | 713 | @as(u32, os.O_WRONLY) |
lib/std/fs/file.zig+4| ... | @@ -101,6 +101,10 @@ pub const File = struct { | ... | @@ -101,6 +101,10 @@ pub const File = struct { |
| 101 | /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions | 101 | /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions |
| 102 | /// related to opening the file, reading, writing, and locking. | 102 | /// related to opening the file, reading, writing, and locking. |
| 103 | intended_io_mode: io.ModeOverride = io.default_mode, | 103 | intended_io_mode: io.ModeOverride = io.default_mode, |
| 104 | |||
| 105 | /// Set this to allow the opened file to automatically become the | ||
| 106 | /// controlling TTY for the current process. | ||
| 107 | allow_ctty: bool = false, | ||
| 104 | }; | 108 | }; |
| 105 | 109 | ||
| 106 | /// TODO https://github.com/ziglang/zig/issues/3802 | 110 | /// TODO https://github.com/ziglang/zig/issues/3802 |
lib/std/hash.zig+1-2| ... | @@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32; | ... | @@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32; |
| 20 | pub const Fnv1a_64 = fnv.Fnv1a_64; | 20 | pub const Fnv1a_64 = fnv.Fnv1a_64; |
| 21 | pub const Fnv1a_128 = fnv.Fnv1a_128; | 21 | pub const Fnv1a_128 = fnv.Fnv1a_128; |
| 22 | 22 | ||
| 23 | const siphash = @import("hash/siphash.zig"); | 23 | const siphash = @import("crypto/siphash.zig"); |
| 24 | pub const SipHash64 = siphash.SipHash64; | 24 | pub const SipHash64 = siphash.SipHash64; |
| 25 | pub const SipHash128 = siphash.SipHash128; | 25 | pub const SipHash128 = siphash.SipHash128; |
| 26 | 26 | ||
| ... | @@ -42,7 +42,6 @@ test "hash" { | ... | @@ -42,7 +42,6 @@ test "hash" { |
| 42 | _ = @import("hash/auto_hash.zig"); | 42 | _ = @import("hash/auto_hash.zig"); |
| 43 | _ = @import("hash/crc.zig"); | 43 | _ = @import("hash/crc.zig"); |
| 44 | _ = @import("hash/fnv.zig"); | 44 | _ = @import("hash/fnv.zig"); |
| 45 | _ = @import("hash/siphash.zig"); | ||
| 46 | _ = @import("hash/murmur.zig"); | 45 | _ = @import("hash/murmur.zig"); |
| 47 | _ = @import("hash/cityhash.zig"); | 46 | _ = @import("hash/cityhash.zig"); |
| 48 | _ = @import("hash/wyhash.zig"); | 47 | _ = @import("hash/wyhash.zig"); |
lib/std/hash/benchmark.zig-12| ... | @@ -25,24 +25,12 @@ const Hash = struct { | ... | @@ -25,24 +25,12 @@ const Hash = struct { |
| 25 | init_u64: ?u64 = null, | 25 | init_u64: ?u64 = null, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | const siphash_key = "0123456789abcdef"; | ||
| 29 | |||
| 30 | const hashes = [_]Hash{ | 28 | const hashes = [_]Hash{ |
| 31 | Hash{ | 29 | Hash{ |
| 32 | .ty = hash.Wyhash, | 30 | .ty = hash.Wyhash, |
| 33 | .name = "wyhash", | 31 | .name = "wyhash", |
| 34 | .init_u64 = 0, | 32 | .init_u64 = 0, |
| 35 | }, | 33 | }, |
| 36 | Hash{ | ||
| 37 | .ty = hash.SipHash64(1, 3), | ||
| 38 | .name = "siphash(1,3)", | ||
| 39 | .init_u8s = siphash_key, | ||
| 40 | }, | ||
| 41 | Hash{ | ||
| 42 | .ty = hash.SipHash64(2, 4), | ||
| 43 | .name = "siphash(2,4)", | ||
| 44 | .init_u8s = siphash_key, | ||
| 45 | }, | ||
| 46 | Hash{ | 34 | Hash{ |
| 47 | .ty = hash.Fnv1a_64, | 35 | .ty = hash.Fnv1a_64, |
| 48 | .name = "fnv1a", | 36 | .name = "fnv1a", |
lib/std/hash/siphash.zig deleted-393| ... | @@ -1,393 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | // Siphash | ||
| 7 | // | ||
| 8 | // SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance | ||
| 9 | // against hash flooding DoS attacks. | ||
| 10 | // | ||
| 11 | // https://131002.net/siphash/ | ||
| 12 | |||
| 13 | const std = @import("../std.zig"); | ||
| 14 | const assert = std.debug.assert; | ||
| 15 | const testing = std.testing; | ||
| 16 | const math = std.math; | ||
| 17 | const mem = std.mem; | ||
| 18 | |||
| 19 | const Endian = std.builtin.Endian; | ||
| 20 | |||
| 21 | pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 22 | return SipHash(u64, c_rounds, d_rounds); | ||
| 23 | } | ||
| 24 | |||
| 25 | pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 26 | return SipHash(u128, c_rounds, d_rounds); | ||
| 27 | } | ||
| 28 | |||
| 29 | fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 30 | assert(T == u64 or T == u128); | ||
| 31 | assert(c_rounds > 0 and d_rounds > 0); | ||
| 32 | |||
| 33 | return struct { | ||
| 34 | const Self = @This(); | ||
| 35 | const digest_size = 64; | ||
| 36 | const block_size = 64; | ||
| 37 | |||
| 38 | v0: u64, | ||
| 39 | v1: u64, | ||
| 40 | v2: u64, | ||
| 41 | v3: u64, | ||
| 42 | msg_len: u8, | ||
| 43 | |||
| 44 | pub fn init(key: []const u8) Self { | ||
| 45 | assert(key.len >= 16); | ||
| 46 | |||
| 47 | const k0 = mem.readIntLittle(u64, key[0..8]); | ||
| 48 | const k1 = mem.readIntLittle(u64, key[8..16]); | ||
| 49 | |||
| 50 | var d = Self{ | ||
| 51 | .v0 = k0 ^ 0x736f6d6570736575, | ||
| 52 | .v1 = k1 ^ 0x646f72616e646f6d, | ||
| 53 | .v2 = k0 ^ 0x6c7967656e657261, | ||
| 54 | .v3 = k1 ^ 0x7465646279746573, | ||
| 55 | .msg_len = 0, | ||
| 56 | }; | ||
| 57 | |||
| 58 | if (T == u128) { | ||
| 59 | d.v1 ^= 0xee; | ||
| 60 | } | ||
| 61 | |||
| 62 | return d; | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn update(self: *Self, b: []const u8) void { | ||
| 66 | std.debug.assert(b.len % 8 == 0); | ||
| 67 | |||
| 68 | var off: usize = 0; | ||
| 69 | while (off < b.len) : (off += 8) { | ||
| 70 | @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 8]}); | ||
| 71 | } | ||
| 72 | |||
| 73 | self.msg_len +%= @truncate(u8, b.len); | ||
| 74 | } | ||
| 75 | |||
| 76 | pub fn final(self: *Self, b: []const u8) T { | ||
| 77 | std.debug.assert(b.len < 8); | ||
| 78 | |||
| 79 | self.msg_len +%= @truncate(u8, b.len); | ||
| 80 | |||
| 81 | var buf = [_]u8{0} ** 8; | ||
| 82 | mem.copy(u8, buf[0..], b[0..]); | ||
| 83 | buf[7] = self.msg_len; | ||
| 84 | self.round(buf[0..]); | ||
| 85 | |||
| 86 | if (T == u128) { | ||
| 87 | self.v2 ^= 0xee; | ||
| 88 | } else { | ||
| 89 | self.v2 ^= 0xff; | ||
| 90 | } | ||
| 91 | |||
| 92 | // TODO this is a workaround, should be able to supply the value without a separate variable | ||
| 93 | const inl = std.builtin.CallOptions{ .modifier = .always_inline }; | ||
| 94 | |||
| 95 | comptime var i: usize = 0; | ||
| 96 | inline while (i < d_rounds) : (i += 1) { | ||
| 97 | @call(inl, sipRound, .{self}); | ||
| 98 | } | ||
| 99 | |||
| 100 | const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; | ||
| 101 | if (T == u64) { | ||
| 102 | return b1; | ||
| 103 | } | ||
| 104 | |||
| 105 | self.v1 ^= 0xdd; | ||
| 106 | |||
| 107 | comptime var j: usize = 0; | ||
| 108 | inline while (j < d_rounds) : (j += 1) { | ||
| 109 | @call(inl, sipRound, .{self}); | ||
| 110 | } | ||
| 111 | |||
| 112 | const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; | ||
| 113 | return (@as(u128, b2) << 64) | b1; | ||
| 114 | } | ||
| 115 | |||
| 116 | fn round(self: *Self, b: []const u8) void { | ||
| 117 | assert(b.len == 8); | ||
| 118 | |||
| 119 | const m = mem.readIntLittle(u64, b[0..8]); | ||
| 120 | self.v3 ^= m; | ||
| 121 | |||
| 122 | // TODO this is a workaround, should be able to supply the value without a separate variable | ||
| 123 | const inl = std.builtin.CallOptions{ .modifier = .always_inline }; | ||
| 124 | comptime var i: usize = 0; | ||
| 125 | inline while (i < c_rounds) : (i += 1) { | ||
| 126 | @call(inl, sipRound, .{self}); | ||
| 127 | } | ||
| 128 | |||
| 129 | self.v0 ^= m; | ||
| 130 | } | ||
| 131 | |||
| 132 | fn sipRound(d: *Self) void { | ||
| 133 | d.v0 +%= d.v1; | ||
| 134 | d.v1 = math.rotl(u64, d.v1, @as(u64, 13)); | ||
| 135 | d.v1 ^= d.v0; | ||
| 136 | d.v0 = math.rotl(u64, d.v0, @as(u64, 32)); | ||
| 137 | d.v2 +%= d.v3; | ||
| 138 | d.v3 = math.rotl(u64, d.v3, @as(u64, 16)); | ||
| 139 | d.v3 ^= d.v2; | ||
| 140 | d.v0 +%= d.v3; | ||
| 141 | d.v3 = math.rotl(u64, d.v3, @as(u64, 21)); | ||
| 142 | d.v3 ^= d.v0; | ||
| 143 | d.v2 +%= d.v1; | ||
| 144 | d.v1 = math.rotl(u64, d.v1, @as(u64, 17)); | ||
| 145 | d.v1 ^= d.v2; | ||
| 146 | d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); | ||
| 147 | } | ||
| 148 | |||
| 149 | pub fn hash(key: []const u8, input: []const u8) T { | ||
| 150 | const aligned_len = input.len - (input.len % 8); | ||
| 151 | |||
| 152 | var c = Self.init(key); | ||
| 153 | @call(.{ .modifier = .always_inline }, c.update, .{input[0..aligned_len]}); | ||
| 154 | return @call(.{ .modifier = .always_inline }, c.final, .{input[aligned_len..]}); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | pub fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type { | ||
| 160 | assert(T == u64 or T == u128); | ||
| 161 | assert(c_rounds > 0 and d_rounds > 0); | ||
| 162 | |||
| 163 | return struct { | ||
| 164 | const State = SipHashStateless(T, c_rounds, d_rounds); | ||
| 165 | const Self = @This(); | ||
| 166 | const digest_size = 64; | ||
| 167 | const block_size = 64; | ||
| 168 | |||
| 169 | state: State, | ||
| 170 | buf: [8]u8, | ||
| 171 | buf_len: usize, | ||
| 172 | |||
| 173 | pub fn init(key: []const u8) Self { | ||
| 174 | return Self{ | ||
| 175 | .state = State.init(key), | ||
| 176 | .buf = undefined, | ||
| 177 | .buf_len = 0, | ||
| 178 | }; | ||
| 179 | } | ||
| 180 | |||
| 181 | pub fn update(self: *Self, b: []const u8) void { | ||
| 182 | var off: usize = 0; | ||
| 183 | |||
| 184 | if (self.buf_len != 0 and self.buf_len + b.len >= 8) { | ||
| 185 | off += 8 - self.buf_len; | ||
| 186 | mem.copy(u8, self.buf[self.buf_len..], b[0..off]); | ||
| 187 | self.state.update(self.buf[0..]); | ||
| 188 | self.buf_len = 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | const remain_len = b.len - off; | ||
| 192 | const aligned_len = remain_len - (remain_len % 8); | ||
| 193 | self.state.update(b[off .. off + aligned_len]); | ||
| 194 | |||
| 195 | mem.copy(u8, self.buf[self.buf_len..], b[off + aligned_len ..]); | ||
| 196 | self.buf_len += @intCast(u8, b[off + aligned_len ..].len); | ||
| 197 | } | ||
| 198 | |||
| 199 | pub fn final(self: *Self) T { | ||
| 200 | return self.state.final(self.buf[0..self.buf_len]); | ||
| 201 | } | ||
| 202 | |||
| 203 | pub fn hash(key: []const u8, input: []const u8) T { | ||
| 204 | return State.hash(key, input); | ||
| 205 | } | ||
| 206 | }; | ||
| 207 | } | ||
| 208 | |||
| 209 | // Test vectors from reference implementation. | ||
| 210 | // https://github.com/veorq/SipHash/blob/master/vectors.h | ||
| 211 | const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"; | ||
| 212 | |||
| 213 | test "siphash64-2-4 sanity" { | ||
| 214 | const vectors = [_][8]u8{ | ||
| 215 | "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72".*, // "" | ||
| 216 | "\xfd\x67\xdc\x93\xc5\x39\xf8\x74".*, // "\x00" | ||
| 217 | "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d".*, // "\x00\x01" ... etc | ||
| 218 | "\x2d\x7e\xfb\xd7\x96\x66\x67\x85".*, | ||
| 219 | "\xb7\x87\x71\x27\xe0\x94\x27\xcf".*, | ||
| 220 | "\x8d\xa6\x99\xcd\x64\x55\x76\x18".*, | ||
| 221 | "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb".*, | ||
| 222 | "\x37\xd1\x01\x8b\xf5\x00\x02\xab".*, | ||
| 223 | "\x62\x24\x93\x9a\x79\xf5\xf5\x93".*, | ||
| 224 | "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e".*, | ||
| 225 | "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a".*, | ||
| 226 | "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4".*, | ||
| 227 | "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75".*, | ||
| 228 | "\x90\x3d\x84\xc0\x27\x56\xea\x14".*, | ||
| 229 | "\xee\xf2\x7a\x8e\x90\xca\x23\xf7".*, | ||
| 230 | "\xe5\x45\xbe\x49\x61\xca\x29\xa1".*, | ||
| 231 | "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f".*, | ||
| 232 | "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69".*, | ||
| 233 | "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b".*, | ||
| 234 | "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb".*, | ||
| 235 | "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe".*, | ||
| 236 | "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0".*, | ||
| 237 | "\x88\x3e\xa3\xe3\x95\x67\x53\x93".*, | ||
| 238 | "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8".*, | ||
| 239 | "\x94\xaf\x49\xf6\xc6\x50\xad\xb8".*, | ||
| 240 | "\xea\xb8\x85\x8a\xde\x92\xe1\xbc".*, | ||
| 241 | "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17".*, | ||
| 242 | "\xad\xcf\x6b\x07\x63\x61\x2e\x2f".*, | ||
| 243 | "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde".*, | ||
| 244 | "\x71\x65\x95\x87\x66\x50\xa2\xa6".*, | ||
| 245 | "\x28\xef\x49\x5c\x53\xa3\x87\xad".*, | ||
| 246 | "\x42\xc3\x41\xd8\xfa\x92\xd8\x32".*, | ||
| 247 | "\xce\x7c\xf2\x72\x2f\x51\x27\x71".*, | ||
| 248 | "\xe3\x78\x59\xf9\x46\x23\xf3\xa7".*, | ||
| 249 | "\x38\x12\x05\xbb\x1a\xb0\xe0\x12".*, | ||
| 250 | "\xae\x97\xa1\x0f\xd4\x34\xe0\x15".*, | ||
| 251 | "\xb4\xa3\x15\x08\xbe\xff\x4d\x31".*, | ||
| 252 | "\x81\x39\x62\x29\xf0\x90\x79\x02".*, | ||
| 253 | "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca".*, | ||
| 254 | "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a".*, | ||
| 255 | "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e".*, | ||
| 256 | "\x92\x59\x58\xfc\xd6\x42\x0c\xad".*, | ||
| 257 | "\xa9\x15\xc2\x9b\xc8\x06\x73\x18".*, | ||
| 258 | "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4".*, | ||
| 259 | "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9".*, | ||
| 260 | "\x87\x57\x75\x19\x04\x8f\x53\xa9".*, | ||
| 261 | "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb".*, | ||
| 262 | "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0".*, | ||
| 263 | "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6".*, | ||
| 264 | "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7".*, | ||
| 265 | "\x72\xfe\x52\x97\x5a\x43\x64\xee".*, | ||
| 266 | "\x5a\x16\x45\xb2\x76\xd5\x92\xa1".*, | ||
| 267 | "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a".*, | ||
| 268 | "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81".*, | ||
| 269 | "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f".*, | ||
| 270 | "\x99\x24\xa4\x3c\xc1\x31\x57\x24".*, | ||
| 271 | "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7".*, | ||
| 272 | "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea".*, | ||
| 273 | "\x13\x50\x79\xa3\x23\x1c\xe6\x60".*, | ||
| 274 | "\x93\x2b\x28\x46\xe4\xd7\x06\x66".*, | ||
| 275 | "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c".*, | ||
| 276 | "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f".*, | ||
| 277 | "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5".*, | ||
| 278 | "\x72\x45\x06\xeb\x4c\x32\x8a\x95".*, | ||
| 279 | }; | ||
| 280 | |||
| 281 | const siphash = SipHash64(2, 4); | ||
| 282 | |||
| 283 | var buffer: [64]u8 = undefined; | ||
| 284 | for (vectors) |vector, i| { | ||
| 285 | buffer[i] = @intCast(u8, i); | ||
| 286 | |||
| 287 | const expected = mem.readIntLittle(u64, &vector); | ||
| 288 | testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | test "siphash128-2-4 sanity" { | ||
| 293 | const vectors = [_][16]u8{ | ||
| 294 | "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93".*, | ||
| 295 | "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45".*, | ||
| 296 | "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4".*, | ||
| 297 | "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51".*, | ||
| 298 | "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79".*, | ||
| 299 | "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27".*, | ||
| 300 | "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e".*, | ||
| 301 | "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39".*, | ||
| 302 | "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4".*, | ||
| 303 | "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed".*, | ||
| 304 | "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba".*, | ||
| 305 | "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18".*, | ||
| 306 | "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25".*, | ||
| 307 | "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7".*, | ||
| 308 | "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02".*, | ||
| 309 | "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9".*, | ||
| 310 | "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77".*, | ||
| 311 | "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40".*, | ||
| 312 | "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23".*, | ||
| 313 | "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1".*, | ||
| 314 | "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb".*, | ||
| 315 | "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12".*, | ||
| 316 | "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae".*, | ||
| 317 | "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c".*, | ||
| 318 | "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad".*, | ||
| 319 | "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f".*, | ||
| 320 | "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66".*, | ||
| 321 | "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94".*, | ||
| 322 | "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4".*, | ||
| 323 | "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7".*, | ||
| 324 | "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87".*, | ||
| 325 | "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35".*, | ||
| 326 | "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68".*, | ||
| 327 | "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf".*, | ||
| 328 | "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde".*, | ||
| 329 | "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8".*, | ||
| 330 | "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11".*, | ||
| 331 | "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b".*, | ||
| 332 | "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5".*, | ||
| 333 | "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9".*, | ||
| 334 | "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8".*, | ||
| 335 | "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb".*, | ||
| 336 | "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b".*, | ||
| 337 | "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89".*, | ||
| 338 | "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42".*, | ||
| 339 | "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c".*, | ||
| 340 | "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02".*, | ||
| 341 | "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b".*, | ||
| 342 | "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16".*, | ||
| 343 | "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03".*, | ||
| 344 | "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f".*, | ||
| 345 | "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38".*, | ||
| 346 | "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c".*, | ||
| 347 | "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e".*, | ||
| 348 | "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87".*, | ||
| 349 | "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda".*, | ||
| 350 | "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36".*, | ||
| 351 | "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e".*, | ||
| 352 | "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d".*, | ||
| 353 | "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59".*, | ||
| 354 | "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40".*, | ||
| 355 | "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a".*, | ||
| 356 | "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd".*, | ||
| 357 | "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c".*, | ||
| 358 | }; | ||
| 359 | |||
| 360 | const siphash = SipHash128(2, 4); | ||
| 361 | |||
| 362 | var buffer: [64]u8 = undefined; | ||
| 363 | for (vectors) |vector, i| { | ||
| 364 | buffer[i] = @intCast(u8, i); | ||
| 365 | |||
| 366 | const expected = mem.readIntLittle(u128, &vector); | ||
| 367 | testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected); | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | test "iterative non-divisible update" { | ||
| 372 | var buf: [1024]u8 = undefined; | ||
| 373 | for (buf) |*e, i| { | ||
| 374 | e.* = @truncate(u8, i); | ||
| 375 | } | ||
| 376 | |||
| 377 | const key = "0x128dad08f12307"; | ||
| 378 | const Siphash = SipHash64(2, 4); | ||
| 379 | |||
| 380 | var end: usize = 9; | ||
| 381 | while (end < buf.len) : (end += 9) { | ||
| 382 | const non_iterative_hash = Siphash.hash(key, buf[0..end]); | ||
| 383 | |||
| 384 | var wy = Siphash.init(key); | ||
| 385 | var i: usize = 0; | ||
| 386 | while (i < end) : (i += 7) { | ||
| 387 | wy.update(buf[i..std.math.min(i + 7, end)]); | ||
| 388 | } | ||
| 389 | const iterative_hash = wy.final(); | ||
| 390 | |||
| 391 | std.testing.expectEqual(iterative_hash, non_iterative_hash); | ||
| 392 | } | ||
| 393 | } | ||
lib/std/heap/general_purpose_allocator.zig+4-2| ... | @@ -433,8 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -433,8 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 433 | const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size]; | 433 | const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size]; |
| 434 | self.backing_allocator.free(bucket_slice); | 434 | self.backing_allocator.free(bucket_slice); |
| 435 | } else { | 435 | } else { |
| 436 | // TODO Set the slot data to undefined. | 436 | @memset(bucket.page + slot_index * size_class, undefined, size_class); |
| 437 | // Related: https://github.com/ziglang/zig/issues/4298 | ||
| 438 | } | 437 | } |
| 439 | } | 438 | } |
| 440 | 439 | ||
| ... | @@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 567 | const new_aligned_size = math.max(new_size, old_align); | 566 | const new_aligned_size = math.max(new_size, old_align); |
| 568 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); | 567 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); |
| 569 | if (new_size_class <= size_class) { | 568 | if (new_size_class <= size_class) { |
| 569 | if (old_mem.len > new_size) { | ||
| 570 | @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size); | ||
| 571 | } | ||
| 570 | return new_size; | 572 | return new_size; |
| 571 | } | 573 | } |
| 572 | return error.OutOfMemory; | 574 | return error.OutOfMemory; |
lib/std/linked_list.zig-12| ... | @@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type { | ... | @@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type { |
| 28 | 28 | ||
| 29 | pub const Data = T; | 29 | pub const Data = T; |
| 30 | 30 | ||
| 31 | pub fn init(data: T) Node { | ||
| 32 | return Node{ | ||
| 33 | .data = data, | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Insert a new node after the current one. | 31 | /// Insert a new node after the current one. |
| 38 | /// | 32 | /// |
| 39 | /// Arguments: | 33 | /// Arguments: |
| ... | @@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type { | ... | @@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type { |
| 175 | prev: ?*Node = null, | 169 | prev: ?*Node = null, |
| 176 | next: ?*Node = null, | 170 | next: ?*Node = null, |
| 177 | data: T, | 171 | data: T, |
| 178 | |||
| 179 | pub fn init(data: T) Node { | ||
| 180 | return Node{ | ||
| 181 | .data = data, | ||
| 182 | }; | ||
| 183 | } | ||
| 184 | }; | 172 | }; |
| 185 | 173 | ||
| 186 | first: ?*Node = null, | 174 | first: ?*Node = null, |
lib/std/macho.zig+78-1| ... | @@ -40,6 +40,24 @@ pub const uuid_command = extern struct { | ... | @@ -40,6 +40,24 @@ pub const uuid_command = extern struct { |
| 40 | uuid: [16]u8, | 40 | uuid: [16]u8, |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | /// The entry_point_command is a replacement for thread_command. | ||
| 44 | /// It is used for main executables to specify the location (file offset) | ||
| 45 | /// of main(). If -stack_size was used at link time, the stacksize | ||
| 46 | /// field will contain the stack size needed for the main thread. | ||
| 47 | pub const entry_point_command = struct { | ||
| 48 | /// LC_MAIN only used in MH_EXECUTE filetypes | ||
| 49 | cmd: u32, | ||
| 50 | |||
| 51 | /// sizeof(struct entry_point_command) | ||
| 52 | cmdsize: u32, | ||
| 53 | |||
| 54 | /// file (__TEXT) offset of main() | ||
| 55 | entryoff: u64, | ||
| 56 | |||
| 57 | /// if not zero, initial stack size | ||
| 58 | stacksize: u64, | ||
| 59 | }; | ||
| 60 | |||
| 43 | /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD | 61 | /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD |
| 44 | /// "stab" style symbol table information as described in the header files | 62 | /// "stab" style symbol table information as described in the header files |
| 45 | /// <nlist.h> and <stab.h>. | 63 | /// <nlist.h> and <stab.h>. |
| ... | @@ -65,7 +83,7 @@ pub const symtab_command = extern struct { | ... | @@ -65,7 +83,7 @@ pub const symtab_command = extern struct { |
| 65 | 83 | ||
| 66 | /// The linkedit_data_command contains the offsets and sizes of a blob | 84 | /// The linkedit_data_command contains the offsets and sizes of a blob |
| 67 | /// of data in the __LINKEDIT segment. | 85 | /// of data in the __LINKEDIT segment. |
| 68 | const linkedit_data_command = extern struct { | 86 | pub const linkedit_data_command = extern struct { |
| 69 | /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. | 87 | /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. |
| 70 | cmd: u32, | 88 | cmd: u32, |
| 71 | 89 | ||
| ... | @@ -79,6 +97,65 @@ const linkedit_data_command = extern struct { | ... | @@ -79,6 +97,65 @@ const linkedit_data_command = extern struct { |
| 79 | datasize: u32, | 97 | datasize: u32, |
| 80 | }; | 98 | }; |
| 81 | 99 | ||
| 100 | /// A program that uses a dynamic linker contains a dylinker_command to identify | ||
| 101 | /// the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker | ||
| 102 | /// contains a dylinker_command to identify the dynamic linker (LC_ID_DYLINKER). | ||
| 103 | /// A file can have at most one of these. | ||
| 104 | /// This struct is also used for the LC_DYLD_ENVIRONMENT load command and contains | ||
| 105 | /// string for dyld to treat like an environment variable. | ||
| 106 | pub const dylinker_command = extern struct { | ||
| 107 | /// LC_ID_DYLINKER, LC_LOAD_DYLINKER, or LC_DYLD_ENVIRONMENT | ||
| 108 | cmd: u32, | ||
| 109 | |||
| 110 | /// includes pathname string | ||
| 111 | cmdsize: u32, | ||
| 112 | |||
| 113 | /// A variable length string in a load command is represented by an lc_str | ||
| 114 | /// union. The strings are stored just after the load command structure and | ||
| 115 | /// the offset is from the start of the load command structure. The size | ||
| 116 | /// of the string is reflected in the cmdsize field of the load command. | ||
| 117 | /// Once again any padded bytes to bring the cmdsize field to a multiple | ||
| 118 | /// of 4 bytes must be zero. | ||
| 119 | name: u32, | ||
| 120 | }; | ||
| 121 | |||
| 122 | /// A dynamically linked shared library (filetype == MH_DYLIB in the mach header) | ||
| 123 | /// contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library. | ||
| 124 | /// An object that uses a dynamically linked shared library also contains a | ||
| 125 | /// dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or | ||
| 126 | /// LC_REEXPORT_DYLIB) for each library it uses. | ||
| 127 | pub const dylib_command = extern struct { | ||
| 128 | /// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB | ||
| 129 | cmd: u32, | ||
| 130 | |||
| 131 | /// includes pathname string | ||
| 132 | cmdsize: u32, | ||
| 133 | |||
| 134 | /// the library identification | ||
| 135 | dylib: dylib, | ||
| 136 | }; | ||
| 137 | |||
| 138 | /// Dynamicaly linked shared libraries are identified by two things. The | ||
| 139 | /// pathname (the name of the library as found for execution), and the | ||
| 140 | /// compatibility version number. The pathname must match and the compatibility | ||
| 141 | /// number in the user of the library must be greater than or equal to the | ||
| 142 | /// library being used. The time stamp is used to record the time a library was | ||
| 143 | /// built and copied into user so it can be use to determined if the library used | ||
| 144 | /// at runtime is exactly the same as used to built the program. | ||
| 145 | pub const dylib = extern struct { | ||
| 146 | /// library's pathname (offset pointing at the end of dylib_command) | ||
| 147 | name: u32, | ||
| 148 | |||
| 149 | /// library's build timestamp | ||
| 150 | timestamp: u32, | ||
| 151 | |||
| 152 | /// library's current version number | ||
| 153 | current_version: u32, | ||
| 154 | |||
| 155 | /// library's compatibility version number | ||
| 156 | compatibility_version: u32, | ||
| 157 | }; | ||
| 158 | |||
| 82 | /// The segment load command indicates that a part of this file is to be | 159 | /// The segment load command indicates that a part of this file is to be |
| 83 | /// mapped into the task's address space. The size of this segment in memory, | 160 | /// mapped into the task's address space. The size of this segment in memory, |
| 84 | /// vmsize, maybe equal to or larger than the amount to map from this file, | 161 | /// vmsize, maybe equal to or larger than the amount to map from this file, |
lib/std/os/bits/linux.zig-1| ... | @@ -24,7 +24,6 @@ pub usingnamespace switch (builtin.arch) { | ... | @@ -24,7 +24,6 @@ pub usingnamespace switch (builtin.arch) { |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | pub usingnamespace @import("linux/netlink.zig"); | 26 | pub usingnamespace @import("linux/netlink.zig"); |
| 27 | pub const BPF = @import("linux/bpf.zig"); | ||
| 28 | 27 | ||
| 29 | const is_mips = builtin.arch.isMIPS(); | 28 | const is_mips = builtin.arch.isMIPS(); |
| 30 | 29 |
lib/std/os/bits/linux/bpf.zig deleted-975| ... | @@ -1,975 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | usingnamespace std.os; | ||
| 7 | const std = @import("../../../std.zig"); | ||
| 8 | const expectEqual = std.testing.expectEqual; | ||
| 9 | const fd_t = std.os.fd_t; | ||
| 10 | const pid_t = std.os.pid_t; | ||
| 11 | |||
| 12 | // instruction classes | ||
| 13 | pub const LD = 0x00; | ||
| 14 | pub const LDX = 0x01; | ||
| 15 | pub const ST = 0x02; | ||
| 16 | pub const STX = 0x03; | ||
| 17 | pub const ALU = 0x04; | ||
| 18 | pub const JMP = 0x05; | ||
| 19 | pub const RET = 0x06; | ||
| 20 | pub const MISC = 0x07; | ||
| 21 | |||
| 22 | /// 32-bit | ||
| 23 | pub const W = 0x00; | ||
| 24 | /// 16-bit | ||
| 25 | pub const H = 0x08; | ||
| 26 | /// 8-bit | ||
| 27 | pub const B = 0x10; | ||
| 28 | /// 64-bit | ||
| 29 | pub const DW = 0x18; | ||
| 30 | |||
| 31 | pub const IMM = 0x00; | ||
| 32 | pub const ABS = 0x20; | ||
| 33 | pub const IND = 0x40; | ||
| 34 | pub const MEM = 0x60; | ||
| 35 | pub const LEN = 0x80; | ||
| 36 | pub const MSH = 0xa0; | ||
| 37 | |||
| 38 | // alu fields | ||
| 39 | pub const ADD = 0x00; | ||
| 40 | pub const SUB = 0x10; | ||
| 41 | pub const MUL = 0x20; | ||
| 42 | pub const DIV = 0x30; | ||
| 43 | pub const OR = 0x40; | ||
| 44 | pub const AND = 0x50; | ||
| 45 | pub const LSH = 0x60; | ||
| 46 | pub const RSH = 0x70; | ||
| 47 | pub const NEG = 0x80; | ||
| 48 | pub const MOD = 0x90; | ||
| 49 | pub const XOR = 0xa0; | ||
| 50 | |||
| 51 | // jmp fields | ||
| 52 | pub const JA = 0x00; | ||
| 53 | pub const JEQ = 0x10; | ||
| 54 | pub const JGT = 0x20; | ||
| 55 | pub const JGE = 0x30; | ||
| 56 | pub const JSET = 0x40; | ||
| 57 | |||
| 58 | //#define BPF_SRC(code) ((code) & 0x08) | ||
| 59 | pub const K = 0x00; | ||
| 60 | pub const X = 0x08; | ||
| 61 | |||
| 62 | pub const MAXINSNS = 4096; | ||
| 63 | |||
| 64 | // instruction classes | ||
| 65 | /// jmp mode in word width | ||
| 66 | pub const JMP32 = 0x06; | ||
| 67 | /// alu mode in double word width | ||
| 68 | pub const ALU64 = 0x07; | ||
| 69 | |||
| 70 | // ld/ldx fields | ||
| 71 | /// exclusive add | ||
| 72 | pub const XADD = 0xc0; | ||
| 73 | |||
| 74 | // alu/jmp fields | ||
| 75 | /// mov reg to reg | ||
| 76 | pub const MOV = 0xb0; | ||
| 77 | /// sign extending arithmetic shift right */ | ||
| 78 | pub const ARSH = 0xc0; | ||
| 79 | |||
| 80 | // change endianness of a register | ||
| 81 | /// flags for endianness conversion: | ||
| 82 | pub const END = 0xd0; | ||
| 83 | /// convert to little-endian */ | ||
| 84 | pub const TO_LE = 0x00; | ||
| 85 | /// convert to big-endian | ||
| 86 | pub const TO_BE = 0x08; | ||
| 87 | pub const FROM_LE = TO_LE; | ||
| 88 | pub const FROM_BE = TO_BE; | ||
| 89 | |||
| 90 | // jmp encodings | ||
| 91 | /// jump != * | ||
| 92 | pub const JNE = 0x50; | ||
| 93 | /// LT is unsigned, '<' | ||
| 94 | pub const JLT = 0xa0; | ||
| 95 | /// LE is unsigned, '<=' * | ||
| 96 | pub const JLE = 0xb0; | ||
| 97 | /// SGT is signed '>', GT in x86 | ||
| 98 | pub const JSGT = 0x60; | ||
| 99 | /// SGE is signed '>=', GE in x86 | ||
| 100 | pub const JSGE = 0x70; | ||
| 101 | /// SLT is signed, '<' | ||
| 102 | pub const JSLT = 0xc0; | ||
| 103 | /// SLE is signed, '<=' | ||
| 104 | pub const JSLE = 0xd0; | ||
| 105 | /// function call | ||
| 106 | pub const CALL = 0x80; | ||
| 107 | /// function return | ||
| 108 | pub const EXIT = 0x90; | ||
| 109 | |||
| 110 | /// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the | ||
| 111 | /// program in this cgroup yields to sub-cgroup program. | ||
| 112 | pub const F_ALLOW_OVERRIDE = 0x1; | ||
| 113 | /// Flag for prog_attach command. If a sub-cgroup installs some bpf program, | ||
| 114 | /// that cgroup program gets run in addition to the program in this cgroup. | ||
| 115 | pub const F_ALLOW_MULTI = 0x2; | ||
| 116 | /// Flag for prog_attach command. | ||
| 117 | pub const F_REPLACE = 0x4; | ||
| 118 | |||
| 119 | /// If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the verifier | ||
| 120 | /// will perform strict alignment checking as if the kernel has been built with | ||
| 121 | /// CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, and NET_IP_ALIGN defined to 2. | ||
| 122 | pub const F_STRICT_ALIGNMENT = 0x1; | ||
| 123 | |||
| 124 | /// If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the verifier will | ||
| 125 | /// allow any alignment whatsoever. On platforms with strict alignment | ||
| 126 | /// requirements for loads ands stores (such as sparc and mips) the verifier | ||
| 127 | /// validates that all loads and stores provably follow this requirement. This | ||
| 128 | /// flag turns that checking and enforcement off. | ||
| 129 | /// | ||
| 130 | /// It is mostly used for testing when we want to validate the context and | ||
| 131 | /// memory access aspects of the verifier, but because of an unaligned access | ||
| 132 | /// the alignment check would trigger before the one we are interested in. | ||
| 133 | pub const F_ANY_ALIGNMENT = 0x2; | ||
| 134 | |||
| 135 | /// BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose. | ||
| 136 | /// Verifier does sub-register def/use analysis and identifies instructions | ||
| 137 | /// whose def only matters for low 32-bit, high 32-bit is never referenced later | ||
| 138 | /// through implicit zero extension. Therefore verifier notifies JIT back-ends | ||
| 139 | /// that it is safe to ignore clearing high 32-bit for these instructions. This | ||
| 140 | /// saves some back-ends a lot of code-gen. However such optimization is not | ||
| 141 | /// necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends | ||
| 142 | /// hence hasn't used verifier's analysis result. But, we really want to have a | ||
| 143 | /// way to be able to verify the correctness of the described optimization on | ||
| 144 | /// x86_64 on which testsuites are frequently exercised. | ||
| 145 | /// | ||
| 146 | /// So, this flag is introduced. Once it is set, verifier will randomize high | ||
| 147 | /// 32-bit for those instructions who has been identified as safe to ignore | ||
| 148 | /// them. Then, if verifier is not doing correct analysis, such randomization | ||
| 149 | /// will regress tests to expose bugs. | ||
| 150 | pub const F_TEST_RND_HI32 = 0x4; | ||
| 151 | |||
| 152 | /// When BPF ldimm64's insn[0].src_reg != 0 then this can have two extensions: | ||
| 153 | /// insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE | ||
| 154 | /// insn[0].imm: map fd map fd | ||
| 155 | /// insn[1].imm: 0 offset into value | ||
| 156 | /// insn[0].off: 0 0 | ||
| 157 | /// insn[1].off: 0 0 | ||
| 158 | /// ldimm64 rewrite: address of map address of map[0]+offset | ||
| 159 | /// verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE | ||
| 160 | pub const PSEUDO_MAP_FD = 1; | ||
| 161 | pub const PSEUDO_MAP_VALUE = 2; | ||
| 162 | |||
| 163 | /// when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative | ||
| 164 | /// offset to another bpf function | ||
| 165 | pub const PSEUDO_CALL = 1; | ||
| 166 | |||
| 167 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing | ||
| 168 | pub const ANY = 0; | ||
| 169 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist | ||
| 170 | pub const NOEXIST = 1; | ||
| 171 | /// flag for BPF_MAP_UPDATE_ELEM command. update existing element | ||
| 172 | pub const EXIST = 2; | ||
| 173 | /// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update | ||
| 174 | pub const F_LOCK = 4; | ||
| 175 | |||
| 176 | /// flag for BPF_MAP_CREATE command */ | ||
| 177 | pub const BPF_F_NO_PREALLOC = 0x1; | ||
| 178 | /// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in | ||
| 179 | /// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can | ||
| 180 | /// scale and perform better. Note, the LRU nodes (including free nodes) cannot | ||
| 181 | /// be moved across different LRU lists. | ||
| 182 | pub const BPF_F_NO_COMMON_LRU = 0x2; | ||
| 183 | /// flag for BPF_MAP_CREATE command. Specify numa node during map creation | ||
| 184 | pub const BPF_F_NUMA_NODE = 0x4; | ||
| 185 | /// flag for BPF_MAP_CREATE command. Flags for BPF object read access from | ||
| 186 | /// syscall side | ||
| 187 | pub const BPF_F_RDONLY = 0x8; | ||
| 188 | /// flag for BPF_MAP_CREATE command. Flags for BPF object write access from | ||
| 189 | /// syscall side | ||
| 190 | pub const BPF_F_WRONLY = 0x10; | ||
| 191 | /// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset | ||
| 192 | /// instead of pointer | ||
| 193 | pub const BPF_F_STACK_BUILD_ID = 0x20; | ||
| 194 | /// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This | ||
| 195 | /// should only be used for testing. | ||
| 196 | pub const BPF_F_ZERO_SEED = 0x40; | ||
| 197 | /// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program | ||
| 198 | /// side. | ||
| 199 | pub const BPF_F_RDONLY_PROG = 0x80; | ||
| 200 | /// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program | ||
| 201 | /// side. | ||
| 202 | pub const BPF_F_WRONLY_PROG = 0x100; | ||
| 203 | /// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted | ||
| 204 | /// socket | ||
| 205 | pub const BPF_F_CLONE = 0x200; | ||
| 206 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map | ||
| 207 | pub const BPF_F_MMAPABLE = 0x400; | ||
| 208 | |||
| 209 | /// These values correspond to "syscalls" within the BPF program's environment | ||
| 210 | pub const Helper = enum(i32) { | ||
| 211 | unspec, | ||
| 212 | map_lookup_elem, | ||
| 213 | map_update_elem, | ||
| 214 | map_delete_elem, | ||
| 215 | probe_read, | ||
| 216 | ktime_get_ns, | ||
| 217 | trace_printk, | ||
| 218 | get_prandom_u32, | ||
| 219 | get_smp_processor_id, | ||
| 220 | skb_store_bytes, | ||
| 221 | l3_csum_replace, | ||
| 222 | l4_csum_replace, | ||
| 223 | tail_call, | ||
| 224 | clone_redirect, | ||
| 225 | get_current_pid_tgid, | ||
| 226 | get_current_uid_gid, | ||
| 227 | get_current_comm, | ||
| 228 | get_cgroup_classid, | ||
| 229 | skb_vlan_push, | ||
| 230 | skb_vlan_pop, | ||
| 231 | skb_get_tunnel_key, | ||
| 232 | skb_set_tunnel_key, | ||
| 233 | perf_event_read, | ||
| 234 | redirect, | ||
| 235 | get_route_realm, | ||
| 236 | perf_event_output, | ||
| 237 | skb_load_bytes, | ||
| 238 | get_stackid, | ||
| 239 | csum_diff, | ||
| 240 | skb_get_tunnel_opt, | ||
| 241 | skb_set_tunnel_opt, | ||
| 242 | skb_change_proto, | ||
| 243 | skb_change_type, | ||
| 244 | skb_under_cgroup, | ||
| 245 | get_hash_recalc, | ||
| 246 | get_current_task, | ||
| 247 | probe_write_user, | ||
| 248 | current_task_under_cgroup, | ||
| 249 | skb_change_tail, | ||
| 250 | skb_pull_data, | ||
| 251 | csum_update, | ||
| 252 | set_hash_invalid, | ||
| 253 | get_numa_node_id, | ||
| 254 | skb_change_head, | ||
| 255 | xdp_adjust_head, | ||
| 256 | probe_read_str, | ||
| 257 | get_socket_cookie, | ||
| 258 | get_socket_uid, | ||
| 259 | set_hash, | ||
| 260 | setsockopt, | ||
| 261 | skb_adjust_room, | ||
| 262 | redirect_map, | ||
| 263 | sk_redirect_map, | ||
| 264 | sock_map_update, | ||
| 265 | xdp_adjust_meta, | ||
| 266 | perf_event_read_value, | ||
| 267 | perf_prog_read_value, | ||
| 268 | getsockopt, | ||
| 269 | override_return, | ||
| 270 | sock_ops_cb_flags_set, | ||
| 271 | msg_redirect_map, | ||
| 272 | msg_apply_bytes, | ||
| 273 | msg_cork_bytes, | ||
| 274 | msg_pull_data, | ||
| 275 | bind, | ||
| 276 | xdp_adjust_tail, | ||
| 277 | skb_get_xfrm_state, | ||
| 278 | get_stack, | ||
| 279 | skb_load_bytes_relative, | ||
| 280 | fib_lookup, | ||
| 281 | sock_hash_update, | ||
| 282 | msg_redirect_hash, | ||
| 283 | sk_redirect_hash, | ||
| 284 | lwt_push_encap, | ||
| 285 | lwt_seg6_store_bytes, | ||
| 286 | lwt_seg6_adjust_srh, | ||
| 287 | lwt_seg6_action, | ||
| 288 | rc_repeat, | ||
| 289 | rc_keydown, | ||
| 290 | skb_cgroup_id, | ||
| 291 | get_current_cgroup_id, | ||
| 292 | get_local_storage, | ||
| 293 | sk_select_reuseport, | ||
| 294 | skb_ancestor_cgroup_id, | ||
| 295 | sk_lookup_tcp, | ||
| 296 | sk_lookup_udp, | ||
| 297 | sk_release, | ||
| 298 | map_push_elem, | ||
| 299 | map_pop_elem, | ||
| 300 | map_peek_elem, | ||
| 301 | msg_push_data, | ||
| 302 | msg_pop_data, | ||
| 303 | rc_pointer_rel, | ||
| 304 | spin_lock, | ||
| 305 | spin_unlock, | ||
| 306 | sk_fullsock, | ||
| 307 | tcp_sock, | ||
| 308 | skb_ecn_set_ce, | ||
| 309 | get_listener_sock, | ||
| 310 | skc_lookup_tcp, | ||
| 311 | tcp_check_syncookie, | ||
| 312 | sysctl_get_name, | ||
| 313 | sysctl_get_current_value, | ||
| 314 | sysctl_get_new_value, | ||
| 315 | sysctl_set_new_value, | ||
| 316 | strtol, | ||
| 317 | strtoul, | ||
| 318 | sk_storage_get, | ||
| 319 | sk_storage_delete, | ||
| 320 | send_signal, | ||
| 321 | tcp_gen_syncookie, | ||
| 322 | skb_output, | ||
| 323 | probe_read_user, | ||
| 324 | probe_read_kernel, | ||
| 325 | probe_read_user_str, | ||
| 326 | probe_read_kernel_str, | ||
| 327 | tcp_send_ack, | ||
| 328 | send_signal_thread, | ||
| 329 | jiffies64, | ||
| 330 | _, | ||
| 331 | }; | ||
| 332 | |||
| 333 | /// a single BPF instruction | ||
| 334 | pub const Insn = packed struct { | ||
| 335 | code: u8, | ||
| 336 | dst: u4, | ||
| 337 | src: u4, | ||
| 338 | off: i16, | ||
| 339 | imm: i32, | ||
| 340 | |||
| 341 | /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack | ||
| 342 | /// frame | ||
| 343 | pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; | ||
| 344 | const Source = packed enum(u1) { reg, imm }; | ||
| 345 | const AluOp = packed enum(u8) { | ||
| 346 | add = ADD, | ||
| 347 | sub = SUB, | ||
| 348 | mul = MUL, | ||
| 349 | div = DIV, | ||
| 350 | op_or = OR, | ||
| 351 | op_and = AND, | ||
| 352 | lsh = LSH, | ||
| 353 | rsh = RSH, | ||
| 354 | neg = NEG, | ||
| 355 | mod = MOD, | ||
| 356 | xor = XOR, | ||
| 357 | mov = MOV, | ||
| 358 | }; | ||
| 359 | |||
| 360 | pub const Size = packed enum(u8) { | ||
| 361 | byte = B, | ||
| 362 | half_word = H, | ||
| 363 | word = W, | ||
| 364 | double_word = DW, | ||
| 365 | }; | ||
| 366 | |||
| 367 | const JmpOp = packed enum(u8) { | ||
| 368 | ja = JA, | ||
| 369 | jeq = JEQ, | ||
| 370 | jgt = JGT, | ||
| 371 | jge = JGE, | ||
| 372 | jset = JSET, | ||
| 373 | }; | ||
| 374 | |||
| 375 | const ImmOrReg = union(Source) { | ||
| 376 | imm: i32, | ||
| 377 | reg: Reg, | ||
| 378 | }; | ||
| 379 | |||
| 380 | fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn { | ||
| 381 | const imm_or_reg = if (@typeInfo(@TypeOf(src)) == .EnumLiteral) | ||
| 382 | ImmOrReg{ .reg = @as(Reg, src) } | ||
| 383 | else | ||
| 384 | ImmOrReg{ .imm = src }; | ||
| 385 | |||
| 386 | const src_type = switch (imm_or_reg) { | ||
| 387 | .imm => K, | ||
| 388 | .reg => X, | ||
| 389 | }; | ||
| 390 | |||
| 391 | return Insn{ | ||
| 392 | .code = code | src_type, | ||
| 393 | .dst = @enumToInt(dst), | ||
| 394 | .src = switch (imm_or_reg) { | ||
| 395 | .imm => 0, | ||
| 396 | .reg => |r| @enumToInt(r), | ||
| 397 | }, | ||
| 398 | .off = off, | ||
| 399 | .imm = switch (imm_or_reg) { | ||
| 400 | .imm => |i| i, | ||
| 401 | .reg => 0, | ||
| 402 | }, | ||
| 403 | }; | ||
| 404 | } | ||
| 405 | |||
| 406 | fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn { | ||
| 407 | const width_bitfield = switch (width) { | ||
| 408 | 32 => ALU, | ||
| 409 | 64 => ALU64, | ||
| 410 | else => @compileError("width must be 32 or 64"), | ||
| 411 | }; | ||
| 412 | |||
| 413 | return imm_reg(width_bitfield | @enumToInt(op), dst, src, 0); | ||
| 414 | } | ||
| 415 | |||
| 416 | pub fn mov(dst: Reg, src: anytype) Insn { | ||
| 417 | return alu(64, .mov, dst, src); | ||
| 418 | } | ||
| 419 | |||
| 420 | pub fn add(dst: Reg, src: anytype) Insn { | ||
| 421 | return alu(64, .add, dst, src); | ||
| 422 | } | ||
| 423 | |||
| 424 | fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn { | ||
| 425 | return imm_reg(JMP | @enumToInt(op), dst, src, off); | ||
| 426 | } | ||
| 427 | |||
| 428 | pub fn jeq(dst: Reg, src: anytype, off: i16) Insn { | ||
| 429 | return jmp(.jeq, dst, src, off); | ||
| 430 | } | ||
| 431 | |||
| 432 | pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn { | ||
| 433 | return Insn{ | ||
| 434 | .code = STX | @enumToInt(size) | MEM, | ||
| 435 | .dst = @enumToInt(dst), | ||
| 436 | .src = @enumToInt(src), | ||
| 437 | .off = off, | ||
| 438 | .imm = 0, | ||
| 439 | }; | ||
| 440 | } | ||
| 441 | |||
| 442 | pub fn xadd(dst: Reg, src: Reg) Insn { | ||
| 443 | return Insn{ | ||
| 444 | .code = STX | XADD | DW, | ||
| 445 | .dst = @enumToInt(dst), | ||
| 446 | .src = @enumToInt(src), | ||
| 447 | .off = 0, | ||
| 448 | .imm = 0, | ||
| 449 | }; | ||
| 450 | } | ||
| 451 | |||
| 452 | /// direct packet access, R0 = *(uint *)(skb->data + imm32) | ||
| 453 | pub fn ld_abs(size: Size, imm: i32) Insn { | ||
| 454 | return Insn{ | ||
| 455 | .code = LD | @enumToInt(size) | ABS, | ||
| 456 | .dst = 0, | ||
| 457 | .src = 0, | ||
| 458 | .off = 0, | ||
| 459 | .imm = imm, | ||
| 460 | }; | ||
| 461 | } | ||
| 462 | |||
| 463 | fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn { | ||
| 464 | return Insn{ | ||
| 465 | .code = LD | DW | IMM, | ||
| 466 | .dst = @enumToInt(dst), | ||
| 467 | .src = @enumToInt(src), | ||
| 468 | .off = 0, | ||
| 469 | .imm = @intCast(i32, @truncate(u32, imm)), | ||
| 470 | }; | ||
| 471 | } | ||
| 472 | |||
| 473 | fn ld_imm_impl2(imm: u64) Insn { | ||
| 474 | return Insn{ | ||
| 475 | .code = 0, | ||
| 476 | .dst = 0, | ||
| 477 | .src = 0, | ||
| 478 | .off = 0, | ||
| 479 | .imm = @intCast(i32, @truncate(u32, imm >> 32)), | ||
| 480 | }; | ||
| 481 | } | ||
| 482 | |||
| 483 | pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn { | ||
| 484 | return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd)); | ||
| 485 | } | ||
| 486 | |||
| 487 | pub fn ld_map_fd2(map_fd: fd_t) Insn { | ||
| 488 | return ld_imm_impl2(@intCast(u64, map_fd)); | ||
| 489 | } | ||
| 490 | |||
| 491 | pub fn call(helper: Helper) Insn { | ||
| 492 | return Insn{ | ||
| 493 | .code = JMP | CALL, | ||
| 494 | .dst = 0, | ||
| 495 | .src = 0, | ||
| 496 | .off = 0, | ||
| 497 | .imm = @enumToInt(helper), | ||
| 498 | }; | ||
| 499 | } | ||
| 500 | |||
| 501 | /// exit BPF program | ||
| 502 | pub fn exit() Insn { | ||
| 503 | return Insn{ | ||
| 504 | .code = JMP | EXIT, | ||
| 505 | .dst = 0, | ||
| 506 | .src = 0, | ||
| 507 | .off = 0, | ||
| 508 | .imm = 0, | ||
| 509 | }; | ||
| 510 | } | ||
| 511 | }; | ||
| 512 | |||
| 513 | fn expect_insn(insn: Insn, val: u64) void { | ||
| 514 | expectEqual(@bitCast(u64, insn), val); | ||
| 515 | } | ||
| 516 | |||
| 517 | test "insn bitsize" { | ||
| 518 | expectEqual(@bitSizeOf(Insn), 64); | ||
| 519 | } | ||
| 520 | |||
| 521 | // mov instructions | ||
| 522 | test "mov imm" { | ||
| 523 | expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7); | ||
| 524 | } | ||
| 525 | |||
| 526 | test "mov reg" { | ||
| 527 | expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf); | ||
| 528 | } | ||
| 529 | |||
| 530 | // alu instructions | ||
| 531 | test "add imm" { | ||
| 532 | expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207); | ||
| 533 | } | ||
| 534 | |||
| 535 | // ld instructions | ||
| 536 | test "ld_abs" { | ||
| 537 | expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030); | ||
| 538 | } | ||
| 539 | |||
| 540 | test "ld_map_fd" { | ||
| 541 | expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118); | ||
| 542 | expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000); | ||
| 543 | } | ||
| 544 | |||
| 545 | // st instructions | ||
| 546 | test "stx_mem" { | ||
| 547 | expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63); | ||
| 548 | } | ||
| 549 | |||
| 550 | test "xadd" { | ||
| 551 | expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db); | ||
| 552 | } | ||
| 553 | |||
| 554 | // jmp instructions | ||
| 555 | test "jeq imm" { | ||
| 556 | expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015); | ||
| 557 | } | ||
| 558 | |||
| 559 | // other instructions | ||
| 560 | test "call" { | ||
| 561 | expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085); | ||
| 562 | } | ||
| 563 | |||
| 564 | test "exit" { | ||
| 565 | expect_insn(Insn.exit(), 0x0000000000000095); | ||
| 566 | } | ||
| 567 | |||
| 568 | pub const Cmd = extern enum(usize) { | ||
| 569 | map_create, | ||
| 570 | map_lookup_elem, | ||
| 571 | map_update_elem, | ||
| 572 | map_delete_elem, | ||
| 573 | map_get_next_key, | ||
| 574 | prog_load, | ||
| 575 | obj_pin, | ||
| 576 | obj_get, | ||
| 577 | prog_attach, | ||
| 578 | prog_detach, | ||
| 579 | prog_test_run, | ||
| 580 | prog_get_next_id, | ||
| 581 | map_get_next_id, | ||
| 582 | prog_get_fd_by_id, | ||
| 583 | map_get_fd_by_id, | ||
| 584 | obj_get_info_by_fd, | ||
| 585 | prog_query, | ||
| 586 | raw_tracepoint_open, | ||
| 587 | btf_load, | ||
| 588 | btf_get_fd_by_id, | ||
| 589 | task_fd_query, | ||
| 590 | map_lookup_and_delete_elem, | ||
| 591 | map_freeze, | ||
| 592 | btf_get_next_id, | ||
| 593 | map_lookup_batch, | ||
| 594 | map_lookup_and_delete_batch, | ||
| 595 | map_update_batch, | ||
| 596 | map_delete_batch, | ||
| 597 | link_create, | ||
| 598 | link_update, | ||
| 599 | link_get_fd_by_id, | ||
| 600 | link_get_next_id, | ||
| 601 | enable_stats, | ||
| 602 | iter_create, | ||
| 603 | link_detach, | ||
| 604 | _, | ||
| 605 | }; | ||
| 606 | |||
| 607 | pub const MapType = extern enum(u32) { | ||
| 608 | unspec, | ||
| 609 | hash, | ||
| 610 | array, | ||
| 611 | prog_array, | ||
| 612 | perf_event_array, | ||
| 613 | percpu_hash, | ||
| 614 | percpu_array, | ||
| 615 | stack_trace, | ||
| 616 | cgroup_array, | ||
| 617 | lru_hash, | ||
| 618 | lru_percpu_hash, | ||
| 619 | lpm_trie, | ||
| 620 | array_of_maps, | ||
| 621 | hash_of_maps, | ||
| 622 | devmap, | ||
| 623 | sockmap, | ||
| 624 | cpumap, | ||
| 625 | xskmap, | ||
| 626 | sockhash, | ||
| 627 | cgroup_storage, | ||
| 628 | reuseport_sockarray, | ||
| 629 | percpu_cgroup_storage, | ||
| 630 | queue, | ||
| 631 | stack, | ||
| 632 | sk_storage, | ||
| 633 | devmap_hash, | ||
| 634 | struct_ops, | ||
| 635 | ringbuf, | ||
| 636 | _, | ||
| 637 | }; | ||
| 638 | |||
| 639 | pub const ProgType = extern enum(u32) { | ||
| 640 | unspec, | ||
| 641 | socket_filter, | ||
| 642 | kprobe, | ||
| 643 | sched_cls, | ||
| 644 | sched_act, | ||
| 645 | tracepoint, | ||
| 646 | xdp, | ||
| 647 | perf_event, | ||
| 648 | cgroup_skb, | ||
| 649 | cgroup_sock, | ||
| 650 | lwt_in, | ||
| 651 | lwt_out, | ||
| 652 | lwt_xmit, | ||
| 653 | sock_ops, | ||
| 654 | sk_skb, | ||
| 655 | cgroup_device, | ||
| 656 | sk_msg, | ||
| 657 | raw_tracepoint, | ||
| 658 | cgroup_sock_addr, | ||
| 659 | lwt_seg6local, | ||
| 660 | lirc_mode2, | ||
| 661 | sk_reuseport, | ||
| 662 | flow_dissector, | ||
| 663 | cgroup_sysctl, | ||
| 664 | raw_tracepoint_writable, | ||
| 665 | cgroup_sockopt, | ||
| 666 | tracing, | ||
| 667 | struct_ops, | ||
| 668 | ext, | ||
| 669 | lsm, | ||
| 670 | sk_lookup, | ||
| 671 | }; | ||
| 672 | |||
| 673 | pub const AttachType = extern enum(u32) { | ||
| 674 | cgroup_inet_ingress, | ||
| 675 | cgroup_inet_egress, | ||
| 676 | cgroup_inet_sock_create, | ||
| 677 | cgroup_sock_ops, | ||
| 678 | sk_skb_stream_parser, | ||
| 679 | sk_skb_stream_verdict, | ||
| 680 | cgroup_device, | ||
| 681 | sk_msg_verdict, | ||
| 682 | cgroup_inet4_bind, | ||
| 683 | cgroup_inet6_bind, | ||
| 684 | cgroup_inet4_connect, | ||
| 685 | cgroup_inet6_connect, | ||
| 686 | cgroup_inet4_post_bind, | ||
| 687 | cgroup_inet6_post_bind, | ||
| 688 | cgroup_udp4_sendmsg, | ||
| 689 | cgroup_udp6_sendmsg, | ||
| 690 | lirc_mode2, | ||
| 691 | flow_dissector, | ||
| 692 | cgroup_sysctl, | ||
| 693 | cgroup_udp4_recvmsg, | ||
| 694 | cgroup_udp6_recvmsg, | ||
| 695 | cgroup_getsockopt, | ||
| 696 | cgroup_setsockopt, | ||
| 697 | trace_raw_tp, | ||
| 698 | trace_fentry, | ||
| 699 | trace_fexit, | ||
| 700 | modify_return, | ||
| 701 | lsm_mac, | ||
| 702 | trace_iter, | ||
| 703 | cgroup_inet4_getpeername, | ||
| 704 | cgroup_inet6_getpeername, | ||
| 705 | cgroup_inet4_getsockname, | ||
| 706 | cgroup_inet6_getsockname, | ||
| 707 | xdp_devmap, | ||
| 708 | cgroup_inet_sock_release, | ||
| 709 | xdp_cpumap, | ||
| 710 | sk_lookup, | ||
| 711 | xdp, | ||
| 712 | _, | ||
| 713 | }; | ||
| 714 | |||
| 715 | const obj_name_len = 16; | ||
| 716 | /// struct used by Cmd.map_create command | ||
| 717 | pub const MapCreateAttr = extern struct { | ||
| 718 | /// one of MapType | ||
| 719 | map_type: u32, | ||
| 720 | /// size of key in bytes | ||
| 721 | key_size: u32, | ||
| 722 | /// size of value in bytes | ||
| 723 | value_size: u32, | ||
| 724 | /// max number of entries in a map | ||
| 725 | max_entries: u32, | ||
| 726 | /// .map_create related flags | ||
| 727 | map_flags: u32, | ||
| 728 | /// fd pointing to the inner map | ||
| 729 | inner_map_fd: fd_t, | ||
| 730 | /// numa node (effective only if MapCreateFlags.numa_node is set) | ||
| 731 | numa_node: u32, | ||
| 732 | map_name: [obj_name_len]u8, | ||
| 733 | /// ifindex of netdev to create on | ||
| 734 | map_ifindex: u32, | ||
| 735 | /// fd pointing to a BTF type data | ||
| 736 | btf_fd: fd_t, | ||
| 737 | /// BTF type_id of the key | ||
| 738 | btf_key_type_id: u32, | ||
| 739 | /// BTF type_id of the value | ||
| 740 | bpf_value_type_id: u32, | ||
| 741 | /// BTF type_id of a kernel struct stored as the map value | ||
| 742 | btf_vmlinux_value_type_id: u32, | ||
| 743 | }; | ||
| 744 | |||
| 745 | /// struct used by Cmd.map_*_elem commands | ||
| 746 | pub const MapElemAttr = extern struct { | ||
| 747 | map_fd: fd_t, | ||
| 748 | key: u64, | ||
| 749 | result: extern union { | ||
| 750 | value: u64, | ||
| 751 | next_key: u64, | ||
| 752 | }, | ||
| 753 | flags: u64, | ||
| 754 | }; | ||
| 755 | |||
| 756 | /// struct used by Cmd.map_*_batch commands | ||
| 757 | pub const MapBatchAttr = extern struct { | ||
| 758 | /// start batch, NULL to start from beginning | ||
| 759 | in_batch: u64, | ||
| 760 | /// output: next start batch | ||
| 761 | out_batch: u64, | ||
| 762 | keys: u64, | ||
| 763 | values: u64, | ||
| 764 | /// input/output: | ||
| 765 | /// input: # of key/value elements | ||
| 766 | /// output: # of filled elements | ||
| 767 | count: u32, | ||
| 768 | map_fd: fd_t, | ||
| 769 | elem_flags: u64, | ||
| 770 | flags: u64, | ||
| 771 | }; | ||
| 772 | |||
| 773 | /// struct used by Cmd.prog_load command | ||
| 774 | pub const ProgLoadAttr = extern struct { | ||
| 775 | /// one of ProgType | ||
| 776 | prog_type: u32, | ||
| 777 | insn_cnt: u32, | ||
| 778 | insns: u64, | ||
| 779 | license: u64, | ||
| 780 | /// verbosity level of verifier | ||
| 781 | log_level: u32, | ||
| 782 | /// size of user buffer | ||
| 783 | log_size: u32, | ||
| 784 | /// user supplied buffer | ||
| 785 | log_buf: u64, | ||
| 786 | /// not used | ||
| 787 | kern_version: u32, | ||
| 788 | prog_flags: u32, | ||
| 789 | prog_name: [obj_name_len]u8, | ||
| 790 | /// ifindex of netdev to prep for. For some prog types expected attach | ||
| 791 | /// type must be known at load time to verify attach type specific parts | ||
| 792 | /// of prog (context accesses, allowed helpers, etc). | ||
| 793 | prog_ifindex: u32, | ||
| 794 | expected_attach_type: u32, | ||
| 795 | /// fd pointing to BTF type data | ||
| 796 | prog_btf_fd: fd_t, | ||
| 797 | /// userspace bpf_func_info size | ||
| 798 | func_info_rec_size: u32, | ||
| 799 | func_info: u64, | ||
| 800 | /// number of bpf_func_info records | ||
| 801 | func_info_cnt: u32, | ||
| 802 | /// userspace bpf_line_info size | ||
| 803 | line_info_rec_size: u32, | ||
| 804 | line_info: u64, | ||
| 805 | /// number of bpf_line_info records | ||
| 806 | line_info_cnt: u32, | ||
| 807 | /// in-kernel BTF type id to attach to | ||
| 808 | attact_btf_id: u32, | ||
| 809 | /// 0 to attach to vmlinux | ||
| 810 | attach_prog_id: u32, | ||
| 811 | }; | ||
| 812 | |||
| 813 | /// struct used by Cmd.obj_* commands | ||
| 814 | pub const ObjAttr = extern struct { | ||
| 815 | pathname: u64, | ||
| 816 | bpf_fd: fd_t, | ||
| 817 | file_flags: u32, | ||
| 818 | }; | ||
| 819 | |||
| 820 | /// struct used by Cmd.prog_attach/detach commands | ||
| 821 | pub const ProgAttachAttr = extern struct { | ||
| 822 | /// container object to attach to | ||
| 823 | target_fd: fd_t, | ||
| 824 | /// eBPF program to attach | ||
| 825 | attach_bpf_fd: fd_t, | ||
| 826 | attach_type: u32, | ||
| 827 | attach_flags: u32, | ||
| 828 | // TODO: BPF_F_REPLACE flags | ||
| 829 | /// previously attached eBPF program to replace if .replace is used | ||
| 830 | replace_bpf_fd: fd_t, | ||
| 831 | }; | ||
| 832 | |||
| 833 | /// struct used by Cmd.prog_test_run command | ||
| 834 | pub const TestAttr = extern struct { | ||
| 835 | prog_fd: fd_t, | ||
| 836 | retval: u32, | ||
| 837 | /// input: len of data_in | ||
| 838 | data_size_in: u32, | ||
| 839 | /// input/output: len of data_out. returns ENOSPC if data_out is too small. | ||
| 840 | data_size_out: u32, | ||
| 841 | data_in: u64, | ||
| 842 | data_out: u64, | ||
| 843 | repeat: u32, | ||
| 844 | duration: u32, | ||
| 845 | /// input: len of ctx_in | ||
| 846 | ctx_size_in: u32, | ||
| 847 | /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small. | ||
| 848 | ctx_size_out: u32, | ||
| 849 | ctx_in: u64, | ||
| 850 | ctx_out: u64, | ||
| 851 | }; | ||
| 852 | |||
| 853 | /// struct used by Cmd.*_get_*_id commands | ||
| 854 | pub const GetIdAttr = extern struct { | ||
| 855 | id: extern union { | ||
| 856 | start_id: u32, | ||
| 857 | prog_id: u32, | ||
| 858 | map_id: u32, | ||
| 859 | btf_id: u32, | ||
| 860 | link_id: u32, | ||
| 861 | }, | ||
| 862 | next_id: u32, | ||
| 863 | open_flags: u32, | ||
| 864 | }; | ||
| 865 | |||
| 866 | /// struct used by Cmd.obj_get_info_by_fd command | ||
| 867 | pub const InfoAttr = extern struct { | ||
| 868 | bpf_fd: fd_t, | ||
| 869 | info_len: u32, | ||
| 870 | info: u64, | ||
| 871 | }; | ||
| 872 | |||
| 873 | /// struct used by Cmd.prog_query command | ||
| 874 | pub const QueryAttr = extern struct { | ||
| 875 | /// container object to query | ||
| 876 | target_fd: fd_t, | ||
| 877 | attach_type: u32, | ||
| 878 | query_flags: u32, | ||
| 879 | attach_flags: u32, | ||
| 880 | prog_ids: u64, | ||
| 881 | prog_cnt: u32, | ||
| 882 | }; | ||
| 883 | |||
| 884 | /// struct used by Cmd.raw_tracepoint_open command | ||
| 885 | pub const RawTracepointAttr = extern struct { | ||
| 886 | name: u64, | ||
| 887 | prog_fd: fd_t, | ||
| 888 | }; | ||
| 889 | |||
| 890 | /// struct used by Cmd.btf_load command | ||
| 891 | pub const BtfLoadAttr = extern struct { | ||
| 892 | btf: u64, | ||
| 893 | btf_log_buf: u64, | ||
| 894 | btf_size: u32, | ||
| 895 | btf_log_size: u32, | ||
| 896 | btf_log_level: u32, | ||
| 897 | }; | ||
| 898 | |||
| 899 | pub const TaskFdQueryAttr = extern struct { | ||
| 900 | /// input: pid | ||
| 901 | pid: pid_t, | ||
| 902 | /// input: fd | ||
| 903 | fd: fd_t, | ||
| 904 | /// input: flags | ||
| 905 | flags: u32, | ||
| 906 | /// input/output: buf len | ||
| 907 | buf_len: u32, | ||
| 908 | /// input/output: | ||
| 909 | /// tp_name for tracepoint | ||
| 910 | /// symbol for kprobe | ||
| 911 | /// filename for uprobe | ||
| 912 | buf: u64, | ||
| 913 | /// output: prod_id | ||
| 914 | prog_id: u32, | ||
| 915 | /// output: BPF_FD_TYPE | ||
| 916 | fd_type: u32, | ||
| 917 | /// output: probe_offset | ||
| 918 | probe_offset: u64, | ||
| 919 | /// output: probe_addr | ||
| 920 | probe_addr: u64, | ||
| 921 | }; | ||
| 922 | |||
| 923 | /// struct used by Cmd.link_create command | ||
| 924 | pub const LinkCreateAttr = extern struct { | ||
| 925 | /// eBPF program to attach | ||
| 926 | prog_fd: fd_t, | ||
| 927 | /// object to attach to | ||
| 928 | target_fd: fd_t, | ||
| 929 | attach_type: u32, | ||
| 930 | /// extra flags | ||
| 931 | flags: u32, | ||
| 932 | }; | ||
| 933 | |||
| 934 | /// struct used by Cmd.link_update command | ||
| 935 | pub const LinkUpdateAttr = extern struct { | ||
| 936 | link_fd: fd_t, | ||
| 937 | /// new program to update link with | ||
| 938 | new_prog_fd: fd_t, | ||
| 939 | /// extra flags | ||
| 940 | flags: u32, | ||
| 941 | /// expected link's program fd, it is specified only if BPF_F_REPLACE is | ||
| 942 | /// set in flags | ||
| 943 | old_prog_fd: fd_t, | ||
| 944 | }; | ||
| 945 | |||
| 946 | /// struct used by Cmd.enable_stats command | ||
| 947 | pub const EnableStatsAttr = extern struct { | ||
| 948 | type: u32, | ||
| 949 | }; | ||
| 950 | |||
| 951 | /// struct used by Cmd.iter_create command | ||
| 952 | pub const IterCreateAttr = extern struct { | ||
| 953 | link_fd: fd_t, | ||
| 954 | flags: u32, | ||
| 955 | }; | ||
| 956 | |||
| 957 | pub const Attr = extern union { | ||
| 958 | map_create: MapCreateAttr, | ||
| 959 | map_elem: MapElemAttr, | ||
| 960 | map_batch: MapBatchAttr, | ||
| 961 | prog_load: ProgLoadAttr, | ||
| 962 | obj: ObjAttr, | ||
| 963 | prog_attach: ProgAttachAttr, | ||
| 964 | test_run: TestRunAttr, | ||
| 965 | get_id: GetIdAttr, | ||
| 966 | info: InfoAttr, | ||
| 967 | query: QueryAttr, | ||
| 968 | raw_tracepoint: RawTracepointAttr, | ||
| 969 | btf_load: BtfLoadAttr, | ||
| 970 | task_fd_query: TaskFdQueryAttr, | ||
| 971 | link_create: LinkCreateAttr, | ||
| 972 | link_update: LinkUpdateAttr, | ||
| 973 | enable_stats: EnableStatsAttr, | ||
| 974 | iter_create: IterCreateAttr, | ||
| 975 | }; | ||
lib/std/os/linux.zig+1| ... | @@ -29,6 +29,7 @@ pub usingnamespace switch (builtin.arch) { | ... | @@ -29,6 +29,7 @@ pub usingnamespace switch (builtin.arch) { |
| 29 | }; | 29 | }; |
| 30 | pub usingnamespace @import("bits.zig"); | 30 | pub usingnamespace @import("bits.zig"); |
| 31 | pub const tls = @import("linux/tls.zig"); | 31 | pub const tls = @import("linux/tls.zig"); |
| 32 | pub const BPF = @import("linux/bpf.zig"); | ||
| 32 | 33 | ||
| 33 | /// Set by startup code, used by `getauxval`. | 34 | /// Set by startup code, used by `getauxval`. |
| 34 | pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; | 35 | pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; |
lib/std/os/linux/bpf.zig created+973| ... | @@ -0,0 +1,973 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | usingnamespace std.os; | ||
| 7 | const std = @import("../../std.zig"); | ||
| 8 | const expectEqual = std.testing.expectEqual; | ||
| 9 | |||
| 10 | // instruction classes | ||
| 11 | pub const LD = 0x00; | ||
| 12 | pub const LDX = 0x01; | ||
| 13 | pub const ST = 0x02; | ||
| 14 | pub const STX = 0x03; | ||
| 15 | pub const ALU = 0x04; | ||
| 16 | pub const JMP = 0x05; | ||
| 17 | pub const RET = 0x06; | ||
| 18 | pub const MISC = 0x07; | ||
| 19 | |||
| 20 | /// 32-bit | ||
| 21 | pub const W = 0x00; | ||
| 22 | /// 16-bit | ||
| 23 | pub const H = 0x08; | ||
| 24 | /// 8-bit | ||
| 25 | pub const B = 0x10; | ||
| 26 | /// 64-bit | ||
| 27 | pub const DW = 0x18; | ||
| 28 | |||
| 29 | pub const IMM = 0x00; | ||
| 30 | pub const ABS = 0x20; | ||
| 31 | pub const IND = 0x40; | ||
| 32 | pub const MEM = 0x60; | ||
| 33 | pub const LEN = 0x80; | ||
| 34 | pub const MSH = 0xa0; | ||
| 35 | |||
| 36 | // alu fields | ||
| 37 | pub const ADD = 0x00; | ||
| 38 | pub const SUB = 0x10; | ||
| 39 | pub const MUL = 0x20; | ||
| 40 | pub const DIV = 0x30; | ||
| 41 | pub const OR = 0x40; | ||
| 42 | pub const AND = 0x50; | ||
| 43 | pub const LSH = 0x60; | ||
| 44 | pub const RSH = 0x70; | ||
| 45 | pub const NEG = 0x80; | ||
| 46 | pub const MOD = 0x90; | ||
| 47 | pub const XOR = 0xa0; | ||
| 48 | |||
| 49 | // jmp fields | ||
| 50 | pub const JA = 0x00; | ||
| 51 | pub const JEQ = 0x10; | ||
| 52 | pub const JGT = 0x20; | ||
| 53 | pub const JGE = 0x30; | ||
| 54 | pub const JSET = 0x40; | ||
| 55 | |||
| 56 | //#define BPF_SRC(code) ((code) & 0x08) | ||
| 57 | pub const K = 0x00; | ||
| 58 | pub const X = 0x08; | ||
| 59 | |||
| 60 | pub const MAXINSNS = 4096; | ||
| 61 | |||
| 62 | // instruction classes | ||
| 63 | /// jmp mode in word width | ||
| 64 | pub const JMP32 = 0x06; | ||
| 65 | /// alu mode in double word width | ||
| 66 | pub const ALU64 = 0x07; | ||
| 67 | |||
| 68 | // ld/ldx fields | ||
| 69 | /// exclusive add | ||
| 70 | pub const XADD = 0xc0; | ||
| 71 | |||
| 72 | // alu/jmp fields | ||
| 73 | /// mov reg to reg | ||
| 74 | pub const MOV = 0xb0; | ||
| 75 | /// sign extending arithmetic shift right */ | ||
| 76 | pub const ARSH = 0xc0; | ||
| 77 | |||
| 78 | // change endianness of a register | ||
| 79 | /// flags for endianness conversion: | ||
| 80 | pub const END = 0xd0; | ||
| 81 | /// convert to little-endian */ | ||
| 82 | pub const TO_LE = 0x00; | ||
| 83 | /// convert to big-endian | ||
| 84 | pub const TO_BE = 0x08; | ||
| 85 | pub const FROM_LE = TO_LE; | ||
| 86 | pub const FROM_BE = TO_BE; | ||
| 87 | |||
| 88 | // jmp encodings | ||
| 89 | /// jump != * | ||
| 90 | pub const JNE = 0x50; | ||
| 91 | /// LT is unsigned, '<' | ||
| 92 | pub const JLT = 0xa0; | ||
| 93 | /// LE is unsigned, '<=' * | ||
| 94 | pub const JLE = 0xb0; | ||
| 95 | /// SGT is signed '>', GT in x86 | ||
| 96 | pub const JSGT = 0x60; | ||
| 97 | /// SGE is signed '>=', GE in x86 | ||
| 98 | pub const JSGE = 0x70; | ||
| 99 | /// SLT is signed, '<' | ||
| 100 | pub const JSLT = 0xc0; | ||
| 101 | /// SLE is signed, '<=' | ||
| 102 | pub const JSLE = 0xd0; | ||
| 103 | /// function call | ||
| 104 | pub const CALL = 0x80; | ||
| 105 | /// function return | ||
| 106 | pub const EXIT = 0x90; | ||
| 107 | |||
| 108 | /// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the | ||
| 109 | /// program in this cgroup yields to sub-cgroup program. | ||
| 110 | pub const F_ALLOW_OVERRIDE = 0x1; | ||
| 111 | /// Flag for prog_attach command. If a sub-cgroup installs some bpf program, | ||
| 112 | /// that cgroup program gets run in addition to the program in this cgroup. | ||
| 113 | pub const F_ALLOW_MULTI = 0x2; | ||
| 114 | /// Flag for prog_attach command. | ||
| 115 | pub const F_REPLACE = 0x4; | ||
| 116 | |||
| 117 | /// If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the verifier | ||
| 118 | /// will perform strict alignment checking as if the kernel has been built with | ||
| 119 | /// CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, and NET_IP_ALIGN defined to 2. | ||
| 120 | pub const F_STRICT_ALIGNMENT = 0x1; | ||
| 121 | |||
| 122 | /// If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the verifier will | ||
| 123 | /// allow any alignment whatsoever. On platforms with strict alignment | ||
| 124 | /// requirements for loads ands stores (such as sparc and mips) the verifier | ||
| 125 | /// validates that all loads and stores provably follow this requirement. This | ||
| 126 | /// flag turns that checking and enforcement off. | ||
| 127 | /// | ||
| 128 | /// It is mostly used for testing when we want to validate the context and | ||
| 129 | /// memory access aspects of the verifier, but because of an unaligned access | ||
| 130 | /// the alignment check would trigger before the one we are interested in. | ||
| 131 | pub const F_ANY_ALIGNMENT = 0x2; | ||
| 132 | |||
| 133 | /// BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose. | ||
| 134 | /// Verifier does sub-register def/use analysis and identifies instructions | ||
| 135 | /// whose def only matters for low 32-bit, high 32-bit is never referenced later | ||
| 136 | /// through implicit zero extension. Therefore verifier notifies JIT back-ends | ||
| 137 | /// that it is safe to ignore clearing high 32-bit for these instructions. This | ||
| 138 | /// saves some back-ends a lot of code-gen. However such optimization is not | ||
| 139 | /// necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends | ||
| 140 | /// hence hasn't used verifier's analysis result. But, we really want to have a | ||
| 141 | /// way to be able to verify the correctness of the described optimization on | ||
| 142 | /// x86_64 on which testsuites are frequently exercised. | ||
| 143 | /// | ||
| 144 | /// So, this flag is introduced. Once it is set, verifier will randomize high | ||
| 145 | /// 32-bit for those instructions who has been identified as safe to ignore | ||
| 146 | /// them. Then, if verifier is not doing correct analysis, such randomization | ||
| 147 | /// will regress tests to expose bugs. | ||
| 148 | pub const F_TEST_RND_HI32 = 0x4; | ||
| 149 | |||
| 150 | /// When BPF ldimm64's insn[0].src_reg != 0 then this can have two extensions: | ||
| 151 | /// insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE | ||
| 152 | /// insn[0].imm: map fd map fd | ||
| 153 | /// insn[1].imm: 0 offset into value | ||
| 154 | /// insn[0].off: 0 0 | ||
| 155 | /// insn[1].off: 0 0 | ||
| 156 | /// ldimm64 rewrite: address of map address of map[0]+offset | ||
| 157 | /// verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE | ||
| 158 | pub const PSEUDO_MAP_FD = 1; | ||
| 159 | pub const PSEUDO_MAP_VALUE = 2; | ||
| 160 | |||
| 161 | /// when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative | ||
| 162 | /// offset to another bpf function | ||
| 163 | pub const PSEUDO_CALL = 1; | ||
| 164 | |||
| 165 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing | ||
| 166 | pub const ANY = 0; | ||
| 167 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist | ||
| 168 | pub const NOEXIST = 1; | ||
| 169 | /// flag for BPF_MAP_UPDATE_ELEM command. update existing element | ||
| 170 | pub const EXIST = 2; | ||
| 171 | /// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update | ||
| 172 | pub const F_LOCK = 4; | ||
| 173 | |||
| 174 | /// flag for BPF_MAP_CREATE command */ | ||
| 175 | pub const BPF_F_NO_PREALLOC = 0x1; | ||
| 176 | /// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in | ||
| 177 | /// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can | ||
| 178 | /// scale and perform better. Note, the LRU nodes (including free nodes) cannot | ||
| 179 | /// be moved across different LRU lists. | ||
| 180 | pub const BPF_F_NO_COMMON_LRU = 0x2; | ||
| 181 | /// flag for BPF_MAP_CREATE command. Specify numa node during map creation | ||
| 182 | pub const BPF_F_NUMA_NODE = 0x4; | ||
| 183 | /// flag for BPF_MAP_CREATE command. Flags for BPF object read access from | ||
| 184 | /// syscall side | ||
| 185 | pub const BPF_F_RDONLY = 0x8; | ||
| 186 | /// flag for BPF_MAP_CREATE command. Flags for BPF object write access from | ||
| 187 | /// syscall side | ||
| 188 | pub const BPF_F_WRONLY = 0x10; | ||
| 189 | /// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset | ||
| 190 | /// instead of pointer | ||
| 191 | pub const BPF_F_STACK_BUILD_ID = 0x20; | ||
| 192 | /// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This | ||
| 193 | /// should only be used for testing. | ||
| 194 | pub const BPF_F_ZERO_SEED = 0x40; | ||
| 195 | /// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program | ||
| 196 | /// side. | ||
| 197 | pub const BPF_F_RDONLY_PROG = 0x80; | ||
| 198 | /// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program | ||
| 199 | /// side. | ||
| 200 | pub const BPF_F_WRONLY_PROG = 0x100; | ||
| 201 | /// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted | ||
| 202 | /// socket | ||
| 203 | pub const BPF_F_CLONE = 0x200; | ||
| 204 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map | ||
| 205 | pub const BPF_F_MMAPABLE = 0x400; | ||
| 206 | |||
| 207 | /// These values correspond to "syscalls" within the BPF program's environment | ||
| 208 | pub const Helper = enum(i32) { | ||
| 209 | unspec, | ||
| 210 | map_lookup_elem, | ||
| 211 | map_update_elem, | ||
| 212 | map_delete_elem, | ||
| 213 | probe_read, | ||
| 214 | ktime_get_ns, | ||
| 215 | trace_printk, | ||
| 216 | get_prandom_u32, | ||
| 217 | get_smp_processor_id, | ||
| 218 | skb_store_bytes, | ||
| 219 | l3_csum_replace, | ||
| 220 | l4_csum_replace, | ||
| 221 | tail_call, | ||
| 222 | clone_redirect, | ||
| 223 | get_current_pid_tgid, | ||
| 224 | get_current_uid_gid, | ||
| 225 | get_current_comm, | ||
| 226 | get_cgroup_classid, | ||
| 227 | skb_vlan_push, | ||
| 228 | skb_vlan_pop, | ||
| 229 | skb_get_tunnel_key, | ||
| 230 | skb_set_tunnel_key, | ||
| 231 | perf_event_read, | ||
| 232 | redirect, | ||
| 233 | get_route_realm, | ||
| 234 | perf_event_output, | ||
| 235 | skb_load_bytes, | ||
| 236 | get_stackid, | ||
| 237 | csum_diff, | ||
| 238 | skb_get_tunnel_opt, | ||
| 239 | skb_set_tunnel_opt, | ||
| 240 | skb_change_proto, | ||
| 241 | skb_change_type, | ||
| 242 | skb_under_cgroup, | ||
| 243 | get_hash_recalc, | ||
| 244 | get_current_task, | ||
| 245 | probe_write_user, | ||
| 246 | current_task_under_cgroup, | ||
| 247 | skb_change_tail, | ||
| 248 | skb_pull_data, | ||
| 249 | csum_update, | ||
| 250 | set_hash_invalid, | ||
| 251 | get_numa_node_id, | ||
| 252 | skb_change_head, | ||
| 253 | xdp_adjust_head, | ||
| 254 | probe_read_str, | ||
| 255 | get_socket_cookie, | ||
| 256 | get_socket_uid, | ||
| 257 | set_hash, | ||
| 258 | setsockopt, | ||
| 259 | skb_adjust_room, | ||
| 260 | redirect_map, | ||
| 261 | sk_redirect_map, | ||
| 262 | sock_map_update, | ||
| 263 | xdp_adjust_meta, | ||
| 264 | perf_event_read_value, | ||
| 265 | perf_prog_read_value, | ||
| 266 | getsockopt, | ||
| 267 | override_return, | ||
| 268 | sock_ops_cb_flags_set, | ||
| 269 | msg_redirect_map, | ||
| 270 | msg_apply_bytes, | ||
| 271 | msg_cork_bytes, | ||
| 272 | msg_pull_data, | ||
| 273 | bind, | ||
| 274 | xdp_adjust_tail, | ||
| 275 | skb_get_xfrm_state, | ||
| 276 | get_stack, | ||
| 277 | skb_load_bytes_relative, | ||
| 278 | fib_lookup, | ||
| 279 | sock_hash_update, | ||
| 280 | msg_redirect_hash, | ||
| 281 | sk_redirect_hash, | ||
| 282 | lwt_push_encap, | ||
| 283 | lwt_seg6_store_bytes, | ||
| 284 | lwt_seg6_adjust_srh, | ||
| 285 | lwt_seg6_action, | ||
| 286 | rc_repeat, | ||
| 287 | rc_keydown, | ||
| 288 | skb_cgroup_id, | ||
| 289 | get_current_cgroup_id, | ||
| 290 | get_local_storage, | ||
| 291 | sk_select_reuseport, | ||
| 292 | skb_ancestor_cgroup_id, | ||
| 293 | sk_lookup_tcp, | ||
| 294 | sk_lookup_udp, | ||
| 295 | sk_release, | ||
| 296 | map_push_elem, | ||
| 297 | map_pop_elem, | ||
| 298 | map_peek_elem, | ||
| 299 | msg_push_data, | ||
| 300 | msg_pop_data, | ||
| 301 | rc_pointer_rel, | ||
| 302 | spin_lock, | ||
| 303 | spin_unlock, | ||
| 304 | sk_fullsock, | ||
| 305 | tcp_sock, | ||
| 306 | skb_ecn_set_ce, | ||
| 307 | get_listener_sock, | ||
| 308 | skc_lookup_tcp, | ||
| 309 | tcp_check_syncookie, | ||
| 310 | sysctl_get_name, | ||
| 311 | sysctl_get_current_value, | ||
| 312 | sysctl_get_new_value, | ||
| 313 | sysctl_set_new_value, | ||
| 314 | strtol, | ||
| 315 | strtoul, | ||
| 316 | sk_storage_get, | ||
| 317 | sk_storage_delete, | ||
| 318 | send_signal, | ||
| 319 | tcp_gen_syncookie, | ||
| 320 | skb_output, | ||
| 321 | probe_read_user, | ||
| 322 | probe_read_kernel, | ||
| 323 | probe_read_user_str, | ||
| 324 | probe_read_kernel_str, | ||
| 325 | tcp_send_ack, | ||
| 326 | send_signal_thread, | ||
| 327 | jiffies64, | ||
| 328 | _, | ||
| 329 | }; | ||
| 330 | |||
| 331 | /// a single BPF instruction | ||
| 332 | pub const Insn = packed struct { | ||
| 333 | code: u8, | ||
| 334 | dst: u4, | ||
| 335 | src: u4, | ||
| 336 | off: i16, | ||
| 337 | imm: i32, | ||
| 338 | |||
| 339 | /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack | ||
| 340 | /// frame | ||
| 341 | pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; | ||
| 342 | const Source = packed enum(u1) { reg, imm }; | ||
| 343 | const AluOp = packed enum(u8) { | ||
| 344 | add = ADD, | ||
| 345 | sub = SUB, | ||
| 346 | mul = MUL, | ||
| 347 | div = DIV, | ||
| 348 | op_or = OR, | ||
| 349 | op_and = AND, | ||
| 350 | lsh = LSH, | ||
| 351 | rsh = RSH, | ||
| 352 | neg = NEG, | ||
| 353 | mod = MOD, | ||
| 354 | xor = XOR, | ||
| 355 | mov = MOV, | ||
| 356 | }; | ||
| 357 | |||
| 358 | pub const Size = packed enum(u8) { | ||
| 359 | byte = B, | ||
| 360 | half_word = H, | ||
| 361 | word = W, | ||
| 362 | double_word = DW, | ||
| 363 | }; | ||
| 364 | |||
| 365 | const JmpOp = packed enum(u8) { | ||
| 366 | ja = JA, | ||
| 367 | jeq = JEQ, | ||
| 368 | jgt = JGT, | ||
| 369 | jge = JGE, | ||
| 370 | jset = JSET, | ||
| 371 | }; | ||
| 372 | |||
| 373 | const ImmOrReg = union(Source) { | ||
| 374 | imm: i32, | ||
| 375 | reg: Reg, | ||
| 376 | }; | ||
| 377 | |||
| 378 | fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn { | ||
| 379 | const imm_or_reg = if (@typeInfo(@TypeOf(src)) == .EnumLiteral) | ||
| 380 | ImmOrReg{ .reg = @as(Reg, src) } | ||
| 381 | else | ||
| 382 | ImmOrReg{ .imm = src }; | ||
| 383 | |||
| 384 | const src_type = switch (imm_or_reg) { | ||
| 385 | .imm => K, | ||
| 386 | .reg => X, | ||
| 387 | }; | ||
| 388 | |||
| 389 | return Insn{ | ||
| 390 | .code = code | src_type, | ||
| 391 | .dst = @enumToInt(dst), | ||
| 392 | .src = switch (imm_or_reg) { | ||
| 393 | .imm => 0, | ||
| 394 | .reg => |r| @enumToInt(r), | ||
| 395 | }, | ||
| 396 | .off = off, | ||
| 397 | .imm = switch (imm_or_reg) { | ||
| 398 | .imm => |i| i, | ||
| 399 | .reg => 0, | ||
| 400 | }, | ||
| 401 | }; | ||
| 402 | } | ||
| 403 | |||
| 404 | fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn { | ||
| 405 | const width_bitfield = switch (width) { | ||
| 406 | 32 => ALU, | ||
| 407 | 64 => ALU64, | ||
| 408 | else => @compileError("width must be 32 or 64"), | ||
| 409 | }; | ||
| 410 | |||
| 411 | return imm_reg(width_bitfield | @enumToInt(op), dst, src, 0); | ||
| 412 | } | ||
| 413 | |||
| 414 | pub fn mov(dst: Reg, src: anytype) Insn { | ||
| 415 | return alu(64, .mov, dst, src); | ||
| 416 | } | ||
| 417 | |||
| 418 | pub fn add(dst: Reg, src: anytype) Insn { | ||
| 419 | return alu(64, .add, dst, src); | ||
| 420 | } | ||
| 421 | |||
| 422 | fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn { | ||
| 423 | return imm_reg(JMP | @enumToInt(op), dst, src, off); | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn jeq(dst: Reg, src: anytype, off: i16) Insn { | ||
| 427 | return jmp(.jeq, dst, src, off); | ||
| 428 | } | ||
| 429 | |||
| 430 | pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn { | ||
| 431 | return Insn{ | ||
| 432 | .code = STX | @enumToInt(size) | MEM, | ||
| 433 | .dst = @enumToInt(dst), | ||
| 434 | .src = @enumToInt(src), | ||
| 435 | .off = off, | ||
| 436 | .imm = 0, | ||
| 437 | }; | ||
| 438 | } | ||
| 439 | |||
| 440 | pub fn xadd(dst: Reg, src: Reg) Insn { | ||
| 441 | return Insn{ | ||
| 442 | .code = STX | XADD | DW, | ||
| 443 | .dst = @enumToInt(dst), | ||
| 444 | .src = @enumToInt(src), | ||
| 445 | .off = 0, | ||
| 446 | .imm = 0, | ||
| 447 | }; | ||
| 448 | } | ||
| 449 | |||
| 450 | /// direct packet access, R0 = *(uint *)(skb->data + imm32) | ||
| 451 | pub fn ld_abs(size: Size, imm: i32) Insn { | ||
| 452 | return Insn{ | ||
| 453 | .code = LD | @enumToInt(size) | ABS, | ||
| 454 | .dst = 0, | ||
| 455 | .src = 0, | ||
| 456 | .off = 0, | ||
| 457 | .imm = imm, | ||
| 458 | }; | ||
| 459 | } | ||
| 460 | |||
| 461 | fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn { | ||
| 462 | return Insn{ | ||
| 463 | .code = LD | DW | IMM, | ||
| 464 | .dst = @enumToInt(dst), | ||
| 465 | .src = @enumToInt(src), | ||
| 466 | .off = 0, | ||
| 467 | .imm = @intCast(i32, @truncate(u32, imm)), | ||
| 468 | }; | ||
| 469 | } | ||
| 470 | |||
| 471 | fn ld_imm_impl2(imm: u64) Insn { | ||
| 472 | return Insn{ | ||
| 473 | .code = 0, | ||
| 474 | .dst = 0, | ||
| 475 | .src = 0, | ||
| 476 | .off = 0, | ||
| 477 | .imm = @intCast(i32, @truncate(u32, imm >> 32)), | ||
| 478 | }; | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn { | ||
| 482 | return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd)); | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn ld_map_fd2(map_fd: fd_t) Insn { | ||
| 486 | return ld_imm_impl2(@intCast(u64, map_fd)); | ||
| 487 | } | ||
| 488 | |||
| 489 | pub fn call(helper: Helper) Insn { | ||
| 490 | return Insn{ | ||
| 491 | .code = JMP | CALL, | ||
| 492 | .dst = 0, | ||
| 493 | .src = 0, | ||
| 494 | .off = 0, | ||
| 495 | .imm = @enumToInt(helper), | ||
| 496 | }; | ||
| 497 | } | ||
| 498 | |||
| 499 | /// exit BPF program | ||
| 500 | pub fn exit() Insn { | ||
| 501 | return Insn{ | ||
| 502 | .code = JMP | EXIT, | ||
| 503 | .dst = 0, | ||
| 504 | .src = 0, | ||
| 505 | .off = 0, | ||
| 506 | .imm = 0, | ||
| 507 | }; | ||
| 508 | } | ||
| 509 | }; | ||
| 510 | |||
| 511 | fn expect_insn(insn: Insn, val: u64) void { | ||
| 512 | expectEqual(@bitCast(u64, insn), val); | ||
| 513 | } | ||
| 514 | |||
| 515 | test "insn bitsize" { | ||
| 516 | expectEqual(@bitSizeOf(Insn), 64); | ||
| 517 | } | ||
| 518 | |||
| 519 | // mov instructions | ||
| 520 | test "mov imm" { | ||
| 521 | expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7); | ||
| 522 | } | ||
| 523 | |||
| 524 | test "mov reg" { | ||
| 525 | expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf); | ||
| 526 | } | ||
| 527 | |||
| 528 | // alu instructions | ||
| 529 | test "add imm" { | ||
| 530 | expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207); | ||
| 531 | } | ||
| 532 | |||
| 533 | // ld instructions | ||
| 534 | test "ld_abs" { | ||
| 535 | expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030); | ||
| 536 | } | ||
| 537 | |||
| 538 | test "ld_map_fd" { | ||
| 539 | expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118); | ||
| 540 | expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000); | ||
| 541 | } | ||
| 542 | |||
| 543 | // st instructions | ||
| 544 | test "stx_mem" { | ||
| 545 | expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63); | ||
| 546 | } | ||
| 547 | |||
| 548 | test "xadd" { | ||
| 549 | expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db); | ||
| 550 | } | ||
| 551 | |||
| 552 | // jmp instructions | ||
| 553 | test "jeq imm" { | ||
| 554 | expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015); | ||
| 555 | } | ||
| 556 | |||
| 557 | // other instructions | ||
| 558 | test "call" { | ||
| 559 | expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085); | ||
| 560 | } | ||
| 561 | |||
| 562 | test "exit" { | ||
| 563 | expect_insn(Insn.exit(), 0x0000000000000095); | ||
| 564 | } | ||
| 565 | |||
| 566 | pub const Cmd = extern enum(usize) { | ||
| 567 | map_create, | ||
| 568 | map_lookup_elem, | ||
| 569 | map_update_elem, | ||
| 570 | map_delete_elem, | ||
| 571 | map_get_next_key, | ||
| 572 | prog_load, | ||
| 573 | obj_pin, | ||
| 574 | obj_get, | ||
| 575 | prog_attach, | ||
| 576 | prog_detach, | ||
| 577 | prog_test_run, | ||
| 578 | prog_get_next_id, | ||
| 579 | map_get_next_id, | ||
| 580 | prog_get_fd_by_id, | ||
| 581 | map_get_fd_by_id, | ||
| 582 | obj_get_info_by_fd, | ||
| 583 | prog_query, | ||
| 584 | raw_tracepoint_open, | ||
| 585 | btf_load, | ||
| 586 | btf_get_fd_by_id, | ||
| 587 | task_fd_query, | ||
| 588 | map_lookup_and_delete_elem, | ||
| 589 | map_freeze, | ||
| 590 | btf_get_next_id, | ||
| 591 | map_lookup_batch, | ||
| 592 | map_lookup_and_delete_batch, | ||
| 593 | map_update_batch, | ||
| 594 | map_delete_batch, | ||
| 595 | link_create, | ||
| 596 | link_update, | ||
| 597 | link_get_fd_by_id, | ||
| 598 | link_get_next_id, | ||
| 599 | enable_stats, | ||
| 600 | iter_create, | ||
| 601 | link_detach, | ||
| 602 | _, | ||
| 603 | }; | ||
| 604 | |||
| 605 | pub const MapType = extern enum(u32) { | ||
| 606 | unspec, | ||
| 607 | hash, | ||
| 608 | array, | ||
| 609 | prog_array, | ||
| 610 | perf_event_array, | ||
| 611 | percpu_hash, | ||
| 612 | percpu_array, | ||
| 613 | stack_trace, | ||
| 614 | cgroup_array, | ||
| 615 | lru_hash, | ||
| 616 | lru_percpu_hash, | ||
| 617 | lpm_trie, | ||
| 618 | array_of_maps, | ||
| 619 | hash_of_maps, | ||
| 620 | devmap, | ||
| 621 | sockmap, | ||
| 622 | cpumap, | ||
| 623 | xskmap, | ||
| 624 | sockhash, | ||
| 625 | cgroup_storage, | ||
| 626 | reuseport_sockarray, | ||
| 627 | percpu_cgroup_storage, | ||
| 628 | queue, | ||
| 629 | stack, | ||
| 630 | sk_storage, | ||
| 631 | devmap_hash, | ||
| 632 | struct_ops, | ||
| 633 | ringbuf, | ||
| 634 | _, | ||
| 635 | }; | ||
| 636 | |||
| 637 | pub const ProgType = extern enum(u32) { | ||
| 638 | unspec, | ||
| 639 | socket_filter, | ||
| 640 | kprobe, | ||
| 641 | sched_cls, | ||
| 642 | sched_act, | ||
| 643 | tracepoint, | ||
| 644 | xdp, | ||
| 645 | perf_event, | ||
| 646 | cgroup_skb, | ||
| 647 | cgroup_sock, | ||
| 648 | lwt_in, | ||
| 649 | lwt_out, | ||
| 650 | lwt_xmit, | ||
| 651 | sock_ops, | ||
| 652 | sk_skb, | ||
| 653 | cgroup_device, | ||
| 654 | sk_msg, | ||
| 655 | raw_tracepoint, | ||
| 656 | cgroup_sock_addr, | ||
| 657 | lwt_seg6local, | ||
| 658 | lirc_mode2, | ||
| 659 | sk_reuseport, | ||
| 660 | flow_dissector, | ||
| 661 | cgroup_sysctl, | ||
| 662 | raw_tracepoint_writable, | ||
| 663 | cgroup_sockopt, | ||
| 664 | tracing, | ||
| 665 | struct_ops, | ||
| 666 | ext, | ||
| 667 | lsm, | ||
| 668 | sk_lookup, | ||
| 669 | }; | ||
| 670 | |||
| 671 | pub const AttachType = extern enum(u32) { | ||
| 672 | cgroup_inet_ingress, | ||
| 673 | cgroup_inet_egress, | ||
| 674 | cgroup_inet_sock_create, | ||
| 675 | cgroup_sock_ops, | ||
| 676 | sk_skb_stream_parser, | ||
| 677 | sk_skb_stream_verdict, | ||
| 678 | cgroup_device, | ||
| 679 | sk_msg_verdict, | ||
| 680 | cgroup_inet4_bind, | ||
| 681 | cgroup_inet6_bind, | ||
| 682 | cgroup_inet4_connect, | ||
| 683 | cgroup_inet6_connect, | ||
| 684 | cgroup_inet4_post_bind, | ||
| 685 | cgroup_inet6_post_bind, | ||
| 686 | cgroup_udp4_sendmsg, | ||
| 687 | cgroup_udp6_sendmsg, | ||
| 688 | lirc_mode2, | ||
| 689 | flow_dissector, | ||
| 690 | cgroup_sysctl, | ||
| 691 | cgroup_udp4_recvmsg, | ||
| 692 | cgroup_udp6_recvmsg, | ||
| 693 | cgroup_getsockopt, | ||
| 694 | cgroup_setsockopt, | ||
| 695 | trace_raw_tp, | ||
| 696 | trace_fentry, | ||
| 697 | trace_fexit, | ||
| 698 | modify_return, | ||
| 699 | lsm_mac, | ||
| 700 | trace_iter, | ||
| 701 | cgroup_inet4_getpeername, | ||
| 702 | cgroup_inet6_getpeername, | ||
| 703 | cgroup_inet4_getsockname, | ||
| 704 | cgroup_inet6_getsockname, | ||
| 705 | xdp_devmap, | ||
| 706 | cgroup_inet_sock_release, | ||
| 707 | xdp_cpumap, | ||
| 708 | sk_lookup, | ||
| 709 | xdp, | ||
| 710 | _, | ||
| 711 | }; | ||
| 712 | |||
| 713 | const obj_name_len = 16; | ||
| 714 | /// struct used by Cmd.map_create command | ||
| 715 | pub const MapCreateAttr = extern struct { | ||
| 716 | /// one of MapType | ||
| 717 | map_type: u32, | ||
| 718 | /// size of key in bytes | ||
| 719 | key_size: u32, | ||
| 720 | /// size of value in bytes | ||
| 721 | value_size: u32, | ||
| 722 | /// max number of entries in a map | ||
| 723 | max_entries: u32, | ||
| 724 | /// .map_create related flags | ||
| 725 | map_flags: u32, | ||
| 726 | /// fd pointing to the inner map | ||
| 727 | inner_map_fd: fd_t, | ||
| 728 | /// numa node (effective only if MapCreateFlags.numa_node is set) | ||
| 729 | numa_node: u32, | ||
| 730 | map_name: [obj_name_len]u8, | ||
| 731 | /// ifindex of netdev to create on | ||
| 732 | map_ifindex: u32, | ||
| 733 | /// fd pointing to a BTF type data | ||
| 734 | btf_fd: fd_t, | ||
| 735 | /// BTF type_id of the key | ||
| 736 | btf_key_type_id: u32, | ||
| 737 | /// BTF type_id of the value | ||
| 738 | bpf_value_type_id: u32, | ||
| 739 | /// BTF type_id of a kernel struct stored as the map value | ||
| 740 | btf_vmlinux_value_type_id: u32, | ||
| 741 | }; | ||
| 742 | |||
| 743 | /// struct used by Cmd.map_*_elem commands | ||
| 744 | pub const MapElemAttr = extern struct { | ||
| 745 | map_fd: fd_t, | ||
| 746 | key: u64, | ||
| 747 | result: extern union { | ||
| 748 | value: u64, | ||
| 749 | next_key: u64, | ||
| 750 | }, | ||
| 751 | flags: u64, | ||
| 752 | }; | ||
| 753 | |||
| 754 | /// struct used by Cmd.map_*_batch commands | ||
| 755 | pub const MapBatchAttr = extern struct { | ||
| 756 | /// start batch, NULL to start from beginning | ||
| 757 | in_batch: u64, | ||
| 758 | /// output: next start batch | ||
| 759 | out_batch: u64, | ||
| 760 | keys: u64, | ||
| 761 | values: u64, | ||
| 762 | /// input/output: | ||
| 763 | /// input: # of key/value elements | ||
| 764 | /// output: # of filled elements | ||
| 765 | count: u32, | ||
| 766 | map_fd: fd_t, | ||
| 767 | elem_flags: u64, | ||
| 768 | flags: u64, | ||
| 769 | }; | ||
| 770 | |||
| 771 | /// struct used by Cmd.prog_load command | ||
| 772 | pub const ProgLoadAttr = extern struct { | ||
| 773 | /// one of ProgType | ||
| 774 | prog_type: u32, | ||
| 775 | insn_cnt: u32, | ||
| 776 | insns: u64, | ||
| 777 | license: u64, | ||
| 778 | /// verbosity level of verifier | ||
| 779 | log_level: u32, | ||
| 780 | /// size of user buffer | ||
| 781 | log_size: u32, | ||
| 782 | /// user supplied buffer | ||
| 783 | log_buf: u64, | ||
| 784 | /// not used | ||
| 785 | kern_version: u32, | ||
| 786 | prog_flags: u32, | ||
| 787 | prog_name: [obj_name_len]u8, | ||
| 788 | /// ifindex of netdev to prep for. For some prog types expected attach | ||
| 789 | /// type must be known at load time to verify attach type specific parts | ||
| 790 | /// of prog (context accesses, allowed helpers, etc). | ||
| 791 | prog_ifindex: u32, | ||
| 792 | expected_attach_type: u32, | ||
| 793 | /// fd pointing to BTF type data | ||
| 794 | prog_btf_fd: fd_t, | ||
| 795 | /// userspace bpf_func_info size | ||
| 796 | func_info_rec_size: u32, | ||
| 797 | func_info: u64, | ||
| 798 | /// number of bpf_func_info records | ||
| 799 | func_info_cnt: u32, | ||
| 800 | /// userspace bpf_line_info size | ||
| 801 | line_info_rec_size: u32, | ||
| 802 | line_info: u64, | ||
| 803 | /// number of bpf_line_info records | ||
| 804 | line_info_cnt: u32, | ||
| 805 | /// in-kernel BTF type id to attach to | ||
| 806 | attact_btf_id: u32, | ||
| 807 | /// 0 to attach to vmlinux | ||
| 808 | attach_prog_id: u32, | ||
| 809 | }; | ||
| 810 | |||
| 811 | /// struct used by Cmd.obj_* commands | ||
| 812 | pub const ObjAttr = extern struct { | ||
| 813 | pathname: u64, | ||
| 814 | bpf_fd: fd_t, | ||
| 815 | file_flags: u32, | ||
| 816 | }; | ||
| 817 | |||
| 818 | /// struct used by Cmd.prog_attach/detach commands | ||
| 819 | pub const ProgAttachAttr = extern struct { | ||
| 820 | /// container object to attach to | ||
| 821 | target_fd: fd_t, | ||
| 822 | /// eBPF program to attach | ||
| 823 | attach_bpf_fd: fd_t, | ||
| 824 | attach_type: u32, | ||
| 825 | attach_flags: u32, | ||
| 826 | // TODO: BPF_F_REPLACE flags | ||
| 827 | /// previously attached eBPF program to replace if .replace is used | ||
| 828 | replace_bpf_fd: fd_t, | ||
| 829 | }; | ||
| 830 | |||
| 831 | /// struct used by Cmd.prog_test_run command | ||
| 832 | pub const TestAttr = extern struct { | ||
| 833 | prog_fd: fd_t, | ||
| 834 | retval: u32, | ||
| 835 | /// input: len of data_in | ||
| 836 | data_size_in: u32, | ||
| 837 | /// input/output: len of data_out. returns ENOSPC if data_out is too small. | ||
| 838 | data_size_out: u32, | ||
| 839 | data_in: u64, | ||
| 840 | data_out: u64, | ||
| 841 | repeat: u32, | ||
| 842 | duration: u32, | ||
| 843 | /// input: len of ctx_in | ||
| 844 | ctx_size_in: u32, | ||
| 845 | /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small. | ||
| 846 | ctx_size_out: u32, | ||
| 847 | ctx_in: u64, | ||
| 848 | ctx_out: u64, | ||
| 849 | }; | ||
| 850 | |||
| 851 | /// struct used by Cmd.*_get_*_id commands | ||
| 852 | pub const GetIdAttr = extern struct { | ||
| 853 | id: extern union { | ||
| 854 | start_id: u32, | ||
| 855 | prog_id: u32, | ||
| 856 | map_id: u32, | ||
| 857 | btf_id: u32, | ||
| 858 | link_id: u32, | ||
| 859 | }, | ||
| 860 | next_id: u32, | ||
| 861 | open_flags: u32, | ||
| 862 | }; | ||
| 863 | |||
| 864 | /// struct used by Cmd.obj_get_info_by_fd command | ||
| 865 | pub const InfoAttr = extern struct { | ||
| 866 | bpf_fd: fd_t, | ||
| 867 | info_len: u32, | ||
| 868 | info: u64, | ||
| 869 | }; | ||
| 870 | |||
| 871 | /// struct used by Cmd.prog_query command | ||
| 872 | pub const QueryAttr = extern struct { | ||
| 873 | /// container object to query | ||
| 874 | target_fd: fd_t, | ||
| 875 | attach_type: u32, | ||
| 876 | query_flags: u32, | ||
| 877 | attach_flags: u32, | ||
| 878 | prog_ids: u64, | ||
| 879 | prog_cnt: u32, | ||
| 880 | }; | ||
| 881 | |||
| 882 | /// struct used by Cmd.raw_tracepoint_open command | ||
| 883 | pub const RawTracepointAttr = extern struct { | ||
| 884 | name: u64, | ||
| 885 | prog_fd: fd_t, | ||
| 886 | }; | ||
| 887 | |||
| 888 | /// struct used by Cmd.btf_load command | ||
| 889 | pub const BtfLoadAttr = extern struct { | ||
| 890 | btf: u64, | ||
| 891 | btf_log_buf: u64, | ||
| 892 | btf_size: u32, | ||
| 893 | btf_log_size: u32, | ||
| 894 | btf_log_level: u32, | ||
| 895 | }; | ||
| 896 | |||
| 897 | pub const TaskFdQueryAttr = extern struct { | ||
| 898 | /// input: pid | ||
| 899 | pid: pid_t, | ||
| 900 | /// input: fd | ||
| 901 | fd: fd_t, | ||
| 902 | /// input: flags | ||
| 903 | flags: u32, | ||
| 904 | /// input/output: buf len | ||
| 905 | buf_len: u32, | ||
| 906 | /// input/output: | ||
| 907 | /// tp_name for tracepoint | ||
| 908 | /// symbol for kprobe | ||
| 909 | /// filename for uprobe | ||
| 910 | buf: u64, | ||
| 911 | /// output: prod_id | ||
| 912 | prog_id: u32, | ||
| 913 | /// output: BPF_FD_TYPE | ||
| 914 | fd_type: u32, | ||
| 915 | /// output: probe_offset | ||
| 916 | probe_offset: u64, | ||
| 917 | /// output: probe_addr | ||
| 918 | probe_addr: u64, | ||
| 919 | }; | ||
| 920 | |||
| 921 | /// struct used by Cmd.link_create command | ||
| 922 | pub const LinkCreateAttr = extern struct { | ||
| 923 | /// eBPF program to attach | ||
| 924 | prog_fd: fd_t, | ||
| 925 | /// object to attach to | ||
| 926 | target_fd: fd_t, | ||
| 927 | attach_type: u32, | ||
| 928 | /// extra flags | ||
| 929 | flags: u32, | ||
| 930 | }; | ||
| 931 | |||
| 932 | /// struct used by Cmd.link_update command | ||
| 933 | pub const LinkUpdateAttr = extern struct { | ||
| 934 | link_fd: fd_t, | ||
| 935 | /// new program to update link with | ||
| 936 | new_prog_fd: fd_t, | ||
| 937 | /// extra flags | ||
| 938 | flags: u32, | ||
| 939 | /// expected link's program fd, it is specified only if BPF_F_REPLACE is | ||
| 940 | /// set in flags | ||
| 941 | old_prog_fd: fd_t, | ||
| 942 | }; | ||
| 943 | |||
| 944 | /// struct used by Cmd.enable_stats command | ||
| 945 | pub const EnableStatsAttr = extern struct { | ||
| 946 | type: u32, | ||
| 947 | }; | ||
| 948 | |||
| 949 | /// struct used by Cmd.iter_create command | ||
| 950 | pub const IterCreateAttr = extern struct { | ||
| 951 | link_fd: fd_t, | ||
| 952 | flags: u32, | ||
| 953 | }; | ||
| 954 | |||
| 955 | pub const Attr = extern union { | ||
| 956 | map_create: MapCreateAttr, | ||
| 957 | map_elem: MapElemAttr, | ||
| 958 | map_batch: MapBatchAttr, | ||
| 959 | prog_load: ProgLoadAttr, | ||
| 960 | obj: ObjAttr, | ||
| 961 | prog_attach: ProgAttachAttr, | ||
| 962 | test_run: TestRunAttr, | ||
| 963 | get_id: GetIdAttr, | ||
| 964 | info: InfoAttr, | ||
| 965 | query: QueryAttr, | ||
| 966 | raw_tracepoint: RawTracepointAttr, | ||
| 967 | btf_load: BtfLoadAttr, | ||
| 968 | task_fd_query: TaskFdQueryAttr, | ||
| 969 | link_create: LinkCreateAttr, | ||
| 970 | link_update: LinkUpdateAttr, | ||
| 971 | enable_stats: EnableStatsAttr, | ||
| 972 | iter_create: IterCreateAttr, | ||
| 973 | }; | ||
lib/std/special/init-exe/build.zig-5| ... | @@ -1,8 +1,3 @@ | ... | @@ -1,8 +1,3 @@ |
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 7 | 2 | ||
| 8 | pub fn build(b: *Builder) void { | 3 | pub fn build(b: *Builder) void { |
lib/std/special/init-exe/src/main.zig-5| ... | @@ -1,8 +1,3 @@ | ... | @@ -1,8 +1,3 @@ |
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | 1 | const std = @import("std"); |
| 7 | 2 | ||
| 8 | pub fn main() anyerror!void { | 3 | pub fn main() anyerror!void { |
lib/std/special/init-lib/build.zig-5| ... | @@ -1,8 +1,3 @@ | ... | @@ -1,8 +1,3 @@ |
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 7 | 2 | ||
| 8 | pub fn build(b: *Builder) void { | 3 | pub fn build(b: *Builder) void { |
lib/std/special/init-lib/src/main.zig-5| ... | @@ -1,8 +1,3 @@ | ... | @@ -1,8 +1,3 @@ |
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | 1 | const std = @import("std"); |
| 7 | const testing = std.testing; | 2 | const testing = std.testing; |
| 8 | 3 |
lib/std/target.zig+18-8| ... | @@ -96,8 +96,12 @@ pub const Target = struct { | ... | @@ -96,8 +96,12 @@ pub const Target = struct { |
| 96 | win10_rs4 = 0x0A000005, | 96 | win10_rs4 = 0x0A000005, |
| 97 | win10_rs5 = 0x0A000006, | 97 | win10_rs5 = 0x0A000006, |
| 98 | win10_19h1 = 0x0A000007, | 98 | win10_19h1 = 0x0A000007, |
| 99 | win10_20h1 = 0x0A000008, | ||
| 99 | _, | 100 | _, |
| 100 | 101 | ||
| 102 | /// Latest Windows version that the Zig Standard Library is aware of | ||
| 103 | pub const latest = WindowsVersion.win10_20h1; | ||
| 104 | |||
| 101 | pub const Range = struct { | 105 | pub const Range = struct { |
| 102 | min: WindowsVersion, | 106 | min: WindowsVersion, |
| 103 | max: WindowsVersion, | 107 | max: WindowsVersion, |
| ... | @@ -124,18 +128,17 @@ pub const Target = struct { | ... | @@ -124,18 +128,17 @@ pub const Target = struct { |
| 124 | out_stream: anytype, | 128 | out_stream: anytype, |
| 125 | ) !void { | 129 | ) !void { |
| 126 | if (fmt.len > 0 and fmt[0] == 's') { | 130 | if (fmt.len > 0 and fmt[0] == 's') { |
| 127 | if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) { | 131 | if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { |
| 128 | try std.fmt.format(out_stream, ".{}", .{@tagName(self)}); | 132 | try std.fmt.format(out_stream, ".{}", .{@tagName(self)}); |
| 129 | } else { | 133 | } else { |
| 130 | try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, {})", .{@enumToInt(self)}); | 134 | // TODO this code path breaks zig triples, but it is used in `builtin` |
| 135 | try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, 0x{X:0>8})", .{@enumToInt(self)}); | ||
| 131 | } | 136 | } |
| 132 | } else { | 137 | } else { |
| 133 | if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) { | 138 | if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { |
| 134 | try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)}); | 139 | try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)}); |
| 135 | } else { | 140 | } else { |
| 136 | try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())}); | 141 | try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)}); |
| 137 | try std.fmt.format(out_stream, "{}", .{@enumToInt(self)}); | ||
| 138 | try out_stream.writeAll(")"); | ||
| 139 | } | 142 | } |
| 140 | } | 143 | } |
| 141 | } | 144 | } |
| ... | @@ -280,7 +283,7 @@ pub const Target = struct { | ... | @@ -280,7 +283,7 @@ pub const Target = struct { |
| 280 | .windows => return .{ | 283 | .windows => return .{ |
| 281 | .windows = .{ | 284 | .windows = .{ |
| 282 | .min = .win8_1, | 285 | .min = .win8_1, |
| 283 | .max = .win10_19h1, | 286 | .max = WindowsVersion.latest, |
| 284 | }, | 287 | }, |
| 285 | }, | 288 | }, |
| 286 | } | 289 | } |
| ... | @@ -663,6 +666,9 @@ pub const Target = struct { | ... | @@ -663,6 +666,9 @@ pub const Target = struct { |
| 663 | renderscript32, | 666 | renderscript32, |
| 664 | renderscript64, | 667 | renderscript64, |
| 665 | ve, | 668 | ve, |
| 669 | // Stage1 currently assumes that architectures above this comment | ||
| 670 | // map one-to-one with the ZigLLVM_ArchType enum. | ||
| 671 | spu_2, | ||
| 666 | 672 | ||
| 667 | pub fn isARM(arch: Arch) bool { | 673 | pub fn isARM(arch: Arch) bool { |
| 668 | return switch (arch) { | 674 | return switch (arch) { |
| ... | @@ -761,6 +767,7 @@ pub const Target = struct { | ... | @@ -761,6 +767,7 @@ pub const Target = struct { |
| 761 | .sparcv9 => ._SPARCV9, | 767 | .sparcv9 => ._SPARCV9, |
| 762 | .s390x => ._S390, | 768 | .s390x => ._S390, |
| 763 | .ve => ._NONE, | 769 | .ve => ._NONE, |
| 770 | .spu_2 => ._SPU_2, | ||
| 764 | }; | 771 | }; |
| 765 | } | 772 | } |
| 766 | 773 | ||
| ... | @@ -803,6 +810,7 @@ pub const Target = struct { | ... | @@ -803,6 +810,7 @@ pub const Target = struct { |
| 803 | .renderscript64, | 810 | .renderscript64, |
| 804 | .shave, | 811 | .shave, |
| 805 | .ve, | 812 | .ve, |
| 813 | .spu_2, | ||
| 806 | => .Little, | 814 | => .Little, |
| 807 | 815 | ||
| 808 | .arc, | 816 | .arc, |
| ... | @@ -827,6 +835,7 @@ pub const Target = struct { | ... | @@ -827,6 +835,7 @@ pub const Target = struct { |
| 827 | switch (arch) { | 835 | switch (arch) { |
| 828 | .avr, | 836 | .avr, |
| 829 | .msp430, | 837 | .msp430, |
| 838 | .spu_2, | ||
| 830 | => return 16, | 839 | => return 16, |
| 831 | 840 | ||
| 832 | .arc, | 841 | .arc, |
| ... | @@ -1317,12 +1326,13 @@ pub const Target = struct { | ... | @@ -1317,12 +1326,13 @@ pub const Target = struct { |
| 1317 | .bpfeb, | 1326 | .bpfeb, |
| 1318 | .nvptx, | 1327 | .nvptx, |
| 1319 | .nvptx64, | 1328 | .nvptx64, |
| 1329 | .spu_2, | ||
| 1330 | .avr, | ||
| 1320 | => return result, | 1331 | => return result, |
| 1321 | 1332 | ||
| 1322 | // TODO go over each item in this list and either move it to the above list, or | 1333 | // TODO go over each item in this list and either move it to the above list, or |
| 1323 | // implement the standard dynamic linker path code for it. | 1334 | // implement the standard dynamic linker path code for it. |
| 1324 | .arc, | 1335 | .arc, |
| 1325 | .avr, | ||
| 1326 | .hexagon, | 1336 | .hexagon, |
| 1327 | .msp430, | 1337 | .msp430, |
| 1328 | .r600, | 1338 | .r600, |
lib/std/zig/system.zig+1-1| ... | @@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct { | ... | @@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct { |
| 249 | // values | 249 | // values |
| 250 | const known_build_numbers = [_]u32{ | 250 | const known_build_numbers = [_]u32{ |
| 251 | 10240, 10586, 14393, 15063, 16299, 17134, 17763, | 251 | 10240, 10586, 14393, 15063, 16299, 17134, 17763, |
| 252 | 18362, 18363, | 252 | 18362, 19041, |
| 253 | }; | 253 | }; |
| 254 | var last_idx: usize = 0; | 254 | var last_idx: usize = 0; |
| 255 | for (known_build_numbers) |build, i| { | 255 | for (known_build_numbers) |build, i| { |
src-self-hosted/Module.zig+42| ... | @@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, | ... | @@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 80 | root_name: []u8, | 80 | root_name: []u8, |
| 81 | keep_source_files_loaded: bool, | 81 | keep_source_files_loaded: bool, |
| 82 | 82 | ||
| 83 | /// Error tags and their values, tag names are duped with mod.gpa. | ||
| 84 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | ||
| 85 | |||
| 83 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | 86 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 84 | 87 | ||
| 85 | const WorkItem = union(enum) { | 88 | const WorkItem = union(enum) { |
| ... | @@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void { | ... | @@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void { |
| 928 | 931 | ||
| 929 | self.symbol_exports.deinit(gpa); | 932 | self.symbol_exports.deinit(gpa); |
| 930 | self.root_scope.destroy(gpa); | 933 | self.root_scope.destroy(gpa); |
| 934 | |||
| 935 | for (self.global_error_set.items()) |entry| { | ||
| 936 | gpa.free(entry.key); | ||
| 937 | } | ||
| 938 | self.global_error_set.deinit(gpa); | ||
| 931 | self.* = undefined; | 939 | self.* = undefined; |
| 932 | } | 940 | } |
| 933 | 941 | ||
| ... | @@ -2072,6 +2080,18 @@ fn createNewDecl( | ... | @@ -2072,6 +2080,18 @@ fn createNewDecl( |
| 2072 | return new_decl; | 2080 | return new_decl; |
| 2073 | } | 2081 | } |
| 2074 | 2082 | ||
| 2083 | /// Get error value for error tag `name`. | ||
| 2084 | pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { | ||
| 2085 | const gop = try self.global_error_set.getOrPut(self.gpa, name); | ||
| 2086 | if (gop.found_existing) | ||
| 2087 | return gop.entry.*; | ||
| 2088 | errdefer self.global_error_set.removeAssertDiscard(name); | ||
| 2089 | |||
| 2090 | gop.entry.key = try self.gpa.dupe(u8, name); | ||
| 2091 | gop.entry.value = @intCast(u16, self.global_error_set.items().len - 1); | ||
| 2092 | return gop.entry.*; | ||
| 2093 | } | ||
| 2094 | |||
| 2075 | /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. | 2095 | /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. |
| 2076 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | 2096 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { |
| 2077 | return scope.cast(Scope.Block) orelse | 2097 | return scope.cast(Scope.Block) orelse |
| ... | @@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_ | ... | @@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_ |
| 3309 | return Type.initPayload(&payload.base); | 3329 | return Type.initPayload(&payload.base); |
| 3310 | } | 3330 | } |
| 3311 | 3331 | ||
| 3332 | pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type { | ||
| 3333 | assert(error_set.zigTypeTag() == .ErrorSet); | ||
| 3334 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { | ||
| 3335 | return Type.initTag(.anyerror_void_error_union); | ||
| 3336 | } | ||
| 3337 | |||
| 3338 | const result = try scope.arena().create(Type.Payload.ErrorUnion); | ||
| 3339 | result.* = .{ | ||
| 3340 | .error_set = error_set, | ||
| 3341 | .payload = payload, | ||
| 3342 | }; | ||
| 3343 | return Type.initPayload(&result.base); | ||
| 3344 | } | ||
| 3345 | |||
| 3346 | pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type { | ||
| 3347 | const result = try scope.arena().create(Type.Payload.AnyFrame); | ||
| 3348 | result.* = .{ | ||
| 3349 | .return_type = return_type, | ||
| 3350 | }; | ||
| 3351 | return Type.initPayload(&result.base); | ||
| 3352 | } | ||
| 3353 | |||
| 3312 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | 3354 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 3313 | const zir_module = scope.namespace(); | 3355 | const zir_module = scope.namespace(); |
| 3314 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); | 3356 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); |
src-self-hosted/astgen.zig+235-198| ... | @@ -18,9 +18,7 @@ pub const ResultLoc = union(enum) { | ... | @@ -18,9 +18,7 @@ pub const ResultLoc = union(enum) { |
| 18 | /// The expression has an inferred type, and it will be evaluated as an rvalue. | 18 | /// The expression has an inferred type, and it will be evaluated as an rvalue. |
| 19 | none, | 19 | none, |
| 20 | /// The expression must generate a pointer rather than a value. For example, the left hand side | 20 | /// The expression must generate a pointer rather than a value. For example, the left hand side |
| 21 | /// of an assignment uses an "LValue" result location. | 21 | /// of an assignment uses this kind of result location. |
| 22 | lvalue, | ||
| 23 | /// The expression must generate a pointer | ||
| 24 | ref, | 22 | ref, |
| 25 | /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. | 23 | /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. |
| 26 | ty: *zir.Inst, | 24 | ty: *zir.Inst, |
| ... | @@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z | ... | @@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z |
| 46 | return expr(mod, scope, type_rl, type_node); | 44 | return expr(mod, scope, type_rl, type_node); |
| 47 | } | 45 | } |
| 48 | 46 | ||
| 49 | /// Turn Zig AST into untyped ZIR istructions. | 47 | fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 50 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { | 48 | switch (node.tag) { |
| 51 | if (rl == .lvalue) { | 49 | .Root => unreachable, |
| 52 | switch (node.tag) { | 50 | .Use => unreachable, |
| 53 | .Root => unreachable, | 51 | .TestDecl => unreachable, |
| 54 | .Use => unreachable, | 52 | .DocComment => unreachable, |
| 55 | .TestDecl => unreachable, | 53 | .VarDecl => unreachable, |
| 56 | .DocComment => unreachable, | 54 | .SwitchCase => unreachable, |
| 57 | .VarDecl => unreachable, | 55 | .SwitchElse => unreachable, |
| 58 | .SwitchCase => unreachable, | 56 | .Else => unreachable, |
| 59 | .SwitchElse => unreachable, | 57 | .Payload => unreachable, |
| 60 | .Else => unreachable, | 58 | .PointerPayload => unreachable, |
| 61 | .Payload => unreachable, | 59 | .PointerIndexPayload => unreachable, |
| 62 | .PointerPayload => unreachable, | 60 | .ErrorTag => unreachable, |
| 63 | .PointerIndexPayload => unreachable, | 61 | .FieldInitializer => unreachable, |
| 64 | .ErrorTag => unreachable, | 62 | .ContainerField => unreachable, |
| 65 | .FieldInitializer => unreachable, | 63 | |
| 66 | .ContainerField => unreachable, | 64 | .Assign, |
| 67 | 65 | .AssignBitAnd, | |
| 68 | .Assign, | 66 | .AssignBitOr, |
| 69 | .AssignBitAnd, | 67 | .AssignBitShiftLeft, |
| 70 | .AssignBitOr, | 68 | .AssignBitShiftRight, |
| 71 | .AssignBitShiftLeft, | 69 | .AssignBitXor, |
| 72 | .AssignBitShiftRight, | 70 | .AssignDiv, |
| 73 | .AssignBitXor, | 71 | .AssignSub, |
| 74 | .AssignDiv, | 72 | .AssignSubWrap, |
| 75 | .AssignSub, | 73 | .AssignMod, |
| 76 | .AssignSubWrap, | 74 | .AssignAdd, |
| 77 | .AssignMod, | 75 | .AssignAddWrap, |
| 78 | .AssignAdd, | 76 | .AssignMul, |
| 79 | .AssignAddWrap, | 77 | .AssignMulWrap, |
| 80 | .AssignMul, | 78 | .Add, |
| 81 | .AssignMulWrap, | 79 | .AddWrap, |
| 82 | .Add, | 80 | .Sub, |
| 83 | .AddWrap, | 81 | .SubWrap, |
| 84 | .Sub, | 82 | .Mul, |
| 85 | .SubWrap, | 83 | .MulWrap, |
| 86 | .Mul, | 84 | .Div, |
| 87 | .MulWrap, | 85 | .Mod, |
| 88 | .Div, | 86 | .BitAnd, |
| 89 | .Mod, | 87 | .BitOr, |
| 90 | .BitAnd, | 88 | .BitShiftLeft, |
| 91 | .BitOr, | 89 | .BitShiftRight, |
| 92 | .BitShiftLeft, | 90 | .BitXor, |
| 93 | .BitShiftRight, | 91 | .BangEqual, |
| 94 | .BitXor, | 92 | .EqualEqual, |
| 95 | .BangEqual, | 93 | .GreaterThan, |
| 96 | .EqualEqual, | 94 | .GreaterOrEqual, |
| 97 | .GreaterThan, | 95 | .LessThan, |
| 98 | .GreaterOrEqual, | 96 | .LessOrEqual, |
| 99 | .LessThan, | 97 | .ArrayCat, |
| 100 | .LessOrEqual, | 98 | .ArrayMult, |
| 101 | .ArrayCat, | 99 | .BoolAnd, |
| 102 | .ArrayMult, | 100 | .BoolOr, |
| 103 | .BoolAnd, | 101 | .Asm, |
| 104 | .BoolOr, | 102 | .StringLiteral, |
| 105 | .Asm, | 103 | .IntegerLiteral, |
| 106 | .StringLiteral, | 104 | .Call, |
| 107 | .IntegerLiteral, | 105 | .Unreachable, |
| 108 | .Call, | 106 | .Return, |
| 109 | .Unreachable, | 107 | .If, |
| 110 | .Return, | 108 | .While, |
| 111 | .If, | 109 | .BoolNot, |
| 112 | .While, | 110 | .AddressOf, |
| 113 | .BoolNot, | 111 | .FloatLiteral, |
| 114 | .AddressOf, | 112 | .UndefinedLiteral, |
| 115 | .FloatLiteral, | 113 | .BoolLiteral, |
| 116 | .UndefinedLiteral, | 114 | .NullLiteral, |
| 117 | .BoolLiteral, | 115 | .OptionalType, |
| 118 | .NullLiteral, | 116 | .Block, |
| 119 | .OptionalType, | 117 | .LabeledBlock, |
| 120 | .Block, | 118 | .Break, |
| 121 | .LabeledBlock, | 119 | .PtrType, |
| 122 | .Break, | 120 | .GroupedExpression, |
| 123 | .PtrType, | 121 | .ArrayType, |
| 124 | .GroupedExpression, | 122 | .ArrayTypeSentinel, |
| 125 | .ArrayType, | 123 | .EnumLiteral, |
| 126 | .ArrayTypeSentinel, | 124 | .MultilineStringLiteral, |
| 127 | .EnumLiteral, | 125 | .CharLiteral, |
| 128 | .MultilineStringLiteral, | 126 | .Defer, |
| 129 | .CharLiteral, | 127 | .Catch, |
| 130 | .Defer, | 128 | .ErrorUnion, |
| 131 | .Catch, | 129 | .MergeErrorSets, |
| 132 | .ErrorUnion, | 130 | .Range, |
| 133 | .MergeErrorSets, | 131 | .OrElse, |
| 134 | .Range, | 132 | .Await, |
| 135 | .OrElse, | 133 | .BitNot, |
| 136 | .Await, | 134 | .Negation, |
| 137 | .BitNot, | 135 | .NegationWrap, |
| 138 | .Negation, | 136 | .Resume, |
| 139 | .NegationWrap, | 137 | .Try, |
| 140 | .Resume, | 138 | .SliceType, |
| 141 | .Try, | 139 | .Slice, |
| 142 | .SliceType, | 140 | .ArrayInitializer, |
| 143 | .Slice, | 141 | .ArrayInitializerDot, |
| 144 | .ArrayInitializer, | 142 | .StructInitializer, |
| 145 | .ArrayInitializerDot, | 143 | .StructInitializerDot, |
| 146 | .StructInitializer, | 144 | .Switch, |
| 147 | .StructInitializerDot, | 145 | .For, |
| 148 | .Switch, | 146 | .Suspend, |
| 149 | .For, | 147 | .Continue, |
| 150 | .Suspend, | 148 | .AnyType, |
| 151 | .Continue, | 149 | .ErrorType, |
| 152 | .AnyType, | 150 | .FnProto, |
| 153 | .ErrorType, | 151 | .AnyFrameType, |
| 154 | .FnProto, | 152 | .ErrorSetDecl, |
| 155 | .AnyFrameType, | 153 | .ContainerDecl, |
| 156 | .ErrorSetDecl, | 154 | .Comptime, |
| 157 | .ContainerDecl, | 155 | .Nosuspend, |
| 158 | .Comptime, | 156 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), |
| 159 | .Nosuspend, | 157 | |
| 160 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | 158 | // @field can be assigned to |
| 161 | 159 | .BuiltinCall => { | |
| 162 | // @field can be assigned to | 160 | const call = node.castTag(.BuiltinCall).?; |
| 163 | .BuiltinCall => { | 161 | const tree = scope.tree(); |
| 164 | const call = node.castTag(.BuiltinCall).?; | 162 | const builtin_name = tree.tokenSlice(call.builtin_token); |
| 165 | const tree = scope.tree(); | 163 | |
| 166 | const builtin_name = tree.tokenSlice(call.builtin_token); | 164 | if (!mem.eql(u8, builtin_name, "@field")) { |
| 167 | 165 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | |
| 168 | if (!mem.eql(u8, builtin_name, "@field")) { | 166 | } |
| 169 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | 167 | }, |
| 170 | } | ||
| 171 | }, | ||
| 172 | 168 | ||
| 173 | // can be assigned to | 169 | // can be assigned to |
| 174 | .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, | 170 | .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, |
| 175 | } | ||
| 176 | } | 171 | } |
| 172 | return expr(mod, scope, .ref, node); | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Turn Zig AST into untyped ZIR istructions. | ||
| 176 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { | ||
| 177 | switch (node.tag) { | 177 | switch (node.tag) { |
| 178 | .Root => unreachable, // Top-level declaration. | 178 | .Root => unreachable, // Top-level declaration. |
| 179 | .Use => unreachable, // Top-level declaration. | 179 | .Use => unreachable, // Top-level declaration. |
| ... | @@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 232 | .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), | 232 | .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), |
| 233 | .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), | 233 | .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), |
| 234 | 234 | ||
| 235 | .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), | ||
| 236 | .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), | ||
| 237 | .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), | ||
| 238 | .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), | ||
| 239 | |||
| 235 | .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), | 240 | .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), |
| 236 | .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), | 241 | .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), |
| 237 | .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), | 242 | .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), |
| ... | @@ -242,9 +247,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -242,9 +247,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 242 | .Return => return ret(mod, scope, node.castTag(.Return).?), | 247 | .Return => return ret(mod, scope, node.castTag(.Return).?), |
| 243 | .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), | 248 | .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), |
| 244 | .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), | 249 | .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), |
| 245 | .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)), | 250 | .Period => return field(mod, scope, rl, node.castTag(.Period).?), |
| 246 | .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), | 251 | .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), |
| 247 | .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), | ||
| 248 | .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), | 252 | .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), |
| 249 | .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), | 253 | .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), |
| 250 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), | 254 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), |
| ... | @@ -263,17 +267,17 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -263,17 +267,17 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 263 | .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), | 267 | .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), |
| 264 | .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), | 268 | .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), |
| 265 | .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), | 269 | .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), |
| 270 | .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), | ||
| 271 | .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), | ||
| 272 | .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), | ||
| 273 | .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?), | ||
| 274 | .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), | ||
| 266 | 275 | ||
| 267 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), | 276 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), |
| 268 | .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), | 277 | .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), |
| 269 | .ErrorUnion => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorUnion", .{}), | ||
| 270 | .MergeErrorSets => return mod.failNode(scope, node, "TODO implement astgen.expr for .MergeErrorSets", .{}), | ||
| 271 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), | 278 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), |
| 272 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), | 279 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), |
| 273 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), | 280 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), |
| 274 | .BitNot => return mod.failNode(scope, node, "TODO implement astgen.expr for .BitNot", .{}), | ||
| 275 | .Negation => return mod.failNode(scope, node, "TODO implement astgen.expr for .Negation", .{}), | ||
| 276 | .NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}), | ||
| 277 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), | 281 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), |
| 278 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | 282 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), |
| 279 | .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}), | 283 | .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}), |
| ... | @@ -287,10 +291,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -287,10 +291,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 287 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), | 291 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 288 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), | 292 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 289 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), | 293 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| 290 | .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), | ||
| 291 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), | 294 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), |
| 292 | .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}), | ||
| 293 | .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), | ||
| 294 | .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), | 295 | .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), |
| 295 | .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), | 296 | .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), |
| 296 | .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), | 297 | .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), |
| ... | @@ -316,7 +317,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr | ... | @@ -316,7 +317,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr |
| 316 | // proper type inference requires peer type resolution on the block's | 317 | // proper type inference requires peer type resolution on the block's |
| 317 | // break operand expressions. | 318 | // break operand expressions. |
| 318 | const branch_rl: ResultLoc = switch (label.result_loc) { | 319 | const branch_rl: ResultLoc = switch (label.result_loc) { |
| 319 | .discard, .none, .ty, .ptr, .lvalue, .ref => label.result_loc, | 320 | .discard, .none, .ty, .ptr, .ref => label.result_loc, |
| 320 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, | 321 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, |
| 321 | }; | 322 | }; |
| 322 | const operand = try expr(mod, parent_scope, branch_rl, rhs); | 323 | const operand = try expr(mod, parent_scope, branch_rl, rhs); |
| ... | @@ -458,7 +459,9 @@ fn varDecl( | ... | @@ -458,7 +459,9 @@ fn varDecl( |
| 458 | const tree = scope.tree(); | 459 | const tree = scope.tree(); |
| 459 | const name_src = tree.token_locs[node.name_token].start; | 460 | const name_src = tree.token_locs[node.name_token].start; |
| 460 | const ident_name = try identifierTokenString(mod, scope, node.name_token); | 461 | const ident_name = try identifierTokenString(mod, scope, node.name_token); |
| 461 | const init_node = node.getTrailer("init_node").?; | 462 | const init_node = node.getTrailer("init_node") orelse |
| 463 | return mod.fail(scope, name_src, "variables must be initialized", .{}); | ||
| 464 | |||
| 462 | switch (tree.token_ids[node.mut_token]) { | 465 | switch (tree.token_ids[node.mut_token]) { |
| 463 | .Keyword_const => { | 466 | .Keyword_const => { |
| 464 | // Depending on the type of AST the initialization expression is, we may need an lvalue | 467 | // Depending on the type of AST the initialization expression is, we may need an lvalue |
| ... | @@ -521,7 +524,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne | ... | @@ -521,7 +524,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne |
| 521 | return; | 524 | return; |
| 522 | } | 525 | } |
| 523 | } | 526 | } |
| 524 | const lvalue = try expr(mod, scope, .lvalue, infix_node.lhs); | 527 | const lvalue = try lvalExpr(mod, scope, infix_node.lhs); |
| 525 | _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs); | 528 | _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs); |
| 526 | } | 529 | } |
| 527 | 530 | ||
| ... | @@ -531,7 +534,7 @@ fn assignOp( | ... | @@ -531,7 +534,7 @@ fn assignOp( |
| 531 | infix_node: *ast.Node.SimpleInfixOp, | 534 | infix_node: *ast.Node.SimpleInfixOp, |
| 532 | op_inst_tag: zir.Inst.Tag, | 535 | op_inst_tag: zir.Inst.Tag, |
| 533 | ) InnerError!void { | 536 | ) InnerError!void { |
| 534 | const lhs_ptr = try expr(mod, scope, .lvalue, infix_node.lhs); | 537 | const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs); |
| 535 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); | 538 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); |
| 536 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); | 539 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); |
| 537 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs); | 540 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs); |
| ... | @@ -554,6 +557,26 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr | ... | @@ -554,6 +557,26 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr |
| 554 | return addZIRUnOp(mod, scope, src, .boolnot, operand); | 557 | return addZIRUnOp(mod, scope, src, .boolnot, operand); |
| 555 | } | 558 | } |
| 556 | 559 | ||
| 560 | fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | ||
| 561 | const tree = scope.tree(); | ||
| 562 | const src = tree.token_locs[node.op_token].start; | ||
| 563 | const operand = try expr(mod, scope, .none, node.rhs); | ||
| 564 | return addZIRUnOp(mod, scope, src, .bitnot, operand); | ||
| 565 | } | ||
| 566 | |||
| 567 | fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { | ||
| 568 | const tree = scope.tree(); | ||
| 569 | const src = tree.token_locs[node.op_token].start; | ||
| 570 | |||
| 571 | const lhs = try addZIRInstConst(mod, scope, src, .{ | ||
| 572 | .ty = Type.initTag(.comptime_int), | ||
| 573 | .val = Value.initTag(.zero), | ||
| 574 | }); | ||
| 575 | const rhs = try expr(mod, scope, .none, node.rhs); | ||
| 576 | |||
| 577 | return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | ||
| 578 | } | ||
| 579 | |||
| 557 | fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 580 | fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { |
| 558 | return expr(mod, scope, .ref, node.rhs); | 581 | return expr(mod, scope, .ref, node.rhs); |
| 559 | } | 582 | } |
| ... | @@ -561,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE | ... | @@ -561,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE |
| 561 | fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 584 | fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { |
| 562 | const tree = scope.tree(); | 585 | const tree = scope.tree(); |
| 563 | const src = tree.token_locs[node.op_token].start; | 586 | const src = tree.token_locs[node.op_token].start; |
| 564 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | 587 | const operand = try typeExpr(mod, scope, node.rhs); |
| 565 | .ty = Type.initTag(.type), | ||
| 566 | .val = Value.initTag(.type_type), | ||
| 567 | }); | ||
| 568 | const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); | ||
| 569 | return addZIRUnOp(mod, scope, src, .optional_type, operand); | 588 | return addZIRUnOp(mod, scope, src, .optional_type, operand); |
| 570 | } | 589 | } |
| 571 | 590 | ||
| ... | @@ -590,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir | ... | @@ -590,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir |
| 590 | } | 609 | } |
| 591 | 610 | ||
| 592 | fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { | 611 | fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { |
| 593 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 594 | .ty = Type.initTag(.type), | ||
| 595 | .val = Value.initTag(.type_type), | ||
| 596 | }); | ||
| 597 | |||
| 598 | const simple = ptr_info.allowzero_token == null and | 612 | const simple = ptr_info.allowzero_token == null and |
| 599 | ptr_info.align_info == null and | 613 | ptr_info.align_info == null and |
| 600 | ptr_info.volatile_token == null and | 614 | ptr_info.volatile_token == null and |
| 601 | ptr_info.sentinel == null; | 615 | ptr_info.sentinel == null; |
| 602 | 616 | ||
| 603 | if (simple) { | 617 | if (simple) { |
| 604 | const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs); | 618 | const child_type = try typeExpr(mod, scope, rhs); |
| 605 | const mutable = ptr_info.const_token == null; | 619 | const mutable = ptr_info.const_token == null; |
| 606 | // TODO stage1 type inference bug | 620 | // TODO stage1 type inference bug |
| 607 | const T = zir.Inst.Tag; | 621 | const T = zir.Inst.Tag; |
| ... | @@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, | ... | @@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, |
| 629 | kw_args.sentinel = try expr(mod, scope, .none, some); | 643 | kw_args.sentinel = try expr(mod, scope, .none, some); |
| 630 | } | 644 | } |
| 631 | 645 | ||
| 632 | const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs); | 646 | const child_type = try typeExpr(mod, scope, rhs); |
| 633 | if (kw_args.sentinel) |some| { | 647 | if (kw_args.sentinel) |some| { |
| 634 | kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some); | 648 | kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some); |
| 635 | } | 649 | } |
| ... | @@ -640,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, | ... | @@ -640,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, |
| 640 | fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { | 654 | fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { |
| 641 | const tree = scope.tree(); | 655 | const tree = scope.tree(); |
| 642 | const src = tree.token_locs[node.op_token].start; | 656 | const src = tree.token_locs[node.op_token].start; |
| 643 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 644 | .ty = Type.initTag(.type), | ||
| 645 | .val = Value.initTag(.type_type), | ||
| 646 | }); | ||
| 647 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 657 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 648 | .ty = Type.initTag(.type), | 658 | .ty = Type.initTag(.type), |
| 649 | .val = Value.initTag(.usize_type), | 659 | .val = Value.initTag(.usize_type), |
| ... | @@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst | ... | @@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst |
| 651 | 661 | ||
| 652 | // TODO check for [_]T | 662 | // TODO check for [_]T |
| 653 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); | 663 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); |
| 654 | const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); | 664 | const elem_type = try typeExpr(mod, scope, node.rhs); |
| 655 | 665 | ||
| 656 | return addZIRBinOp(mod, scope, src, .array_type, len, child_type); | 666 | return addZIRBinOp(mod, scope, src, .array_type, len, elem_type); |
| 657 | } | 667 | } |
| 658 | 668 | ||
| 659 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { | 669 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { |
| 660 | const tree = scope.tree(); | 670 | const tree = scope.tree(); |
| 661 | const src = tree.token_locs[node.op_token].start; | 671 | const src = tree.token_locs[node.op_token].start; |
| 662 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 663 | .ty = Type.initTag(.type), | ||
| 664 | .val = Value.initTag(.type_type), | ||
| 665 | }); | ||
| 666 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 672 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 667 | .ty = Type.initTag(.type), | 673 | .ty = Type.initTag(.type), |
| 668 | .val = Value.initTag(.usize_type), | 674 | .val = Value.initTag(.usize_type), |
| ... | @@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti | ... | @@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti |
| 671 | // TODO check for [_]T | 677 | // TODO check for [_]T |
| 672 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); | 678 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); |
| 673 | const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); | 679 | const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); |
| 674 | const elem_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); | 680 | const elem_type = try typeExpr(mod, scope, node.rhs); |
| 675 | const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); | 681 | const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); |
| 676 | 682 | ||
| 677 | return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ | 683 | return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ |
| ... | @@ -681,6 +687,28 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti | ... | @@ -681,6 +687,28 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti |
| 681 | }, .{}); | 687 | }, .{}); |
| 682 | } | 688 | } |
| 683 | 689 | ||
| 690 | fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst { | ||
| 691 | const tree = scope.tree(); | ||
| 692 | const src = tree.token_locs[node.anyframe_token].start; | ||
| 693 | if (node.result) |some| { | ||
| 694 | const return_type = try typeExpr(mod, scope, some.return_type); | ||
| 695 | return addZIRUnOp(mod, scope, src, .anyframe_type, return_type); | ||
| 696 | } else { | ||
| 697 | return addZIRInstConst(mod, scope, src, .{ | ||
| 698 | .ty = Type.initTag(.type), | ||
| 699 | .val = Value.initTag(.anyframe_type), | ||
| 700 | }); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { | ||
| 705 | const tree = scope.tree(); | ||
| 706 | const src = tree.token_locs[node.op_token].start; | ||
| 707 | const error_set = try typeExpr(mod, scope, node.lhs); | ||
| 708 | const payload = try typeExpr(mod, scope, node.rhs); | ||
| 709 | return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); | ||
| 710 | } | ||
| 711 | |||
| 684 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { | 712 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { |
| 685 | const tree = scope.tree(); | 713 | const tree = scope.tree(); |
| 686 | const src = tree.token_locs[node.name].start; | 714 | const src = tree.token_locs[node.name].start; |
| ... | @@ -694,10 +722,31 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si | ... | @@ -694,10 +722,31 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si |
| 694 | const src = tree.token_locs[node.rtoken].start; | 722 | const src = tree.token_locs[node.rtoken].start; |
| 695 | 723 | ||
| 696 | const operand = try expr(mod, scope, .ref, node.lhs); | 724 | const operand = try expr(mod, scope, .ref, node.lhs); |
| 697 | const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); | 725 | return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand)); |
| 698 | if (rl == .lvalue or rl == .ref) return unwrapped_ptr; | 726 | } |
| 727 | |||
| 728 | fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { | ||
| 729 | const tree = scope.tree(); | ||
| 730 | const src = tree.token_locs[node.error_token].start; | ||
| 731 | const decls = node.decls(); | ||
| 732 | const fields = try scope.arena().alloc([]const u8, decls.len); | ||
| 733 | |||
| 734 | for (decls) |decl, i| { | ||
| 735 | const tag = decl.castTag(.ErrorTag).?; | ||
| 736 | fields[i] = try identifierTokenString(mod, scope, tag.name_token); | ||
| 737 | } | ||
| 699 | 738 | ||
| 700 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); | 739 | // analyzing the error set results in a decl ref, so we might need to dereference it |
| 740 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{})); | ||
| 741 | } | ||
| 742 | |||
| 743 | fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | ||
| 744 | const tree = scope.tree(); | ||
| 745 | const src = tree.token_locs[node.token].start; | ||
| 746 | return addZIRInstConst(mod, scope, src, .{ | ||
| 747 | .ty = Type.initTag(.type), | ||
| 748 | .val = Value.initTag(.anyerror_type), | ||
| 749 | }); | ||
| 701 | } | 750 | } |
| 702 | 751 | ||
| 703 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. | 752 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. |
| ... | @@ -737,16 +786,16 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke | ... | @@ -737,16 +786,16 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke |
| 737 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); | 786 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); |
| 738 | } | 787 | } |
| 739 | 788 | ||
| 740 | fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { | 789 | fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { |
| 741 | // TODO introduce lvalues | ||
| 742 | const tree = scope.tree(); | 790 | const tree = scope.tree(); |
| 743 | const src = tree.token_locs[node.op_token].start; | 791 | const src = tree.token_locs[node.op_token].start; |
| 744 | 792 | ||
| 745 | const lhs = try expr(mod, scope, .none, node.lhs); | 793 | const lhs = try expr(mod, scope, .ref, node.lhs); |
| 746 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); | 794 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 747 | 795 | ||
| 748 | const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}); | 796 | const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}); |
| 749 | return addZIRUnOp(mod, scope, src, .deref, pointer); | 797 | if (rl == .ref) return pointer; |
| 798 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer)); | ||
| 750 | } | 799 | } |
| 751 | 800 | ||
| 752 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { | 801 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { |
| ... | @@ -971,7 +1020,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -971,7 +1020,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 971 | // proper type inference requires peer type resolution on the if's | 1020 | // proper type inference requires peer type resolution on the if's |
| 972 | // branches. | 1021 | // branches. |
| 973 | const branch_rl: ResultLoc = switch (rl) { | 1022 | const branch_rl: ResultLoc = switch (rl) { |
| 974 | .discard, .none, .ty, .ptr, .lvalue, .ref => rl, | 1023 | .discard, .none, .ty, .ptr, .ref => rl, |
| 975 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, | 1024 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, |
| 976 | }; | 1025 | }; |
| 977 | 1026 | ||
| ... | @@ -1101,7 +1150,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W | ... | @@ -1101,7 +1150,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1101 | // proper type inference requires peer type resolution on the while's | 1150 | // proper type inference requires peer type resolution on the while's |
| 1102 | // branches. | 1151 | // branches. |
| 1103 | const branch_rl: ResultLoc = switch (rl) { | 1152 | const branch_rl: ResultLoc = switch (rl) { |
| 1104 | .discard, .none, .ty, .ptr, .lvalue, .ref => rl, | 1153 | .discard, .none, .ty, .ptr, .ref => rl, |
| 1105 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block }, | 1154 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block }, |
| 1106 | }; | 1155 | }; |
| 1107 | 1156 | ||
| ... | @@ -1232,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo | ... | @@ -1232,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 1232 | .local_ptr => { | 1281 | .local_ptr => { |
| 1233 | const local_ptr = s.cast(Scope.LocalPtr).?; | 1282 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 1234 | if (mem.eql(u8, local_ptr.name, ident_name)) { | 1283 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 1235 | if (rl == .lvalue or rl == .ref) { | 1284 | return rlWrapPtr(mod, scope, rl, local_ptr.ptr); |
| 1236 | return local_ptr.ptr; | ||
| 1237 | } else { | ||
| 1238 | const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr); | ||
| 1239 | return rlWrap(mod, scope, rl, result); | ||
| 1240 | } | ||
| 1241 | } | 1285 | } |
| 1242 | s = local_ptr.parent; | 1286 | s = local_ptr.parent; |
| 1243 | }, | 1287 | }, |
| ... | @@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo | ... | @@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 1247 | } | 1291 | } |
| 1248 | 1292 | ||
| 1249 | if (mod.lookupDeclName(scope, ident_name)) |decl| { | 1293 | if (mod.lookupDeclName(scope, ident_name)) |decl| { |
| 1250 | const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | 1294 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); |
| 1251 | if (rl == .lvalue or rl == .ref) | ||
| 1252 | return result; | ||
| 1253 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, result)); | ||
| 1254 | } | 1295 | } |
| 1255 | 1296 | ||
| 1256 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); | 1297 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); |
| ... | @@ -1466,12 +1507,8 @@ fn simpleCast( | ... | @@ -1466,12 +1507,8 @@ fn simpleCast( |
| 1466 | try ensureBuiltinParamCount(mod, scope, call, 2); | 1507 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 1467 | const tree = scope.tree(); | 1508 | const tree = scope.tree(); |
| 1468 | const src = tree.token_locs[call.builtin_token].start; | 1509 | const src = tree.token_locs[call.builtin_token].start; |
| 1469 | const type_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 1470 | .ty = Type.initTag(.type), | ||
| 1471 | .val = Value.initTag(.type_type), | ||
| 1472 | }); | ||
| 1473 | const params = call.params(); | 1510 | const params = call.params(); |
| 1474 | const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); | 1511 | const dest_type = try typeExpr(mod, scope, params[0]); |
| 1475 | const rhs = try expr(mod, scope, .none, params[1]); | 1512 | const rhs = try expr(mod, scope, .none, params[1]); |
| 1476 | const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); | 1513 | const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); |
| 1477 | return rlWrap(mod, scope, rl, result); | 1514 | return rlWrap(mod, scope, rl, result); |
| ... | @@ -1498,7 +1535,6 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I | ... | @@ -1498,7 +1535,6 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I |
| 1498 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | 1535 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1499 | return result; | 1536 | return result; |
| 1500 | }, | 1537 | }, |
| 1501 | .lvalue => unreachable, | ||
| 1502 | .ref => { | 1538 | .ref => { |
| 1503 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); | 1539 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); |
| 1504 | return addZIRUnOp(mod, scope, result.src, .ref, result); | 1540 | return addZIRUnOp(mod, scope, result.src, .ref, result); |
| ... | @@ -1533,12 +1569,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa | ... | @@ -1533,12 +1569,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa |
| 1533 | try ensureBuiltinParamCount(mod, scope, call, 2); | 1569 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 1534 | const tree = scope.tree(); | 1570 | const tree = scope.tree(); |
| 1535 | const src = tree.token_locs[call.builtin_token].start; | 1571 | const src = tree.token_locs[call.builtin_token].start; |
| 1536 | const type_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 1537 | .ty = Type.initTag(.type), | ||
| 1538 | .val = Value.initTag(.type_type), | ||
| 1539 | }); | ||
| 1540 | const params = call.params(); | 1572 | const params = call.params(); |
| 1541 | const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); | 1573 | const dest_type = try typeExpr(mod, scope, params[0]); |
| 1542 | switch (rl) { | 1574 | switch (rl) { |
| 1543 | .none => { | 1575 | .none => { |
| 1544 | const operand = try expr(mod, scope, .none, params[1]); | 1576 | const operand = try expr(mod, scope, .none, params[1]); |
| ... | @@ -1550,7 +1582,6 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa | ... | @@ -1550,7 +1582,6 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa |
| 1550 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | 1582 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1551 | return result; | 1583 | return result; |
| 1552 | }, | 1584 | }, |
| 1553 | .lvalue => unreachable, | ||
| 1554 | .ref => { | 1585 | .ref => { |
| 1555 | const operand = try expr(mod, scope, .ref, params[1]); | 1586 | const operand = try expr(mod, scope, .ref, params[1]); |
| 1556 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); | 1587 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); |
| ... | @@ -1818,7 +1849,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr | ... | @@ -1818,7 +1849,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr |
| 1818 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | 1849 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1819 | return result; | 1850 | return result; |
| 1820 | }, | 1851 | }, |
| 1821 | .lvalue, .ref => { | 1852 | .ref => { |
| 1822 | // We need a pointer but we have a value. | 1853 | // We need a pointer but we have a value. |
| 1823 | return addZIRUnOp(mod, scope, result.src, .ref, result); | 1854 | return addZIRUnOp(mod, scope, result.src, .ref, result); |
| 1824 | }, | 1855 | }, |
| ... | @@ -1852,6 +1883,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul | ... | @@ -1852,6 +1883,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul |
| 1852 | return rlWrap(mod, scope, rl, void_inst); | 1883 | return rlWrap(mod, scope, rl, void_inst); |
| 1853 | } | 1884 | } |
| 1854 | 1885 | ||
| 1886 | fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst { | ||
| 1887 | if (rl == .ref) return ptr; | ||
| 1888 | |||
| 1889 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr)); | ||
| 1890 | } | ||
| 1891 | |||
| 1855 | pub fn addZIRInstSpecial( | 1892 | pub fn addZIRInstSpecial( |
| 1856 | mod: *Module, | 1893 | mod: *Module, |
| 1857 | scope: *Scope, | 1894 | scope: *Scope, |
src-self-hosted/codegen.zig+361-64| ... | @@ -14,6 +14,7 @@ const Allocator = mem.Allocator; | ... | @@ -14,6 +14,7 @@ const Allocator = mem.Allocator; |
| 14 | const trace = @import("tracy.zig").trace; | 14 | const trace = @import("tracy.zig").trace; |
| 15 | const DW = std.dwarf; | 15 | const DW = std.dwarf; |
| 16 | const leb128 = std.debug.leb; | 16 | const leb128 = std.debug.leb; |
| 17 | const log = std.log.scoped(.codegen); | ||
| 17 | 18 | ||
| 18 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | 19 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. |
| 19 | // zig fmt: off | 20 | // zig fmt: off |
| ... | @@ -75,8 +76,8 @@ pub fn generateSymbol( | ... | @@ -75,8 +76,8 @@ pub fn generateSymbol( |
| 75 | switch (bin_file.options.target.cpu.arch) { | 76 | switch (bin_file.options.target.cpu.arch) { |
| 76 | .wasm32 => unreachable, // has its own code path | 77 | .wasm32 => unreachable, // has its own code path |
| 77 | .wasm64 => unreachable, // has its own code path | 78 | .wasm64 => unreachable, // has its own code path |
| 78 | //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 79 | .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 79 | //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 80 | .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 80 | //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 81 | //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 81 | //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 82 | //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 82 | //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 83 | //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| ... | @@ -101,6 +102,7 @@ pub fn generateSymbol( | ... | @@ -101,6 +102,7 @@ pub fn generateSymbol( |
| 101 | //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 102 | //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 102 | //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 103 | //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 103 | //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 104 | //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 105 | .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | ||
| 104 | //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 106 | //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 105 | //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 107 | //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 106 | //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | 108 | //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| ... | @@ -344,6 +346,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -344,6 +346,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 344 | 346 | ||
| 345 | const Branch = struct { | 347 | const Branch = struct { |
| 346 | inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{}, | 348 | inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{}, |
| 349 | /// The key must be canonical register. | ||
| 347 | registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{}, | 350 | registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{}, |
| 348 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), | 351 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| 349 | 352 | ||
| ... | @@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 381 | self.free_registers &= ~(@as(FreeRegInt, 1) << free_index); | 384 | self.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 382 | const reg = callee_preserved_regs[free_index]; | 385 | const reg = callee_preserved_regs[free_index]; |
| 383 | self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst }); | 386 | self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst }); |
| 387 | log.debug("alloc {} => {*}", .{reg, inst}); | ||
| 384 | return reg; | 388 | return reg; |
| 385 | } | 389 | } |
| 386 | 390 | ||
| 391 | /// Does not track the register. | ||
| 392 | fn findUnusedReg(self: *Branch) ?Register { | ||
| 393 | const free_index = @ctz(FreeRegInt, self.free_registers); | ||
| 394 | if (free_index >= callee_preserved_regs.len) { | ||
| 395 | return null; | ||
| 396 | } | ||
| 397 | return callee_preserved_regs[free_index]; | ||
| 398 | } | ||
| 399 | |||
| 387 | fn deinit(self: *Branch, gpa: *Allocator) void { | 400 | fn deinit(self: *Branch, gpa: *Allocator) void { |
| 388 | self.inst_table.deinit(gpa); | 401 | self.inst_table.deinit(gpa); |
| 389 | self.registers.deinit(gpa); | 402 | self.registers.deinit(gpa); |
| ... | @@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 570 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 583 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 571 | const inst_table = &branch.inst_table; | 584 | const inst_table = &branch.inst_table; |
| 572 | for (body.instructions) |inst| { | 585 | for (body.instructions) |inst| { |
| 573 | const new_inst = try self.genFuncInst(inst); | 586 | const mcv = try self.genFuncInst(inst); |
| 574 | try inst_table.putNoClobber(self.gpa, inst, new_inst); | 587 | log.debug("{*} => {}", .{inst, mcv}); |
| 588 | // TODO don't put void or dead things in here | ||
| 589 | try inst_table.putNoClobber(self.gpa, inst, mcv); | ||
| 575 | 590 | ||
| 576 | var i: ir.Inst.DeathsBitIndex = 0; | 591 | var i: ir.Inst.DeathsBitIndex = 0; |
| 577 | while (inst.getOperand(i)) |operand| : (i += 1) { | 592 | while (inst.getOperand(i)) |operand| : (i += 1) { |
| ... | @@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 714 | return self.allocMem(inst, abi_size, abi_align); | 729 | return self.allocMem(inst, abi_size, abi_align); |
| 715 | } | 730 | } |
| 716 | 731 | ||
| 717 | fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue { | 732 | fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue { |
| 718 | const elem_ty = inst.ty; | 733 | const elem_ty = inst.ty; |
| 719 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { | 734 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 720 | return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty}); | 735 | return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty}); |
| ... | @@ -724,30 +739,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -724,30 +739,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 724 | self.stack_align = abi_align; | 739 | self.stack_align = abi_align; |
| 725 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 740 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 726 | 741 | ||
| 727 | // Make sure the type can fit in a register before we try to allocate one. | 742 | if (reg_ok) { |
| 728 | const ptr_bits = arch.ptrBitWidth(); | 743 | // Make sure the type can fit in a register before we try to allocate one. |
| 729 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 744 | const ptr_bits = arch.ptrBitWidth(); |
| 730 | if (abi_size <= ptr_bytes) { | 745 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 731 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); | 746 | if (abi_size <= ptr_bytes) { |
| 732 | if (branch.allocReg(inst)) |reg| { | 747 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); |
| 733 | return MCValue{ .register = registerAlias(reg, abi_size) }; | 748 | if (branch.allocReg(inst)) |reg| { |
| 749 | return MCValue{ .register = registerAlias(reg, abi_size) }; | ||
| 750 | } | ||
| 734 | } | 751 | } |
| 735 | } | 752 | } |
| 736 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); | 753 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 737 | return MCValue{ .stack_offset = stack_offset }; | 754 | return MCValue{ .stack_offset = stack_offset }; |
| 738 | } | 755 | } |
| 739 | 756 | ||
| 740 | /// Does not "move" the instruction. | 757 | /// Copies a value to a register without tracking the register. The register is not considered |
| 741 | fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue { | 758 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 759 | /// This can have a side effect of spilling instructions to the stack to free up a register. | ||
| 760 | fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register { | ||
| 761 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 762 | |||
| 763 | const reg = branch.findUnusedReg() orelse b: { | ||
| 764 | // We'll take over the first register. Move the instruction that was previously | ||
| 765 | // there to a stack allocation. | ||
| 766 | const reg = callee_preserved_regs[0]; | ||
| 767 | const regs_entry = branch.registers.remove(reg).?; | ||
| 768 | const spilled_inst = regs_entry.value.inst; | ||
| 769 | |||
| 770 | const stack_mcv = try self.allocRegOrMem(spilled_inst, false); | ||
| 771 | const inst_entry = branch.inst_table.getEntry(spilled_inst).?; | ||
| 772 | const reg_mcv = inst_entry.value; | ||
| 773 | assert(reg == toCanonicalReg(reg_mcv.register)); | ||
| 774 | inst_entry.value = stack_mcv; | ||
| 775 | try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv); | ||
| 776 | |||
| 777 | break :b reg; | ||
| 778 | }; | ||
| 779 | try self.genSetReg(src, reg, mcv); | ||
| 780 | return reg; | ||
| 781 | } | ||
| 782 | |||
| 783 | /// Allocates a new register and copies `mcv` into it. | ||
| 784 | /// `reg_owner` is the instruction that gets associated with the register in the register table. | ||
| 785 | /// This can have a side effect of spilling instructions to the stack to free up a register. | ||
| 786 | fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue { | ||
| 742 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 787 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 743 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); | 788 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); |
| 744 | 789 | ||
| 745 | const reg = branch.allocReg(inst) orelse | 790 | const reg = branch.allocReg(reg_owner) orelse b: { |
| 746 | return self.fail(inst.src, "TODO implement spilling register to stack", .{}); | 791 | // We'll take over the first register. Move the instruction that was previously |
| 747 | const old_mcv = branch.inst_table.get(inst).?; | 792 | // there to a stack allocation. |
| 748 | const new_mcv: MCValue = .{ .register = reg }; | 793 | const reg = callee_preserved_regs[0]; |
| 749 | try self.genSetReg(inst.src, reg, old_mcv); | 794 | const regs_entry = branch.registers.getEntry(reg).?; |
| 750 | return new_mcv; | 795 | const spilled_inst = regs_entry.value.inst; |
| 796 | regs_entry.value = .{ .inst = reg_owner }; | ||
| 797 | |||
| 798 | const stack_mcv = try self.allocRegOrMem(spilled_inst, false); | ||
| 799 | const inst_entry = branch.inst_table.getEntry(spilled_inst).?; | ||
| 800 | const reg_mcv = inst_entry.value; | ||
| 801 | assert(reg == toCanonicalReg(reg_mcv.register)); | ||
| 802 | inst_entry.value = stack_mcv; | ||
| 803 | try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv); | ||
| 804 | |||
| 805 | break :b reg; | ||
| 806 | }; | ||
| 807 | try self.genSetReg(reg_owner.src, reg, mcv); | ||
| 808 | return MCValue{ .register = reg }; | ||
| 751 | } | 809 | } |
| 752 | 810 | ||
| 753 | fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue { | 811 | fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue { |
| ... | @@ -868,13 +926,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -868,13 +926,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 868 | } | 926 | } |
| 869 | } | 927 | } |
| 870 | 928 | ||
| 871 | fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool { | 929 | fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool { |
| 872 | if (!inst.operandDies(op_index) or !mcv.isMutable()) | 930 | if (!inst.operandDies(op_index)) |
| 873 | return false; | 931 | return false; |
| 874 | 932 | ||
| 875 | // OK we're going to do it, but we need to clear the operand death bit so that | 933 | switch (mcv) { |
| 876 | // it stays allocated. | 934 | .register => |reg| { |
| 935 | // If it's in the registers table, need to associate the register with the | ||
| 936 | // new instruction. | ||
| 937 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 938 | if (branch.registers.getEntry(toCanonicalReg(reg))) |entry| { | ||
| 939 | entry.value = .{ .inst = inst }; | ||
| 940 | } | ||
| 941 | log.debug("reusing {} => {*}", .{reg, inst}); | ||
| 942 | }, | ||
| 943 | .stack_offset => |off| { | ||
| 944 | log.debug("reusing stack offset {} => {*}", .{off, inst}); | ||
| 945 | return true; | ||
| 946 | }, | ||
| 947 | else => return false, | ||
| 948 | } | ||
| 949 | |||
| 950 | // Prevent the operand deaths processing code from deallocating it. | ||
| 877 | inst.clearOperandDeath(op_index); | 951 | inst.clearOperandDeath(op_index); |
| 952 | |||
| 878 | return true; | 953 | return true; |
| 879 | } | 954 | } |
| 880 | 955 | ||
| ... | @@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 887 | if (inst.base.isUnused() and !is_volatile) | 962 | if (inst.base.isUnused() and !is_volatile) |
| 888 | return MCValue.dead; | 963 | return MCValue.dead; |
| 889 | const dst_mcv: MCValue = blk: { | 964 | const dst_mcv: MCValue = blk: { |
| 890 | if (reuseOperand(&inst.base, 0, ptr)) { | 965 | if (self.reuseOperand(&inst.base, 0, ptr)) { |
| 891 | // The MCValue that holds the pointer can be re-used as the value. | 966 | // The MCValue that holds the pointer can be re-used as the value. |
| 892 | break :blk ptr; | 967 | break :blk ptr; |
| 893 | } else { | 968 | } else { |
| 894 | break :blk try self.allocRegOrMem(&inst.base); | 969 | break :blk try self.allocRegOrMem(&inst.base, true); |
| 895 | } | 970 | } |
| 896 | }; | 971 | }; |
| 897 | switch (ptr) { | 972 | switch (ptr) { |
| ... | @@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 985 | var dst_mcv: MCValue = undefined; | 1060 | var dst_mcv: MCValue = undefined; |
| 986 | var src_mcv: MCValue = undefined; | 1061 | var src_mcv: MCValue = undefined; |
| 987 | var src_inst: *ir.Inst = undefined; | 1062 | var src_inst: *ir.Inst = undefined; |
| 988 | if (reuseOperand(inst, 0, lhs)) { | 1063 | if (self.reuseOperand(inst, 0, lhs)) { |
| 989 | // LHS dies; use it as the destination. | 1064 | // LHS dies; use it as the destination. |
| 990 | // Both operands cannot be memory. | 1065 | // Both operands cannot be memory. |
| 991 | src_inst = op_rhs; | 1066 | src_inst = op_rhs; |
| 992 | if (lhs.isMemory() and rhs.isMemory()) { | 1067 | if (lhs.isMemory() and rhs.isMemory()) { |
| 993 | dst_mcv = try self.copyToNewRegister(op_lhs); | 1068 | dst_mcv = try self.copyToNewRegister(inst, lhs); |
| 994 | src_mcv = rhs; | 1069 | src_mcv = rhs; |
| 995 | } else { | 1070 | } else { |
| 996 | dst_mcv = lhs; | 1071 | dst_mcv = lhs; |
| 997 | src_mcv = rhs; | 1072 | src_mcv = rhs; |
| 998 | } | 1073 | } |
| 999 | } else if (reuseOperand(inst, 1, rhs)) { | 1074 | } else if (self.reuseOperand(inst, 1, rhs)) { |
| 1000 | // RHS dies; use it as the destination. | 1075 | // RHS dies; use it as the destination. |
| 1001 | // Both operands cannot be memory. | 1076 | // Both operands cannot be memory. |
| 1002 | src_inst = op_lhs; | 1077 | src_inst = op_lhs; |
| 1003 | if (lhs.isMemory() and rhs.isMemory()) { | 1078 | if (lhs.isMemory() and rhs.isMemory()) { |
| 1004 | dst_mcv = try self.copyToNewRegister(op_rhs); | 1079 | dst_mcv = try self.copyToNewRegister(inst, rhs); |
| 1005 | src_mcv = lhs; | 1080 | src_mcv = lhs; |
| 1006 | } else { | 1081 | } else { |
| 1007 | dst_mcv = rhs; | 1082 | dst_mcv = rhs; |
| ... | @@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1009 | } | 1084 | } |
| 1010 | } else { | 1085 | } else { |
| 1011 | if (lhs.isMemory()) { | 1086 | if (lhs.isMemory()) { |
| 1012 | dst_mcv = try self.copyToNewRegister(op_lhs); | 1087 | dst_mcv = try self.copyToNewRegister(inst, lhs); |
| 1013 | src_mcv = rhs; | 1088 | src_mcv = rhs; |
| 1014 | src_inst = op_rhs; | 1089 | src_inst = op_rhs; |
| 1015 | } else { | 1090 | } else { |
| 1016 | dst_mcv = try self.copyToNewRegister(op_rhs); | 1091 | dst_mcv = try self.copyToNewRegister(inst, rhs); |
| 1017 | src_mcv = lhs; | 1092 | src_mcv = lhs; |
| 1018 | src_inst = op_lhs; | 1093 | src_inst = op_lhs; |
| 1019 | } | 1094 | } |
| ... | @@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1026 | switch (src_mcv) { | 1101 | switch (src_mcv) { |
| 1027 | .immediate => |imm| { | 1102 | .immediate => |imm| { |
| 1028 | if (imm > math.maxInt(u31)) { | 1103 | if (imm > math.maxInt(u31)) { |
| 1029 | src_mcv = try self.copyToNewRegister(src_inst); | 1104 | src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) }; |
| 1030 | } | 1105 | } |
| 1031 | }, | 1106 | }, |
| 1032 | else => {}, | 1107 | else => {}, |
| 1033 | } | 1108 | } |
| 1034 | 1109 | ||
| 1035 | try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr); | 1110 | try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, opx, mr); |
| 1036 | 1111 | ||
| 1037 | return dst_mcv; | 1112 | return dst_mcv; |
| 1038 | } | 1113 | } |
| 1039 | 1114 | ||
| 1040 | fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void { | 1115 | fn genX8664BinMathCode( |
| 1116 | self: *Self, | ||
| 1117 | src: usize, | ||
| 1118 | dst_ty: Type, | ||
| 1119 | dst_mcv: MCValue, | ||
| 1120 | src_mcv: MCValue, | ||
| 1121 | opx: u8, | ||
| 1122 | mr: u8, | ||
| 1123 | ) !void { | ||
| 1041 | switch (dst_mcv) { | 1124 | switch (dst_mcv) { |
| 1042 | .none => unreachable, | 1125 | .none => unreachable, |
| 1043 | .undef => unreachable, | 1126 | .undef => unreachable, |
| ... | @@ -1087,12 +1170,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1087,12 +1170,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1087 | }, | 1170 | }, |
| 1088 | } | 1171 | } |
| 1089 | }, | 1172 | }, |
| 1090 | .embedded_in_code, .memory, .stack_offset => { | 1173 | .stack_offset => |off| { |
| 1174 | switch (src_mcv) { | ||
| 1175 | .none => unreachable, | ||
| 1176 | .undef => return self.genSetStack(src, dst_ty, off, .undef), | ||
| 1177 | .dead, .unreach => unreachable, | ||
| 1178 | .ptr_stack_offset => unreachable, | ||
| 1179 | .ptr_embedded_in_code => unreachable, | ||
| 1180 | .register => |src_reg| { | ||
| 1181 | try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1); | ||
| 1182 | }, | ||
| 1183 | .immediate => |imm| { | ||
| 1184 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{}); | ||
| 1185 | }, | ||
| 1186 | .embedded_in_code, .memory, .stack_offset => { | ||
| 1187 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{}); | ||
| 1188 | }, | ||
| 1189 | .compare_flags_unsigned => { | ||
| 1190 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); | ||
| 1191 | }, | ||
| 1192 | .compare_flags_signed => { | ||
| 1193 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{}); | ||
| 1194 | }, | ||
| 1195 | } | ||
| 1196 | }, | ||
| 1197 | .embedded_in_code, .memory => { | ||
| 1091 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{}); | 1198 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{}); |
| 1092 | }, | 1199 | }, |
| 1093 | } | 1200 | } |
| 1094 | } | 1201 | } |
| 1095 | 1202 | ||
| 1203 | fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void { | ||
| 1204 | const abi_size = ty.abiSize(self.target.*); | ||
| 1205 | const adj_off = off + abi_size; | ||
| 1206 | try self.code.ensureCapacity(self.code.items.len + 7); | ||
| 1207 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() }); | ||
| 1208 | const reg_id: u8 = @truncate(u3, reg.id()); | ||
| 1209 | if (adj_off <= 128) { | ||
| 1210 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx | ||
| 1211 | const RM = @as(u8, 0b01_000_101) | (reg_id << 3); | ||
| 1212 | const negative_offset = @intCast(i8, -@intCast(i32, adj_off)); | ||
| 1213 | const twos_comp = @bitCast(u8, negative_offset); | ||
| 1214 | self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp }); | ||
| 1215 | } else if (adj_off <= 2147483648) { | ||
| 1216 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx | ||
| 1217 | const RM = @as(u8, 0b10_000_101) | (reg_id << 3); | ||
| 1218 | const negative_offset = @intCast(i32, -@intCast(i33, adj_off)); | ||
| 1219 | const twos_comp = @bitCast(u32, negative_offset); | ||
| 1220 | self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM }); | ||
| 1221 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp); | ||
| 1222 | } else { | ||
| 1223 | return self.fail(src, "stack offset too large", .{}); | ||
| 1224 | } | ||
| 1225 | } | ||
| 1226 | |||
| 1096 | fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue { | 1227 | fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue { |
| 1097 | if (FreeRegInt == u0) { | 1228 | if (FreeRegInt == u0) { |
| 1098 | return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch}); | 1229 | return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch}); |
| ... | @@ -1109,7 +1240,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1109,7 +1240,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1109 | const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1]; | 1240 | const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1]; |
| 1110 | switch (result) { | 1241 | switch (result) { |
| 1111 | .register => |reg| { | 1242 | .register => |reg| { |
| 1112 | branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base }); | 1243 | branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base }); |
| 1113 | branch.markRegUsed(reg); | 1244 | branch.markRegUsed(reg); |
| 1114 | 1245 | ||
| 1115 | try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len); | 1246 | try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len); |
| ... | @@ -1134,6 +1265,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1134,6 +1265,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1134 | .riscv64 => { | 1265 | .riscv64 => { |
| 1135 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32()); | 1266 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32()); |
| 1136 | }, | 1267 | }, |
| 1268 | .spu_2 => { | ||
| 1269 | try self.code.resize(self.code.items.len + 2); | ||
| 1270 | var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 }; | ||
| 1271 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr)); | ||
| 1272 | }, | ||
| 1273 | .arm => { | ||
| 1274 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32()); | ||
| 1275 | }, | ||
| 1276 | .armeb => { | ||
| 1277 | mem.writeIntBig(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32()); | ||
| 1278 | }, | ||
| 1137 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), | 1279 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), |
| 1138 | } | 1280 | } |
| 1139 | return .none; | 1281 | return .none; |
| ... | @@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1219 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); | 1361 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); |
| 1220 | } | 1362 | } |
| 1221 | }, | 1363 | }, |
| 1364 | .spu_2 => { | ||
| 1365 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { | ||
| 1366 | if (info.args.len != 0) { | ||
| 1367 | return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{}); | ||
| 1368 | } | ||
| 1369 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | ||
| 1370 | const func = func_val.func; | ||
| 1371 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | ||
| 1372 | const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2); | ||
| 1373 | const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType(); | ||
| 1374 | // First, push the return address, then jump; if noreturn, don't bother with the first step | ||
| 1375 | // TODO: implement packed struct -> u16 at comptime and move the bitcast here | ||
| 1376 | var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 }; | ||
| 1377 | if (return_type.zigTypeTag() == .NoReturn) { | ||
| 1378 | try self.code.resize(self.code.items.len + 4); | ||
| 1379 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr)); | ||
| 1380 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr); | ||
| 1381 | return MCValue.unreach; | ||
| 1382 | } else { | ||
| 1383 | try self.code.resize(self.code.items.len + 8); | ||
| 1384 | var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget }; | ||
| 1385 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push)); | ||
| 1386 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4)); | ||
| 1387 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr)); | ||
| 1388 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr); | ||
| 1389 | switch (return_type.zigTypeTag()) { | ||
| 1390 | .Void => return MCValue{ .none = {} }, | ||
| 1391 | .NoReturn => unreachable, | ||
| 1392 | else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}), | ||
| 1393 | } | ||
| 1394 | } | ||
| 1395 | } else { | ||
| 1396 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); | ||
| 1397 | } | ||
| 1398 | } else { | ||
| 1399 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); | ||
| 1400 | } | ||
| 1401 | }, | ||
| 1402 | .arm => { | ||
| 1403 | if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch}); | ||
| 1404 | |||
| 1405 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { | ||
| 1406 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | ||
| 1407 | const func = func_val.func; | ||
| 1408 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | ||
| 1409 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | ||
| 1410 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | ||
| 1411 | const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); | ||
| 1412 | |||
| 1413 | // TODO only works with leaf functions | ||
| 1414 | // at the moment, which works fine for | ||
| 1415 | // Hello World, but not for real code | ||
| 1416 | // of course. Add pushing lr to stack | ||
| 1417 | // and popping after call | ||
| 1418 | try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); | ||
| 1419 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); | ||
| 1420 | } else { | ||
| 1421 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); | ||
| 1422 | } | ||
| 1423 | } else { | ||
| 1424 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); | ||
| 1425 | } | ||
| 1426 | }, | ||
| 1222 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), | 1427 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), |
| 1223 | } | 1428 | } |
| 1224 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 1429 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 1225 | return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO", .{}); | 1430 | switch (arch) { |
| 1431 | .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}), | ||
| 1432 | .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}), | ||
| 1433 | else => unreachable, | ||
| 1434 | } | ||
| 1226 | } else { | 1435 | } else { |
| 1227 | unreachable; | 1436 | unreachable; |
| 1228 | } | 1437 | } |
| ... | @@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1275 | .riscv64 => { | 1484 | .riscv64 => { |
| 1276 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); | 1485 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); |
| 1277 | }, | 1486 | }, |
| 1487 | .arm => { | ||
| 1488 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); | ||
| 1489 | }, | ||
| 1278 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), | 1490 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 1279 | } | 1491 | } |
| 1280 | return .unreach; | 1492 | return .unreach; |
| ... | @@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1304 | // Either one, but not both, can be a memory operand. | 1516 | // Either one, but not both, can be a memory operand. |
| 1305 | // Source operand can be an immediate, 8 bits or 32 bits. | 1517 | // Source operand can be an immediate, 8 bits or 32 bits. |
| 1306 | const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory())) | 1518 | const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory())) |
| 1307 | try self.copyToNewRegister(inst.lhs) | 1519 | try self.copyToNewRegister(&inst.base, lhs) |
| 1308 | else | 1520 | else |
| 1309 | lhs; | 1521 | lhs; |
| 1310 | // This instruction supports only signed 32-bit immediates at most. | 1522 | // This instruction supports only signed 32-bit immediates at most. |
| 1311 | const src_mcv = try self.limitImmediateType(inst.rhs, i32); | 1523 | const src_mcv = try self.limitImmediateType(inst.rhs, i32); |
| 1312 | 1524 | ||
| 1313 | try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38); | 1525 | try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38); |
| 1314 | const info = inst.lhs.ty.intInfo(self.target.*); | 1526 | const info = inst.lhs.ty.intInfo(self.target.*); |
| 1315 | if (info.signed) { | 1527 | if (info.signed) { |
| 1316 | return MCValue{ .compare_flags_signed = op }; | 1528 | return MCValue{ .compare_flags_signed = op }; |
| ... | @@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1512 | if (!inst.is_volatile and inst.base.isUnused()) | 1724 | if (!inst.is_volatile and inst.base.isUnused()) |
| 1513 | return MCValue.dead; | 1725 | return MCValue.dead; |
| 1514 | switch (arch) { | 1726 | switch (arch) { |
| 1727 | .spu_2 => { | ||
| 1728 | if (inst.inputs.len > 0 or inst.output != null) { | ||
| 1729 | return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{}); | ||
| 1730 | } | ||
| 1731 | if (mem.eql(u8, inst.asm_source, "undefined0")) { | ||
| 1732 | try self.code.resize(self.code.items.len + 2); | ||
| 1733 | var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 }; | ||
| 1734 | mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr)); | ||
| 1735 | return MCValue.none; | ||
| 1736 | } else { | ||
| 1737 | return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{}); | ||
| 1738 | } | ||
| 1739 | }, | ||
| 1740 | .arm => { | ||
| 1741 | for (inst.inputs) |input, i| { | ||
| 1742 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { | ||
| 1743 | return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); | ||
| 1744 | } | ||
| 1745 | const reg_name = input[1 .. input.len - 1]; | ||
| 1746 | const reg = parseRegName(reg_name) orelse | ||
| 1747 | return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); | ||
| 1748 | const arg = try self.resolveInst(inst.args[i]); | ||
| 1749 | try self.genSetReg(inst.base.src, reg, arg); | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | if (mem.eql(u8, inst.asm_source, "svc #0")) { | ||
| 1753 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32()); | ||
| 1754 | } else { | ||
| 1755 | return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | if (inst.output) |output| { | ||
| 1759 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | ||
| 1760 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); | ||
| 1761 | } | ||
| 1762 | const reg_name = output[2 .. output.len - 1]; | ||
| 1763 | const reg = parseRegName(reg_name) orelse | ||
| 1764 | return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); | ||
| 1765 | return MCValue{ .register = reg }; | ||
| 1766 | } else { | ||
| 1767 | return MCValue.none; | ||
| 1768 | } | ||
| 1769 | }, | ||
| 1515 | .riscv64 => { | 1770 | .riscv64 => { |
| 1516 | for (inst.inputs) |input, i| { | 1771 | for (inst.inputs) |input, i| { |
| 1517 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { | 1772 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { |
| ... | @@ -1584,7 +1839,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1584,7 +1839,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1584 | /// resulting REX is meaningful, but will remain the same if it is not. | 1839 | /// resulting REX is meaningful, but will remain the same if it is not. |
| 1585 | /// * Deliberately inserting a "meaningless REX" requires explicit usage of | 1840 | /// * Deliberately inserting a "meaningless REX" requires explicit usage of |
| 1586 | /// 0x40, and cannot be done via this function. | 1841 | /// 0x40, and cannot be done via this function. |
| 1842 | /// W => 64 bit mode | ||
| 1843 | /// R => extension to the MODRM.reg field | ||
| 1844 | /// X => extension to the SIB.index field | ||
| 1845 | /// B => extension to the MODRM.rm field or the SIB.base field | ||
| 1587 | fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void { | 1846 | fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void { |
| 1847 | comptime assert(arch == .x86_64); | ||
| 1588 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. | 1848 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. |
| 1589 | var value: u8 = 0x40; | 1849 | var value: u8 = 0x40; |
| 1590 | if (arg.b) { | 1850 | if (arg.b) { |
| ... | @@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1681 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); | 1941 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); |
| 1682 | }, | 1942 | }, |
| 1683 | .register => |reg| { | 1943 | .register => |reg| { |
| 1684 | const abi_size = ty.abiSize(self.target.*); | 1944 | try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); |
| 1685 | const adj_off = stack_offset + abi_size; | ||
| 1686 | try self.code.ensureCapacity(self.code.items.len + 7); | ||
| 1687 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() }); | ||
| 1688 | const reg_id: u8 = @truncate(u3, reg.id()); | ||
| 1689 | if (adj_off <= 128) { | ||
| 1690 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx | ||
| 1691 | const RM = @as(u8, 0b01_000_101) | (reg_id << 3); | ||
| 1692 | const negative_offset = @intCast(i8, -@intCast(i32, adj_off)); | ||
| 1693 | const twos_comp = @bitCast(u8, negative_offset); | ||
| 1694 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp }); | ||
| 1695 | } else if (adj_off <= 2147483648) { | ||
| 1696 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx | ||
| 1697 | const RM = @as(u8, 0b10_000_101) | (reg_id << 3); | ||
| 1698 | const negative_offset = @intCast(i32, -@intCast(i33, adj_off)); | ||
| 1699 | const twos_comp = @bitCast(u32, negative_offset); | ||
| 1700 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM }); | ||
| 1701 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp); | ||
| 1702 | } else { | ||
| 1703 | return self.fail(src, "stack offset too large", .{}); | ||
| 1704 | } | ||
| 1705 | }, | 1945 | }, |
| 1706 | .memory => |vaddr| { | 1946 | .memory => |vaddr| { |
| 1707 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); | 1947 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); |
| ... | @@ -1709,7 +1949,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1709,7 +1949,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1709 | .stack_offset => |off| { | 1949 | .stack_offset => |off| { |
| 1710 | if (stack_offset == off) | 1950 | if (stack_offset == off) |
| 1711 | return; // Copy stack variable to itself; nothing to do. | 1951 | return; // Copy stack variable to itself; nothing to do. |
| 1712 | return self.fail(src, "TODO implement copy stack variable to stack variable", .{}); | 1952 | |
| 1953 | const reg = try self.copyToTmpRegister(src, mcv); | ||
| 1954 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); | ||
| 1713 | }, | 1955 | }, |
| 1714 | }, | 1956 | }, |
| 1715 | else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}), | 1957 | else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}), |
| ... | @@ -1718,6 +1960,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1718,6 +1960,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1718 | 1960 | ||
| 1719 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { | 1961 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { |
| 1720 | switch (arch) { | 1962 | switch (arch) { |
| 1963 | .arm => switch (mcv) { | ||
| 1964 | .dead => unreachable, | ||
| 1965 | .ptr_stack_offset => unreachable, | ||
| 1966 | .ptr_embedded_in_code => unreachable, | ||
| 1967 | .unreach, .none => return, // Nothing to do. | ||
| 1968 | .undef => { | ||
| 1969 | if (!self.wantSafety()) | ||
| 1970 | return; // The already existing value will do just fine. | ||
| 1971 | // Write the debug undefined value. | ||
| 1972 | return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); | ||
| 1973 | }, | ||
| 1974 | .immediate => |x| { | ||
| 1975 | // TODO better analysis of x to determine the | ||
| 1976 | // least amount of necessary instructions (use | ||
| 1977 | // more intelligent rotating) | ||
| 1978 | if (x <= math.maxInt(u8)) { | ||
| 1979 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | ||
| 1980 | return; | ||
| 1981 | } else if (x <= math.maxInt(u16)) { | ||
| 1982 | // TODO Use movw Note: Not supported on | ||
| 1983 | // all ARM targets! | ||
| 1984 | |||
| 1985 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | ||
| 1986 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | ||
| 1987 | } else if (x <= math.maxInt(u32)) { | ||
| 1988 | // TODO Use movw and movt Note: Not | ||
| 1989 | // supported on all ARM targets! Also TODO | ||
| 1990 | // write constant to code and load | ||
| 1991 | // relative to pc | ||
| 1992 | |||
| 1993 | // immediate: 0xaabbccdd | ||
| 1994 | // mov reg, #0xaa | ||
| 1995 | // orr reg, reg, #0xbb, 24 | ||
| 1996 | // orr reg, reg, #0xcc, 16 | ||
| 1997 | // orr reg, reg, #0xdd, 8 | ||
| 1998 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | ||
| 1999 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | ||
| 2000 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); | ||
| 2001 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); | ||
| 2002 | return; | ||
| 2003 | } else { | ||
| 2004 | return self.fail(src, "ARM registers are 32-bit wide", .{}); | ||
| 2005 | } | ||
| 2006 | }, | ||
| 2007 | .memory => |addr| { | ||
| 2008 | // The value is in memory at a hard-coded address. | ||
| 2009 | // If the type is a pointer, it means the pointer address is at this memory location. | ||
| 2010 | try self.genSetReg(src, reg, .{ .immediate = addr }); | ||
| 2011 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32()); | ||
| 2012 | }, | ||
| 2013 | else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), | ||
| 2014 | }, | ||
| 1721 | .riscv64 => switch (mcv) { | 2015 | .riscv64 => switch (mcv) { |
| 1722 | .dead => unreachable, | 2016 | .dead => unreachable, |
| 1723 | .ptr_stack_offset => unreachable, | 2017 | .ptr_stack_offset => unreachable, |
| ... | @@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2027 | }, | 2321 | }, |
| 2028 | }); | 2322 | }); |
| 2029 | if (imm >= math.maxInt(U)) { | 2323 | if (imm >= math.maxInt(U)) { |
| 2030 | return self.copyToNewRegister(inst); | 2324 | return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) }; |
| 2031 | } | 2325 | } |
| 2032 | }, | 2326 | }, |
| 2033 | else => {}, | 2327 | else => {}, |
| ... | @@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2150 | result.stack_byte_count = next_stack_offset; | 2444 | result.stack_byte_count = next_stack_offset; |
| 2151 | result.stack_align = 16; | 2445 | result.stack_align = 16; |
| 2152 | }, | 2446 | }, |
| 2153 | else => return self.fail(src, "TODO implement function parameters for {}", .{cc}), | 2447 | else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}), |
| 2154 | } | 2448 | } |
| 2155 | }, | 2449 | }, |
| 2156 | else => if (param_types.len != 0) | 2450 | else => if (param_types.len != 0) |
| ... | @@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2197 | .i386 => @import("codegen/x86.zig"), | 2491 | .i386 => @import("codegen/x86.zig"), |
| 2198 | .x86_64 => @import("codegen/x86_64.zig"), | 2492 | .x86_64 => @import("codegen/x86_64.zig"), |
| 2199 | .riscv64 => @import("codegen/riscv64.zig"), | 2493 | .riscv64 => @import("codegen/riscv64.zig"), |
| 2494 | .spu_2 => @import("codegen/spu-mk2.zig"), | ||
| 2495 | .arm => @import("codegen/arm.zig"), | ||
| 2496 | .armeb => @import("codegen/arm.zig"), | ||
| 2200 | else => struct { | 2497 | else => struct { |
| 2201 | pub const Register = enum { | 2498 | pub const Register = enum { |
| 2202 | dummy, | 2499 | dummy, |
src-self-hosted/codegen/arm.zig created+607| ... | @@ -0,0 +1,607 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | /// The condition field specifies the flags neccessary for an | ||
| 6 | /// Instruction to be executed | ||
| 7 | pub const Condition = enum(u4) { | ||
| 8 | /// equal | ||
| 9 | eq, | ||
| 10 | /// not equal | ||
| 11 | ne, | ||
| 12 | /// unsigned higher or same | ||
| 13 | cs, | ||
| 14 | /// unsigned lower | ||
| 15 | cc, | ||
| 16 | /// negative | ||
| 17 | mi, | ||
| 18 | /// positive or zero | ||
| 19 | pl, | ||
| 20 | /// overflow | ||
| 21 | vs, | ||
| 22 | /// no overflow | ||
| 23 | vc, | ||
| 24 | /// unsigned higer | ||
| 25 | hi, | ||
| 26 | /// unsigned lower or same | ||
| 27 | ls, | ||
| 28 | /// greater or equal | ||
| 29 | ge, | ||
| 30 | /// less than | ||
| 31 | lt, | ||
| 32 | /// greater than | ||
| 33 | gt, | ||
| 34 | /// less than or equal | ||
| 35 | le, | ||
| 36 | /// always | ||
| 37 | al, | ||
| 38 | }; | ||
| 39 | |||
| 40 | /// Represents a register in the ARM instruction set architecture | ||
| 41 | pub const Register = enum(u5) { | ||
| 42 | r0, | ||
| 43 | r1, | ||
| 44 | r2, | ||
| 45 | r3, | ||
| 46 | r4, | ||
| 47 | r5, | ||
| 48 | r6, | ||
| 49 | r7, | ||
| 50 | r8, | ||
| 51 | r9, | ||
| 52 | r10, | ||
| 53 | r11, | ||
| 54 | r12, | ||
| 55 | r13, | ||
| 56 | r14, | ||
| 57 | r15, | ||
| 58 | |||
| 59 | /// Argument / result / scratch register 1 | ||
| 60 | a1, | ||
| 61 | /// Argument / result / scratch register 2 | ||
| 62 | a2, | ||
| 63 | /// Argument / scratch register 3 | ||
| 64 | a3, | ||
| 65 | /// Argument / scratch register 4 | ||
| 66 | a4, | ||
| 67 | /// Variable-register 1 | ||
| 68 | v1, | ||
| 69 | /// Variable-register 2 | ||
| 70 | v2, | ||
| 71 | /// Variable-register 3 | ||
| 72 | v3, | ||
| 73 | /// Variable-register 4 | ||
| 74 | v4, | ||
| 75 | /// Variable-register 5 | ||
| 76 | v5, | ||
| 77 | /// Platform register | ||
| 78 | v6, | ||
| 79 | /// Variable-register 7 | ||
| 80 | v7, | ||
| 81 | /// Frame pointer or Variable-register 8 | ||
| 82 | fp, | ||
| 83 | /// Intra-Procedure-call scratch register | ||
| 84 | ip, | ||
| 85 | /// Stack pointer | ||
| 86 | sp, | ||
| 87 | /// Link register | ||
| 88 | lr, | ||
| 89 | /// Program counter | ||
| 90 | pc, | ||
| 91 | |||
| 92 | /// Returns the unique 4-bit ID of this register which is used in | ||
| 93 | /// the machine code | ||
| 94 | pub fn id(self: Register) u4 { | ||
| 95 | return @truncate(u4, @enumToInt(self)); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Returns the index into `callee_preserved_regs`. | ||
| 99 | pub fn allocIndex(self: Register) ?u4 { | ||
| 100 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 101 | if (self.id() == cpreg.id()) return i; | ||
| 102 | } | ||
| 103 | return null; | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 107 | return @as(u8, self.id()) + DW.OP_reg0; | ||
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | test "Register.id" { | ||
| 112 | testing.expectEqual(@as(u4, 15), Register.r15.id()); | ||
| 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); | ||
| 114 | } | ||
| 115 | |||
| 116 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; | ||
| 117 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 118 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ||
| 119 | |||
| 120 | /// Represents an instruction in the ARM instruction set architecture | ||
| 121 | pub const Instruction = union(enum) { | ||
| 122 | DataProcessing: packed struct { | ||
| 123 | // Note to self: The order of the fields top-to-bottom is | ||
| 124 | // right-to-left in the actual 32-bit int representation | ||
| 125 | op2: u12, | ||
| 126 | rd: u4, | ||
| 127 | rn: u4, | ||
| 128 | s: u1, | ||
| 129 | opcode: u4, | ||
| 130 | i: u1, | ||
| 131 | fixed: u2 = 0b00, | ||
| 132 | cond: u4, | ||
| 133 | }, | ||
| 134 | SingleDataTransfer: packed struct { | ||
| 135 | offset: u12, | ||
| 136 | rd: u4, | ||
| 137 | rn: u4, | ||
| 138 | l: u1, | ||
| 139 | w: u1, | ||
| 140 | b: u1, | ||
| 141 | u: u1, | ||
| 142 | p: u1, | ||
| 143 | i: u1, | ||
| 144 | fixed: u2 = 0b01, | ||
| 145 | cond: u4, | ||
| 146 | }, | ||
| 147 | Branch: packed struct { | ||
| 148 | offset: u24, | ||
| 149 | link: u1, | ||
| 150 | fixed: u3 = 0b101, | ||
| 151 | cond: u4, | ||
| 152 | }, | ||
| 153 | BranchExchange: packed struct { | ||
| 154 | rn: u4, | ||
| 155 | fixed_1: u1 = 0b1, | ||
| 156 | link: u1, | ||
| 157 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | ||
| 158 | cond: u4, | ||
| 159 | }, | ||
| 160 | SupervisorCall: packed struct { | ||
| 161 | comment: u24, | ||
| 162 | fixed: u4 = 0b1111, | ||
| 163 | cond: u4, | ||
| 164 | }, | ||
| 165 | Breakpoint: packed struct { | ||
| 166 | imm4: u4, | ||
| 167 | fixed_1: u4 = 0b0111, | ||
| 168 | imm12: u12, | ||
| 169 | fixed_2_and_cond: u12 = 0b1110_0001_0010, | ||
| 170 | }, | ||
| 171 | |||
| 172 | /// Represents the possible operations which can be performed by a | ||
| 173 | /// DataProcessing instruction | ||
| 174 | const Opcode = enum(u4) { | ||
| 175 | // Rd := Op1 AND Op2 | ||
| 176 | @"and", | ||
| 177 | // Rd := Op1 EOR Op2 | ||
| 178 | eor, | ||
| 179 | // Rd := Op1 - Op2 | ||
| 180 | sub, | ||
| 181 | // Rd := Op2 - Op1 | ||
| 182 | rsb, | ||
| 183 | // Rd := Op1 + Op2 | ||
| 184 | add, | ||
| 185 | // Rd := Op1 + Op2 + C | ||
| 186 | adc, | ||
| 187 | // Rd := Op1 - Op2 + C - 1 | ||
| 188 | sbc, | ||
| 189 | // Rd := Op2 - Op1 + C - 1 | ||
| 190 | rsc, | ||
| 191 | // set condition codes on Op1 AND Op2 | ||
| 192 | tst, | ||
| 193 | // set condition codes on Op1 EOR Op2 | ||
| 194 | teq, | ||
| 195 | // set condition codes on Op1 - Op2 | ||
| 196 | cmp, | ||
| 197 | // set condition codes on Op1 + Op2 | ||
| 198 | cmn, | ||
| 199 | // Rd := Op1 OR Op2 | ||
| 200 | orr, | ||
| 201 | // Rd := Op2 | ||
| 202 | mov, | ||
| 203 | // Rd := Op1 AND NOT Op2 | ||
| 204 | bic, | ||
| 205 | // Rd := NOT Op2 | ||
| 206 | mvn, | ||
| 207 | }; | ||
| 208 | |||
| 209 | /// Represents the second operand to a data processing instruction | ||
| 210 | /// which can either be content from a register or an immediate | ||
| 211 | /// value | ||
| 212 | pub const Operand = union(enum) { | ||
| 213 | Register: packed struct { | ||
| 214 | rm: u4, | ||
| 215 | shift: u8, | ||
| 216 | }, | ||
| 217 | Immediate: packed struct { | ||
| 218 | imm: u8, | ||
| 219 | rotate: u4, | ||
| 220 | }, | ||
| 221 | |||
| 222 | /// Represents multiple ways a register can be shifted. A | ||
| 223 | /// register can be shifted by a specific immediate value or | ||
| 224 | /// by the contents of another register | ||
| 225 | pub const Shift = union(enum) { | ||
| 226 | Immediate: packed struct { | ||
| 227 | fixed: u1 = 0b0, | ||
| 228 | typ: u2, | ||
| 229 | amount: u5, | ||
| 230 | }, | ||
| 231 | Register: packed struct { | ||
| 232 | fixed_1: u1 = 0b1, | ||
| 233 | typ: u2, | ||
| 234 | fixed_2: u1 = 0b0, | ||
| 235 | rs: u4, | ||
| 236 | }, | ||
| 237 | |||
| 238 | const Type = enum(u2) { | ||
| 239 | LogicalLeft, | ||
| 240 | LogicalRight, | ||
| 241 | ArithmeticRight, | ||
| 242 | RotateRight, | ||
| 243 | }; | ||
| 244 | |||
| 245 | const none = Shift{ | ||
| 246 | .Immediate = .{ | ||
| 247 | .amount = 0, | ||
| 248 | .typ = 0, | ||
| 249 | }, | ||
| 250 | }; | ||
| 251 | |||
| 252 | pub fn toU8(self: Shift) u8 { | ||
| 253 | return switch (self) { | ||
| 254 | .Register => |v| @bitCast(u8, v), | ||
| 255 | .Immediate => |v| @bitCast(u8, v), | ||
| 256 | }; | ||
| 257 | } | ||
| 258 | |||
| 259 | pub fn reg(rs: Register, typ: Type) Shift { | ||
| 260 | return Shift{ | ||
| 261 | .Register = .{ | ||
| 262 | .rs = rs.id(), | ||
| 263 | .typ = @enumToInt(typ), | ||
| 264 | }, | ||
| 265 | }; | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn imm(amount: u5, typ: Type) Shift { | ||
| 269 | return Shift{ | ||
| 270 | .Immediate = .{ | ||
| 271 | .amount = amount, | ||
| 272 | .typ = @enumToInt(typ), | ||
| 273 | }, | ||
| 274 | }; | ||
| 275 | } | ||
| 276 | }; | ||
| 277 | |||
| 278 | pub fn toU12(self: Operand) u12 { | ||
| 279 | return switch (self) { | ||
| 280 | .Register => |v| @bitCast(u12, v), | ||
| 281 | .Immediate => |v| @bitCast(u12, v), | ||
| 282 | }; | ||
| 283 | } | ||
| 284 | |||
| 285 | pub fn reg(rm: Register, shift: Shift) Operand { | ||
| 286 | return Operand{ | ||
| 287 | .Register = .{ | ||
| 288 | .rm = rm.id(), | ||
| 289 | .shift = shift.toU8(), | ||
| 290 | }, | ||
| 291 | }; | ||
| 292 | } | ||
| 293 | |||
| 294 | pub fn imm(immediate: u8, rotate: u4) Operand { | ||
| 295 | return Operand{ | ||
| 296 | .Immediate = .{ | ||
| 297 | .imm = immediate, | ||
| 298 | .rotate = rotate, | ||
| 299 | }, | ||
| 300 | }; | ||
| 301 | } | ||
| 302 | }; | ||
| 303 | |||
| 304 | /// Represents the offset operand of a load or store | ||
| 305 | /// instruction. Data can be loaded from memory with either an | ||
| 306 | /// immediate offset or an offset that is stored in some register. | ||
| 307 | pub const Offset = union(enum) { | ||
| 308 | Immediate: u12, | ||
| 309 | Register: packed struct { | ||
| 310 | rm: u4, | ||
| 311 | shift: u8, | ||
| 312 | }, | ||
| 313 | |||
| 314 | pub const none = Offset{ | ||
| 315 | .Immediate = 0, | ||
| 316 | }; | ||
| 317 | |||
| 318 | pub fn toU12(self: Offset) u12 { | ||
| 319 | return switch (self) { | ||
| 320 | .Register => |v| @bitCast(u12, v), | ||
| 321 | .Immediate => |v| v, | ||
| 322 | }; | ||
| 323 | } | ||
| 324 | |||
| 325 | pub fn reg(rm: Register, shift: u8) Offset { | ||
| 326 | return Offset{ | ||
| 327 | .Register = .{ | ||
| 328 | .rm = rm.id(), | ||
| 329 | .shift = shift, | ||
| 330 | }, | ||
| 331 | }; | ||
| 332 | } | ||
| 333 | |||
| 334 | pub fn imm(immediate: u8) Offset { | ||
| 335 | return Offset{ | ||
| 336 | .Immediate = immediate, | ||
| 337 | }; | ||
| 338 | } | ||
| 339 | }; | ||
| 340 | |||
| 341 | pub fn toU32(self: Instruction) u32 { | ||
| 342 | return switch (self) { | ||
| 343 | .DataProcessing => |v| @bitCast(u32, v), | ||
| 344 | .SingleDataTransfer => |v| @bitCast(u32, v), | ||
| 345 | .Branch => |v| @bitCast(u32, v), | ||
| 346 | .BranchExchange => |v| @bitCast(u32, v), | ||
| 347 | .SupervisorCall => |v| @bitCast(u32, v), | ||
| 348 | .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), | ||
| 349 | }; | ||
| 350 | } | ||
| 351 | |||
| 352 | // Helper functions for the "real" functions below | ||
| 353 | |||
| 354 | fn dataProcessing( | ||
| 355 | cond: Condition, | ||
| 356 | opcode: Opcode, | ||
| 357 | s: u1, | ||
| 358 | rd: Register, | ||
| 359 | rn: Register, | ||
| 360 | op2: Operand, | ||
| 361 | ) Instruction { | ||
| 362 | return Instruction{ | ||
| 363 | .DataProcessing = .{ | ||
| 364 | .cond = @enumToInt(cond), | ||
| 365 | .i = if (op2 == .Immediate) 1 else 0, | ||
| 366 | .opcode = @enumToInt(opcode), | ||
| 367 | .s = s, | ||
| 368 | .rn = rn.id(), | ||
| 369 | .rd = rd.id(), | ||
| 370 | .op2 = op2.toU12(), | ||
| 371 | }, | ||
| 372 | }; | ||
| 373 | } | ||
| 374 | |||
| 375 | fn singleDataTransfer( | ||
| 376 | cond: Condition, | ||
| 377 | rd: Register, | ||
| 378 | rn: Register, | ||
| 379 | offset: Offset, | ||
| 380 | pre_post: u1, | ||
| 381 | up_down: u1, | ||
| 382 | byte_word: u1, | ||
| 383 | writeback: u1, | ||
| 384 | load_store: u1, | ||
| 385 | ) Instruction { | ||
| 386 | return Instruction{ | ||
| 387 | .SingleDataTransfer = .{ | ||
| 388 | .cond = @enumToInt(cond), | ||
| 389 | .rn = rn.id(), | ||
| 390 | .rd = rd.id(), | ||
| 391 | .offset = offset.toU12(), | ||
| 392 | .l = load_store, | ||
| 393 | .w = writeback, | ||
| 394 | .b = byte_word, | ||
| 395 | .u = up_down, | ||
| 396 | .p = pre_post, | ||
| 397 | .i = if (offset == .Immediate) 0 else 1, | ||
| 398 | }, | ||
| 399 | }; | ||
| 400 | } | ||
| 401 | |||
| 402 | fn branch(cond: Condition, offset: i24, link: u1) Instruction { | ||
| 403 | return Instruction{ | ||
| 404 | .Branch = .{ | ||
| 405 | .cond = @enumToInt(cond), | ||
| 406 | .link = link, | ||
| 407 | .offset = @bitCast(u24, offset), | ||
| 408 | }, | ||
| 409 | }; | ||
| 410 | } | ||
| 411 | |||
| 412 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | ||
| 413 | return Instruction{ | ||
| 414 | .BranchExchange = .{ | ||
| 415 | .cond = @enumToInt(cond), | ||
| 416 | .link = link, | ||
| 417 | .rn = rn.id(), | ||
| 418 | }, | ||
| 419 | }; | ||
| 420 | } | ||
| 421 | |||
| 422 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | ||
| 423 | return Instruction{ | ||
| 424 | .SupervisorCall = .{ | ||
| 425 | .cond = @enumToInt(cond), | ||
| 426 | .comment = comment, | ||
| 427 | }, | ||
| 428 | }; | ||
| 429 | } | ||
| 430 | |||
| 431 | fn breakpoint(imm: u16) Instruction { | ||
| 432 | return Instruction{ | ||
| 433 | .Breakpoint = .{ | ||
| 434 | .imm12 = @truncate(u12, imm >> 4), | ||
| 435 | .imm4 = @truncate(u4, imm), | ||
| 436 | }, | ||
| 437 | }; | ||
| 438 | } | ||
| 439 | |||
| 440 | // Public functions replicating assembler syntax as closely as | ||
| 441 | // possible | ||
| 442 | |||
| 443 | // Data processing | ||
| 444 | |||
| 445 | pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 446 | return dataProcessing(cond, .@"and", s, rd, rn, op2); | ||
| 447 | } | ||
| 448 | |||
| 449 | pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 450 | return dataProcessing(cond, .eor, s, rd, rn, op2); | ||
| 451 | } | ||
| 452 | |||
| 453 | pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 454 | return dataProcessing(cond, .sub, s, rd, rn, op2); | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 458 | return dataProcessing(cond, .rsb, s, rd, rn, op2); | ||
| 459 | } | ||
| 460 | |||
| 461 | pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 462 | return dataProcessing(cond, .add, s, rd, rn, op2); | ||
| 463 | } | ||
| 464 | |||
| 465 | pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 466 | return dataProcessing(cond, .adc, s, rd, rn, op2); | ||
| 467 | } | ||
| 468 | |||
| 469 | pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 470 | return dataProcessing(cond, .sbc, s, rd, rn, op2); | ||
| 471 | } | ||
| 472 | |||
| 473 | pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 474 | return dataProcessing(cond, .rsc, s, rd, rn, op2); | ||
| 475 | } | ||
| 476 | |||
| 477 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 478 | return dataProcessing(cond, .tst, 1, .r0, rn, op2); | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 482 | return dataProcessing(cond, .teq, 1, .r0, rn, op2); | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 486 | return dataProcessing(cond, .cmp, 1, .r0, rn, op2); | ||
| 487 | } | ||
| 488 | |||
| 489 | pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 490 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | ||
| 491 | } | ||
| 492 | |||
| 493 | pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 494 | return dataProcessing(cond, .orr, s, rd, rn, op2); | ||
| 495 | } | ||
| 496 | |||
| 497 | pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | ||
| 498 | return dataProcessing(cond, .mov, s, rd, .r0, op2); | ||
| 499 | } | ||
| 500 | |||
| 501 | pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | ||
| 502 | return dataProcessing(cond, .bic, s, rd, rn, op2); | ||
| 503 | } | ||
| 504 | |||
| 505 | pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | ||
| 506 | return dataProcessing(cond, .mvn, s, rd, .r0, op2); | ||
| 507 | } | ||
| 508 | |||
| 509 | // Single data transfer | ||
| 510 | |||
| 511 | pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | ||
| 512 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); | ||
| 513 | } | ||
| 514 | |||
| 515 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | ||
| 516 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); | ||
| 517 | } | ||
| 518 | |||
| 519 | // Branch | ||
| 520 | |||
| 521 | pub fn b(cond: Condition, offset: i24) Instruction { | ||
| 522 | return branch(cond, offset, 0); | ||
| 523 | } | ||
| 524 | |||
| 525 | pub fn bl(cond: Condition, offset: i24) Instruction { | ||
| 526 | return branch(cond, offset, 1); | ||
| 527 | } | ||
| 528 | |||
| 529 | // Branch and exchange | ||
| 530 | |||
| 531 | pub fn bx(cond: Condition, rn: Register) Instruction { | ||
| 532 | return branchExchange(cond, rn, 0); | ||
| 533 | } | ||
| 534 | |||
| 535 | pub fn blx(cond: Condition, rn: Register) Instruction { | ||
| 536 | return branchExchange(cond, rn, 1); | ||
| 537 | } | ||
| 538 | |||
| 539 | // Supervisor Call | ||
| 540 | |||
| 541 | pub const swi = svc; | ||
| 542 | |||
| 543 | pub fn svc(cond: Condition, comment: u24) Instruction { | ||
| 544 | return supervisorCall(cond, comment); | ||
| 545 | } | ||
| 546 | |||
| 547 | // Breakpoint | ||
| 548 | |||
| 549 | pub fn bkpt(imm: u16) Instruction { | ||
| 550 | return breakpoint(imm); | ||
| 551 | } | ||
| 552 | }; | ||
| 553 | |||
| 554 | test "serialize instructions" { | ||
| 555 | const Testcase = struct { | ||
| 556 | inst: Instruction, | ||
| 557 | expected: u32, | ||
| 558 | }; | ||
| 559 | |||
| 560 | const testcases = [_]Testcase{ | ||
| 561 | .{ // add r0, r0, r0 | ||
| 562 | .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | ||
| 563 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | ||
| 564 | }, | ||
| 565 | .{ // mov r4, r2 | ||
| 566 | .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | ||
| 567 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | ||
| 568 | }, | ||
| 569 | .{ // mov r0, #42 | ||
| 570 | .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), | ||
| 571 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | ||
| 572 | }, | ||
| 573 | .{ // ldr r0, [r2, #42] | ||
| 574 | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), | ||
| 575 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | ||
| 576 | }, | ||
| 577 | .{ // str r0, [r3] | ||
| 578 | .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), | ||
| 579 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | ||
| 580 | }, | ||
| 581 | .{ // b #12 | ||
| 582 | .inst = Instruction.b(.al, 12), | ||
| 583 | .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100, | ||
| 584 | }, | ||
| 585 | .{ // bl #-4 | ||
| 586 | .inst = Instruction.bl(.al, -4), | ||
| 587 | .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100, | ||
| 588 | }, | ||
| 589 | .{ // bx lr | ||
| 590 | .inst = Instruction.bx(.al, .lr), | ||
| 591 | .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, | ||
| 592 | }, | ||
| 593 | .{ // svc #0 | ||
| 594 | .inst = Instruction.svc(.al, 0), | ||
| 595 | .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, | ||
| 596 | }, | ||
| 597 | .{ // bkpt #42 | ||
| 598 | .inst = Instruction.bkpt(42), | ||
| 599 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | ||
| 600 | }, | ||
| 601 | }; | ||
| 602 | |||
| 603 | for (testcases) |case| { | ||
| 604 | const actual = case.inst.toU32(); | ||
| 605 | testing.expectEqual(case.expected, actual); | ||
| 606 | } | ||
| 607 | } | ||
src-self-hosted/codegen/spu-mk2.zig created+170| ... | @@ -0,0 +1,170 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter; | ||
| 4 | |||
| 5 | pub const ExecutionCondition = enum(u3) { | ||
| 6 | always = 0, | ||
| 7 | when_zero = 1, | ||
| 8 | not_zero = 2, | ||
| 9 | greater_zero = 3, | ||
| 10 | less_than_zero = 4, | ||
| 11 | greater_or_equal_zero = 5, | ||
| 12 | less_or_equal_zero = 6, | ||
| 13 | overflow = 7, | ||
| 14 | }; | ||
| 15 | |||
| 16 | pub const InputBehaviour = enum(u2) { | ||
| 17 | zero = 0, | ||
| 18 | immediate = 1, | ||
| 19 | peek = 2, | ||
| 20 | pop = 3, | ||
| 21 | }; | ||
| 22 | |||
| 23 | pub const OutputBehaviour = enum(u2) { | ||
| 24 | discard = 0, | ||
| 25 | push = 1, | ||
| 26 | jump = 2, | ||
| 27 | jump_relative = 3, | ||
| 28 | }; | ||
| 29 | |||
| 30 | pub const Command = enum(u5) { | ||
| 31 | copy = 0, | ||
| 32 | ipget = 1, | ||
| 33 | get = 2, | ||
| 34 | set = 3, | ||
| 35 | store8 = 4, | ||
| 36 | store16 = 5, | ||
| 37 | load8 = 6, | ||
| 38 | load16 = 7, | ||
| 39 | undefined0 = 8, | ||
| 40 | undefined1 = 9, | ||
| 41 | frget = 10, | ||
| 42 | frset = 11, | ||
| 43 | bpget = 12, | ||
| 44 | bpset = 13, | ||
| 45 | spget = 14, | ||
| 46 | spset = 15, | ||
| 47 | add = 16, | ||
| 48 | sub = 17, | ||
| 49 | mul = 18, | ||
| 50 | div = 19, | ||
| 51 | mod = 20, | ||
| 52 | @"and" = 21, | ||
| 53 | @"or" = 22, | ||
| 54 | xor = 23, | ||
| 55 | not = 24, | ||
| 56 | signext = 25, | ||
| 57 | rol = 26, | ||
| 58 | ror = 27, | ||
| 59 | bswap = 28, | ||
| 60 | asr = 29, | ||
| 61 | lsl = 30, | ||
| 62 | lsr = 31, | ||
| 63 | }; | ||
| 64 | |||
| 65 | pub const Instruction = packed struct { | ||
| 66 | condition: ExecutionCondition, | ||
| 67 | input0: InputBehaviour, | ||
| 68 | input1: InputBehaviour, | ||
| 69 | modify_flags: bool, | ||
| 70 | output: OutputBehaviour, | ||
| 71 | command: Command, | ||
| 72 | reserved: u1 = 0, | ||
| 73 | |||
| 74 | pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void { | ||
| 75 | try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)}); | ||
| 76 | try out.writeAll(switch (instr.condition) { | ||
| 77 | .always => " ", | ||
| 78 | .when_zero => "== 0", | ||
| 79 | .not_zero => "!= 0", | ||
| 80 | .greater_zero => " > 0", | ||
| 81 | .less_than_zero => " < 0", | ||
| 82 | .greater_or_equal_zero => ">= 0", | ||
| 83 | .less_or_equal_zero => "<= 0", | ||
| 84 | .overflow => "ovfl", | ||
| 85 | }); | ||
| 86 | try out.writeAll(" "); | ||
| 87 | try out.writeAll(switch (instr.input0) { | ||
| 88 | .zero => "zero", | ||
| 89 | .immediate => "imm ", | ||
| 90 | .peek => "peek", | ||
| 91 | .pop => "pop ", | ||
| 92 | }); | ||
| 93 | try out.writeAll(" "); | ||
| 94 | try out.writeAll(switch (instr.input1) { | ||
| 95 | .zero => "zero", | ||
| 96 | .immediate => "imm ", | ||
| 97 | .peek => "peek", | ||
| 98 | .pop => "pop ", | ||
| 99 | }); | ||
| 100 | try out.writeAll(" "); | ||
| 101 | try out.writeAll(switch (instr.command) { | ||
| 102 | .copy => "copy ", | ||
| 103 | .ipget => "ipget ", | ||
| 104 | .get => "get ", | ||
| 105 | .set => "set ", | ||
| 106 | .store8 => "store8 ", | ||
| 107 | .store16 => "store16 ", | ||
| 108 | .load8 => "load8 ", | ||
| 109 | .load16 => "load16 ", | ||
| 110 | .undefined0 => "undefined", | ||
| 111 | .undefined1 => "undefined", | ||
| 112 | .frget => "frget ", | ||
| 113 | .frset => "frset ", | ||
| 114 | .bpget => "bpget ", | ||
| 115 | .bpset => "bpset ", | ||
| 116 | .spget => "spget ", | ||
| 117 | .spset => "spset ", | ||
| 118 | .add => "add ", | ||
| 119 | .sub => "sub ", | ||
| 120 | .mul => "mul ", | ||
| 121 | .div => "div ", | ||
| 122 | .mod => "mod ", | ||
| 123 | .@"and" => "and ", | ||
| 124 | .@"or" => "or ", | ||
| 125 | .xor => "xor ", | ||
| 126 | .not => "not ", | ||
| 127 | .signext => "signext ", | ||
| 128 | .rol => "rol ", | ||
| 129 | .ror => "ror ", | ||
| 130 | .bswap => "bswap ", | ||
| 131 | .asr => "asr ", | ||
| 132 | .lsl => "lsl ", | ||
| 133 | .lsr => "lsr ", | ||
| 134 | }); | ||
| 135 | try out.writeAll(" "); | ||
| 136 | try out.writeAll(switch (instr.output) { | ||
| 137 | .discard => "discard", | ||
| 138 | .push => "push ", | ||
| 139 | .jump => "jmp ", | ||
| 140 | .jump_relative => "rjmp ", | ||
| 141 | }); | ||
| 142 | try out.writeAll(" "); | ||
| 143 | try out.writeAll(if (instr.modify_flags) | ||
| 144 | "+ flags" | ||
| 145 | else | ||
| 146 | " "); | ||
| 147 | } | ||
| 148 | }; | ||
| 149 | |||
| 150 | pub const FlagRegister = packed struct { | ||
| 151 | zero: bool, | ||
| 152 | negative: bool, | ||
| 153 | carry: bool, | ||
| 154 | carry_enabled: bool, | ||
| 155 | interrupt0_enabled: bool, | ||
| 156 | interrupt1_enabled: bool, | ||
| 157 | interrupt2_enabled: bool, | ||
| 158 | interrupt3_enabled: bool, | ||
| 159 | reserved: u8 = 0, | ||
| 160 | }; | ||
| 161 | |||
| 162 | pub const Register = enum { | ||
| 163 | dummy, | ||
| 164 | |||
| 165 | pub fn allocIndex(self: Register) ?u4 { | ||
| 166 | return null; | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | |||
| 170 | pub const callee_preserved_regs = [_]Register{}; | ||
src-self-hosted/codegen/spu-mk2/interpreter.zig created+166| ... | @@ -0,0 +1,166 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const log = std.log.scoped(.SPU_2_Interpreter); | ||
| 3 | const spu = @import("../spu-mk2.zig"); | ||
| 4 | const FlagRegister = spu.FlagRegister; | ||
| 5 | const Instruction = spu.Instruction; | ||
| 6 | const ExecutionCondition = spu.ExecutionCondition; | ||
| 7 | |||
| 8 | pub fn Interpreter(comptime Bus: type) type { | ||
| 9 | return struct { | ||
| 10 | ip: u16 = 0, | ||
| 11 | sp: u16 = undefined, | ||
| 12 | bp: u16 = undefined, | ||
| 13 | fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)), | ||
| 14 | /// This is set to true when we hit an undefined0 instruction, allowing it to | ||
| 15 | /// be used as a trap for testing purposes | ||
| 16 | undefined0: bool = false, | ||
| 17 | /// This is set to true when we hit an undefined1 instruction, allowing it to | ||
| 18 | /// be used as a trap for testing purposes. undefined1 is used as a breakpoint. | ||
| 19 | undefined1: bool = false, | ||
| 20 | bus: Bus, | ||
| 21 | |||
| 22 | pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void { | ||
| 23 | var count: usize = 0; | ||
| 24 | while (size == null or count < size.?) { | ||
| 25 | count += 1; | ||
| 26 | var instruction = @bitCast(Instruction, self.bus.read16(self.ip)); | ||
| 27 | |||
| 28 | log.debug("Executing {}\n", .{instruction}); | ||
| 29 | |||
| 30 | self.ip +%= 2; | ||
| 31 | |||
| 32 | const execute = switch (instruction.condition) { | ||
| 33 | .always => true, | ||
| 34 | .not_zero => !self.fr.zero, | ||
| 35 | .when_zero => self.fr.zero, | ||
| 36 | .overflow => self.fr.carry, | ||
| 37 | ExecutionCondition.greater_or_equal_zero => !self.fr.negative, | ||
| 38 | else => return error.Unimplemented, | ||
| 39 | }; | ||
| 40 | |||
| 41 | if (execute) { | ||
| 42 | const val0 = switch (instruction.input0) { | ||
| 43 | .zero => @as(u16, 0), | ||
| 44 | .immediate => i: { | ||
| 45 | const val = self.bus.read16(@intCast(u16, self.ip)); | ||
| 46 | self.ip +%= 2; | ||
| 47 | break :i val; | ||
| 48 | }, | ||
| 49 | else => |e| e: { | ||
| 50 | // peek or pop; show value at current SP, and if pop, increment sp | ||
| 51 | const val = self.bus.read16(self.sp); | ||
| 52 | if (e == .pop) { | ||
| 53 | self.sp +%= 2; | ||
| 54 | } | ||
| 55 | break :e val; | ||
| 56 | }, | ||
| 57 | }; | ||
| 58 | const val1 = switch (instruction.input1) { | ||
| 59 | .zero => @as(u16, 0), | ||
| 60 | .immediate => i: { | ||
| 61 | const val = self.bus.read16(@intCast(u16, self.ip)); | ||
| 62 | self.ip +%= 2; | ||
| 63 | break :i val; | ||
| 64 | }, | ||
| 65 | else => |e| e: { | ||
| 66 | // peek or pop; show value at current SP, and if pop, increment sp | ||
| 67 | const val = self.bus.read16(self.sp); | ||
| 68 | if (e == .pop) { | ||
| 69 | self.sp +%= 2; | ||
| 70 | } | ||
| 71 | break :e val; | ||
| 72 | }, | ||
| 73 | }; | ||
| 74 | |||
| 75 | const output: u16 = switch (instruction.command) { | ||
| 76 | .get => self.bus.read16(self.bp +% (2 *% val0)), | ||
| 77 | .set => a: { | ||
| 78 | self.bus.write16(self.bp +% 2 *% val0, val1); | ||
| 79 | break :a val1; | ||
| 80 | }, | ||
| 81 | .load8 => self.bus.read8(val0), | ||
| 82 | .load16 => self.bus.read16(val0), | ||
| 83 | .store8 => a: { | ||
| 84 | const val = @truncate(u8, val1); | ||
| 85 | self.bus.write8(val0, val); | ||
| 86 | break :a val; | ||
| 87 | }, | ||
| 88 | .store16 => a: { | ||
| 89 | self.bus.write16(val0, val1); | ||
| 90 | break :a val1; | ||
| 91 | }, | ||
| 92 | .copy => val0, | ||
| 93 | .add => a: { | ||
| 94 | var val: u16 = undefined; | ||
| 95 | self.fr.carry = @addWithOverflow(u16, val0, val1, &val); | ||
| 96 | break :a val; | ||
| 97 | }, | ||
| 98 | .sub => a: { | ||
| 99 | var val: u16 = undefined; | ||
| 100 | self.fr.carry = @subWithOverflow(u16, val0, val1, &val); | ||
| 101 | break :a val; | ||
| 102 | }, | ||
| 103 | .spset => a: { | ||
| 104 | self.sp = val0; | ||
| 105 | break :a val0; | ||
| 106 | }, | ||
| 107 | .bpset => a: { | ||
| 108 | self.bp = val0; | ||
| 109 | break :a val0; | ||
| 110 | }, | ||
| 111 | .frset => a: { | ||
| 112 | const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1); | ||
| 113 | self.fr = @bitCast(FlagRegister, val); | ||
| 114 | break :a val; | ||
| 115 | }, | ||
| 116 | .bswap => (val0 >> 8) | (val0 << 8), | ||
| 117 | .bpget => self.bp, | ||
| 118 | .spget => self.sp, | ||
| 119 | .ipget => self.ip +% (2 *% val0), | ||
| 120 | .lsl => val0 << 1, | ||
| 121 | .lsr => val0 >> 1, | ||
| 122 | .@"and" => val0 & val1, | ||
| 123 | .@"or" => val0 | val1, | ||
| 124 | .xor => val0 ^ val1, | ||
| 125 | .not => ~val0, | ||
| 126 | .undefined0 => { | ||
| 127 | self.undefined0 = true; | ||
| 128 | // Break out of the loop, and let the caller decide what to do | ||
| 129 | return; | ||
| 130 | }, | ||
| 131 | .undefined1 => { | ||
| 132 | self.undefined1 = true; | ||
| 133 | // Break out of the loop, and let the caller decide what to do | ||
| 134 | return; | ||
| 135 | }, | ||
| 136 | .signext => if ((val0 & 0x80) != 0) | ||
| 137 | (val0 & 0xFF) | 0xFF00 | ||
| 138 | else | ||
| 139 | (val0 & 0xFF), | ||
| 140 | else => return error.Unimplemented, | ||
| 141 | }; | ||
| 142 | |||
| 143 | switch (instruction.output) { | ||
| 144 | .discard => {}, | ||
| 145 | .push => { | ||
| 146 | self.sp -%= 2; | ||
| 147 | self.bus.write16(self.sp, output); | ||
| 148 | }, | ||
| 149 | .jump => { | ||
| 150 | self.ip = output; | ||
| 151 | }, | ||
| 152 | else => return error.Unimplemented, | ||
| 153 | } | ||
| 154 | if (instruction.modify_flags) { | ||
| 155 | self.fr.negative = (output & 0x8000) != 0; | ||
| 156 | self.fr.zero = (output == 0x0000); | ||
| 157 | } | ||
| 158 | } else { | ||
| 159 | if (instruction.input0 == .immediate) self.ip +%= 2; | ||
| 160 | if (instruction.input1 == .immediate) self.ip +%= 2; | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | } | ||
src-self-hosted/link.zig+4| ... | @@ -5,6 +5,9 @@ const fs = std.fs; | ... | @@ -5,6 +5,9 @@ const fs = std.fs; |
| 5 | const trace = @import("tracy.zig").trace; | 5 | const trace = @import("tracy.zig").trace; |
| 6 | const Package = @import("Package.zig"); | 6 | const Package = @import("Package.zig"); |
| 7 | const Type = @import("type.zig").Type; | 7 | const Type = @import("type.zig").Type; |
| 8 | const build_options = @import("build_options"); | ||
| 9 | |||
| 10 | pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; | ||
| 8 | 11 | ||
| 9 | pub const Options = struct { | 12 | pub const Options = struct { |
| 10 | target: std.Target, | 13 | target: std.Target, |
| ... | @@ -20,6 +23,7 @@ pub const Options = struct { | ... | @@ -20,6 +23,7 @@ pub const Options = struct { |
| 20 | /// Used for calculating how much space to reserve for executable program code in case | 23 | /// Used for calculating how much space to reserve for executable program code in case |
| 21 | /// the binary file deos not already have such a section. | 24 | /// the binary file deos not already have such a section. |
| 22 | program_code_size_hint: u64 = 256 * 1024, | 25 | program_code_size_hint: u64 = 256 * 1024, |
| 26 | entry_addr: ?u64 = null, | ||
| 23 | }; | 27 | }; |
| 24 | 28 | ||
| 25 | pub const File = struct { | 29 | pub const File = struct { |
src-self-hosted/link/Elf.zig+60-27| ... | @@ -14,12 +14,10 @@ const leb128 = std.debug.leb; | ... | @@ -14,12 +14,10 @@ const leb128 = std.debug.leb; |
| 14 | const Package = @import("../Package.zig"); | 14 | const Package = @import("../Package.zig"); |
| 15 | const Value = @import("../value.zig").Value; | 15 | const Value = @import("../value.zig").Value; |
| 16 | const Type = @import("../type.zig").Type; | 16 | const Type = @import("../type.zig").Type; |
| 17 | const build_options = @import("build_options"); | ||
| 18 | const link = @import("../link.zig"); | 17 | const link = @import("../link.zig"); |
| 19 | const File = link.File; | 18 | const File = link.File; |
| 20 | const Elf = @This(); | 19 | const Elf = @This(); |
| 21 | 20 | ||
| 22 | const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; | ||
| 23 | const default_entry_addr = 0x8000000; | 21 | const default_entry_addr = 0x8000000; |
| 24 | 22 | ||
| 25 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | 23 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. |
| ... | @@ -249,8 +247,8 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { | ... | @@ -249,8 +247,8 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 249 | .allocator = allocator, | 247 | .allocator = allocator, |
| 250 | }, | 248 | }, |
| 251 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | 249 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 252 | 32 => .p32, | 250 | 0 ... 32 => .p32, |
| 253 | 64 => .p64, | 251 | 33 ... 64 => .p64, |
| 254 | else => return error.UnsupportedELFArchitecture, | 252 | else => return error.UnsupportedELFArchitecture, |
| 255 | }, | 253 | }, |
| 256 | }; | 254 | }; |
| ... | @@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf | ... | @@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf |
| 278 | .file = file, | 276 | .file = file, |
| 279 | }, | 277 | }, |
| 280 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { | 278 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 281 | 32 => .p32, | 279 | 0 ... 32 => .p32, |
| 282 | 64 => .p64, | 280 | 33 ... 64 => .p64, |
| 283 | else => return error.UnsupportedELFArchitecture, | 281 | else => return error.UnsupportedELFArchitecture, |
| 284 | }, | 282 | }, |
| 285 | .shdr_table_dirty = true, | 283 | .shdr_table_dirty = true, |
| ... | @@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 { | ... | @@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 { |
| 346 | 344 | ||
| 347 | /// Returns end pos of collision, if any. | 345 | /// Returns end pos of collision, if any. |
| 348 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | 346 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 349 | const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32; | 347 | const small_ptr = self.ptr_width == .p32; |
| 350 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); | 348 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); |
| 351 | if (start < ehdr_size) | 349 | if (start < ehdr_size) |
| 352 | return ehdr_size; | 350 | return ehdr_size; |
| ... | @@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 462 | const p_align = 0x1000; | 460 | const p_align = 0x1000; |
| 463 | const off = self.findFreeSpace(file_size, p_align); | 461 | const off = self.findFreeSpace(file_size, p_align); |
| 464 | log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 462 | log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 463 | const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; | ||
| 465 | try self.program_headers.append(self.base.allocator, .{ | 464 | try self.program_headers.append(self.base.allocator, .{ |
| 466 | .p_type = elf.PT_LOAD, | 465 | .p_type = elf.PT_LOAD, |
| 467 | .p_offset = off, | 466 | .p_offset = off, |
| 468 | .p_filesz = file_size, | 467 | .p_filesz = file_size, |
| 469 | .p_vaddr = default_entry_addr, | 468 | .p_vaddr = entry_addr, |
| 470 | .p_paddr = default_entry_addr, | 469 | .p_paddr = entry_addr, |
| 471 | .p_memsz = file_size, | 470 | .p_memsz = file_size, |
| 472 | .p_align = p_align, | 471 | .p_align = p_align, |
| 473 | .p_flags = elf.PF_X | elf.PF_R, | 472 | .p_flags = elf.PF_X | elf.PF_R, |
| ... | @@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 486 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. | 485 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 487 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | 486 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 488 | // else in virtual memory. | 487 | // else in virtual memory. |
| 489 | const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000; | 488 | const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; |
| 490 | try self.program_headers.append(self.base.allocator, .{ | 489 | try self.program_headers.append(self.base.allocator, .{ |
| 491 | .p_type = elf.PT_LOAD, | 490 | .p_type = elf.PT_LOAD, |
| 492 | .p_offset = off, | 491 | .p_offset = off, |
| 493 | .p_filesz = file_size, | 492 | .p_filesz = file_size, |
| 494 | .p_vaddr = default_got_addr, | 493 | .p_vaddr = got_addr, |
| 495 | .p_paddr = default_got_addr, | 494 | .p_paddr = got_addr, |
| 496 | .p_memsz = file_size, | 495 | .p_memsz = file_size, |
| 497 | .p_align = p_align, | 496 | .p_align = p_align, |
| 498 | .p_flags = elf.PF_R, | 497 | .p_flags = elf.PF_R, |
| ... | @@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void { | ... | @@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 863 | // Write the form for the compile unit, which must match the abbrev table above. | 862 | // Write the form for the compile unit, which must match the abbrev table above. |
| 864 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); | 863 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); |
| 865 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); | 864 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); |
| 866 | const producer_strp = try self.makeDebugString(producer_string); | 865 | const producer_strp = try self.makeDebugString(link.producer_string); |
| 867 | // Currently only one compilation unit is supported, so the address range is simply | 866 | // Currently only one compilation unit is supported, so the address range is simply |
| 868 | // identical to the main program header virtual address and memory size. | 867 | // identical to the main program header virtual address and memory size. |
| 869 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 868 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| ... | @@ -1349,6 +1348,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { | ... | @@ -1349,6 +1348,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { |
| 1349 | var already_have_free_list_node = false; | 1348 | var already_have_free_list_node = false; |
| 1350 | { | 1349 | { |
| 1351 | var i: usize = 0; | 1350 | var i: usize = 0; |
| 1351 | // TODO turn text_block_free_list into a hash map | ||
| 1352 | while (i < self.text_block_free_list.items.len) { | 1352 | while (i < self.text_block_free_list.items.len) { |
| 1353 | if (self.text_block_free_list.items[i] == text_block) { | 1353 | if (self.text_block_free_list.items[i] == text_block) { |
| 1354 | _ = self.text_block_free_list.swapRemove(i); | 1354 | _ = self.text_block_free_list.swapRemove(i); |
| ... | @@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { | ... | @@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { |
| 1360 | i += 1; | 1360 | i += 1; |
| 1361 | } | 1361 | } |
| 1362 | } | 1362 | } |
| 1363 | // TODO process free list for dbg info just like we do above for vaddrs | ||
| 1363 | 1364 | ||
| 1364 | if (self.last_text_block == text_block) { | 1365 | if (self.last_text_block == text_block) { |
| 1365 | // TODO shrink the .text section size here | 1366 | // TODO shrink the .text section size here |
| 1366 | self.last_text_block = text_block.prev; | 1367 | self.last_text_block = text_block.prev; |
| 1367 | } | 1368 | } |
| 1369 | if (self.dbg_info_decl_first == text_block) { | ||
| 1370 | self.dbg_info_decl_first = text_block.dbg_info_next; | ||
| 1371 | } | ||
| 1372 | if (self.dbg_info_decl_last == text_block) { | ||
| 1373 | // TODO shrink the .debug_info section size here | ||
| 1374 | self.dbg_info_decl_last = text_block.dbg_info_prev; | ||
| 1375 | } | ||
| 1368 | 1376 | ||
| 1369 | if (text_block.prev) |prev| { | 1377 | if (text_block.prev) |prev| { |
| 1370 | prev.next = text_block.next; | 1378 | prev.next = text_block.next; |
| ... | @@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { | ... | @@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { |
| 1383 | } else { | 1391 | } else { |
| 1384 | text_block.next = null; | 1392 | text_block.next = null; |
| 1385 | } | 1393 | } |
| 1394 | |||
| 1395 | if (text_block.dbg_info_prev) |prev| { | ||
| 1396 | prev.dbg_info_next = text_block.dbg_info_next; | ||
| 1397 | |||
| 1398 | // TODO the free list logic like we do for text blocks above | ||
| 1399 | } else { | ||
| 1400 | text_block.dbg_info_prev = null; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | if (text_block.dbg_info_next) |next| { | ||
| 1404 | next.dbg_info_prev = text_block.dbg_info_prev; | ||
| 1405 | } else { | ||
| 1406 | text_block.dbg_info_next = null; | ||
| 1407 | } | ||
| 1386 | } | 1408 | } |
| 1387 | 1409 | ||
| 1388 | fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void { | 1410 | fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void { |
| ... | @@ -1584,10 +1606,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { | ... | @@ -1584,10 +1606,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { |
| 1584 | next.prev = null; | 1606 | next.prev = null; |
| 1585 | } | 1607 | } |
| 1586 | if (self.dbg_line_fn_first == &decl.fn_link.elf) { | 1608 | if (self.dbg_line_fn_first == &decl.fn_link.elf) { |
| 1587 | self.dbg_line_fn_first = null; | 1609 | self.dbg_line_fn_first = decl.fn_link.elf.next; |
| 1588 | } | 1610 | } |
| 1589 | if (self.dbg_line_fn_last == &decl.fn_link.elf) { | 1611 | if (self.dbg_line_fn_last == &decl.fn_link.elf) { |
| 1590 | self.dbg_line_fn_last = null; | 1612 | self.dbg_line_fn_last = decl.fn_link.elf.prev; |
| 1591 | } | 1613 | } |
| 1592 | } | 1614 | } |
| 1593 | 1615 | ||
| ... | @@ -2151,29 +2173,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void { | ... | @@ -2151,29 +2173,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void { |
| 2151 | fn writeProgHeader(self: *Elf, index: usize) !void { | 2173 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| 2152 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 2174 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 2153 | const offset = self.program_headers.items[index].p_offset; | 2175 | const offset = self.program_headers.items[index].p_offset; |
| 2154 | switch (self.base.options.target.cpu.arch.ptrBitWidth()) { | 2176 | switch (self.ptr_width) { |
| 2155 | 32 => { | 2177 | .p32 => { |
| 2156 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; | 2178 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; |
| 2157 | if (foreign_endian) { | 2179 | if (foreign_endian) { |
| 2158 | bswapAllFields(elf.Elf32_Phdr, &phdr[0]); | 2180 | bswapAllFields(elf.Elf32_Phdr, &phdr[0]); |
| 2159 | } | 2181 | } |
| 2160 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); | 2182 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); |
| 2161 | }, | 2183 | }, |
| 2162 | 64 => { | 2184 | .p64 => { |
| 2163 | var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; | 2185 | var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; |
| 2164 | if (foreign_endian) { | 2186 | if (foreign_endian) { |
| 2165 | bswapAllFields(elf.Elf64_Phdr, &phdr[0]); | 2187 | bswapAllFields(elf.Elf64_Phdr, &phdr[0]); |
| 2166 | } | 2188 | } |
| 2167 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); | 2189 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); |
| 2168 | }, | 2190 | }, |
| 2169 | else => return error.UnsupportedArchitecture, | ||
| 2170 | } | 2191 | } |
| 2171 | } | 2192 | } |
| 2172 | 2193 | ||
| 2173 | fn writeSectHeader(self: *Elf, index: usize) !void { | 2194 | fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2174 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 2195 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 2175 | switch (self.base.options.target.cpu.arch.ptrBitWidth()) { | 2196 | switch (self.ptr_width) { |
| 2176 | 32 => { | 2197 | .p32 => { |
| 2177 | var shdr: [1]elf.Elf32_Shdr = undefined; | 2198 | var shdr: [1]elf.Elf32_Shdr = undefined; |
| 2178 | shdr[0] = sectHeaderTo32(self.sections.items[index]); | 2199 | shdr[0] = sectHeaderTo32(self.sections.items[index]); |
| 2179 | if (foreign_endian) { | 2200 | if (foreign_endian) { |
| ... | @@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { | ... | @@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2182 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); | 2203 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); |
| 2183 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); | 2204 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2184 | }, | 2205 | }, |
| 2185 | 64 => { | 2206 | .p64 => { |
| 2186 | var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; | 2207 | var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; |
| 2187 | if (foreign_endian) { | 2208 | if (foreign_endian) { |
| 2188 | bswapAllFields(elf.Elf64_Shdr, &shdr[0]); | 2209 | bswapAllFields(elf.Elf64_Shdr, &shdr[0]); |
| ... | @@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void { | ... | @@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2190 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); | 2211 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); |
| 2191 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); | 2212 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2192 | }, | 2213 | }, |
| 2193 | else => return error.UnsupportedArchitecture, | ||
| 2194 | } | 2214 | } |
| 2195 | } | 2215 | } |
| 2196 | 2216 | ||
| 2197 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | 2217 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2198 | const shdr = &self.sections.items[self.got_section_index.?]; | 2218 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 2199 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | 2219 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 2200 | const entry_size: u16 = self.ptrWidthBytes(); | 2220 | const entry_size: u16 = self.archPtrWidthBytes(); |
| 2201 | if (self.offset_table_count_dirty) { | 2221 | if (self.offset_table_count_dirty) { |
| 2202 | // TODO Also detect virtual address collisions. | 2222 | // TODO Also detect virtual address collisions. |
| 2203 | const allocated_size = self.allocatedSize(shdr.sh_offset); | 2223 | const allocated_size = self.allocatedSize(shdr.sh_offset); |
| ... | @@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2221 | } | 2241 | } |
| 2222 | const endian = self.base.options.target.cpu.arch.endian(); | 2242 | const endian = self.base.options.target.cpu.arch.endian(); |
| 2223 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | 2243 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2224 | switch (self.ptr_width) { | 2244 | switch (entry_size) { |
| 2225 | .p32 => { | 2245 | 2 => { |
| 2246 | var buf: [2]u8 = undefined; | ||
| 2247 | mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian); | ||
| 2248 | try self.base.file.?.pwriteAll(&buf, off); | ||
| 2249 | }, | ||
| 2250 | 4 => { | ||
| 2226 | var buf: [4]u8 = undefined; | 2251 | var buf: [4]u8 = undefined; |
| 2227 | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); | 2252 | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); |
| 2228 | try self.base.file.?.pwriteAll(&buf, off); | 2253 | try self.base.file.?.pwriteAll(&buf, off); |
| 2229 | }, | 2254 | }, |
| 2230 | .p64 => { | 2255 | 8 => { |
| 2231 | var buf: [8]u8 = undefined; | 2256 | var buf: [8]u8 = undefined; |
| 2232 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | 2257 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 2233 | try self.base.file.?.pwriteAll(&buf, off); | 2258 | try self.base.file.?.pwriteAll(&buf, off); |
| 2234 | }, | 2259 | }, |
| 2260 | else => unreachable, | ||
| 2235 | } | 2261 | } |
| 2236 | } | 2262 | } |
| 2237 | 2263 | ||
| ... | @@ -2344,6 +2370,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { | ... | @@ -2344,6 +2370,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { |
| 2344 | } | 2370 | } |
| 2345 | } | 2371 | } |
| 2346 | 2372 | ||
| 2373 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. | ||
| 2347 | fn ptrWidthBytes(self: Elf) u8 { | 2374 | fn ptrWidthBytes(self: Elf) u8 { |
| 2348 | return switch (self.ptr_width) { | 2375 | return switch (self.ptr_width) { |
| 2349 | .p32 => 4, | 2376 | .p32 => 4, |
| ... | @@ -2351,6 +2378,12 @@ fn ptrWidthBytes(self: Elf) u8 { | ... | @@ -2351,6 +2378,12 @@ fn ptrWidthBytes(self: Elf) u8 { |
| 2351 | }; | 2378 | }; |
| 2352 | } | 2379 | } |
| 2353 | 2380 | ||
| 2381 | /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes | ||
| 2382 | /// in a 32-bit ELF file. | ||
| 2383 | fn archPtrWidthBytes(self: Elf) u8 { | ||
| 2384 | return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); | ||
| 2385 | } | ||
| 2386 | |||
| 2354 | /// The reloc offset for the virtual address of a function in its Line Number Program. | 2387 | /// The reloc offset for the virtual address of a function in its Line Number Program. |
| 2355 | /// Size is a virtual address integer. | 2388 | /// Size is a virtual address integer. |
| 2356 | const dbg_line_vaddr_reloc_index = 3; | 2389 | const dbg_line_vaddr_reloc_index = 3; |
src-self-hosted/link/MachO.zig+177-38| ... | @@ -6,29 +6,66 @@ const assert = std.debug.assert; | ... | @@ -6,29 +6,66 @@ const assert = std.debug.assert; |
| 6 | const fs = std.fs; | 6 | const fs = std.fs; |
| 7 | const log = std.log.scoped(.link); | 7 | const log = std.log.scoped(.link); |
| 8 | const macho = std.macho; | 8 | const macho = std.macho; |
| 9 | const codegen = @import("../codegen.zig"); | ||
| 9 | const math = std.math; | 10 | const math = std.math; |
| 10 | const mem = std.mem; | 11 | const mem = std.mem; |
| 12 | const trace = @import("../tracy.zig").trace; | ||
| 13 | const Type = @import("../type.zig").Type; | ||
| 11 | 14 | ||
| 12 | const Module = @import("../Module.zig"); | 15 | const Module = @import("../Module.zig"); |
| 13 | const link = @import("../link.zig"); | 16 | const link = @import("../link.zig"); |
| 14 | const File = link.File; | 17 | const File = link.File; |
| 15 | 18 | ||
| 19 | const is_darwin = std.Target.current.os.tag.isDarwin(); | ||
| 20 | |||
| 16 | pub const base_tag: File.Tag = File.Tag.macho; | 21 | pub const base_tag: File.Tag = File.Tag.macho; |
| 17 | 22 | ||
| 18 | base: File, | 23 | base: File, |
| 19 | 24 | ||
| 20 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 25 | /// List of all load command headers that are in the file. |
| 21 | /// Same order as in the file. | 26 | /// We use it to track number and size of all commands needed by the header. |
| 22 | segment_cmds: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, | 27 | commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){}, |
| 28 | command_file_offset: ?u64 = null, | ||
| 23 | 29 | ||
| 24 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 30 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 25 | /// Same order as in the file. | 31 | /// Same order as in the file. |
| 32 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, | ||
| 26 | sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){}, | 33 | sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){}, |
| 34 | segment_table_offset: ?u64 = null, | ||
| 27 | 35 | ||
| 36 | /// Entry point load command | ||
| 37 | entry_point_cmd: ?macho.entry_point_command = null, | ||
| 28 | entry_addr: ?u64 = null, | 38 | entry_addr: ?u64 = null, |
| 29 | 39 | ||
| 40 | /// Default VM start address set at 4GB | ||
| 41 | vm_start_address: u64 = 0x100000000, | ||
| 42 | |||
| 43 | seg_table_dirty: bool = false, | ||
| 44 | |||
| 30 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 45 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 31 | 46 | ||
| 47 | /// TODO ultimately this will be propagated down from main() and set (in this form or another) | ||
| 48 | /// when user links against system lib. | ||
| 49 | link_against_system: bool = false, | ||
| 50 | |||
| 51 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | ||
| 52 | const alloc_num = 4; | ||
| 53 | const alloc_den = 3; | ||
| 54 | |||
| 55 | /// Default path to dyld | ||
| 56 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths | ||
| 57 | /// instead but this will do for now. | ||
| 58 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; | ||
| 59 | |||
| 60 | /// Default lib search path | ||
| 61 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths | ||
| 62 | /// instead but this will do for now. | ||
| 63 | const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib"; | ||
| 64 | |||
| 65 | const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; | ||
| 66 | /// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it | ||
| 67 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; | ||
| 68 | |||
| 32 | pub const TextBlock = struct { | 69 | pub const TextBlock = struct { |
| 33 | pub const empty = TextBlock{}; | 70 | pub const empty = TextBlock{}; |
| 34 | }; | 71 | }; |
| ... | @@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO | ... | @@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO |
| 80 | /// Truncates the existing file contents and overwrites the contents. | 117 | /// Truncates the existing file contents and overwrites the contents. |
| 81 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 118 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 82 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { | 119 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { |
| 83 | switch (options.output_mode) { | ||
| 84 | .Exe => {}, | ||
| 85 | .Obj => {}, | ||
| 86 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 87 | } | ||
| 88 | |||
| 89 | var self: MachO = .{ | 120 | var self: MachO = .{ |
| 90 | .base = .{ | 121 | .base = .{ |
| 91 | .file = file, | 122 | .file = file, |
| ... | @@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach | ... | @@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach |
| 96 | }; | 127 | }; |
| 97 | errdefer self.deinit(); | 128 | errdefer self.deinit(); |
| 98 | 129 | ||
| 99 | if (options.output_mode == .Exe) { | 130 | switch (options.output_mode) { |
| 100 | // The first segment command for executables is always a __PAGEZERO segment. | 131 | .Exe => { |
| 101 | try self.segment_cmds.append(allocator, .{ | 132 | // The first segment command for executables is always a __PAGEZERO segment. |
| 102 | .cmd = macho.LC_SEGMENT_64, | 133 | const pagezero = .{ |
| 103 | .cmdsize = @sizeOf(macho.segment_command_64), | 134 | .cmd = macho.LC_SEGMENT_64, |
| 104 | .segname = self.makeString("__PAGEZERO"), | 135 | .cmdsize = commandSize(@sizeOf(macho.segment_command_64)), |
| 105 | .vmaddr = 0, | 136 | .segname = makeString("__PAGEZERO"), |
| 106 | .vmsize = 0, | 137 | .vmaddr = 0, |
| 107 | .fileoff = 0, | 138 | .vmsize = self.vm_start_address, |
| 108 | .filesize = 0, | 139 | .fileoff = 0, |
| 109 | .maxprot = 0, | 140 | .filesize = 0, |
| 110 | .initprot = 0, | 141 | .maxprot = 0, |
| 111 | .nsects = 0, | 142 | .initprot = 0, |
| 112 | .flags = 0, | 143 | .nsects = 0, |
| 113 | }); | 144 | .flags = 0, |
| 145 | }; | ||
| 146 | try self.commands.append(allocator, .{ | ||
| 147 | .cmd = pagezero.cmd, | ||
| 148 | .cmdsize = pagezero.cmdsize, | ||
| 149 | }); | ||
| 150 | try self.segments.append(allocator, pagezero); | ||
| 151 | }, | ||
| 152 | .Obj => return error.TODOImplementWritingObjFiles, | ||
| 153 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 114 | } | 154 | } |
| 115 | 155 | ||
| 116 | return self; | 156 | try self.populateMissingMetadata(); |
| 117 | } | ||
| 118 | 157 | ||
| 119 | fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 { | 158 | return self; |
| 120 | var buf: [16]u8 = undefined; | ||
| 121 | if (bytes.len > buf.len) @compileError("MachO segment/section name too long"); | ||
| 122 | mem.copy(u8, buf[0..], bytes); | ||
| 123 | return buf; | ||
| 124 | } | 159 | } |
| 125 | 160 | ||
| 126 | fn writeMachOHeader(self: *MachO) !void { | 161 | fn writeMachOHeader(self: *MachO) !void { |
| ... | @@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void { | ... | @@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void { |
| 156 | }; | 191 | }; |
| 157 | hdr.filetype = filetype; | 192 | hdr.filetype = filetype; |
| 158 | 193 | ||
| 159 | // TODO consider other commands | 194 | const ncmds = try math.cast(u32, self.commands.items.len); |
| 160 | const ncmds = try math.cast(u32, self.segment_cmds.items.len); | ||
| 161 | hdr.ncmds = ncmds; | 195 | hdr.ncmds = ncmds; |
| 162 | hdr.sizeofcmds = ncmds * @sizeOf(macho.segment_command_64); | 196 | |
| 197 | var sizeof_cmds: u32 = 0; | ||
| 198 | for (self.commands.items) |cmd| { | ||
| 199 | sizeof_cmds += cmd.cmdsize; | ||
| 200 | } | ||
| 201 | hdr.sizeofcmds = sizeof_cmds; | ||
| 163 | 202 | ||
| 164 | // TODO should these be set to something else? | 203 | // TODO should these be set to something else? |
| 165 | hdr.flags = 0; | 204 | hdr.flags = 0; |
| ... | @@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void { | ... | @@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void { |
| 169 | } | 208 | } |
| 170 | 209 | ||
| 171 | pub fn flush(self: *MachO, module: *Module) !void { | 210 | pub fn flush(self: *MachO, module: *Module) !void { |
| 172 | // TODO implement flush | 211 | // Save segments first |
| 173 | { | 212 | { |
| 174 | const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segment_cmds.items.len); | 213 | const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segments.items.len); |
| 175 | defer self.base.allocator.free(buf); | 214 | defer self.base.allocator.free(buf); |
| 176 | 215 | ||
| 216 | self.command_file_offset = @sizeOf(macho.mach_header_64); | ||
| 217 | |||
| 177 | for (buf) |*seg, i| { | 218 | for (buf) |*seg, i| { |
| 178 | seg.* = self.segment_cmds.items[i]; | 219 | seg.* = self.segments.items[i]; |
| 220 | self.command_file_offset.? += self.segments.items[i].cmdsize; | ||
| 179 | } | 221 | } |
| 180 | 222 | ||
| 181 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64)); | 223 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64)); |
| 182 | } | 224 | } |
| 183 | 225 | ||
| 226 | switch (self.base.options.output_mode) { | ||
| 227 | .Exe => { | ||
| 228 | if (self.link_against_system) { | ||
| 229 | if (is_darwin) { | ||
| 230 | { | ||
| 231 | // Specify path to dynamic linker dyld | ||
| 232 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH))); | ||
| 233 | const load_dylinker = [1]macho.dylinker_command{ | ||
| 234 | .{ | ||
| 235 | .cmd = macho.LC_LOAD_DYLINKER, | ||
| 236 | .cmdsize = cmdsize, | ||
| 237 | .name = @sizeOf(macho.dylinker_command), | ||
| 238 | }, | ||
| 239 | }; | ||
| 240 | try self.commands.append(self.base.allocator, .{ | ||
| 241 | .cmd = macho.LC_LOAD_DYLINKER, | ||
| 242 | .cmdsize = cmdsize, | ||
| 243 | }); | ||
| 244 | |||
| 245 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?); | ||
| 246 | |||
| 247 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command); | ||
| 248 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); | ||
| 249 | |||
| 250 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); | ||
| 251 | self.command_file_offset.? += cmdsize; | ||
| 252 | } | ||
| 253 | |||
| 254 | { | ||
| 255 | // Link against libSystem | ||
| 256 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH))); | ||
| 257 | // According to Apple's manual, we should obtain current libSystem version using libc call | ||
| 258 | // NSVersionOfRunTimeLibrary. | ||
| 259 | const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME); | ||
| 260 | const dylib = .{ | ||
| 261 | .name = @sizeOf(macho.dylib_command), | ||
| 262 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | ||
| 263 | .current_version = version, | ||
| 264 | .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering | ||
| 265 | }; | ||
| 266 | const load_dylib = [1]macho.dylib_command{ | ||
| 267 | .{ | ||
| 268 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 269 | .cmdsize = cmdsize, | ||
| 270 | .dylib = dylib, | ||
| 271 | }, | ||
| 272 | }; | ||
| 273 | try self.commands.append(self.base.allocator, .{ | ||
| 274 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 275 | .cmdsize = cmdsize, | ||
| 276 | }); | ||
| 277 | |||
| 278 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?); | ||
| 279 | |||
| 280 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command); | ||
| 281 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); | ||
| 282 | |||
| 283 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); | ||
| 284 | self.command_file_offset.? += cmdsize; | ||
| 285 | } | ||
| 286 | } else { | ||
| 287 | @panic("linking against libSystem on non-native target is unsupported"); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | }, | ||
| 291 | .Obj => return error.TODOImplementWritingObjFiles, | ||
| 292 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 293 | } | ||
| 294 | |||
| 184 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 295 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 185 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 296 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 186 | self.error_flags.no_entry_point_found = true; | 297 | self.error_flags.no_entry_point_found = true; |
| ... | @@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void { | ... | @@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 192 | } | 303 | } |
| 193 | 304 | ||
| 194 | pub fn deinit(self: *MachO) void { | 305 | pub fn deinit(self: *MachO) void { |
| 195 | self.segment_cmds.deinit(self.base.allocator); | 306 | self.commands.deinit(self.base.allocator); |
| 307 | self.segments.deinit(self.base.allocator); | ||
| 196 | self.sections.deinit(self.base.allocator); | 308 | self.sections.deinit(self.base.allocator); |
| 197 | } | 309 | } |
| 198 | 310 | ||
| ... | @@ -214,3 +326,30 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} | ... | @@ -214,3 +326,30 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} |
| 214 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | 326 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 215 | @panic("TODO implement getDeclVAddr for MachO"); | 327 | @panic("TODO implement getDeclVAddr for MachO"); |
| 216 | } | 328 | } |
| 329 | |||
| 330 | pub fn populateMissingMetadata(self: *MachO) !void {} | ||
| 331 | |||
| 332 | fn makeString(comptime bytes: []const u8) [16]u8 { | ||
| 333 | var buf: [16]u8 = undefined; | ||
| 334 | if (bytes.len > buf.len) @compileError("MachO segment/section name too long"); | ||
| 335 | mem.copy(u8, buf[0..], bytes); | ||
| 336 | return buf; | ||
| 337 | } | ||
| 338 | |||
| 339 | fn commandSize(min_size: u32) u32 { | ||
| 340 | if (min_size % @sizeOf(u64) == 0) return min_size; | ||
| 341 | |||
| 342 | const div = min_size / @sizeOf(u64); | ||
| 343 | return (div + 1) * @sizeOf(u64); | ||
| 344 | } | ||
| 345 | |||
| 346 | fn addPadding(self: *MachO, size: u32, file_offset: u64) !void { | ||
| 347 | if (size == 0) return; | ||
| 348 | |||
| 349 | const buf = try self.base.allocator.alloc(u8, size); | ||
| 350 | defer self.base.allocator.free(buf); | ||
| 351 | |||
| 352 | mem.set(u8, buf[0..], 0); | ||
| 353 | |||
| 354 | try self.base.file.?.pwriteAll(buf, file_offset); | ||
| 355 | } |
src-self-hosted/test.zig+115-2| ... | @@ -583,7 +583,10 @@ pub const TestContext = struct { | ... | @@ -583,7 +583,10 @@ pub const TestContext = struct { |
| 583 | 583 | ||
| 584 | switch (case.target.getExternalExecutor()) { | 584 | switch (case.target.getExternalExecutor()) { |
| 585 | .native => try argv.append(exe_path), | 585 | .native => try argv.append(exe_path), |
| 586 | .unavailable => return, // No executor available; pass test. | 586 | .unavailable => { |
| 587 | try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name); | ||
| 588 | return; // Pass test. | ||
| 589 | }, | ||
| 587 | 590 | ||
| 588 | .qemu => |qemu_bin_name| if (enable_qemu) { | 591 | .qemu => |qemu_bin_name| if (enable_qemu) { |
| 589 | // TODO Ability for test cases to specify whether to link libc. | 592 | // TODO Ability for test cases to specify whether to link libc. |
| ... | @@ -635,7 +638,6 @@ pub const TestContext = struct { | ... | @@ -635,7 +638,6 @@ pub const TestContext = struct { |
| 635 | var test_node = update_node.start("test", null); | 638 | var test_node = update_node.start("test", null); |
| 636 | test_node.activate(); | 639 | test_node.activate(); |
| 637 | defer test_node.end(); | 640 | defer test_node.end(); |
| 638 | |||
| 639 | defer allocator.free(exec_result.stdout); | 641 | defer allocator.free(exec_result.stdout); |
| 640 | defer allocator.free(exec_result.stderr); | 642 | defer allocator.free(exec_result.stderr); |
| 641 | switch (exec_result.term) { | 643 | switch (exec_result.term) { |
| ... | @@ -657,4 +659,115 @@ pub const TestContext = struct { | ... | @@ -657,4 +659,115 @@ pub const TestContext = struct { |
| 657 | } | 659 | } |
| 658 | } | 660 | } |
| 659 | } | 661 | } |
| 662 | |||
| 663 | fn runInterpreterIfAvailable( | ||
| 664 | self: *TestContext, | ||
| 665 | gpa: *Allocator, | ||
| 666 | node: *std.Progress.Node, | ||
| 667 | case: Case, | ||
| 668 | tmp_dir: std.fs.Dir, | ||
| 669 | bin_name: []const u8, | ||
| 670 | ) !void { | ||
| 671 | const arch = case.target.cpu_arch orelse return; | ||
| 672 | switch (arch) { | ||
| 673 | .spu_2 => return self.runSpu2Interpreter(gpa, node, case, tmp_dir, bin_name), | ||
| 674 | else => return, | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | fn runSpu2Interpreter( | ||
| 679 | self: *TestContext, | ||
| 680 | gpa: *Allocator, | ||
| 681 | update_node: *std.Progress.Node, | ||
| 682 | case: Case, | ||
| 683 | tmp_dir: std.fs.Dir, | ||
| 684 | bin_name: []const u8, | ||
| 685 | ) !void { | ||
| 686 | const spu = @import("codegen/spu-mk2.zig"); | ||
| 687 | if (case.target.os_tag) |os| { | ||
| 688 | if (os != .freestanding) { | ||
| 689 | std.debug.panic("Only freestanding makes sense for SPU-II tests!", .{}); | ||
| 690 | } | ||
| 691 | } else { | ||
| 692 | std.debug.panic("SPU_2 has no native OS, check the test!", .{}); | ||
| 693 | } | ||
| 694 | |||
| 695 | var interpreter = spu.Interpreter(struct { | ||
| 696 | RAM: [0x10000]u8 = undefined, | ||
| 697 | |||
| 698 | pub fn read8(bus: @This(), addr: u16) u8 { | ||
| 699 | return bus.RAM[addr]; | ||
| 700 | } | ||
| 701 | pub fn read16(bus: @This(), addr: u16) u16 { | ||
| 702 | return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); | ||
| 703 | } | ||
| 704 | |||
| 705 | pub fn write8(bus: *@This(), addr: u16, val: u8) void { | ||
| 706 | bus.RAM[addr] = val; | ||
| 707 | } | ||
| 708 | |||
| 709 | pub fn write16(bus: *@This(), addr: u16, val: u16) void { | ||
| 710 | std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); | ||
| 711 | } | ||
| 712 | }){ | ||
| 713 | .bus = .{}, | ||
| 714 | }; | ||
| 715 | |||
| 716 | { | ||
| 717 | var load_node = update_node.start("load", null); | ||
| 718 | load_node.activate(); | ||
| 719 | defer load_node.end(); | ||
| 720 | |||
| 721 | var file = try tmp_dir.openFile(bin_name, .{ .read = true }); | ||
| 722 | defer file.close(); | ||
| 723 | |||
| 724 | const header = try std.elf.readHeader(file); | ||
| 725 | var iterator = header.program_header_iterator(file); | ||
| 726 | |||
| 727 | var none_loaded = true; | ||
| 728 | |||
| 729 | while (try iterator.next()) |phdr| { | ||
| 730 | if (phdr.p_type != std.elf.PT_LOAD) { | ||
| 731 | std.debug.print("Encountered unexpected ELF program header: type {}\n", .{phdr.p_type}); | ||
| 732 | std.process.exit(1); | ||
| 733 | } | ||
| 734 | if (phdr.p_paddr != phdr.p_vaddr) { | ||
| 735 | std.debug.print("Physical address does not match virtual address in ELF header!\n", .{}); | ||
| 736 | std.process.exit(1); | ||
| 737 | } | ||
| 738 | if (phdr.p_filesz != phdr.p_memsz) { | ||
| 739 | std.debug.print("Physical size does not match virtual size in ELF header!\n", .{}); | ||
| 740 | std.process.exit(1); | ||
| 741 | } | ||
| 742 | if ((try file.pread(interpreter.bus.RAM[phdr.p_paddr .. phdr.p_paddr + phdr.p_filesz], phdr.p_offset)) != phdr.p_filesz) { | ||
| 743 | std.debug.print("Read less than expected from ELF file!", .{}); | ||
| 744 | std.process.exit(1); | ||
| 745 | } | ||
| 746 | std.log.scoped(.spu2_test).debug("Loaded 0x{x} bytes to 0x{x:0<4}\n", .{ phdr.p_filesz, phdr.p_paddr }); | ||
| 747 | none_loaded = false; | ||
| 748 | } | ||
| 749 | if (none_loaded) { | ||
| 750 | std.debug.print("No data found in ELF file!\n", .{}); | ||
| 751 | std.process.exit(1); | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | var exec_node = update_node.start("execute", null); | ||
| 756 | exec_node.activate(); | ||
| 757 | defer exec_node.end(); | ||
| 758 | |||
| 759 | var blocks: u16 = 1000; | ||
| 760 | const block_size = 1000; | ||
| 761 | while (!interpreter.undefined0) { | ||
| 762 | const pre_ip = interpreter.ip; | ||
| 763 | if (blocks > 0) { | ||
| 764 | blocks -= 1; | ||
| 765 | try interpreter.ExecuteBlock(block_size); | ||
| 766 | if (pre_ip == interpreter.ip) { | ||
| 767 | std.debug.print("Infinite loop detected in SPU II test!\n", .{}); | ||
| 768 | std.process.exit(1); | ||
| 769 | } | ||
| 770 | } | ||
| 771 | } | ||
| 772 | } | ||
| 660 | }; | 773 | }; |
src-self-hosted/type.zig+251-7| ... | @@ -3,6 +3,7 @@ const Value = @import("value.zig").Value; | ... | @@ -3,6 +3,7 @@ const Value = @import("value.zig").Value; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Target = std.Target; | 5 | const Target = std.Target; |
| 6 | const Module = @import("Module.zig"); | ||
| 6 | 7 | ||
| 7 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. | 8 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. |
| 8 | /// It's important for this type to be small. | 9 | /// It's important for this type to be small. |
| ... | @@ -52,7 +53,7 @@ pub const Type = extern union { | ... | @@ -52,7 +53,7 @@ pub const Type = extern union { |
| 52 | .bool => return .Bool, | 53 | .bool => return .Bool, |
| 53 | .void => return .Void, | 54 | .void => return .Void, |
| 54 | .type => return .Type, | 55 | .type => return .Type, |
| 55 | .anyerror => return .ErrorSet, | 56 | .error_set, .error_set_single, .anyerror => return .ErrorSet, |
| 56 | .comptime_int => return .ComptimeInt, | 57 | .comptime_int => return .ComptimeInt, |
| 57 | .comptime_float => return .ComptimeFloat, | 58 | .comptime_float => return .ComptimeFloat, |
| 58 | .noreturn => return .NoReturn, | 59 | .noreturn => return .NoReturn, |
| ... | @@ -84,6 +85,10 @@ pub const Type = extern union { | ... | @@ -84,6 +85,10 @@ pub const Type = extern union { |
| 84 | .optional_single_mut_pointer, | 85 | .optional_single_mut_pointer, |
| 85 | => return .Optional, | 86 | => return .Optional, |
| 86 | .enum_literal => return .EnumLiteral, | 87 | .enum_literal => return .EnumLiteral, |
| 88 | |||
| 89 | .anyerror_void_error_union, .error_union => return .ErrorUnion, | ||
| 90 | |||
| 91 | .anyframe_T, .@"anyframe" => return .AnyFrame, | ||
| 87 | } | 92 | } |
| 88 | } | 93 | } |
| 89 | 94 | ||
| ... | @@ -151,6 +156,9 @@ pub const Type = extern union { | ... | @@ -151,6 +156,9 @@ pub const Type = extern union { |
| 151 | .ComptimeInt => return true, | 156 | .ComptimeInt => return true, |
| 152 | .Undefined => return true, | 157 | .Undefined => return true, |
| 153 | .Null => return true, | 158 | .Null => return true, |
| 159 | .AnyFrame => { | ||
| 160 | return a.elemType().eql(b.elemType()); | ||
| 161 | }, | ||
| 154 | .Pointer => { | 162 | .Pointer => { |
| 155 | // Hot path for common case: | 163 | // Hot path for common case: |
| 156 | if (a.castPointer()) |a_payload| { | 164 | if (a.castPointer()) |a_payload| { |
| ... | @@ -225,7 +233,6 @@ pub const Type = extern union { | ... | @@ -225,7 +233,6 @@ pub const Type = extern union { |
| 225 | .BoundFn, | 233 | .BoundFn, |
| 226 | .Opaque, | 234 | .Opaque, |
| 227 | .Frame, | 235 | .Frame, |
| 228 | .AnyFrame, | ||
| 229 | .Vector, | 236 | .Vector, |
| 230 | => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), | 237 | => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), |
| 231 | } | 238 | } |
| ... | @@ -343,6 +350,8 @@ pub const Type = extern union { | ... | @@ -343,6 +350,8 @@ pub const Type = extern union { |
| 343 | .single_const_pointer_to_comptime_int, | 350 | .single_const_pointer_to_comptime_int, |
| 344 | .const_slice_u8, | 351 | .const_slice_u8, |
| 345 | .enum_literal, | 352 | .enum_literal, |
| 353 | .anyerror_void_error_union, | ||
| 354 | .@"anyframe", | ||
| 346 | => unreachable, | 355 | => unreachable, |
| 347 | 356 | ||
| 348 | .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), | 357 | .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), |
| ... | @@ -397,6 +406,7 @@ pub const Type = extern union { | ... | @@ -397,6 +406,7 @@ pub const Type = extern union { |
| 397 | .optional_single_mut_pointer, | 406 | .optional_single_mut_pointer, |
| 398 | .optional_single_const_pointer, | 407 | .optional_single_const_pointer, |
| 399 | => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"), | 408 | => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"), |
| 409 | .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"), | ||
| 400 | 410 | ||
| 401 | .pointer => { | 411 | .pointer => { |
| 402 | const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise); | 412 | const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise); |
| ... | @@ -416,6 +426,19 @@ pub const Type = extern union { | ... | @@ -416,6 +426,19 @@ pub const Type = extern union { |
| 416 | }; | 426 | }; |
| 417 | return Type{ .ptr_otherwise = &new_payload.base }; | 427 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 418 | }, | 428 | }, |
| 429 | .error_union => { | ||
| 430 | const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise); | ||
| 431 | const new_payload = try allocator.create(Payload.ErrorUnion); | ||
| 432 | new_payload.* = .{ | ||
| 433 | .base = payload.base, | ||
| 434 | |||
| 435 | .error_set = try payload.error_set.copy(allocator), | ||
| 436 | .payload = try payload.payload.copy(allocator), | ||
| 437 | }; | ||
| 438 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 439 | }, | ||
| 440 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | ||
| 441 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle), | ||
| 419 | } | 442 | } |
| 420 | } | 443 | } |
| 421 | 444 | ||
| ... | @@ -482,6 +505,8 @@ pub const Type = extern union { | ... | @@ -482,6 +505,8 @@ pub const Type = extern union { |
| 482 | .@"null" => return out_stream.writeAll("@TypeOf(null)"), | 505 | .@"null" => return out_stream.writeAll("@TypeOf(null)"), |
| 483 | .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), | 506 | .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), |
| 484 | 507 | ||
| 508 | .@"anyframe" => return out_stream.writeAll("anyframe"), | ||
| 509 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), | ||
| 485 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 510 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 486 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), | 511 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| 487 | .fn_void_no_args => return out_stream.writeAll("fn() void"), | 512 | .fn_void_no_args => return out_stream.writeAll("fn() void"), |
| ... | @@ -500,6 +525,12 @@ pub const Type = extern union { | ... | @@ -500,6 +525,12 @@ pub const Type = extern union { |
| 500 | continue; | 525 | continue; |
| 501 | }, | 526 | }, |
| 502 | 527 | ||
| 528 | .anyframe_T => { | ||
| 529 | const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise); | ||
| 530 | try out_stream.print("anyframe->", .{}); | ||
| 531 | ty = payload.return_type; | ||
| 532 | continue; | ||
| 533 | }, | ||
| 503 | .array_u8 => { | 534 | .array_u8 => { |
| 504 | const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise); | 535 | const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise); |
| 505 | return out_stream.print("[{}]u8", .{payload.len}); | 536 | return out_stream.print("[{}]u8", .{payload.len}); |
| ... | @@ -622,6 +653,21 @@ pub const Type = extern union { | ... | @@ -622,6 +653,21 @@ pub const Type = extern union { |
| 622 | ty = payload.pointee_type; | 653 | ty = payload.pointee_type; |
| 623 | continue; | 654 | continue; |
| 624 | }, | 655 | }, |
| 656 | .error_union => { | ||
| 657 | const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise); | ||
| 658 | try payload.error_set.format("", .{}, out_stream); | ||
| 659 | try out_stream.writeAll("!"); | ||
| 660 | ty = payload.payload; | ||
| 661 | continue; | ||
| 662 | }, | ||
| 663 | .error_set => { | ||
| 664 | const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise); | ||
| 665 | return out_stream.writeAll(std.mem.spanZ(payload.decl.name)); | ||
| 666 | }, | ||
| 667 | .error_set_single => { | ||
| 668 | const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise); | ||
| 669 | return out_stream.print("error{{{}}}", .{payload.name}); | ||
| 670 | }, | ||
| 625 | } | 671 | } |
| 626 | unreachable; | 672 | unreachable; |
| 627 | } | 673 | } |
| ... | @@ -715,6 +761,11 @@ pub const Type = extern union { | ... | @@ -715,6 +761,11 @@ pub const Type = extern union { |
| 715 | .optional, | 761 | .optional, |
| 716 | .optional_single_mut_pointer, | 762 | .optional_single_mut_pointer, |
| 717 | .optional_single_const_pointer, | 763 | .optional_single_const_pointer, |
| 764 | .@"anyframe", | ||
| 765 | .anyframe_T, | ||
| 766 | .anyerror_void_error_union, | ||
| 767 | .error_set, | ||
| 768 | .error_set_single, | ||
| 718 | => true, | 769 | => true, |
| 719 | // TODO lazy types | 770 | // TODO lazy types |
| 720 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, | 771 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| ... | @@ -723,6 +774,11 @@ pub const Type = extern union { | ... | @@ -723,6 +774,11 @@ pub const Type = extern union { |
| 723 | .int_signed => self.cast(Payload.IntSigned).?.bits == 0, | 774 | .int_signed => self.cast(Payload.IntSigned).?.bits == 0, |
| 724 | .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0, | 775 | .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0, |
| 725 | 776 | ||
| 777 | .error_union => { | ||
| 778 | const payload = self.cast(Payload.ErrorUnion).?; | ||
| 779 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); | ||
| 780 | }, | ||
| 781 | |||
| 726 | .c_void, | 782 | .c_void, |
| 727 | .void, | 783 | .void, |
| 728 | .type, | 784 | .type, |
| ... | @@ -756,6 +812,7 @@ pub const Type = extern union { | ... | @@ -756,6 +812,7 @@ pub const Type = extern union { |
| 756 | .fn_ccc_void_no_args, // represents machine code; not a pointer | 812 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 757 | .function, // represents machine code; not a pointer | 813 | .function, // represents machine code; not a pointer |
| 758 | => return switch (target.cpu.arch) { | 814 | => return switch (target.cpu.arch) { |
| 815 | .arm => 4, | ||
| 759 | .riscv64 => 2, | 816 | .riscv64 => 2, |
| 760 | else => 1, | 817 | else => 1, |
| 761 | }, | 818 | }, |
| ... | @@ -778,6 +835,8 @@ pub const Type = extern union { | ... | @@ -778,6 +835,8 @@ pub const Type = extern union { |
| 778 | .mut_slice, | 835 | .mut_slice, |
| 779 | .optional_single_const_pointer, | 836 | .optional_single_const_pointer, |
| 780 | .optional_single_mut_pointer, | 837 | .optional_single_mut_pointer, |
| 838 | .@"anyframe", | ||
| 839 | .anyframe_T, | ||
| 781 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 840 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 782 | 841 | ||
| 783 | .pointer => { | 842 | .pointer => { |
| ... | @@ -802,7 +861,11 @@ pub const Type = extern union { | ... | @@ -802,7 +861,11 @@ pub const Type = extern union { |
| 802 | .f128 => return 16, | 861 | .f128 => return 16, |
| 803 | .c_longdouble => return 16, | 862 | .c_longdouble => return 16, |
| 804 | 863 | ||
| 805 | .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type | 864 | .error_set, |
| 865 | .error_set_single, | ||
| 866 | .anyerror_void_error_union, | ||
| 867 | .anyerror, | ||
| 868 | => return 2, // TODO revisit this when we have the concept of the error tag type | ||
| 806 | 869 | ||
| 807 | .array, .array_sentinel => return self.elemType().abiAlignment(target), | 870 | .array, .array_sentinel => return self.elemType().abiAlignment(target), |
| 808 | 871 | ||
| ... | @@ -828,6 +891,16 @@ pub const Type = extern union { | ... | @@ -828,6 +891,16 @@ pub const Type = extern union { |
| 828 | return child_type.abiAlignment(target); | 891 | return child_type.abiAlignment(target); |
| 829 | }, | 892 | }, |
| 830 | 893 | ||
| 894 | .error_union => { | ||
| 895 | const payload = self.cast(Payload.ErrorUnion).?; | ||
| 896 | if (!payload.error_set.hasCodeGenBits()) { | ||
| 897 | return payload.payload.abiAlignment(target); | ||
| 898 | } else if (!payload.payload.hasCodeGenBits()) { | ||
| 899 | return payload.error_set.abiAlignment(target); | ||
| 900 | } | ||
| 901 | @panic("TODO abiAlignment error union"); | ||
| 902 | }, | ||
| 903 | |||
| 831 | .c_void, | 904 | .c_void, |
| 832 | .void, | 905 | .void, |
| 833 | .type, | 906 | .type, |
| ... | @@ -881,12 +954,15 @@ pub const Type = extern union { | ... | @@ -881,12 +954,15 @@ pub const Type = extern union { |
| 881 | .i32, .u32 => return 4, | 954 | .i32, .u32 => return 4, |
| 882 | .i64, .u64 => return 8, | 955 | .i64, .u64 => return 8, |
| 883 | 956 | ||
| 884 | .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 957 | .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 885 | 958 | ||
| 886 | .const_slice, | 959 | .const_slice, |
| 887 | .mut_slice, | 960 | .mut_slice, |
| 888 | .const_slice_u8, | 961 | => { |
| 889 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, | 962 | if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2; |
| 963 | return @divExact(target.cpu.arch.ptrBitWidth(), 8); | ||
| 964 | }, | ||
| 965 | .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, | ||
| 890 | 966 | ||
| 891 | .optional_single_const_pointer, | 967 | .optional_single_const_pointer, |
| 892 | .optional_single_mut_pointer, | 968 | .optional_single_mut_pointer, |
| ... | @@ -922,7 +998,11 @@ pub const Type = extern union { | ... | @@ -922,7 +998,11 @@ pub const Type = extern union { |
| 922 | .f128 => return 16, | 998 | .f128 => return 16, |
| 923 | .c_longdouble => return 16, | 999 | .c_longdouble => return 16, |
| 924 | 1000 | ||
| 925 | .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type | 1001 | .error_set, |
| 1002 | .error_set_single, | ||
| 1003 | .anyerror_void_error_union, | ||
| 1004 | .anyerror, | ||
| 1005 | => return 2, // TODO revisit this when we have the concept of the error tag type | ||
| 926 | 1006 | ||
| 927 | .int_signed, .int_unsigned => { | 1007 | .int_signed, .int_unsigned => { |
| 928 | const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| | 1008 | const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| |
| ... | @@ -949,6 +1029,18 @@ pub const Type = extern union { | ... | @@ -949,6 +1029,18 @@ pub const Type = extern union { |
| 949 | // to the child type's ABI alignment. | 1029 | // to the child type's ABI alignment. |
| 950 | return child_type.abiAlignment(target) + child_type.abiSize(target); | 1030 | return child_type.abiAlignment(target) + child_type.abiSize(target); |
| 951 | }, | 1031 | }, |
| 1032 | |||
| 1033 | .error_union => { | ||
| 1034 | const payload = self.cast(Payload.ErrorUnion).?; | ||
| 1035 | if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) { | ||
| 1036 | return 0; | ||
| 1037 | } else if (!payload.error_set.hasCodeGenBits()) { | ||
| 1038 | return payload.payload.abiSize(target); | ||
| 1039 | } else if (!payload.payload.hasCodeGenBits()) { | ||
| 1040 | return payload.error_set.abiSize(target); | ||
| 1041 | } | ||
| 1042 | @panic("TODO abiSize error union"); | ||
| 1043 | }, | ||
| 952 | }; | 1044 | }; |
| 953 | } | 1045 | } |
| 954 | 1046 | ||
| ... | @@ -1009,6 +1101,12 @@ pub const Type = extern union { | ... | @@ -1009,6 +1101,12 @@ pub const Type = extern union { |
| 1009 | .c_mut_pointer, | 1101 | .c_mut_pointer, |
| 1010 | .const_slice, | 1102 | .const_slice, |
| 1011 | .mut_slice, | 1103 | .mut_slice, |
| 1104 | .error_union, | ||
| 1105 | .@"anyframe", | ||
| 1106 | .anyframe_T, | ||
| 1107 | .anyerror_void_error_union, | ||
| 1108 | .error_set, | ||
| 1109 | .error_set_single, | ||
| 1012 | => false, | 1110 | => false, |
| 1013 | 1111 | ||
| 1014 | .single_const_pointer, | 1112 | .single_const_pointer, |
| ... | @@ -1077,6 +1175,12 @@ pub const Type = extern union { | ... | @@ -1077,6 +1175,12 @@ pub const Type = extern union { |
| 1077 | .optional_single_mut_pointer, | 1175 | .optional_single_mut_pointer, |
| 1078 | .optional_single_const_pointer, | 1176 | .optional_single_const_pointer, |
| 1079 | .enum_literal, | 1177 | .enum_literal, |
| 1178 | .error_union, | ||
| 1179 | .@"anyframe", | ||
| 1180 | .anyframe_T, | ||
| 1181 | .anyerror_void_error_union, | ||
| 1182 | .error_set, | ||
| 1183 | .error_set_single, | ||
| 1080 | => false, | 1184 | => false, |
| 1081 | 1185 | ||
| 1082 | .const_slice, | 1186 | .const_slice, |
| ... | @@ -1142,6 +1246,12 @@ pub const Type = extern union { | ... | @@ -1142,6 +1246,12 @@ pub const Type = extern union { |
| 1142 | .optional_single_const_pointer, | 1246 | .optional_single_const_pointer, |
| 1143 | .enum_literal, | 1247 | .enum_literal, |
| 1144 | .mut_slice, | 1248 | .mut_slice, |
| 1249 | .error_union, | ||
| 1250 | .@"anyframe", | ||
| 1251 | .anyframe_T, | ||
| 1252 | .anyerror_void_error_union, | ||
| 1253 | .error_set, | ||
| 1254 | .error_set_single, | ||
| 1145 | => false, | 1255 | => false, |
| 1146 | 1256 | ||
| 1147 | .single_const_pointer, | 1257 | .single_const_pointer, |
| ... | @@ -1216,6 +1326,12 @@ pub const Type = extern union { | ... | @@ -1216,6 +1326,12 @@ pub const Type = extern union { |
| 1216 | .optional_single_mut_pointer, | 1326 | .optional_single_mut_pointer, |
| 1217 | .optional_single_const_pointer, | 1327 | .optional_single_const_pointer, |
| 1218 | .enum_literal, | 1328 | .enum_literal, |
| 1329 | .error_union, | ||
| 1330 | .@"anyframe", | ||
| 1331 | .anyframe_T, | ||
| 1332 | .anyerror_void_error_union, | ||
| 1333 | .error_set, | ||
| 1334 | .error_set_single, | ||
| 1219 | => false, | 1335 | => false, |
| 1220 | 1336 | ||
| 1221 | .pointer => { | 1337 | .pointer => { |
| ... | @@ -1327,6 +1443,12 @@ pub const Type = extern union { | ... | @@ -1327,6 +1443,12 @@ pub const Type = extern union { |
| 1327 | .optional_single_const_pointer, | 1443 | .optional_single_const_pointer, |
| 1328 | .optional_single_mut_pointer, | 1444 | .optional_single_mut_pointer, |
| 1329 | .enum_literal, | 1445 | .enum_literal, |
| 1446 | .error_union, | ||
| 1447 | .@"anyframe", | ||
| 1448 | .anyframe_T, | ||
| 1449 | .anyerror_void_error_union, | ||
| 1450 | .error_set, | ||
| 1451 | .error_set_single, | ||
| 1330 | => unreachable, | 1452 | => unreachable, |
| 1331 | 1453 | ||
| 1332 | .array => self.cast(Payload.Array).?.elem_type, | 1454 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | @@ -1448,6 +1570,12 @@ pub const Type = extern union { | ... | @@ -1448,6 +1570,12 @@ pub const Type = extern union { |
| 1448 | .optional_single_mut_pointer, | 1570 | .optional_single_mut_pointer, |
| 1449 | .optional_single_const_pointer, | 1571 | .optional_single_const_pointer, |
| 1450 | .enum_literal, | 1572 | .enum_literal, |
| 1573 | .error_union, | ||
| 1574 | .@"anyframe", | ||
| 1575 | .anyframe_T, | ||
| 1576 | .anyerror_void_error_union, | ||
| 1577 | .error_set, | ||
| 1578 | .error_set_single, | ||
| 1451 | => unreachable, | 1579 | => unreachable, |
| 1452 | 1580 | ||
| 1453 | .array => self.cast(Payload.Array).?.len, | 1581 | .array => self.cast(Payload.Array).?.len, |
| ... | @@ -1515,6 +1643,12 @@ pub const Type = extern union { | ... | @@ -1515,6 +1643,12 @@ pub const Type = extern union { |
| 1515 | .optional_single_mut_pointer, | 1643 | .optional_single_mut_pointer, |
| 1516 | .optional_single_const_pointer, | 1644 | .optional_single_const_pointer, |
| 1517 | .enum_literal, | 1645 | .enum_literal, |
| 1646 | .error_union, | ||
| 1647 | .@"anyframe", | ||
| 1648 | .anyframe_T, | ||
| 1649 | .anyerror_void_error_union, | ||
| 1650 | .error_set, | ||
| 1651 | .error_set_single, | ||
| 1518 | => unreachable, | 1652 | => unreachable, |
| 1519 | 1653 | ||
| 1520 | .array, .array_u8 => return null, | 1654 | .array, .array_u8 => return null, |
| ... | @@ -1580,6 +1714,12 @@ pub const Type = extern union { | ... | @@ -1580,6 +1714,12 @@ pub const Type = extern union { |
| 1580 | .optional_single_mut_pointer, | 1714 | .optional_single_mut_pointer, |
| 1581 | .optional_single_const_pointer, | 1715 | .optional_single_const_pointer, |
| 1582 | .enum_literal, | 1716 | .enum_literal, |
| 1717 | .error_union, | ||
| 1718 | .@"anyframe", | ||
| 1719 | .anyframe_T, | ||
| 1720 | .anyerror_void_error_union, | ||
| 1721 | .error_set, | ||
| 1722 | .error_set_single, | ||
| 1583 | => false, | 1723 | => false, |
| 1584 | 1724 | ||
| 1585 | .int_signed, | 1725 | .int_signed, |
| ... | @@ -1648,6 +1788,12 @@ pub const Type = extern union { | ... | @@ -1648,6 +1788,12 @@ pub const Type = extern union { |
| 1648 | .optional_single_mut_pointer, | 1788 | .optional_single_mut_pointer, |
| 1649 | .optional_single_const_pointer, | 1789 | .optional_single_const_pointer, |
| 1650 | .enum_literal, | 1790 | .enum_literal, |
| 1791 | .error_union, | ||
| 1792 | .@"anyframe", | ||
| 1793 | .anyframe_T, | ||
| 1794 | .anyerror_void_error_union, | ||
| 1795 | .error_set, | ||
| 1796 | .error_set_single, | ||
| 1651 | => false, | 1797 | => false, |
| 1652 | 1798 | ||
| 1653 | .int_unsigned, | 1799 | .int_unsigned, |
| ... | @@ -1706,6 +1852,12 @@ pub const Type = extern union { | ... | @@ -1706,6 +1852,12 @@ pub const Type = extern union { |
| 1706 | .optional_single_mut_pointer, | 1852 | .optional_single_mut_pointer, |
| 1707 | .optional_single_const_pointer, | 1853 | .optional_single_const_pointer, |
| 1708 | .enum_literal, | 1854 | .enum_literal, |
| 1855 | .error_union, | ||
| 1856 | .@"anyframe", | ||
| 1857 | .anyframe_T, | ||
| 1858 | .anyerror_void_error_union, | ||
| 1859 | .error_set, | ||
| 1860 | .error_set_single, | ||
| 1709 | => unreachable, | 1861 | => unreachable, |
| 1710 | 1862 | ||
| 1711 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, | 1863 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| ... | @@ -1782,6 +1934,12 @@ pub const Type = extern union { | ... | @@ -1782,6 +1934,12 @@ pub const Type = extern union { |
| 1782 | .optional_single_mut_pointer, | 1934 | .optional_single_mut_pointer, |
| 1783 | .optional_single_const_pointer, | 1935 | .optional_single_const_pointer, |
| 1784 | .enum_literal, | 1936 | .enum_literal, |
| 1937 | .error_union, | ||
| 1938 | .@"anyframe", | ||
| 1939 | .anyframe_T, | ||
| 1940 | .anyerror_void_error_union, | ||
| 1941 | .error_set, | ||
| 1942 | .error_set_single, | ||
| 1785 | => false, | 1943 | => false, |
| 1786 | 1944 | ||
| 1787 | .usize, | 1945 | .usize, |
| ... | @@ -1887,6 +2045,12 @@ pub const Type = extern union { | ... | @@ -1887,6 +2045,12 @@ pub const Type = extern union { |
| 1887 | .optional_single_mut_pointer, | 2045 | .optional_single_mut_pointer, |
| 1888 | .optional_single_const_pointer, | 2046 | .optional_single_const_pointer, |
| 1889 | .enum_literal, | 2047 | .enum_literal, |
| 2048 | .error_union, | ||
| 2049 | .@"anyframe", | ||
| 2050 | .anyframe_T, | ||
| 2051 | .anyerror_void_error_union, | ||
| 2052 | .error_set, | ||
| 2053 | .error_set_single, | ||
| 1890 | => unreachable, | 2054 | => unreachable, |
| 1891 | }; | 2055 | }; |
| 1892 | } | 2056 | } |
| ... | @@ -1958,6 +2122,12 @@ pub const Type = extern union { | ... | @@ -1958,6 +2122,12 @@ pub const Type = extern union { |
| 1958 | .optional_single_mut_pointer, | 2122 | .optional_single_mut_pointer, |
| 1959 | .optional_single_const_pointer, | 2123 | .optional_single_const_pointer, |
| 1960 | .enum_literal, | 2124 | .enum_literal, |
| 2125 | .error_union, | ||
| 2126 | .@"anyframe", | ||
| 2127 | .anyframe_T, | ||
| 2128 | .anyerror_void_error_union, | ||
| 2129 | .error_set, | ||
| 2130 | .error_set_single, | ||
| 1961 | => unreachable, | 2131 | => unreachable, |
| 1962 | } | 2132 | } |
| 1963 | } | 2133 | } |
| ... | @@ -2028,6 +2198,12 @@ pub const Type = extern union { | ... | @@ -2028,6 +2198,12 @@ pub const Type = extern union { |
| 2028 | .optional_single_mut_pointer, | 2198 | .optional_single_mut_pointer, |
| 2029 | .optional_single_const_pointer, | 2199 | .optional_single_const_pointer, |
| 2030 | .enum_literal, | 2200 | .enum_literal, |
| 2201 | .error_union, | ||
| 2202 | .@"anyframe", | ||
| 2203 | .anyframe_T, | ||
| 2204 | .anyerror_void_error_union, | ||
| 2205 | .error_set, | ||
| 2206 | .error_set_single, | ||
| 2031 | => unreachable, | 2207 | => unreachable, |
| 2032 | } | 2208 | } |
| 2033 | } | 2209 | } |
| ... | @@ -2098,6 +2274,12 @@ pub const Type = extern union { | ... | @@ -2098,6 +2274,12 @@ pub const Type = extern union { |
| 2098 | .optional_single_mut_pointer, | 2274 | .optional_single_mut_pointer, |
| 2099 | .optional_single_const_pointer, | 2275 | .optional_single_const_pointer, |
| 2100 | .enum_literal, | 2276 | .enum_literal, |
| 2277 | .error_union, | ||
| 2278 | .@"anyframe", | ||
| 2279 | .anyframe_T, | ||
| 2280 | .anyerror_void_error_union, | ||
| 2281 | .error_set, | ||
| 2282 | .error_set_single, | ||
| 2101 | => unreachable, | 2283 | => unreachable, |
| 2102 | }; | 2284 | }; |
| 2103 | } | 2285 | } |
| ... | @@ -2165,6 +2347,12 @@ pub const Type = extern union { | ... | @@ -2165,6 +2347,12 @@ pub const Type = extern union { |
| 2165 | .optional_single_mut_pointer, | 2347 | .optional_single_mut_pointer, |
| 2166 | .optional_single_const_pointer, | 2348 | .optional_single_const_pointer, |
| 2167 | .enum_literal, | 2349 | .enum_literal, |
| 2350 | .error_union, | ||
| 2351 | .@"anyframe", | ||
| 2352 | .anyframe_T, | ||
| 2353 | .anyerror_void_error_union, | ||
| 2354 | .error_set, | ||
| 2355 | .error_set_single, | ||
| 2168 | => unreachable, | 2356 | => unreachable, |
| 2169 | }; | 2357 | }; |
| 2170 | } | 2358 | } |
| ... | @@ -2232,6 +2420,12 @@ pub const Type = extern union { | ... | @@ -2232,6 +2420,12 @@ pub const Type = extern union { |
| 2232 | .optional_single_mut_pointer, | 2420 | .optional_single_mut_pointer, |
| 2233 | .optional_single_const_pointer, | 2421 | .optional_single_const_pointer, |
| 2234 | .enum_literal, | 2422 | .enum_literal, |
| 2423 | .error_union, | ||
| 2424 | .@"anyframe", | ||
| 2425 | .anyframe_T, | ||
| 2426 | .anyerror_void_error_union, | ||
| 2427 | .error_set, | ||
| 2428 | .error_set_single, | ||
| 2235 | => unreachable, | 2429 | => unreachable, |
| 2236 | }; | 2430 | }; |
| 2237 | } | 2431 | } |
| ... | @@ -2299,6 +2493,12 @@ pub const Type = extern union { | ... | @@ -2299,6 +2493,12 @@ pub const Type = extern union { |
| 2299 | .optional_single_mut_pointer, | 2493 | .optional_single_mut_pointer, |
| 2300 | .optional_single_const_pointer, | 2494 | .optional_single_const_pointer, |
| 2301 | .enum_literal, | 2495 | .enum_literal, |
| 2496 | .error_union, | ||
| 2497 | .@"anyframe", | ||
| 2498 | .anyframe_T, | ||
| 2499 | .anyerror_void_error_union, | ||
| 2500 | .error_set, | ||
| 2501 | .error_set_single, | ||
| 2302 | => false, | 2502 | => false, |
| 2303 | }; | 2503 | }; |
| 2304 | } | 2504 | } |
| ... | @@ -2350,6 +2550,12 @@ pub const Type = extern union { | ... | @@ -2350,6 +2550,12 @@ pub const Type = extern union { |
| 2350 | .optional_single_mut_pointer, | 2550 | .optional_single_mut_pointer, |
| 2351 | .optional_single_const_pointer, | 2551 | .optional_single_const_pointer, |
| 2352 | .enum_literal, | 2552 | .enum_literal, |
| 2553 | .anyerror_void_error_union, | ||
| 2554 | .anyframe_T, | ||
| 2555 | .@"anyframe", | ||
| 2556 | .error_union, | ||
| 2557 | .error_set, | ||
| 2558 | .error_set_single, | ||
| 2353 | => return null, | 2559 | => return null, |
| 2354 | 2560 | ||
| 2355 | .void => return Value.initTag(.void_value), | 2561 | .void => return Value.initTag(.void_value), |
| ... | @@ -2453,6 +2659,12 @@ pub const Type = extern union { | ... | @@ -2453,6 +2659,12 @@ pub const Type = extern union { |
| 2453 | .optional_single_mut_pointer, | 2659 | .optional_single_mut_pointer, |
| 2454 | .optional_single_const_pointer, | 2660 | .optional_single_const_pointer, |
| 2455 | .enum_literal, | 2661 | .enum_literal, |
| 2662 | .error_union, | ||
| 2663 | .@"anyframe", | ||
| 2664 | .anyframe_T, | ||
| 2665 | .anyerror_void_error_union, | ||
| 2666 | .error_set, | ||
| 2667 | .error_set_single, | ||
| 2456 | => return false, | 2668 | => return false, |
| 2457 | 2669 | ||
| 2458 | .c_const_pointer, | 2670 | .c_const_pointer, |
| ... | @@ -2510,6 +2722,8 @@ pub const Type = extern union { | ... | @@ -2510,6 +2722,8 @@ pub const Type = extern union { |
| 2510 | fn_naked_noreturn_no_args, | 2722 | fn_naked_noreturn_no_args, |
| 2511 | fn_ccc_void_no_args, | 2723 | fn_ccc_void_no_args, |
| 2512 | single_const_pointer_to_comptime_int, | 2724 | single_const_pointer_to_comptime_int, |
| 2725 | anyerror_void_error_union, | ||
| 2726 | @"anyframe", | ||
| 2513 | const_slice_u8, // See last_no_payload_tag below. | 2727 | const_slice_u8, // See last_no_payload_tag below. |
| 2514 | // After this, the tag requires a payload. | 2728 | // After this, the tag requires a payload. |
| 2515 | 2729 | ||
| ... | @@ -2532,6 +2746,10 @@ pub const Type = extern union { | ... | @@ -2532,6 +2746,10 @@ pub const Type = extern union { |
| 2532 | optional, | 2746 | optional, |
| 2533 | optional_single_mut_pointer, | 2747 | optional_single_mut_pointer, |
| 2534 | optional_single_const_pointer, | 2748 | optional_single_const_pointer, |
| 2749 | error_union, | ||
| 2750 | anyframe_T, | ||
| 2751 | error_set, | ||
| 2752 | error_set_single, | ||
| 2535 | 2753 | ||
| 2536 | pub const last_no_payload_tag = Tag.const_slice_u8; | 2754 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 2537 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 2755 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -2613,6 +2831,32 @@ pub const Type = extern union { | ... | @@ -2613,6 +2831,32 @@ pub const Type = extern union { |
| 2613 | @"volatile": bool, | 2831 | @"volatile": bool, |
| 2614 | size: std.builtin.TypeInfo.Pointer.Size, | 2832 | size: std.builtin.TypeInfo.Pointer.Size, |
| 2615 | }; | 2833 | }; |
| 2834 | |||
| 2835 | pub const ErrorUnion = struct { | ||
| 2836 | base: Payload = .{ .tag = .error_union }, | ||
| 2837 | |||
| 2838 | error_set: Type, | ||
| 2839 | payload: Type, | ||
| 2840 | }; | ||
| 2841 | |||
| 2842 | pub const AnyFrame = struct { | ||
| 2843 | base: Payload = .{ .tag = .anyframe_T }, | ||
| 2844 | |||
| 2845 | return_type: Type, | ||
| 2846 | }; | ||
| 2847 | |||
| 2848 | pub const ErrorSet = struct { | ||
| 2849 | base: Payload = .{ .tag = .error_set }, | ||
| 2850 | |||
| 2851 | decl: *Module.Decl, | ||
| 2852 | }; | ||
| 2853 | |||
| 2854 | pub const ErrorSetSingle = struct { | ||
| 2855 | base: Payload = .{ .tag = .error_set_single }, | ||
| 2856 | |||
| 2857 | /// memory is owned by `Module` | ||
| 2858 | name: []const u8, | ||
| 2859 | }; | ||
| 2616 | }; | 2860 | }; |
| 2617 | }; | 2861 | }; |
| 2618 | 2862 |
src-self-hosted/value.zig+88-3| ... | @@ -61,6 +61,7 @@ pub const Value = extern union { | ... | @@ -61,6 +61,7 @@ pub const Value = extern union { |
| 61 | single_const_pointer_to_comptime_int_type, | 61 | single_const_pointer_to_comptime_int_type, |
| 62 | const_slice_u8_type, | 62 | const_slice_u8_type, |
| 63 | enum_literal_type, | 63 | enum_literal_type, |
| 64 | anyframe_type, | ||
| 64 | 65 | ||
| 65 | undef, | 66 | undef, |
| 66 | zero, | 67 | zero, |
| ... | @@ -90,6 +91,8 @@ pub const Value = extern union { | ... | @@ -90,6 +91,8 @@ pub const Value = extern union { |
| 90 | float_64, | 91 | float_64, |
| 91 | float_128, | 92 | float_128, |
| 92 | enum_literal, | 93 | enum_literal, |
| 94 | error_set, | ||
| 95 | @"error", | ||
| 93 | 96 | ||
| 94 | pub const last_no_payload_tag = Tag.bool_false; | 97 | pub const last_no_payload_tag = Tag.bool_false; |
| 95 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 98 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -168,6 +171,7 @@ pub const Value = extern union { | ... | @@ -168,6 +171,7 @@ pub const Value = extern union { |
| 168 | .single_const_pointer_to_comptime_int_type, | 171 | .single_const_pointer_to_comptime_int_type, |
| 169 | .const_slice_u8_type, | 172 | .const_slice_u8_type, |
| 170 | .enum_literal_type, | 173 | .enum_literal_type, |
| 174 | .anyframe_type, | ||
| 171 | .undef, | 175 | .undef, |
| 172 | .zero, | 176 | .zero, |
| 173 | .void_value, | 177 | .void_value, |
| ... | @@ -241,6 +245,10 @@ pub const Value = extern union { | ... | @@ -241,6 +245,10 @@ pub const Value = extern union { |
| 241 | }; | 245 | }; |
| 242 | return Value{ .ptr_otherwise = &new_payload.base }; | 246 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 243 | }, | 247 | }, |
| 248 | .@"error" => return self.copyPayloadShallow(allocator, Payload.Error), | ||
| 249 | |||
| 250 | // memory is managed by the declaration | ||
| 251 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | ||
| 244 | } | 252 | } |
| 245 | } | 253 | } |
| 246 | 254 | ||
| ... | @@ -300,6 +308,7 @@ pub const Value = extern union { | ... | @@ -300,6 +308,7 @@ pub const Value = extern union { |
| 300 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), | 308 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 301 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), | 309 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 302 | .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), | 310 | .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), |
| 311 | .anyframe_type => return out_stream.writeAll("anyframe"), | ||
| 303 | 312 | ||
| 304 | .null_value => return out_stream.writeAll("null"), | 313 | .null_value => return out_stream.writeAll("null"), |
| 305 | .undef => return out_stream.writeAll("undefined"), | 314 | .undef => return out_stream.writeAll("undefined"), |
| ... | @@ -343,6 +352,15 @@ pub const Value = extern union { | ... | @@ -343,6 +352,15 @@ pub const Value = extern union { |
| 343 | .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), | 352 | .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), |
| 344 | .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), | 353 | .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), |
| 345 | .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), | 354 | .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), |
| 355 | .error_set => { | ||
| 356 | const error_set = val.cast(Payload.ErrorSet).?; | ||
| 357 | try out_stream.writeAll("error{"); | ||
| 358 | for (error_set.fields.items()) |entry| { | ||
| 359 | try out_stream.print("{},", .{entry.value}); | ||
| 360 | } | ||
| 361 | return out_stream.writeAll("}"); | ||
| 362 | }, | ||
| 363 | .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}), | ||
| 346 | }; | 364 | }; |
| 347 | } | 365 | } |
| 348 | 366 | ||
| ... | @@ -363,11 +381,9 @@ pub const Value = extern union { | ... | @@ -363,11 +381,9 @@ pub const Value = extern union { |
| 363 | } | 381 | } |
| 364 | 382 | ||
| 365 | /// Asserts that the value is representable as a type. | 383 | /// Asserts that the value is representable as a type. |
| 366 | pub fn toType(self: Value) Type { | 384 | pub fn toType(self: Value, allocator: *Allocator) !Type { |
| 367 | return switch (self.tag()) { | 385 | return switch (self.tag()) { |
| 368 | .ty => self.cast(Payload.Ty).?.ty, | 386 | .ty => self.cast(Payload.Ty).?.ty, |
| 369 | .int_type => @panic("TODO int type to type"), | ||
| 370 | |||
| 371 | .u8_type => Type.initTag(.u8), | 387 | .u8_type => Type.initTag(.u8), |
| 372 | .i8_type => Type.initTag(.i8), | 388 | .i8_type => Type.initTag(.i8), |
| 373 | .u16_type => Type.initTag(.u16), | 389 | .u16_type => Type.initTag(.u16), |
| ... | @@ -408,6 +424,26 @@ pub const Value = extern union { | ... | @@ -408,6 +424,26 @@ pub const Value = extern union { |
| 408 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), | 424 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 409 | .const_slice_u8_type => Type.initTag(.const_slice_u8), | 425 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 410 | .enum_literal_type => Type.initTag(.enum_literal), | 426 | .enum_literal_type => Type.initTag(.enum_literal), |
| 427 | .anyframe_type => Type.initTag(.@"anyframe"), | ||
| 428 | |||
| 429 | .int_type => { | ||
| 430 | const payload = self.cast(Payload.IntType).?; | ||
| 431 | if (payload.signed) { | ||
| 432 | const new = try allocator.create(Type.Payload.IntSigned); | ||
| 433 | new.* = .{ .bits = payload.bits }; | ||
| 434 | return Type.initPayload(&new.base); | ||
| 435 | } else { | ||
| 436 | const new = try allocator.create(Type.Payload.IntUnsigned); | ||
| 437 | new.* = .{ .bits = payload.bits }; | ||
| 438 | return Type.initPayload(&new.base); | ||
| 439 | } | ||
| 440 | }, | ||
| 441 | .error_set => { | ||
| 442 | const payload = self.cast(Payload.ErrorSet).?; | ||
| 443 | const new = try allocator.create(Type.Payload.ErrorSet); | ||
| 444 | new.* = .{ .decl = payload.decl }; | ||
| 445 | return Type.initPayload(&new.base); | ||
| 446 | }, | ||
| 411 | 447 | ||
| 412 | .undef, | 448 | .undef, |
| 413 | .zero, | 449 | .zero, |
| ... | @@ -433,6 +469,7 @@ pub const Value = extern union { | ... | @@ -433,6 +469,7 @@ pub const Value = extern union { |
| 433 | .float_64, | 469 | .float_64, |
| 434 | .float_128, | 470 | .float_128, |
| 435 | .enum_literal, | 471 | .enum_literal, |
| 472 | .@"error", | ||
| 436 | => unreachable, | 473 | => unreachable, |
| 437 | }; | 474 | }; |
| 438 | } | 475 | } |
| ... | @@ -482,6 +519,7 @@ pub const Value = extern union { | ... | @@ -482,6 +519,7 @@ pub const Value = extern union { |
| 482 | .single_const_pointer_to_comptime_int_type, | 519 | .single_const_pointer_to_comptime_int_type, |
| 483 | .const_slice_u8_type, | 520 | .const_slice_u8_type, |
| 484 | .enum_literal_type, | 521 | .enum_literal_type, |
| 522 | .anyframe_type, | ||
| 485 | .null_value, | 523 | .null_value, |
| 486 | .function, | 524 | .function, |
| 487 | .variable, | 525 | .variable, |
| ... | @@ -498,6 +536,8 @@ pub const Value = extern union { | ... | @@ -498,6 +536,8 @@ pub const Value = extern union { |
| 498 | .unreachable_value, | 536 | .unreachable_value, |
| 499 | .empty_array, | 537 | .empty_array, |
| 500 | .enum_literal, | 538 | .enum_literal, |
| 539 | .error_set, | ||
| 540 | .@"error", | ||
| 501 | => unreachable, | 541 | => unreachable, |
| 502 | 542 | ||
| 503 | .undef => unreachable, | 543 | .undef => unreachable, |
| ... | @@ -560,6 +600,7 @@ pub const Value = extern union { | ... | @@ -560,6 +600,7 @@ pub const Value = extern union { |
| 560 | .single_const_pointer_to_comptime_int_type, | 600 | .single_const_pointer_to_comptime_int_type, |
| 561 | .const_slice_u8_type, | 601 | .const_slice_u8_type, |
| 562 | .enum_literal_type, | 602 | .enum_literal_type, |
| 603 | .anyframe_type, | ||
| 563 | .null_value, | 604 | .null_value, |
| 564 | .function, | 605 | .function, |
| 565 | .variable, | 606 | .variable, |
| ... | @@ -576,6 +617,8 @@ pub const Value = extern union { | ... | @@ -576,6 +617,8 @@ pub const Value = extern union { |
| 576 | .unreachable_value, | 617 | .unreachable_value, |
| 577 | .empty_array, | 618 | .empty_array, |
| 578 | .enum_literal, | 619 | .enum_literal, |
| 620 | .error_set, | ||
| 621 | .@"error", | ||
| 579 | => unreachable, | 622 | => unreachable, |
| 580 | 623 | ||
| 581 | .undef => unreachable, | 624 | .undef => unreachable, |
| ... | @@ -638,6 +681,7 @@ pub const Value = extern union { | ... | @@ -638,6 +681,7 @@ pub const Value = extern union { |
| 638 | .single_const_pointer_to_comptime_int_type, | 681 | .single_const_pointer_to_comptime_int_type, |
| 639 | .const_slice_u8_type, | 682 | .const_slice_u8_type, |
| 640 | .enum_literal_type, | 683 | .enum_literal_type, |
| 684 | .anyframe_type, | ||
| 641 | .null_value, | 685 | .null_value, |
| 642 | .function, | 686 | .function, |
| 643 | .variable, | 687 | .variable, |
| ... | @@ -654,6 +698,8 @@ pub const Value = extern union { | ... | @@ -654,6 +698,8 @@ pub const Value = extern union { |
| 654 | .unreachable_value, | 698 | .unreachable_value, |
| 655 | .empty_array, | 699 | .empty_array, |
| 656 | .enum_literal, | 700 | .enum_literal, |
| 701 | .error_set, | ||
| 702 | .@"error", | ||
| 657 | => unreachable, | 703 | => unreachable, |
| 658 | 704 | ||
| 659 | .undef => unreachable, | 705 | .undef => unreachable, |
| ... | @@ -742,6 +788,7 @@ pub const Value = extern union { | ... | @@ -742,6 +788,7 @@ pub const Value = extern union { |
| 742 | .single_const_pointer_to_comptime_int_type, | 788 | .single_const_pointer_to_comptime_int_type, |
| 743 | .const_slice_u8_type, | 789 | .const_slice_u8_type, |
| 744 | .enum_literal_type, | 790 | .enum_literal_type, |
| 791 | .anyframe_type, | ||
| 745 | .null_value, | 792 | .null_value, |
| 746 | .function, | 793 | .function, |
| 747 | .variable, | 794 | .variable, |
| ... | @@ -759,6 +806,8 @@ pub const Value = extern union { | ... | @@ -759,6 +806,8 @@ pub const Value = extern union { |
| 759 | .unreachable_value, | 806 | .unreachable_value, |
| 760 | .empty_array, | 807 | .empty_array, |
| 761 | .enum_literal, | 808 | .enum_literal, |
| 809 | .error_set, | ||
| 810 | .@"error", | ||
| 762 | => unreachable, | 811 | => unreachable, |
| 763 | 812 | ||
| 764 | .zero, | 813 | .zero, |
| ... | @@ -825,6 +874,7 @@ pub const Value = extern union { | ... | @@ -825,6 +874,7 @@ pub const Value = extern union { |
| 825 | .single_const_pointer_to_comptime_int_type, | 874 | .single_const_pointer_to_comptime_int_type, |
| 826 | .const_slice_u8_type, | 875 | .const_slice_u8_type, |
| 827 | .enum_literal_type, | 876 | .enum_literal_type, |
| 877 | .anyframe_type, | ||
| 828 | .null_value, | 878 | .null_value, |
| 829 | .function, | 879 | .function, |
| 830 | .variable, | 880 | .variable, |
| ... | @@ -841,6 +891,8 @@ pub const Value = extern union { | ... | @@ -841,6 +891,8 @@ pub const Value = extern union { |
| 841 | .unreachable_value, | 891 | .unreachable_value, |
| 842 | .empty_array, | 892 | .empty_array, |
| 843 | .enum_literal, | 893 | .enum_literal, |
| 894 | .error_set, | ||
| 895 | .@"error", | ||
| 844 | => unreachable, | 896 | => unreachable, |
| 845 | 897 | ||
| 846 | .zero, | 898 | .zero, |
| ... | @@ -988,6 +1040,7 @@ pub const Value = extern union { | ... | @@ -988,6 +1040,7 @@ pub const Value = extern union { |
| 988 | .single_const_pointer_to_comptime_int_type, | 1040 | .single_const_pointer_to_comptime_int_type, |
| 989 | .const_slice_u8_type, | 1041 | .const_slice_u8_type, |
| 990 | .enum_literal_type, | 1042 | .enum_literal_type, |
| 1043 | .anyframe_type, | ||
| 991 | .bool_true, | 1044 | .bool_true, |
| 992 | .bool_false, | 1045 | .bool_false, |
| 993 | .null_value, | 1046 | .null_value, |
| ... | @@ -1007,6 +1060,8 @@ pub const Value = extern union { | ... | @@ -1007,6 +1060,8 @@ pub const Value = extern union { |
| 1007 | .void_value, | 1060 | .void_value, |
| 1008 | .unreachable_value, | 1061 | .unreachable_value, |
| 1009 | .enum_literal, | 1062 | .enum_literal, |
| 1063 | .error_set, | ||
| 1064 | .@"error", | ||
| 1010 | => unreachable, | 1065 | => unreachable, |
| 1011 | 1066 | ||
| 1012 | .zero => false, | 1067 | .zero => false, |
| ... | @@ -1063,6 +1118,7 @@ pub const Value = extern union { | ... | @@ -1063,6 +1118,7 @@ pub const Value = extern union { |
| 1063 | .single_const_pointer_to_comptime_int_type, | 1118 | .single_const_pointer_to_comptime_int_type, |
| 1064 | .const_slice_u8_type, | 1119 | .const_slice_u8_type, |
| 1065 | .enum_literal_type, | 1120 | .enum_literal_type, |
| 1121 | .anyframe_type, | ||
| 1066 | .null_value, | 1122 | .null_value, |
| 1067 | .function, | 1123 | .function, |
| 1068 | .variable, | 1124 | .variable, |
| ... | @@ -1076,6 +1132,8 @@ pub const Value = extern union { | ... | @@ -1076,6 +1132,8 @@ pub const Value = extern union { |
| 1076 | .unreachable_value, | 1132 | .unreachable_value, |
| 1077 | .empty_array, | 1133 | .empty_array, |
| 1078 | .enum_literal, | 1134 | .enum_literal, |
| 1135 | .error_set, | ||
| 1136 | .@"error", | ||
| 1079 | => unreachable, | 1137 | => unreachable, |
| 1080 | 1138 | ||
| 1081 | .zero, | 1139 | .zero, |
| ... | @@ -1197,6 +1255,7 @@ pub const Value = extern union { | ... | @@ -1197,6 +1255,7 @@ pub const Value = extern union { |
| 1197 | .single_const_pointer_to_comptime_int_type, | 1255 | .single_const_pointer_to_comptime_int_type, |
| 1198 | .const_slice_u8_type, | 1256 | .const_slice_u8_type, |
| 1199 | .enum_literal_type, | 1257 | .enum_literal_type, |
| 1258 | .anyframe_type, | ||
| 1200 | .zero, | 1259 | .zero, |
| 1201 | .bool_true, | 1260 | .bool_true, |
| 1202 | .bool_false, | 1261 | .bool_false, |
| ... | @@ -1218,6 +1277,8 @@ pub const Value = extern union { | ... | @@ -1218,6 +1277,8 @@ pub const Value = extern union { |
| 1218 | .unreachable_value, | 1277 | .unreachable_value, |
| 1219 | .empty_array, | 1278 | .empty_array, |
| 1220 | .enum_literal, | 1279 | .enum_literal, |
| 1280 | .error_set, | ||
| 1281 | .@"error", | ||
| 1221 | => unreachable, | 1282 | => unreachable, |
| 1222 | 1283 | ||
| 1223 | .ref_val => self.cast(Payload.RefVal).?.val, | 1284 | .ref_val => self.cast(Payload.RefVal).?.val, |
| ... | @@ -1276,6 +1337,7 @@ pub const Value = extern union { | ... | @@ -1276,6 +1337,7 @@ pub const Value = extern union { |
| 1276 | .single_const_pointer_to_comptime_int_type, | 1337 | .single_const_pointer_to_comptime_int_type, |
| 1277 | .const_slice_u8_type, | 1338 | .const_slice_u8_type, |
| 1278 | .enum_literal_type, | 1339 | .enum_literal_type, |
| 1340 | .anyframe_type, | ||
| 1279 | .zero, | 1341 | .zero, |
| 1280 | .bool_true, | 1342 | .bool_true, |
| 1281 | .bool_false, | 1343 | .bool_false, |
| ... | @@ -1297,6 +1359,8 @@ pub const Value = extern union { | ... | @@ -1297,6 +1359,8 @@ pub const Value = extern union { |
| 1297 | .void_value, | 1359 | .void_value, |
| 1298 | .unreachable_value, | 1360 | .unreachable_value, |
| 1299 | .enum_literal, | 1361 | .enum_literal, |
| 1362 | .error_set, | ||
| 1363 | .@"error", | ||
| 1300 | => unreachable, | 1364 | => unreachable, |
| 1301 | 1365 | ||
| 1302 | .empty_array => unreachable, // out of bounds array index | 1366 | .empty_array => unreachable, // out of bounds array index |
| ... | @@ -1372,6 +1436,7 @@ pub const Value = extern union { | ... | @@ -1372,6 +1436,7 @@ pub const Value = extern union { |
| 1372 | .single_const_pointer_to_comptime_int_type, | 1436 | .single_const_pointer_to_comptime_int_type, |
| 1373 | .const_slice_u8_type, | 1437 | .const_slice_u8_type, |
| 1374 | .enum_literal_type, | 1438 | .enum_literal_type, |
| 1439 | .anyframe_type, | ||
| 1375 | .zero, | 1440 | .zero, |
| 1376 | .empty_array, | 1441 | .empty_array, |
| 1377 | .bool_true, | 1442 | .bool_true, |
| ... | @@ -1393,6 +1458,8 @@ pub const Value = extern union { | ... | @@ -1393,6 +1458,8 @@ pub const Value = extern union { |
| 1393 | .float_128, | 1458 | .float_128, |
| 1394 | .void_value, | 1459 | .void_value, |
| 1395 | .enum_literal, | 1460 | .enum_literal, |
| 1461 | .error_set, | ||
| 1462 | .@"error", | ||
| 1396 | => false, | 1463 | => false, |
| 1397 | 1464 | ||
| 1398 | .undef => unreachable, | 1465 | .undef => unreachable, |
| ... | @@ -1522,6 +1589,24 @@ pub const Value = extern union { | ... | @@ -1522,6 +1589,24 @@ pub const Value = extern union { |
| 1522 | base: Payload = .{ .tag = .float_128 }, | 1589 | base: Payload = .{ .tag = .float_128 }, |
| 1523 | val: f128, | 1590 | val: f128, |
| 1524 | }; | 1591 | }; |
| 1592 | |||
| 1593 | pub const ErrorSet = struct { | ||
| 1594 | base: Payload = .{ .tag = .error_set }, | ||
| 1595 | |||
| 1596 | // TODO revisit this when we have the concept of the error tag type | ||
| 1597 | fields: std.StringHashMapUnmanaged(u16), | ||
| 1598 | decl: *Module.Decl, | ||
| 1599 | }; | ||
| 1600 | |||
| 1601 | pub const Error = struct { | ||
| 1602 | base: Payload = .{ .tag = .@"error" }, | ||
| 1603 | |||
| 1604 | // TODO revisit this when we have the concept of the error tag type | ||
| 1605 | /// `name` is owned by `Module` and will be valid for the entire | ||
| 1606 | /// duration of the compilation. | ||
| 1607 | name: []const u8, | ||
| 1608 | value: u16, | ||
| 1609 | }; | ||
| 1525 | }; | 1610 | }; |
| 1526 | 1611 | ||
| 1527 | /// Big enough to fit any non-BigInt value | 1612 | /// Big enough to fit any non-BigInt value |
src-self-hosted/zir.zig+56-1| ... | @@ -43,6 +43,8 @@ pub const Inst = struct { | ... | @@ -43,6 +43,8 @@ pub const Inst = struct { |
| 43 | alloc, | 43 | alloc, |
| 44 | /// Same as `alloc` except the type is inferred. | 44 | /// Same as `alloc` except the type is inferred. |
| 45 | alloc_inferred, | 45 | alloc_inferred, |
| 46 | /// Create an `anyframe->T`. | ||
| 47 | anyframe_type, | ||
| 46 | /// Array concatenation. `a ++ b` | 48 | /// Array concatenation. `a ++ b` |
| 47 | array_cat, | 49 | array_cat, |
| 48 | /// Array multiplication `a ** b` | 50 | /// Array multiplication `a ** b` |
| ... | @@ -70,6 +72,8 @@ pub const Inst = struct { | ... | @@ -70,6 +72,8 @@ pub const Inst = struct { |
| 70 | /// A typed result location pointer is bitcasted to a new result location pointer. | 72 | /// A typed result location pointer is bitcasted to a new result location pointer. |
| 71 | /// The new result location pointer has an inferred type. | 73 | /// The new result location pointer has an inferred type. |
| 72 | bitcast_result_ptr, | 74 | bitcast_result_ptr, |
| 75 | /// Bitwise NOT. `~` | ||
| 76 | bitnot, | ||
| 73 | /// Bitwise OR. `|` | 77 | /// Bitwise OR. `|` |
| 74 | bitor, | 78 | bitor, |
| 75 | /// A labeled block of code, which can return a value. | 79 | /// A labeled block of code, which can return a value. |
| ... | @@ -133,6 +137,10 @@ pub const Inst = struct { | ... | @@ -133,6 +137,10 @@ pub const Inst = struct { |
| 133 | ensure_result_used, | 137 | ensure_result_used, |
| 134 | /// Emits a compile error if an error is ignored. | 138 | /// Emits a compile error if an error is ignored. |
| 135 | ensure_result_non_error, | 139 | ensure_result_non_error, |
| 140 | /// Create a `E!T` type. | ||
| 141 | error_union_type, | ||
| 142 | /// Create an error set. | ||
| 143 | error_set, | ||
| 136 | /// Export the provided Decl as the provided name in the compilation's output object file. | 144 | /// Export the provided Decl as the provided name in the compilation's output object file. |
| 137 | @"export", | 145 | @"export", |
| 138 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer | 146 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| ... | @@ -160,6 +168,8 @@ pub const Inst = struct { | ... | @@ -160,6 +168,8 @@ pub const Inst = struct { |
| 160 | /// A labeled block of code that loops forever. At the end of the body it is implied | 168 | /// A labeled block of code that loops forever. At the end of the body it is implied |
| 161 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | 169 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. |
| 162 | loop, | 170 | loop, |
| 171 | /// Merge two error sets into one, `E1 || E2`. | ||
| 172 | merge_error_sets, | ||
| 163 | /// Ambiguously remainder division or modulus. If the computation would possibly have | 173 | /// Ambiguously remainder division or modulus. If the computation would possibly have |
| 164 | /// a different value depending on whether the operation is remainder division or modulus, | 174 | /// a different value depending on whether the operation is remainder division or modulus, |
| 165 | /// a compile error is emitted. Otherwise the computation is performed. | 175 | /// a compile error is emitted. Otherwise the computation is performed. |
| ... | @@ -286,6 +296,8 @@ pub const Inst = struct { | ... | @@ -286,6 +296,8 @@ pub const Inst = struct { |
| 286 | .unwrap_err_safe, | 296 | .unwrap_err_safe, |
| 287 | .unwrap_err_unsafe, | 297 | .unwrap_err_unsafe, |
| 288 | .ensure_err_payload_void, | 298 | .ensure_err_payload_void, |
| 299 | .anyframe_type, | ||
| 300 | .bitnot, | ||
| 289 | => UnOp, | 301 | => UnOp, |
| 290 | 302 | ||
| 291 | .add, | 303 | .add, |
| ... | @@ -316,6 +328,8 @@ pub const Inst = struct { | ... | @@ -316,6 +328,8 @@ pub const Inst = struct { |
| 316 | .bitcast, | 328 | .bitcast, |
| 317 | .coerce_result_ptr, | 329 | .coerce_result_ptr, |
| 318 | .xor, | 330 | .xor, |
| 331 | .error_union_type, | ||
| 332 | .merge_error_sets, | ||
| 319 | => BinOp, | 333 | => BinOp, |
| 320 | 334 | ||
| 321 | .arg => Arg, | 335 | .arg => Arg, |
| ... | @@ -347,6 +361,7 @@ pub const Inst = struct { | ... | @@ -347,6 +361,7 @@ pub const Inst = struct { |
| 347 | .condbr => CondBr, | 361 | .condbr => CondBr, |
| 348 | .ptr_type => PtrType, | 362 | .ptr_type => PtrType, |
| 349 | .enum_literal => EnumLiteral, | 363 | .enum_literal => EnumLiteral, |
| 364 | .error_set => ErrorSet, | ||
| 350 | }; | 365 | }; |
| 351 | } | 366 | } |
| 352 | 367 | ||
| ... | @@ -438,6 +453,11 @@ pub const Inst = struct { | ... | @@ -438,6 +453,11 @@ pub const Inst = struct { |
| 438 | .ptr_type, | 453 | .ptr_type, |
| 439 | .ensure_err_payload_void, | 454 | .ensure_err_payload_void, |
| 440 | .enum_literal, | 455 | .enum_literal, |
| 456 | .merge_error_sets, | ||
| 457 | .anyframe_type, | ||
| 458 | .error_union_type, | ||
| 459 | .bitnot, | ||
| 460 | .error_set, | ||
| 441 | => false, | 461 | => false, |
| 442 | 462 | ||
| 443 | .@"break", | 463 | .@"break", |
| ... | @@ -908,6 +928,16 @@ pub const Inst = struct { | ... | @@ -908,6 +928,16 @@ pub const Inst = struct { |
| 908 | }, | 928 | }, |
| 909 | kw_args: struct {}, | 929 | kw_args: struct {}, |
| 910 | }; | 930 | }; |
| 931 | |||
| 932 | pub const ErrorSet = struct { | ||
| 933 | pub const base_tag = Tag.error_set; | ||
| 934 | base: Inst, | ||
| 935 | |||
| 936 | positionals: struct { | ||
| 937 | fields: [][]const u8, | ||
| 938 | }, | ||
| 939 | kw_args: struct {}, | ||
| 940 | }; | ||
| 911 | }; | 941 | }; |
| 912 | 942 | ||
| 913 | pub const ErrorMsg = struct { | 943 | pub const ErrorMsg = struct { |
| ... | @@ -1142,6 +1172,16 @@ const Writer = struct { | ... | @@ -1142,6 +1172,16 @@ const Writer = struct { |
| 1142 | const name = self.loop_table.get(param).?; | 1172 | const name = self.loop_table.get(param).?; |
| 1143 | return std.zig.renderStringLiteral(name, stream); | 1173 | return std.zig.renderStringLiteral(name, stream); |
| 1144 | }, | 1174 | }, |
| 1175 | [][]const u8 => { | ||
| 1176 | try stream.writeByte('['); | ||
| 1177 | for (param) |str, i| { | ||
| 1178 | if (i != 0) { | ||
| 1179 | try stream.writeAll(", "); | ||
| 1180 | } | ||
| 1181 | try std.zig.renderStringLiteral(str, stream); | ||
| 1182 | } | ||
| 1183 | try stream.writeByte(']'); | ||
| 1184 | }, | ||
| 1145 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), | 1185 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 1146 | } | 1186 | } |
| 1147 | } | 1187 | } |
| ... | @@ -1539,6 +1579,21 @@ const Parser = struct { | ... | @@ -1539,6 +1579,21 @@ const Parser = struct { |
| 1539 | const name = try self.parseStringLiteral(); | 1579 | const name = try self.parseStringLiteral(); |
| 1540 | return self.loop_table.get(name).?; | 1580 | return self.loop_table.get(name).?; |
| 1541 | }, | 1581 | }, |
| 1582 | [][]const u8 => { | ||
| 1583 | try requireEatBytes(self, "["); | ||
| 1584 | skipSpace(self); | ||
| 1585 | if (eatByte(self, ']')) return &[0][]const u8{}; | ||
| 1586 | |||
| 1587 | var strings = std.ArrayList([]const u8).init(&self.arena.allocator); | ||
| 1588 | while (true) { | ||
| 1589 | skipSpace(self); | ||
| 1590 | try strings.append(try self.parseStringLiteral()); | ||
| 1591 | skipSpace(self); | ||
| 1592 | if (!eatByte(self, ',')) break; | ||
| 1593 | } | ||
| 1594 | try requireEatBytes(self, "]"); | ||
| 1595 | return strings.toOwnedSlice(); | ||
| 1596 | }, | ||
| 1542 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), | 1597 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 1543 | } | 1598 | } |
| 1544 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); | 1599 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | @@ -1961,7 +2016,7 @@ const EmitZIR = struct { | ... | @@ -1961,7 +2016,7 @@ const EmitZIR = struct { |
| 1961 | return self.emitUnnamedDecl(&as_inst.base); | 2016 | return self.emitUnnamedDecl(&as_inst.base); |
| 1962 | }, | 2017 | }, |
| 1963 | .Type => { | 2018 | .Type => { |
| 1964 | const ty = typed_value.val.toType(); | 2019 | const ty = try typed_value.val.toType(&self.arena.allocator); |
| 1965 | return self.emitType(src, ty); | 2020 | return self.emitType(src, ty); |
| 1966 | }, | 2021 | }, |
| 1967 | .Fn => { | 2022 | .Fn => { |
src-self-hosted/zir_sema.zig+126-4| ... | @@ -97,6 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -97,6 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 97 | .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), | 97 | .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), |
| 98 | .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), | 98 | .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), |
| 99 | .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), | 99 | .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), |
| 100 | .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?), | ||
| 100 | .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), | 101 | .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), |
| 101 | .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), | 102 | .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), |
| 102 | .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), | 103 | .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), |
| ... | @@ -122,6 +123,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -122,6 +123,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 122 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), | 123 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), |
| 123 | .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), | 124 | .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), |
| 124 | .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), | 125 | .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), |
| 126 | .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?), | ||
| 127 | .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?), | ||
| 128 | .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?), | ||
| 129 | .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?), | ||
| 125 | } | 130 | } |
| 126 | } | 131 | } |
| 127 | 132 | ||
| ... | @@ -145,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir | ... | @@ -145,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir |
| 145 | for (block_scope.instructions.items) |inst| { | 150 | for (block_scope.instructions.items) |inst| { |
| 146 | if (inst.castTag(.ret)) |ret| { | 151 | if (inst.castTag(.ret)) |ret| { |
| 147 | const val = try mod.resolveConstValue(&block_scope.base, ret.operand); | 152 | const val = try mod.resolveConstValue(&block_scope.base, ret.operand); |
| 148 | return val.toType(); | 153 | return val.toType(block_scope.base.arena()); |
| 149 | } else { | 154 | } else { |
| 150 | return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); | 155 | return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); |
| 151 | } | 156 | } |
| ... | @@ -270,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { | ... | @@ -270,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { |
| 270 | const wanted_type = Type.initTag(.@"type"); | 275 | const wanted_type = Type.initTag(.@"type"); |
| 271 | const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); | 276 | const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); |
| 272 | const val = try mod.resolveConstValue(scope, coerced_inst); | 277 | const val = try mod.resolveConstValue(scope, coerced_inst); |
| 273 | return val.toType(); | 278 | return val.toType(scope.arena()); |
| 274 | } | 279 | } |
| 275 | 280 | ||
| 276 | fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 { | 281 | fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 { |
| ... | @@ -431,6 +436,7 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr | ... | @@ -431,6 +436,7 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr |
| 431 | // The bytes references memory inside the ZIR module, which can get deallocated | 436 | // The bytes references memory inside the ZIR module, which can get deallocated |
| 432 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. | 437 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. |
| 433 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | 438 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 439 | errdefer new_decl_arena.deinit(); | ||
| 434 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); | 440 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); |
| 435 | 441 | ||
| 436 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | 442 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); |
| ... | @@ -716,6 +722,54 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar | ... | @@ -716,6 +722,54 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar |
| 716 | return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); | 722 | return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); |
| 717 | } | 723 | } |
| 718 | 724 | ||
| 725 | fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | ||
| 726 | const error_union = try resolveType(mod, scope, inst.positionals.lhs); | ||
| 727 | const payload = try resolveType(mod, scope, inst.positionals.rhs); | ||
| 728 | |||
| 729 | if (error_union.zigTypeTag() != .ErrorSet) { | ||
| 730 | return mod.fail(scope, inst.base.src, "expected error set type, found {}", .{error_union.elemType()}); | ||
| 731 | } | ||
| 732 | |||
| 733 | return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload)); | ||
| 734 | } | ||
| 735 | |||
| 736 | fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | ||
| 737 | const return_type = try resolveType(mod, scope, inst.positionals.operand); | ||
| 738 | |||
| 739 | return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); | ||
| 740 | } | ||
| 741 | |||
| 742 | fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { | ||
| 743 | // The declarations arena will store the hashmap. | ||
| 744 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 745 | errdefer new_decl_arena.deinit(); | ||
| 746 | |||
| 747 | const payload = try scope.arena().create(Value.Payload.ErrorSet); | ||
| 748 | payload.* = .{ | ||
| 749 | .fields = .{}, | ||
| 750 | .decl = undefined, // populated below | ||
| 751 | }; | ||
| 752 | try payload.fields.ensureCapacity(&new_decl_arena.allocator, inst.positionals.fields.len); | ||
| 753 | |||
| 754 | for (inst.positionals.fields) |field_name| { | ||
| 755 | const entry = try mod.getErrorValue(field_name); | ||
| 756 | if (payload.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| { | ||
| 757 | return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name}); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | // TODO create name in format "error:line:column" | ||
| 761 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 762 | .ty = Type.initTag(.type), | ||
| 763 | .val = Value.initPayload(&payload.base), | ||
| 764 | }); | ||
| 765 | payload.decl = new_decl; | ||
| 766 | return mod.analyzeDeclRef(scope, inst.base.src, new_decl); | ||
| 767 | } | ||
| 768 | |||
| 769 | fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | ||
| 770 | return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{}); | ||
| 771 | } | ||
| 772 | |||
| 719 | fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { | 773 | fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { |
| 720 | const payload = try scope.arena().create(Value.Payload.Bytes); | 774 | const payload = try scope.arena().create(Value.Payload.Bytes); |
| 721 | payload.* = .{ | 775 | payload.* = .{ |
| ... | @@ -858,8 +912,72 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr | ... | @@ -858,8 +912,72 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr |
| 858 | ); | 912 | ); |
| 859 | } | 913 | } |
| 860 | }, | 914 | }, |
| 861 | else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), | 915 | .Pointer => { |
| 916 | const ptr_child = elem_ty.elemType(); | ||
| 917 | switch (ptr_child.zigTypeTag()) { | ||
| 918 | .Array => { | ||
| 919 | if (mem.eql(u8, field_name, "len")) { | ||
| 920 | const len_payload = try scope.arena().create(Value.Payload.Int_u64); | ||
| 921 | len_payload.* = .{ .int = ptr_child.arrayLen() }; | ||
| 922 | |||
| 923 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 924 | ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) }; | ||
| 925 | |||
| 926 | return mod.constInst(scope, fieldptr.base.src, .{ | ||
| 927 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 928 | .val = Value.initPayload(&ref_payload.base), | ||
| 929 | }); | ||
| 930 | } else { | ||
| 931 | return mod.fail( | ||
| 932 | scope, | ||
| 933 | fieldptr.positionals.field_name.src, | ||
| 934 | "no member named '{}' in '{}'", | ||
| 935 | .{ field_name, elem_ty }, | ||
| 936 | ); | ||
| 937 | } | ||
| 938 | }, | ||
| 939 | else => {}, | ||
| 940 | } | ||
| 941 | }, | ||
| 942 | .Type => { | ||
| 943 | _ = try mod.resolveConstValue(scope, object_ptr); | ||
| 944 | const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src); | ||
| 945 | const val = result.value().?; | ||
| 946 | const child_type = try val.toType(scope.arena()); | ||
| 947 | switch (child_type.zigTypeTag()) { | ||
| 948 | .ErrorSet => { | ||
| 949 | // TODO resolve inferred error sets | ||
| 950 | const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| | ||
| 951 | (payload.fields.getEntry(field_name) orelse | ||
| 952 | return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* | ||
| 953 | else try mod.getErrorValue(field_name); | ||
| 954 | |||
| 955 | const error_payload = try scope.arena().create(Value.Payload.Error); | ||
| 956 | error_payload.* = .{ | ||
| 957 | .name = entry.key, | ||
| 958 | .value = entry.value, | ||
| 959 | }; | ||
| 960 | |||
| 961 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 962 | ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) }; | ||
| 963 | |||
| 964 | const result_type = if (child_type.tag() == .anyerror) blk: { | ||
| 965 | const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle); | ||
| 966 | result_payload.* = .{ .name = entry.key }; | ||
| 967 | break :blk Type.initPayload(&result_payload.base); | ||
| 968 | } else child_type; | ||
| 969 | |||
| 970 | return mod.constInst(scope, fieldptr.base.src, .{ | ||
| 971 | .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One), | ||
| 972 | .val = Value.initPayload(&ref_payload.base), | ||
| 973 | }); | ||
| 974 | }, | ||
| 975 | else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), | ||
| 976 | } | ||
| 977 | }, | ||
| 978 | else => {}, | ||
| 862 | } | 979 | } |
| 980 | return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}); | ||
| 863 | } | 981 | } |
| 864 | 982 | ||
| 865 | fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 983 | fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| ... | @@ -983,6 +1101,10 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE | ... | @@ -983,6 +1101,10 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE |
| 983 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); | 1101 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); |
| 984 | } | 1102 | } |
| 985 | 1103 | ||
| 1104 | fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | ||
| 1105 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{}); | ||
| 1106 | } | ||
| 1107 | |||
| 986 | fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 1108 | fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 987 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); | 1109 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); |
| 988 | } | 1110 | } |
| ... | @@ -1348,7 +1470,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne | ... | @@ -1348,7 +1470,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne |
| 1348 | 1470 | ||
| 1349 | if (host_size != 0 and bit_offset >= host_size * 8) | 1471 | if (host_size != 0 and bit_offset >= host_size * 8) |
| 1350 | return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{}); | 1472 | return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{}); |
| 1351 | 1473 | ||
| 1352 | const sentinel = if (inst.kw_args.sentinel) |some| | 1474 | const sentinel = if (inst.kw_args.sentinel) |some| |
| 1353 | (try resolveInstConst(mod, scope, some)).val | 1475 | (try resolveInstConst(mod, scope, some)).val |
| 1354 | else | 1476 | else |
src/analyze.cpp+122-103| ... | @@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2586 | return ErrorNone; | 2586 | return ErrorNone; |
| 2587 | 2587 | ||
| 2588 | AstNode *decl_node = enum_type->data.enumeration.decl_node; | 2588 | AstNode *decl_node = enum_type->data.enumeration.decl_node; |
| 2589 | assert(decl_node->type == NodeTypeContainerDecl); | ||
| 2590 | 2589 | ||
| 2591 | if (enum_type->data.enumeration.resolve_loop_flag) { | 2590 | if (enum_type->data.enumeration.resolve_loop_flag) { |
| 2592 | if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) { | 2591 | if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) { |
| ... | @@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2600 | 2599 | ||
| 2601 | enum_type->data.enumeration.resolve_loop_flag = true; | 2600 | enum_type->data.enumeration.resolve_loop_flag = true; |
| 2602 | 2601 | ||
| 2603 | assert(!enum_type->data.enumeration.fields); | 2602 | uint32_t field_count; |
| 2604 | uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; | 2603 | if (decl_node->type == NodeTypeContainerDecl) { |
| 2605 | if (field_count == 0) { | 2604 | assert(!enum_type->data.enumeration.fields); |
| 2606 | add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); | 2605 | field_count = (uint32_t)decl_node->data.container_decl.fields.length; |
| 2606 | if (field_count == 0) { | ||
| 2607 | add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); | ||
| 2607 | 2608 | ||
| 2608 | enum_type->data.enumeration.src_field_count = field_count; | 2609 | enum_type->data.enumeration.src_field_count = field_count; |
| 2609 | enum_type->data.enumeration.fields = nullptr; | 2610 | enum_type->data.enumeration.fields = nullptr; |
| 2610 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2611 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2611 | return ErrorSemanticAnalyzeFail; | 2612 | return ErrorSemanticAnalyzeFail; |
| 2613 | } | ||
| 2614 | } else { | ||
| 2615 | field_count = enum_type->data.enumeration.src_field_count; | ||
| 2612 | } | 2616 | } |
| 2613 | 2617 | ||
| 2614 | Scope *scope = &enum_type->data.enumeration.decls_scope->base; | 2618 | Scope *scope = &enum_type->data.enumeration.decls_scope->base; |
| ... | @@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2624 | enum_type->abi_size = tag_int_type->abi_size; | 2628 | enum_type->abi_size = tag_int_type->abi_size; |
| 2625 | enum_type->abi_align = tag_int_type->abi_align; | 2629 | enum_type->abi_align = tag_int_type->abi_align; |
| 2626 | 2630 | ||
| 2627 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { | 2631 | ZigType *wanted_tag_int_type = nullptr; |
| 2628 | ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); | 2632 | if (decl_node->type == NodeTypeContainerDecl) { |
| 2633 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { | ||
| 2634 | wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); | ||
| 2635 | } | ||
| 2636 | } else { | ||
| 2637 | wanted_tag_int_type = enum_type->data.enumeration.tag_int_type; | ||
| 2638 | } | ||
| 2639 | |||
| 2640 | if (wanted_tag_int_type != nullptr) { | ||
| 2629 | if (type_is_invalid(wanted_tag_int_type)) { | 2641 | if (type_is_invalid(wanted_tag_int_type)) { |
| 2630 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2642 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2631 | } else if (wanted_tag_int_type->id != ZigTypeIdInt && | 2643 | } else if (wanted_tag_int_type->id != ZigTypeIdInt && |
| ... | @@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2654 | } | 2666 | } |
| 2655 | } | 2667 | } |
| 2656 | 2668 | ||
| 2657 | enum_type->data.enumeration.non_exhaustive = false; | ||
| 2658 | enum_type->data.enumeration.tag_int_type = tag_int_type; | 2669 | enum_type->data.enumeration.tag_int_type = tag_int_type; |
| 2659 | enum_type->size_in_bits = tag_int_type->size_in_bits; | 2670 | enum_type->size_in_bits = tag_int_type->size_in_bits; |
| 2660 | enum_type->abi_size = tag_int_type->abi_size; | 2671 | enum_type->abi_size = tag_int_type->abi_size; |
| ... | @@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2663 | BigInt bi_one; | 2674 | BigInt bi_one; |
| 2664 | bigint_init_unsigned(&bi_one, 1); | 2675 | bigint_init_unsigned(&bi_one, 1); |
| 2665 | 2676 | ||
| 2666 | AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); | 2677 | if (decl_node->type == NodeTypeContainerDecl) { |
| 2667 | if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { | 2678 | AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); |
| 2679 | if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { | ||
| 2680 | if (last_field_node->data.struct_field.value != nullptr) { | ||
| 2681 | add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); | ||
| 2682 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2683 | } | ||
| 2684 | if (decl_node->data.container_decl.init_arg_expr == nullptr) { | ||
| 2685 | add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size")); | ||
| 2686 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2687 | } | ||
| 2688 | enum_type->data.enumeration.non_exhaustive = true; | ||
| 2689 | } else { | ||
| 2690 | enum_type->data.enumeration.non_exhaustive = false; | ||
| 2691 | } | ||
| 2692 | } | ||
| 2693 | |||
| 2694 | if (enum_type->data.enumeration.non_exhaustive) { | ||
| 2668 | field_count -= 1; | 2695 | field_count -= 1; |
| 2669 | if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { | 2696 | if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { |
| 2670 | add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value")); | 2697 | add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value")); |
| 2671 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2698 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2672 | } | 2699 | } |
| 2673 | if (decl_node->data.container_decl.init_arg_expr == nullptr) { | ||
| 2674 | add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size")); | ||
| 2675 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2676 | } | ||
| 2677 | if (last_field_node->data.struct_field.value != nullptr) { | ||
| 2678 | add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); | ||
| 2679 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2680 | } | ||
| 2681 | enum_type->data.enumeration.non_exhaustive = true; | ||
| 2682 | } | 2700 | } |
| 2683 | 2701 | ||
| 2684 | enum_type->data.enumeration.src_field_count = field_count; | 2702 | if (decl_node->type == NodeTypeContainerDecl) { |
| 2685 | enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count); | 2703 | enum_type->data.enumeration.src_field_count = field_count; |
| 2686 | enum_type->data.enumeration.fields_by_name.init(field_count); | 2704 | enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count); |
| 2687 | 2705 | enum_type->data.enumeration.fields_by_name.init(field_count); | |
| 2688 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; | ||
| 2689 | occupied_tag_values.init(field_count); | ||
| 2690 | |||
| 2691 | TypeEnumField *last_enum_field = nullptr; | ||
| 2692 | |||
| 2693 | for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { | ||
| 2694 | AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); | ||
| 2695 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; | ||
| 2696 | type_enum_field->name = field_node->data.struct_field.name; | ||
| 2697 | type_enum_field->decl_index = field_i; | ||
| 2698 | type_enum_field->decl_node = field_node; | ||
| 2699 | 2706 | ||
| 2700 | if (field_node->data.struct_field.type != nullptr) { | 2707 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; |
| 2701 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, | 2708 | occupied_tag_values.init(field_count); |
| 2702 | buf_sprintf("structs and unions, not enums, support field types")); | ||
| 2703 | add_error_note(g, msg, decl_node, | ||
| 2704 | buf_sprintf("consider 'union(enum)' here")); | ||
| 2705 | } else if (field_node->data.struct_field.align_expr != nullptr) { | ||
| 2706 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr, | ||
| 2707 | buf_sprintf("structs and unions, not enums, support field alignment")); | ||
| 2708 | add_error_note(g, msg, decl_node, | ||
| 2709 | buf_sprintf("consider 'union(enum)' here")); | ||
| 2710 | } | ||
| 2711 | 2709 | ||
| 2712 | if (buf_eql_str(type_enum_field->name, "_")) { | 2710 | TypeEnumField *last_enum_field = nullptr; |
| 2713 | add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); | ||
| 2714 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2715 | } | ||
| 2716 | 2711 | ||
| 2717 | auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); | 2712 | for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { |
| 2718 | if (field_entry != nullptr) { | 2713 | AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); |
| 2719 | ErrorMsg *msg = add_node_error(g, field_node, | 2714 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; |
| 2720 | buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); | 2715 | type_enum_field->name = field_node->data.struct_field.name; |
| 2721 | add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); | 2716 | type_enum_field->decl_index = field_i; |
| 2722 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2717 | type_enum_field->decl_node = field_node; |
| 2723 | continue; | 2718 | |
| 2724 | } | 2719 | if (field_node->data.struct_field.type != nullptr) { |
| 2725 | 2720 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, | |
| 2726 | AstNode *tag_value = field_node->data.struct_field.value; | 2721 | buf_sprintf("structs and unions, not enums, support field types")); |
| 2722 | add_error_note(g, msg, decl_node, | ||
| 2723 | buf_sprintf("consider 'union(enum)' here")); | ||
| 2724 | } else if (field_node->data.struct_field.align_expr != nullptr) { | ||
| 2725 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr, | ||
| 2726 | buf_sprintf("structs and unions, not enums, support field alignment")); | ||
| 2727 | add_error_note(g, msg, decl_node, | ||
| 2728 | buf_sprintf("consider 'union(enum)' here")); | ||
| 2729 | } | ||
| 2730 | |||
| 2731 | if (buf_eql_str(type_enum_field->name, "_")) { | ||
| 2732 | add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); | ||
| 2733 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2734 | } | ||
| 2727 | 2735 | ||
| 2728 | if (tag_value != nullptr) { | 2736 | auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); |
| 2729 | // A user-specified value is available | 2737 | if (field_entry != nullptr) { |
| 2730 | ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, | 2738 | ErrorMsg *msg = add_node_error(g, field_node, |
| 2731 | nullptr, UndefBad); | 2739 | buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); |
| 2732 | if (type_is_invalid(result->type)) { | 2740 | add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); |
| 2733 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2741 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2734 | continue; | 2742 | continue; |
| 2735 | } | 2743 | } |
| 2736 | 2744 | ||
| 2737 | assert(result->special != ConstValSpecialRuntime); | 2745 | AstNode *tag_value = field_node->data.struct_field.value; |
| 2738 | assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); | ||
| 2739 | 2746 | ||
| 2740 | bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); | 2747 | if (tag_value != nullptr) { |
| 2741 | } else { | 2748 | // A user-specified value is available |
| 2742 | // No value was explicitly specified: allocate the last value + 1 | 2749 | ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, |
| 2743 | // or, if this is the first element, zero | 2750 | nullptr, UndefBad); |
| 2744 | if (last_enum_field != nullptr) { | 2751 | if (type_is_invalid(result->type)) { |
| 2745 | bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); | 2752 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2753 | continue; | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | assert(result->special != ConstValSpecialRuntime); | ||
| 2757 | assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); | ||
| 2758 | |||
| 2759 | bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); | ||
| 2746 | } else { | 2760 | } else { |
| 2747 | bigint_init_unsigned(&type_enum_field->value, 0); | 2761 | // No value was explicitly specified: allocate the last value + 1 |
| 2762 | // or, if this is the first element, zero | ||
| 2763 | if (last_enum_field != nullptr) { | ||
| 2764 | bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); | ||
| 2765 | } else { | ||
| 2766 | bigint_init_unsigned(&type_enum_field->value, 0); | ||
| 2767 | } | ||
| 2768 | |||
| 2769 | // Make sure we can represent this number with tag_int_type | ||
| 2770 | if (!bigint_fits_in_bits(&type_enum_field->value, | ||
| 2771 | tag_int_type->size_in_bits, | ||
| 2772 | tag_int_type->data.integral.is_signed)) { | ||
| 2773 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2774 | |||
| 2775 | Buf *val_buf = buf_alloc(); | ||
| 2776 | bigint_append_buf(val_buf, &type_enum_field->value, 10); | ||
| 2777 | add_node_error(g, field_node, | ||
| 2778 | buf_sprintf("enumeration value %s too large for type '%s'", | ||
| 2779 | buf_ptr(val_buf), buf_ptr(&tag_int_type->name))); | ||
| 2780 | |||
| 2781 | break; | ||
| 2782 | } | ||
| 2748 | } | 2783 | } |
| 2749 | 2784 | ||
| 2750 | // Make sure we can represent this number with tag_int_type | 2785 | // Make sure the value is unique |
| 2751 | if (!bigint_fits_in_bits(&type_enum_field->value, | 2786 | auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); |
| 2752 | tag_int_type->size_in_bits, | 2787 | if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { |
| 2753 | tag_int_type->data.integral.is_signed)) { | ||
| 2754 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | 2788 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2755 | 2789 | ||
| 2756 | Buf *val_buf = buf_alloc(); | 2790 | Buf *val_buf = buf_alloc(); |
| 2757 | bigint_append_buf(val_buf, &type_enum_field->value, 10); | 2791 | bigint_append_buf(val_buf, &type_enum_field->value, 10); |
| 2758 | add_node_error(g, field_node, | ||
| 2759 | buf_sprintf("enumeration value %s too large for type '%s'", | ||
| 2760 | buf_ptr(val_buf), buf_ptr(&tag_int_type->name))); | ||
| 2761 | 2792 | ||
| 2762 | break; | 2793 | ErrorMsg *msg = add_node_error(g, field_node, |
| 2794 | buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); | ||
| 2795 | add_error_note(g, msg, entry->value, | ||
| 2796 | buf_sprintf("other occurrence here")); | ||
| 2763 | } | 2797 | } |
| 2764 | } | ||
| 2765 | |||
| 2766 | // Make sure the value is unique | ||
| 2767 | auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); | ||
| 2768 | if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { | ||
| 2769 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | ||
| 2770 | |||
| 2771 | Buf *val_buf = buf_alloc(); | ||
| 2772 | bigint_append_buf(val_buf, &type_enum_field->value, 10); | ||
| 2773 | 2798 | ||
| 2774 | ErrorMsg *msg = add_node_error(g, field_node, | 2799 | last_enum_field = type_enum_field; |
| 2775 | buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); | ||
| 2776 | add_error_note(g, msg, entry->value, | ||
| 2777 | buf_sprintf("other occurrence here")); | ||
| 2778 | } | 2800 | } |
| 2779 | 2801 | occupied_tag_values.deinit(); | |
| 2780 | last_enum_field = type_enum_field; | ||
| 2781 | } | 2802 | } |
| 2782 | 2803 | ||
| 2783 | if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid) | 2804 | if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid) |
| ... | @@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2786 | enum_type->data.enumeration.resolve_loop_flag = false; | 2807 | enum_type->data.enumeration.resolve_loop_flag = false; |
| 2787 | enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; | 2808 | enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; |
| 2788 | 2809 | ||
| 2789 | occupied_tag_values.deinit(); | ||
| 2790 | |||
| 2791 | return ErrorNone; | 2810 | return ErrorNone; |
| 2792 | } | 2811 | } |
| 2793 | 2812 |
src/ir.cpp+127-11| ... | @@ -2147,6 +2147,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN | ... | @@ -2147,6 +2147,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN |
| 2147 | IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node); | 2147 | IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node); |
| 2148 | ir_instruction_append(irb->current_basic_block, &const_instruction->base); | 2148 | ir_instruction_append(irb->current_basic_block, &const_instruction->base); |
| 2149 | const_instruction->value = irb->codegen->intern.for_undefined(); | 2149 | const_instruction->value = irb->codegen->intern.for_undefined(); |
| 2150 | const_instruction->value->special = ConstValSpecialUndef; | ||
| 2150 | return &const_instruction->base; | 2151 | return &const_instruction->base; |
| 2151 | } | 2152 | } |
| 2152 | 2153 | ||
| ... | @@ -14917,6 +14918,9 @@ static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* so | ... | @@ -14917,6 +14918,9 @@ static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* so |
| 14917 | field_val->parent.data.p_struct.struct_val = const_result->value; | 14918 | field_val->parent.data.p_struct.struct_val = const_result->value; |
| 14918 | field_val->parent.data.p_struct.field_index = dst_field->src_index; | 14919 | field_val->parent.data.p_struct.field_index = dst_field->src_index; |
| 14919 | field_values[dst_field->src_index] = field_val; | 14920 | field_values[dst_field->src_index] = field_val; |
| 14921 | if (field_val->type->id == ZigTypeIdUndefined && dst_field->type_entry->id != ZigTypeIdUndefined) { | ||
| 14922 | field_values[dst_field->src_index]->special = ConstValSpecialUndef; | ||
| 14923 | } | ||
| 14920 | } else { | 14924 | } else { |
| 14921 | is_comptime = false; | 14925 | is_comptime = false; |
| 14922 | } | 14926 | } |
| ... | @@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, | ... | @@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, |
| 15649 | wanted_type->data.array.len == field_count) | 15653 | wanted_type->data.array.len == field_count) |
| 15650 | { | 15654 | { |
| 15651 | return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); | 15655 | return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); |
| 15652 | } else if (wanted_type->id == ZigTypeIdStruct && | 15656 | } else if (wanted_type->id == ZigTypeIdStruct && !is_slice(wanted_type) && |
| 15653 | (!is_array_init || field_count == 0)) | 15657 | (!is_array_init || field_count == 0)) |
| 15654 | { | 15658 | { |
| 15655 | return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); | 15659 | return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); |
| ... | @@ -20692,8 +20696,13 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr, | ... | @@ -20692,8 +20696,13 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr, |
| 20692 | if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) && | 20696 | if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) && |
| 20693 | expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet) | 20697 | expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet) |
| 20694 | { | 20698 | { |
| 20695 | add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, | 20699 | if (call_result_loc->id == ResultLocIdReturn) { |
| 20696 | ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error")); | 20700 | add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, |
| 20701 | ira->explicit_return_type_source_node, buf_sprintf("function cannot return an error")); | ||
| 20702 | } else { | ||
| 20703 | add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, result_loc->base.source_node, | ||
| 20704 | buf_sprintf("cannot store an error in type '%s'", buf_ptr(&expected_return_type->name))); | ||
| 20705 | } | ||
| 20697 | } | 20706 | } |
| 20698 | return ira->codegen->invalid_inst_gen; | 20707 | return ira->codegen->invalid_inst_gen; |
| 20699 | } | 20708 | } |
| ... | @@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, | ... | @@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 22302 | 22311 | ||
| 22303 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { | 22312 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { |
| 22304 | if (field->init_val != nullptr) return; | 22313 | if (field->init_val != nullptr) return; |
| 22314 | if (field->decl_node == nullptr) return; | ||
| 22305 | if (field->decl_node->type != NodeTypeStructField) return; | 22315 | if (field->decl_node->type != NodeTypeStructField) return; |
| 22306 | AstNode *init_node = field->decl_node->data.struct_field.value; | 22316 | AstNode *init_node = field->decl_node->data.struct_field.value; |
| 22307 | if (init_node == nullptr) return; | 22317 | if (init_node == nullptr) return; |
| ... | @@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25495 | error_val->special = ConstValSpecialStatic; | 25505 | error_val->special = ConstValSpecialStatic; |
| 25496 | error_val->type = type_info_error_type; | 25506 | error_val->type = type_info_error_type; |
| 25497 | 25507 | ||
| 25498 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); | 25508 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1); |
| 25499 | inner_fields[1]->special = ConstValSpecialStatic; | ||
| 25500 | inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; | ||
| 25501 | 25509 | ||
| 25502 | ZigValue *name = nullptr; | 25510 | ZigValue *name = nullptr; |
| 25503 | if (error->cached_error_name_val != nullptr) | 25511 | if (error->cached_error_name_val != nullptr) |
| ... | @@ -25505,7 +25513,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25505,7 +25513,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25505 | if (name == nullptr) | 25513 | if (name == nullptr) |
| 25506 | name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; | 25514 | name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; |
| 25507 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); | 25515 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); |
| 25508 | bigint_init_unsigned(&inner_fields[1]->data.x_bigint, error->value); | ||
| 25509 | 25516 | ||
| 25510 | error_val->data.x_struct.fields = inner_fields; | 25517 | error_val->data.x_struct.fields = inner_fields; |
| 25511 | error_val->parent.id = ConstParentIdArray; | 25518 | error_val->parent.id = ConstParentIdArray; |
| ... | @@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26020 | assert(payload->special == ConstValSpecialStatic); | 26027 | assert(payload->special == ConstValSpecialStatic); |
| 26021 | assert(payload->type == type_info_pointer_type); | 26028 | assert(payload->type == type_info_pointer_type); |
| 26022 | ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0); | 26029 | ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0); |
| 26030 | if (size_value == nullptr) | ||
| 26031 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26032 | |||
| 26023 | assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); | 26033 | assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); |
| 26024 | BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); | 26034 | BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); |
| 26025 | PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); | 26035 | PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); |
| ... | @@ -26103,13 +26113,21 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26103,13 +26113,21 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26103 | assert(payload->special == ConstValSpecialStatic); | 26113 | assert(payload->special == ConstValSpecialStatic); |
| 26104 | assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); | 26114 | assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); |
| 26105 | ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0); | 26115 | ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0); |
| 26116 | if (type_is_invalid(child_type)) | ||
| 26117 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26106 | return get_optional_type(ira->codegen, child_type); | 26118 | return get_optional_type(ira->codegen, child_type); |
| 26107 | } | 26119 | } |
| 26108 | case ZigTypeIdErrorUnion: { | 26120 | case ZigTypeIdErrorUnion: { |
| 26109 | assert(payload->special == ConstValSpecialStatic); | 26121 | assert(payload->special == ConstValSpecialStatic); |
| 26110 | assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); | 26122 | assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); |
| 26111 | ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0); | 26123 | ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0); |
| 26124 | if (type_is_invalid(err_set_type)) | ||
| 26125 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26126 | |||
| 26112 | ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1); | 26127 | ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1); |
| 26128 | if (type_is_invalid(payload_type)) | ||
| 26129 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26130 | |||
| 26113 | return get_error_union_type(ira->codegen, err_set_type, payload_type); | 26131 | return get_error_union_type(ira->codegen, err_set_type, payload_type); |
| 26114 | } | 26132 | } |
| 26115 | case ZigTypeIdOpaque: { | 26133 | case ZigTypeIdOpaque: { |
| ... | @@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26123 | assert(payload->special == ConstValSpecialStatic); | 26141 | assert(payload->special == ConstValSpecialStatic); |
| 26124 | assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); | 26142 | assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); |
| 26125 | BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); | 26143 | BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); |
| 26144 | if (len == nullptr) | ||
| 26145 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26146 | |||
| 26126 | ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); | 26147 | ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); |
| 26127 | Error err; | ||
| 26128 | if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { | 26148 | if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { |
| 26129 | return ira->codegen->invalid_inst_gen->value->type; | 26149 | return ira->codegen->invalid_inst_gen->value->type; |
| 26130 | } | 26150 | } |
| ... | @@ -26134,6 +26154,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26134,6 +26154,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26134 | assert(payload->special == ConstValSpecialStatic); | 26154 | assert(payload->special == ConstValSpecialStatic); |
| 26135 | assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); | 26155 | assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); |
| 26136 | ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0); | 26156 | ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0); |
| 26157 | if (child_type != nullptr && type_is_invalid(child_type)) | ||
| 26158 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26159 | |||
| 26137 | return get_any_frame_type(ira->codegen, child_type); | 26160 | return get_any_frame_type(ira->codegen, child_type); |
| 26138 | } | 26161 | } |
| 26139 | case ZigTypeIdEnumLiteral: | 26162 | case ZigTypeIdEnumLiteral: |
| ... | @@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26142 | assert(payload->special == ConstValSpecialStatic); | 26165 | assert(payload->special == ConstValSpecialStatic); |
| 26143 | assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr)); | 26166 | assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr)); |
| 26144 | ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0); | 26167 | ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0); |
| 26168 | if (function == nullptr) | ||
| 26169 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26170 | |||
| 26145 | assert(function->type->id == ZigTypeIdFn); | 26171 | assert(function->type->id == ZigTypeIdFn); |
| 26146 | ZigFn *fn = function->data.x_ptr.data.fn.fn_entry; | 26172 | ZigFn *fn = function->data.x_ptr.data.fn.fn_entry; |
| 26147 | return get_fn_frame_type(ira->codegen, fn); | 26173 | return get_fn_frame_type(ira->codegen, fn); |
| ... | @@ -26176,7 +26202,6 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26176,7 +26202,6 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26176 | assert(error->type == ir_type_info_get_type(ira, "Error", nullptr)); | 26202 | assert(error->type == ir_type_info_get_type(ira, "Error", nullptr)); |
| 26177 | ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>(); | 26203 | ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>(); |
| 26178 | err_entry->decl_node = source_instr->source_node; | 26204 | err_entry->decl_node = source_instr->source_node; |
| 26179 | Error err; | ||
| 26180 | if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name))) | 26205 | if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name))) |
| 26181 | return ira->codegen->invalid_inst_gen->value->type; | 26206 | return ira->codegen->invalid_inst_gen->value->type; |
| 26182 | auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry); | 26207 | auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry); |
| ... | @@ -26203,11 +26228,15 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26203,11 +26228,15 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26203 | assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr)); | 26228 | assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr)); |
| 26204 | 26229 | ||
| 26205 | ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); | 26230 | ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); |
| 26231 | if (layout_value == nullptr) | ||
| 26232 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26206 | assert(layout_value->special == ConstValSpecialStatic); | 26233 | assert(layout_value->special == ConstValSpecialStatic); |
| 26207 | assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); | 26234 | assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); |
| 26208 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); | 26235 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); |
| 26209 | 26236 | ||
| 26210 | ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1); | 26237 | ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1); |
| 26238 | if (fields_value == nullptr) | ||
| 26239 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26211 | assert(fields_value->special == ConstValSpecialStatic); | 26240 | assert(fields_value->special == ConstValSpecialStatic); |
| 26212 | assert(is_slice(fields_value->type)); | 26241 | assert(is_slice(fields_value->type)); |
| 26213 | ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; | 26242 | ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; |
| ... | @@ -26215,6 +26244,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26215,6 +26244,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26215 | size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); | 26244 | size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); |
| 26216 | 26245 | ||
| 26217 | ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2); | 26246 | ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2); |
| 26247 | if (decls_value == nullptr) | ||
| 26248 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26218 | assert(decls_value->special == ConstValSpecialStatic); | 26249 | assert(decls_value->special == ConstValSpecialStatic); |
| 26219 | assert(is_slice(decls_value->type)); | 26250 | assert(is_slice(decls_value->type)); |
| 26220 | ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; | 26251 | ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; |
| ... | @@ -26225,7 +26256,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26225,7 +26256,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26225 | } | 26256 | } |
| 26226 | 26257 | ||
| 26227 | bool is_tuple; | 26258 | bool is_tuple; |
| 26228 | get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple); | 26259 | if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple))) |
| 26260 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26229 | 26261 | ||
| 26230 | ZigType *entry = new_type_table_entry(ZigTypeIdStruct); | 26262 | ZigType *entry = new_type_table_entry(ZigTypeIdStruct); |
| 26231 | buf_init_from_buf(&entry->name, | 26263 | buf_init_from_buf(&entry->name, |
| ... | @@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26253 | return ira->codegen->invalid_inst_gen->value->type; | 26285 | return ira->codegen->invalid_inst_gen->value->type; |
| 26254 | field->decl_node = source_instr->source_node; | 26286 | field->decl_node = source_instr->source_node; |
| 26255 | ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); | 26287 | ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); |
| 26288 | if (type_value == nullptr) | ||
| 26289 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26256 | field->type_val = type_value; | 26290 | field->type_val = type_value; |
| 26257 | field->type_entry = type_value->data.x_type; | 26291 | field->type_entry = type_value->data.x_type; |
| 26258 | if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) { | 26292 | if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) { |
| ... | @@ -26260,6 +26294,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26260,6 +26294,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26260 | return ira->codegen->invalid_inst_gen->value->type; | 26294 | return ira->codegen->invalid_inst_gen->value->type; |
| 26261 | } | 26295 | } |
| 26262 | ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2); | 26296 | ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2); |
| 26297 | if (default_value == nullptr) | ||
| 26298 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26263 | if (default_value->type->id == ZigTypeIdNull) { | 26299 | if (default_value->type->id == ZigTypeIdNull) { |
| 26264 | field->init_val = nullptr; | 26300 | field->init_val = nullptr; |
| 26265 | } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) { | 26301 | } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) { |
| ... | @@ -26277,7 +26313,87 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26277,7 +26313,87 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26277 | 26313 | ||
| 26278 | return entry; | 26314 | return entry; |
| 26279 | } | 26315 | } |
| 26280 | case ZigTypeIdEnum: | 26316 | case ZigTypeIdEnum: { |
| 26317 | assert(payload->special == ConstValSpecialStatic); | ||
| 26318 | assert(payload->type == ir_type_info_get_type(ira, "Enum", nullptr)); | ||
| 26319 | |||
| 26320 | ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); | ||
| 26321 | if (layout_value == nullptr) | ||
| 26322 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26323 | |||
| 26324 | assert(layout_value->special == ConstValSpecialStatic); | ||
| 26325 | assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); | ||
| 26326 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); | ||
| 26327 | |||
| 26328 | ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); | ||
| 26329 | |||
| 26330 | ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); | ||
| 26331 | if (fields_value == nullptr) | ||
| 26332 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26333 | |||
| 26334 | assert(fields_value->special == ConstValSpecialStatic); | ||
| 26335 | assert(is_slice(fields_value->type)); | ||
| 26336 | ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; | ||
| 26337 | ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; | ||
| 26338 | size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); | ||
| 26339 | |||
| 26340 | ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); | ||
| 26341 | if (decls_value == nullptr) | ||
| 26342 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26343 | |||
| 26344 | assert(decls_value->special == ConstValSpecialStatic); | ||
| 26345 | assert(is_slice(decls_value->type)); | ||
| 26346 | ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; | ||
| 26347 | size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); | ||
| 26348 | if (decls_len != 0) { | ||
| 26349 | ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Enum.decls must be empty for @Type")); | ||
| 26350 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26351 | } | ||
| 26352 | |||
| 26353 | Error err; | ||
| 26354 | bool is_exhaustive; | ||
| 26355 | if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_exhaustive", 4, &is_exhaustive))) | ||
| 26356 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26357 | |||
| 26358 | ZigType *entry = new_type_table_entry(ZigTypeIdEnum); | ||
| 26359 | buf_init_from_buf(&entry->name, | ||
| 26360 | get_anon_type_name(ira->codegen, ira->old_irb.exec, "enum", source_instr->scope, source_instr->source_node, &entry->name)); | ||
| 26361 | entry->data.enumeration.decl_node = source_instr->source_node; | ||
| 26362 | entry->data.enumeration.tag_int_type = tag_type; | ||
| 26363 | entry->data.enumeration.decls_scope = create_decls_scope( | ||
| 26364 | ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); | ||
| 26365 | entry->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(fields_len); | ||
| 26366 | entry->data.enumeration.fields_by_name.init(fields_len); | ||
| 26367 | entry->data.enumeration.src_field_count = fields_len; | ||
| 26368 | entry->data.enumeration.layout = layout; | ||
| 26369 | entry->data.enumeration.non_exhaustive = !is_exhaustive; | ||
| 26370 | |||
| 26371 | assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); | ||
| 26372 | assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); | ||
| 26373 | ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; | ||
| 26374 | assert(fields_arr->special == ConstValSpecialStatic); | ||
| 26375 | assert(fields_arr->data.x_array.special == ConstArraySpecialNone); | ||
| 26376 | for (size_t i = 0; i < fields_len; i++) { | ||
| 26377 | ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; | ||
| 26378 | assert(field_value->type == ir_type_info_get_type(ira, "EnumField", nullptr)); | ||
| 26379 | TypeEnumField *field = &entry->data.enumeration.fields[i]; | ||
| 26380 | field->name = buf_alloc(); | ||
| 26381 | if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) | ||
| 26382 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26383 | field->decl_index = i; | ||
| 26384 | field->decl_node = source_instr->source_node; | ||
| 26385 | if (entry->data.enumeration.fields_by_name.put_unique(field->name, field) != nullptr) { | ||
| 26386 | ir_add_error(ira, source_instr, buf_sprintf("duplicate enum field '%s'", buf_ptr(field->name))); | ||
| 26387 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26388 | } | ||
| 26389 | BigInt *field_int_value = get_const_field_lit_int(ira, source_instr->source_node, field_value, "value", 1); | ||
| 26390 | if (field_int_value == nullptr) | ||
| 26391 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26392 | field->value = *field_int_value; | ||
| 26393 | } | ||
| 26394 | |||
| 26395 | return entry; | ||
| 26396 | } | ||
| 26281 | case ZigTypeIdUnion: | 26397 | case ZigTypeIdUnion: |
| 26282 | ir_add_error(ira, source_instr, buf_sprintf( | 26398 | ir_add_error(ira, source_instr, buf_sprintf( |
| 26283 | "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId))); | 26399 | "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId))); |
test/compile_errors.zig+45-13| ... | @@ -2,6 +2,41 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,41 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("@Type with undefined", | ||
| 6 | \\comptime { | ||
| 7 | \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } }); | ||
| 8 | \\} | ||
| 9 | \\comptime { | ||
| 10 | \\ _ = @Type(.{ | ||
| 11 | \\ .Struct = .{ | ||
| 12 | \\ .fields = undefined, | ||
| 13 | \\ .decls = undefined, | ||
| 14 | \\ .is_tuple = false, | ||
| 15 | \\ .layout = .Auto, | ||
| 16 | \\ }, | ||
| 17 | \\ }); | ||
| 18 | \\} | ||
| 19 | , &[_][]const u8{ | ||
| 20 | "tmp.zig:2:16: error: use of undefined value here causes undefined behavior", | ||
| 21 | "tmp.zig:5:16: error: use of undefined value here causes undefined behavior", | ||
| 22 | }); | ||
| 23 | |||
| 24 | cases.add("struct with declarations unavailable for @Type", | ||
| 25 | \\export fn entry() void { | ||
| 26 | \\ _ = @Type(@typeInfo(struct { const foo = 1; })); | ||
| 27 | \\} | ||
| 28 | , &[_][]const u8{ | ||
| 29 | "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type", | ||
| 30 | }); | ||
| 31 | |||
| 32 | cases.add("enum with declarations unavailable for @Type", | ||
| 33 | \\export fn entry() void { | ||
| 34 | \\ _ = @Type(@typeInfo(enum { foo, const bar = 1; })); | ||
| 35 | \\} | ||
| 36 | , &[_][]const u8{ | ||
| 37 | "tmp.zig:2:15: error: TypeInfo.Enum.decls must be empty for @Type", | ||
| 38 | }); | ||
| 39 | |||
| 5 | cases.addTest("reject extern variables with initializers", | 40 | cases.addTest("reject extern variables with initializers", |
| 6 | \\extern var foo: int = 2; | 41 | \\extern var foo: int = 2; |
| 7 | , &[_][]const u8{ | 42 | , &[_][]const u8{ |
| ... | @@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 123 | \\export fn baz() void { | 158 | \\export fn baz() void { |
| 124 | \\ try bar(); | 159 | \\ try bar(); |
| 125 | \\} | 160 | \\} |
| 126 | \\export fn quux() u32 { | 161 | \\export fn qux() u32 { |
| 127 | \\ return bar(); | 162 | \\ return bar(); |
| 128 | \\} | 163 | \\} |
| 164 | \\export fn quux() u32 { | ||
| 165 | \\ var buf: u32 = 0; | ||
| 166 | \\ buf = bar(); | ||
| 167 | \\} | ||
| 129 | , &[_][]const u8{ | 168 | , &[_][]const u8{ |
| 130 | "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'", | 169 | "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'", |
| 131 | "tmp.zig:1:17: note: function cannot return an error", | 170 | "tmp.zig:1:17: note: function cannot return an error", |
| 132 | "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'", | 171 | "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'", |
| 133 | "tmp.zig:7:17: note: function cannot return an error", | 172 | "tmp.zig:7:17: note: function cannot return an error", |
| 134 | "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'", | 173 | "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'", |
| 135 | "tmp.zig:10:18: note: function cannot return an error", | 174 | "tmp.zig:10:17: note: function cannot return an error", |
| 175 | "tmp.zig:15:14: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'", | ||
| 176 | "tmp.zig:14:5: note: cannot store an error in type 'u32'", | ||
| 136 | }); | 177 | }); |
| 137 | 178 | ||
| 138 | cases.addTest("int/float conversion to comptime_int/float", | 179 | cases.addTest("int/float conversion to comptime_int/float", |
| ... | @@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 598 | \\ _ = C; | 639 | \\ _ = C; |
| 599 | \\} | 640 | \\} |
| 600 | , &[_][]const u8{ | 641 | , &[_][]const u8{ |
| 601 | "tmp.zig:4:5: error: non-exhaustive enum must specify size", | 642 | "tmp.zig:4:5: error: value assigned to '_' field of non-exhaustive enum", |
| 602 | "error: value assigned to '_' field of non-exhaustive enum", | 643 | "error: non-exhaustive enum must specify size", |
| 603 | "error: non-exhaustive enum specifies every value", | 644 | "error: non-exhaustive enum specifies every value", |
| 604 | "error: '_' field of non-exhaustive enum must be last", | 645 | "error: '_' field of non-exhaustive enum must be last", |
| 605 | }); | 646 | }); |
| ... | @@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1400 | , &[_][]const u8{ | 1441 | , &[_][]const u8{ |
| 1401 | "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'", | 1442 | "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'", |
| 1402 | }); | 1443 | }); |
| 1403 | |||
| 1404 | cases.add("struct with declarations unavailable for @Type", | ||
| 1405 | \\export fn entry() void { | ||
| 1406 | \\ _ = @Type(@typeInfo(struct { const foo = 1; })); | ||
| 1407 | \\} | ||
| 1408 | , &[_][]const u8{ | ||
| 1409 | "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type", | ||
| 1410 | }); | ||
| 1411 | |||
| 1412 | cases.add("wrong type for argument tuple to @asyncCall", | 1444 | cases.add("wrong type for argument tuple to @asyncCall", |
| 1413 | \\export fn entry1() void { | 1445 | \\export fn entry1() void { |
| 1414 | \\ var frame: @Frame(foo) = undefined; | 1446 | \\ var frame: @Frame(foo) = undefined; |
test/stage1/behavior/type.zig+34| ... | @@ -280,3 +280,37 @@ test "Type.Struct" { | ... | @@ -280,3 +280,37 @@ test "Type.Struct" { |
| 280 | testing.expectEqual(@as(usize, 0), infoC.decls.len); | 280 | testing.expectEqual(@as(usize, 0), infoC.decls.len); |
| 281 | testing.expectEqual(@as(bool, false), infoC.is_tuple); | 281 | testing.expectEqual(@as(bool, false), infoC.is_tuple); |
| 282 | } | 282 | } |
| 283 | |||
| 284 | test "Type.Enum" { | ||
| 285 | const Foo = @Type(.{ | ||
| 286 | .Enum = .{ | ||
| 287 | .layout = .Auto, | ||
| 288 | .tag_type = u8, | ||
| 289 | .fields = &[_]TypeInfo.EnumField{ | ||
| 290 | .{ .name = "a", .value = 1 }, | ||
| 291 | .{ .name = "b", .value = 5 }, | ||
| 292 | }, | ||
| 293 | .decls = &[_]TypeInfo.Declaration{}, | ||
| 294 | .is_exhaustive = true, | ||
| 295 | }, | ||
| 296 | }); | ||
| 297 | testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); | ||
| 298 | testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); | ||
| 299 | testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); | ||
| 300 | const Bar = @Type(.{ | ||
| 301 | .Enum = .{ | ||
| 302 | .layout = .Extern, | ||
| 303 | .tag_type = u32, | ||
| 304 | .fields = &[_]TypeInfo.EnumField{ | ||
| 305 | .{ .name = "a", .value = 1 }, | ||
| 306 | .{ .name = "b", .value = 5 }, | ||
| 307 | }, | ||
| 308 | .decls = &[_]TypeInfo.Declaration{}, | ||
| 309 | .is_exhaustive = false, | ||
| 310 | }, | ||
| 311 | }); | ||
| 312 | testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); | ||
| 313 | testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); | ||
| 314 | testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); | ||
| 315 | testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); | ||
| 316 | } |
test/stage1/behavior/type_info.zig-1| ... | @@ -153,7 +153,6 @@ fn testErrorSet() void { | ... | @@ -153,7 +153,6 @@ fn testErrorSet() void { |
| 153 | expect(error_set_info == .ErrorSet); | 153 | expect(error_set_info == .ErrorSet); |
| 154 | expect(error_set_info.ErrorSet.?.len == 3); | 154 | expect(error_set_info.ErrorSet.?.len == 3); |
| 155 | expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First")); | 155 | expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First")); |
| 156 | expect(error_set_info.ErrorSet.?[2].value == @errorToInt(TestErrorSet.Third)); | ||
| 157 | 156 | ||
| 158 | const error_union_info = @typeInfo(TestErrorSet!usize); | 157 | const error_union_info = @typeInfo(TestErrorSet!usize); |
| 159 | expect(error_union_info == .ErrorUnion); | 158 | expect(error_union_info == .ErrorUnion); |
test/stage2/spu-ii.zig created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | ||
| 3 | |||
| 4 | const spu = std.zig.CrossTarget{ | ||
| 5 | .cpu_arch = .spu_2, | ||
| 6 | .os_tag = .freestanding, | ||
| 7 | }; | ||
| 8 | |||
| 9 | pub fn addCases(ctx: *TestContext) !void { | ||
| 10 | { | ||
| 11 | var case = ctx.exe("SPU-II Basic Test", spu); | ||
| 12 | case.addCompareOutput( | ||
| 13 | \\fn killEmulator() noreturn { | ||
| 14 | \\ asm volatile ("undefined0"); | ||
| 15 | \\ unreachable; | ||
| 16 | \\} | ||
| 17 | \\ | ||
| 18 | \\export fn _start() noreturn { | ||
| 19 | \\ killEmulator(); | ||
| 20 | \\} | ||
| 21 | , ""); | ||
| 22 | } | ||
| 23 | } | ||
test/stage2/test.zig+94| ... | @@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{ | ... | @@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{ |
| 18 | .os_tag = .linux, | 18 | .os_tag = .linux, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | const linux_arm = std.zig.CrossTarget{ | ||
| 22 | .cpu_arch = .arm, | ||
| 23 | .os_tag = .linux, | ||
| 24 | }; | ||
| 25 | |||
| 21 | const wasi = std.zig.CrossTarget{ | 26 | const wasi = std.zig.CrossTarget{ |
| 22 | .cpu_arch = .wasm32, | 27 | .cpu_arch = .wasm32, |
| 23 | .os_tag = .wasi, | 28 | .os_tag = .wasi, |
| ... | @@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{ | ... | @@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{ |
| 26 | pub fn addCases(ctx: *TestContext) !void { | 31 | pub fn addCases(ctx: *TestContext) !void { |
| 27 | try @import("zir.zig").addCases(ctx); | 32 | try @import("zir.zig").addCases(ctx); |
| 28 | try @import("cbe.zig").addCases(ctx); | 33 | try @import("cbe.zig").addCases(ctx); |
| 34 | try @import("spu-ii.zig").addCases(ctx); | ||
| 35 | |||
| 29 | { | 36 | { |
| 30 | var case = ctx.exe("hello world with updates", linux_x64); | 37 | var case = ctx.exe("hello world with updates", linux_x64); |
| 31 | 38 | ||
| ... | @@ -179,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -179,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void { |
| 179 | ); | 186 | ); |
| 180 | } | 187 | } |
| 181 | 188 | ||
| 189 | { | ||
| 190 | var case = ctx.exe("hello world", linux_arm); | ||
| 191 | // Regular old hello world | ||
| 192 | case.addCompareOutput( | ||
| 193 | \\export fn _start() noreturn { | ||
| 194 | \\ print(); | ||
| 195 | \\ exit(); | ||
| 196 | \\} | ||
| 197 | \\ | ||
| 198 | \\fn print() void { | ||
| 199 | \\ asm volatile ("svc #0" | ||
| 200 | \\ : | ||
| 201 | \\ : [number] "{r7}" (4), | ||
| 202 | \\ [arg1] "{r0}" (1), | ||
| 203 | \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), | ||
| 204 | \\ [arg3] "{r2}" (14) | ||
| 205 | \\ : "memory" | ||
| 206 | \\ ); | ||
| 207 | \\ return; | ||
| 208 | \\} | ||
| 209 | \\ | ||
| 210 | \\fn exit() noreturn { | ||
| 211 | \\ asm volatile ("svc #0" | ||
| 212 | \\ : | ||
| 213 | \\ : [number] "{r7}" (1), | ||
| 214 | \\ [arg1] "{r0}" (0) | ||
| 215 | \\ : "memory" | ||
| 216 | \\ ); | ||
| 217 | \\ unreachable; | ||
| 218 | \\} | ||
| 219 | , | ||
| 220 | "Hello, World!\n", | ||
| 221 | ); | ||
| 222 | } | ||
| 223 | |||
| 182 | { | 224 | { |
| 183 | var case = ctx.exe("adding numbers at comptime", linux_x64); | 225 | var case = ctx.exe("adding numbers at comptime", linux_x64); |
| 184 | case.addCompareOutput( | 226 | case.addCompareOutput( |
| ... | @@ -600,6 +642,58 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -600,6 +642,58 @@ pub fn addCases(ctx: *TestContext) !void { |
| 600 | "", | 642 | "", |
| 601 | ); | 643 | ); |
| 602 | 644 | ||
| 645 | // Spilling registers to the stack. | ||
| 646 | case.addCompareOutput( | ||
| 647 | \\export fn _start() noreturn { | ||
| 648 | \\ assert(add(3, 4) == 791); | ||
| 649 | \\ | ||
| 650 | \\ exit(); | ||
| 651 | \\} | ||
| 652 | \\ | ||
| 653 | \\fn add(a: u32, b: u32) u32 { | ||
| 654 | \\ const x: u32 = blk: { | ||
| 655 | \\ const c = a + b; // 7 | ||
| 656 | \\ const d = a + c; // 10 | ||
| 657 | \\ const e = d + b; // 14 | ||
| 658 | \\ const f = d + e; // 24 | ||
| 659 | \\ const g = e + f; // 38 | ||
| 660 | \\ const h = f + g; // 62 | ||
| 661 | \\ const i = g + h; // 100 | ||
| 662 | \\ const j = i + d; // 110 | ||
| 663 | \\ const k = i + j; // 210 | ||
| 664 | \\ const l = k + c; // 217 | ||
| 665 | \\ const m = l + d; // 227 | ||
| 666 | \\ const n = m + e; // 241 | ||
| 667 | \\ const o = n + f; // 265 | ||
| 668 | \\ const p = o + g; // 303 | ||
| 669 | \\ const q = p + h; // 365 | ||
| 670 | \\ const r = q + i; // 465 | ||
| 671 | \\ const s = r + j; // 575 | ||
| 672 | \\ const t = s + k; // 785 | ||
| 673 | \\ break :blk t; | ||
| 674 | \\ }; | ||
| 675 | \\ const y = x + a; // 788 | ||
| 676 | \\ const z = y + a; // 791 | ||
| 677 | \\ return z; | ||
| 678 | \\} | ||
| 679 | \\ | ||
| 680 | \\pub fn assert(ok: bool) void { | ||
| 681 | \\ if (!ok) unreachable; // assertion failure | ||
| 682 | \\} | ||
| 683 | \\ | ||
| 684 | \\fn exit() noreturn { | ||
| 685 | \\ asm volatile ("syscall" | ||
| 686 | \\ : | ||
| 687 | \\ : [number] "{rax}" (231), | ||
| 688 | \\ [arg1] "{rdi}" (0) | ||
| 689 | \\ : "rcx", "r11", "memory" | ||
| 690 | \\ ); | ||
| 691 | \\ unreachable; | ||
| 692 | \\} | ||
| 693 | , | ||
| 694 | "", | ||
| 695 | ); | ||
| 696 | |||
| 603 | // Character literals and multiline strings. | 697 | // Character literals and multiline strings. |
| 604 | case.addCompareOutput( | 698 | case.addCompareOutput( |
| 605 | \\export fn _start() noreturn { | 699 | \\export fn _start() noreturn { |
tools/process_headers.zig+3-2| ... | @@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch; | ... | @@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch; |
| 15 | const Abi = std.Target.Abi; | 15 | const Abi = std.Target.Abi; |
| 16 | const OsTag = std.Target.Os.Tag; | 16 | const OsTag = std.Target.Os.Tag; |
| 17 | const assert = std.debug.assert; | 17 | const assert = std.debug.assert; |
| 18 | const Sha256 = std.crypto.hash.sha2.Sha256; | ||
| 18 | 19 | ||
| 19 | const LibCTarget = struct { | 20 | const LibCTarget = struct { |
| 20 | name: []const u8, | 21 | name: []const u8, |
| ... | @@ -313,7 +314,7 @@ pub fn main() !void { | ... | @@ -313,7 +314,7 @@ pub fn main() !void { |
| 313 | var max_bytes_saved: usize = 0; | 314 | var max_bytes_saved: usize = 0; |
| 314 | var total_bytes: usize = 0; | 315 | var total_bytes: usize = 0; |
| 315 | 316 | ||
| 316 | var hasher = std.crypto.hash.sha2.Sha256.init(.{}); | 317 | var hasher = Sha256.init(.{}); |
| 317 | 318 | ||
| 318 | for (libc_targets) |libc_target| { | 319 | for (libc_targets) |libc_target| { |
| 319 | const dest_target = DestTarget{ | 320 | const dest_target = DestTarget{ |
| ... | @@ -359,7 +360,7 @@ pub fn main() !void { | ... | @@ -359,7 +360,7 @@ pub fn main() !void { |
| 359 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); | 360 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); |
| 360 | total_bytes += raw_bytes.len; | 361 | total_bytes += raw_bytes.len; |
| 361 | const hash = try allocator.alloc(u8, 32); | 362 | const hash = try allocator.alloc(u8, 32); |
| 362 | hasher.reset(); | 363 | hasher = Sha256.init(.{}); |
| 363 | hasher.update(rel_path); | 364 | hasher.update(rel_path); |
| 364 | hasher.update(trimmed); | 365 | hasher.update(trimmed); |
| 365 | hasher.final(hash); | 366 | hasher.final(hash); |
tools/update_glibc.zig+20-20| ... | @@ -148,12 +148,12 @@ pub fn main() !void { | ... | @@ -148,12 +148,12 @@ pub fn main() !void { |
| 148 | for (abi_lists) |*abi_list| { | 148 | for (abi_lists) |*abi_list| { |
| 149 | const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list)); | 149 | const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list)); |
| 150 | if (!target_funcs_gop.found_existing) { | 150 | if (!target_funcs_gop.found_existing) { |
| 151 | target_funcs_gop.kv.value = FunctionSet{ | 151 | target_funcs_gop.entry.value = FunctionSet{ |
| 152 | .list = std.ArrayList(VersionedFn).init(allocator), | 152 | .list = std.ArrayList(VersionedFn).init(allocator), |
| 153 | .fn_vers_list = FnVersionList.init(allocator), | 153 | .fn_vers_list = FnVersionList.init(allocator), |
| 154 | }; | 154 | }; |
| 155 | } | 155 | } |
| 156 | const fn_set = &target_funcs_gop.kv.value.list; | 156 | const fn_set = &target_funcs_gop.entry.value.list; |
| 157 | 157 | ||
| 158 | for (lib_names) |lib_name, lib_name_index| { | 158 | for (lib_names) |lib_name, lib_name_index| { |
| 159 | const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; | 159 | const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; |
| ... | @@ -203,11 +203,11 @@ pub fn main() !void { | ... | @@ -203,11 +203,11 @@ pub fn main() !void { |
| 203 | _ = try global_ver_set.put(ver, undefined); | 203 | _ = try global_ver_set.put(ver, undefined); |
| 204 | const gop = try global_fn_set.getOrPut(name); | 204 | const gop = try global_fn_set.getOrPut(name); |
| 205 | if (gop.found_existing) { | 205 | if (gop.found_existing) { |
| 206 | if (!std.mem.eql(u8, gop.kv.value.lib, "c")) { | 206 | if (!std.mem.eql(u8, gop.entry.value.lib, "c")) { |
| 207 | gop.kv.value.lib = lib_name; | 207 | gop.entry.value.lib = lib_name; |
| 208 | } | 208 | } |
| 209 | } else { | 209 | } else { |
| 210 | gop.kv.value = Function{ | 210 | gop.entry.value = Function{ |
| 211 | .name = name, | 211 | .name = name, |
| 212 | .lib = lib_name, | 212 | .lib = lib_name, |
| 213 | .index = undefined, | 213 | .index = undefined, |
| ... | @@ -224,14 +224,14 @@ pub fn main() !void { | ... | @@ -224,14 +224,14 @@ pub fn main() !void { |
| 224 | const global_fn_list = blk: { | 224 | const global_fn_list = blk: { |
| 225 | var list = std.ArrayList([]const u8).init(allocator); | 225 | var list = std.ArrayList([]const u8).init(allocator); |
| 226 | var it = global_fn_set.iterator(); | 226 | var it = global_fn_set.iterator(); |
| 227 | while (it.next()) |kv| try list.append(kv.key); | 227 | while (it.next()) |entry| try list.append(entry.key); |
| 228 | std.sort.sort([]const u8, list.span(), {}, strCmpLessThan); | 228 | std.sort.sort([]const u8, list.span(), {}, strCmpLessThan); |
| 229 | break :blk list.span(); | 229 | break :blk list.span(); |
| 230 | }; | 230 | }; |
| 231 | const global_ver_list = blk: { | 231 | const global_ver_list = blk: { |
| 232 | var list = std.ArrayList([]const u8).init(allocator); | 232 | var list = std.ArrayList([]const u8).init(allocator); |
| 233 | var it = global_ver_set.iterator(); | 233 | var it = global_ver_set.iterator(); |
| 234 | while (it.next()) |kv| try list.append(kv.key); | 234 | while (it.next()) |entry| try list.append(entry.key); |
| 235 | std.sort.sort([]const u8, list.span(), {}, versionLessThan); | 235 | std.sort.sort([]const u8, list.span(), {}, versionLessThan); |
| 236 | break :blk list.span(); | 236 | break :blk list.span(); |
| 237 | }; | 237 | }; |
| ... | @@ -254,9 +254,9 @@ pub fn main() !void { | ... | @@ -254,9 +254,9 @@ pub fn main() !void { |
| 254 | var buffered = std.io.bufferedOutStream(fns_txt_file.outStream()); | 254 | var buffered = std.io.bufferedOutStream(fns_txt_file.outStream()); |
| 255 | const fns_txt = buffered.outStream(); | 255 | const fns_txt = buffered.outStream(); |
| 256 | for (global_fn_list) |name, i| { | 256 | for (global_fn_list) |name, i| { |
| 257 | const kv = global_fn_set.get(name).?; | 257 | const entry = global_fn_set.getEntry(name).?; |
| 258 | kv.value.index = i; | 258 | entry.value.index = i; |
| 259 | try fns_txt.print("{} {}\n", .{ name, kv.value.lib }); | 259 | try fns_txt.print("{} {}\n", .{ name, entry.value.lib }); |
| 260 | } | 260 | } |
| 261 | try buffered.flush(); | 261 | try buffered.flush(); |
| 262 | } | 262 | } |
| ... | @@ -264,16 +264,16 @@ pub fn main() !void { | ... | @@ -264,16 +264,16 @@ pub fn main() !void { |
| 264 | // Now the mapping of version and function to integer index is complete. | 264 | // Now the mapping of version and function to integer index is complete. |
| 265 | // Here we create a mapping of function name to list of versions. | 265 | // Here we create a mapping of function name to list of versions. |
| 266 | for (abi_lists) |*abi_list, abi_index| { | 266 | for (abi_lists) |*abi_list, abi_index| { |
| 267 | const kv = target_functions.get(@ptrToInt(abi_list)).?; | 267 | const entry = target_functions.getEntry(@ptrToInt(abi_list)).?; |
| 268 | const fn_vers_list = &kv.value.fn_vers_list; | 268 | const fn_vers_list = &entry.value.fn_vers_list; |
| 269 | for (kv.value.list.span()) |*ver_fn| { | 269 | for (entry.value.list.span()) |*ver_fn| { |
| 270 | const gop = try fn_vers_list.getOrPut(ver_fn.name); | 270 | const gop = try fn_vers_list.getOrPut(ver_fn.name); |
| 271 | if (!gop.found_existing) { | 271 | if (!gop.found_existing) { |
| 272 | gop.kv.value = std.ArrayList(usize).init(allocator); | 272 | gop.entry.value = std.ArrayList(usize).init(allocator); |
| 273 | } | 273 | } |
| 274 | const ver_index = global_ver_set.get(ver_fn.ver).?.value; | 274 | const ver_index = global_ver_set.getEntry(ver_fn.ver).?.value; |
| 275 | if (std.mem.indexOfScalar(usize, gop.kv.value.span(), ver_index) == null) { | 275 | if (std.mem.indexOfScalar(usize, gop.entry.value.span(), ver_index) == null) { |
| 276 | try gop.kv.value.append(ver_index); | 276 | try gop.entry.value.append(ver_index); |
| 277 | } | 277 | } |
| 278 | } | 278 | } |
| 279 | } | 279 | } |
| ... | @@ -287,7 +287,7 @@ pub fn main() !void { | ... | @@ -287,7 +287,7 @@ pub fn main() !void { |
| 287 | 287 | ||
| 288 | // first iterate over the abi lists | 288 | // first iterate over the abi lists |
| 289 | for (abi_lists) |*abi_list, abi_index| { | 289 | for (abi_lists) |*abi_list, abi_index| { |
| 290 | const fn_vers_list = &target_functions.get(@ptrToInt(abi_list)).?.value.fn_vers_list; | 290 | const fn_vers_list = &target_functions.getEntry(@ptrToInt(abi_list)).?.value.fn_vers_list; |
| 291 | for (abi_list.targets) |target, it_i| { | 291 | for (abi_list.targets) |target, it_i| { |
| 292 | if (it_i != 0) try abilist_txt.writeByte(' '); | 292 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 293 | try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) }); | 293 | try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) }); |
| ... | @@ -295,11 +295,11 @@ pub fn main() !void { | ... | @@ -295,11 +295,11 @@ pub fn main() !void { |
| 295 | try abilist_txt.writeByte('\n'); | 295 | try abilist_txt.writeByte('\n'); |
| 296 | // next, each line implicitly corresponds to a function | 296 | // next, each line implicitly corresponds to a function |
| 297 | for (global_fn_list) |name| { | 297 | for (global_fn_list) |name| { |
| 298 | const kv = fn_vers_list.get(name) orelse { | 298 | const entry = fn_vers_list.getEntry(name) orelse { |
| 299 | try abilist_txt.writeByte('\n'); | 299 | try abilist_txt.writeByte('\n'); |
| 300 | continue; | 300 | continue; |
| 301 | }; | 301 | }; |
| 302 | for (kv.value.span()) |ver_index, it_i| { | 302 | for (entry.value.span()) |ver_index, it_i| { |
| 303 | if (it_i != 0) try abilist_txt.writeByte(' '); | 303 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 304 | try abilist_txt.print("{d}", .{ver_index}); | 304 | try abilist_txt.print("{d}", .{ver_index}); |
| 305 | } | 305 | } |