| 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 | 289 | /// therefore must be kept in sync with the compiler implementation. |
| 290 | 290 | pub const Error = struct { |
| 291 | 291 | name: []const u8, |
| 292 | /// This field is ignored when using @Type(). | |
| 293 | value: comptime_int, | |
| 294 | 292 | }; |
| 295 | 293 | |
| 296 | 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 | 11 | usingnamespace @import("../os/bits.zig"); |
| 12 | 12 | |
| 13 | 13 | extern "c" fn __error() *c_int; |
| 14 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; | |
| 14 | 15 | pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; |
| 15 | 16 | pub extern "c" fn _dyld_image_count() u32; |
| 16 | 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 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 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 | 9 | const fs = std.fs; |
| 9 | 10 | const base64 = std.base64; |
| 10 | 11 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator; |
| 16 | 17 | |
| 17 | 18 | const base64_encoder = fs.base64_encoder; |
| 18 | 19 | const base64_decoder = fs.base64_decoder; |
| 19 | /// This is 70 more bits than UUIDs. For an analysis of probability of collisions, see: | |
| 20 | /// https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions | |
| 21 | const BIN_DIGEST_LEN = 24; | |
| 20 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 | |
| 21 | const BIN_DIGEST_LEN = 16; | |
| 22 | 22 | const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); |
| 23 | 23 | |
| 24 | 24 | const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; |
| ... | ... | @@ -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 | 49 | pub const CacheHash = struct { |
| 47 | 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 | 53 | manifest_dir: fs.Dir, |
| 50 | 54 | manifest_file: ?fs.File, |
| 51 | 55 | manifest_dirty: bool, |
| ... | ... | @@ -54,9 +58,11 @@ pub const CacheHash = struct { |
| 54 | 58 | |
| 55 | 59 | /// Be sure to call release after successful initialization. |
| 56 | 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 | 62 | return CacheHash{ |
| 58 | 63 | .allocator = allocator, |
| 59 | .blake3 = Blake3.init(.{}), | |
| 64 | .hasher_init = hasher_init, | |
| 65 | .hasher = hasher_init, | |
| 60 | 66 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), |
| 61 | 67 | .manifest_file = null, |
| 62 | 68 | .manifest_dirty = false, |
| ... | ... | @@ -69,8 +75,8 @@ pub const CacheHash = struct { |
| 69 | 75 | pub fn addSlice(self: *CacheHash, val: []const u8) void { |
| 70 | 76 | assert(self.manifest_file == null); |
| 71 | 77 | |
| 72 | self.blake3.update(val); | |
| 73 | self.blake3.update(&[_]u8{0}); | |
| 78 | self.hasher.update(val); | |
| 79 | self.hasher.update(&[_]u8{0}); | |
| 74 | 80 | } |
| 75 | 81 | |
| 76 | 82 | /// Convert the input value into bytes and record it as a dependency of the |
| ... | ... | @@ -133,12 +139,12 @@ pub const CacheHash = struct { |
| 133 | 139 | assert(self.manifest_file == null); |
| 134 | 140 | |
| 135 | 141 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 136 | self.blake3.final(&bin_digest); | |
| 142 | self.hasher.final(&bin_digest); | |
| 137 | 143 | |
| 138 | 144 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); |
| 139 | 145 | |
| 140 | self.blake3 = Blake3.init(.{}); | |
| 141 | self.blake3.update(&bin_digest); | |
| 146 | self.hasher = self.hasher_init; | |
| 147 | self.hasher.update(&bin_digest); | |
| 142 | 148 | |
| 143 | 149 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); |
| 144 | 150 | defer self.allocator.free(manifest_file_path); |
| ... | ... | @@ -238,7 +244,7 @@ pub const CacheHash = struct { |
| 238 | 244 | } |
| 239 | 245 | |
| 240 | 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 | 249 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { |
| 244 | 250 | cache_hash_file.bin_digest = actual_digest; |
| ... | ... | @@ -248,7 +254,7 @@ pub const CacheHash = struct { |
| 248 | 254 | } |
| 249 | 255 | |
| 250 | 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 | 262 | // cache miss |
| 257 | 263 | // keep the manifest file open |
| 258 | 264 | // reset the hash |
| 259 | self.blake3 = Blake3.init(.{}); | |
| 260 | self.blake3.update(&bin_digest); | |
| 265 | self.hasher = self.hasher_init; | |
| 266 | self.hasher.update(&bin_digest); | |
| 261 | 267 | |
| 262 | 268 | // Remove files not in the initial hash |
| 263 | 269 | for (self.files.items[input_file_count..]) |*file| { |
| ... | ... | @@ -266,7 +272,7 @@ pub const CacheHash = struct { |
| 266 | 272 | self.files.shrink(input_file_count); |
| 267 | 273 | |
| 268 | 274 | for (self.files.items) |file| { |
| 269 | self.blake3.update(&file.bin_digest); | |
| 275 | self.hasher.update(&file.bin_digest); | |
| 270 | 276 | } |
| 271 | 277 | return null; |
| 272 | 278 | } |
| ... | ... | @@ -304,23 +310,23 @@ pub const CacheHash = struct { |
| 304 | 310 | |
| 305 | 311 | // Hash while reading from disk, to keep the contents in the cpu cache while |
| 306 | 312 | // doing hashing. |
| 307 | var blake3 = Blake3.init(.{}); | |
| 313 | var hasher = self.hasher_init; | |
| 308 | 314 | var off: usize = 0; |
| 309 | 315 | while (true) { |
| 310 | 316 | // give me everything you've got, captain |
| 311 | 317 | const bytes_read = try file.read(contents[off..]); |
| 312 | 318 | if (bytes_read == 0) break; |
| 313 | blake3.update(contents[off..][0..bytes_read]); | |
| 319 | hasher.update(contents[off..][0..bytes_read]); | |
| 314 | 320 | off += bytes_read; |
| 315 | 321 | } |
| 316 | blake3.final(&ch_file.bin_digest); | |
| 322 | hasher.final(&ch_file.bin_digest); | |
| 317 | 323 | |
| 318 | 324 | ch_file.contents = contents; |
| 319 | 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 | 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 | 388 | // the artifacts to cache. |
| 383 | 389 | |
| 384 | 390 | var bin_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 385 | self.blake3.final(&bin_digest); | |
| 391 | self.hasher.final(&bin_digest); | |
| 386 | 392 | |
| 387 | 393 | var out_digest: [BASE64_DIGEST_LEN]u8 = undefined; |
| 388 | 394 | base64_encoder.encode(&out_digest, &bin_digest); |
| ... | ... | @@ -433,17 +439,17 @@ pub const CacheHash = struct { |
| 433 | 439 | } |
| 434 | 440 | }; |
| 435 | 441 | |
| 436 | fn hashFile(file: fs.File, bin_digest: []u8) !void { | |
| 437 | var blake3 = Blake3.init(.{}); | |
| 442 | fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void { | |
| 438 | 443 | var buf: [1024]u8 = undefined; |
| 439 | 444 | |
| 445 | var hasher = hasher_init; | |
| 440 | 446 | while (true) { |
| 441 | 447 | const bytes_read = try file.read(&buf); |
| 442 | 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 | 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 | 513 | _ = try ch.addFile(temp_file, null); |
| 508 | 514 | |
| 509 | 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 | 518 | digest1 = ch.final(); |
| 513 | 519 | } |
| ... | ... | @@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" { |
| 575 | 581 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 576 | 582 | |
| 577 | 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 | 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 | 598 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 593 | 599 | |
| 594 | 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 | 603 | // The cache system does not keep the contents of re-hashed input files. |
| 598 | 604 | testing.expect(ch.files.items[temp_file_idx].contents == null); |
| ... | ... | @@ -625,7 +631,7 @@ test "no file inputs" { |
| 625 | 631 | ch.add("1234"); |
| 626 | 632 | |
| 627 | 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 | 636 | digest1 = ch.final(); |
| 631 | 637 | } |
| ... | ... | @@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" { |
| 672 | 678 | _ = try ch.addFile(temp_file1, null); |
| 673 | 679 | |
| 674 | 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 | 683 | _ = try ch.addFilePost(temp_file2); |
| 678 | 684 | |
| ... | ... | @@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" { |
| 705 | 711 | _ = try ch.addFile(temp_file1, null); |
| 706 | 712 | |
| 707 | 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 | 716 | _ = try ch.addFilePost(temp_file2); |
| 711 | 717 |
lib/std/crypto.zig+2| ... | ... | @@ -18,6 +18,7 @@ pub const hash = struct { |
| 18 | 18 | /// Authentication (MAC) functions. |
| 19 | 19 | pub const auth = struct { |
| 20 | 20 | pub const hmac = @import("crypto/hmac.zig"); |
| 21 | pub const siphash = @import("crypto/siphash.zig"); | |
| 21 | 22 | }; |
| 22 | 23 | |
| 23 | 24 | /// Authenticated Encryption with Associated Data |
| ... | ... | @@ -80,6 +81,7 @@ test "crypto" { |
| 80 | 81 | _ = @import("crypto/sha1.zig"); |
| 81 | 82 | _ = @import("crypto/sha2.zig"); |
| 82 | 83 | _ = @import("crypto/sha3.zig"); |
| 84 | _ = @import("crypto/siphash.zig"); | |
| 83 | 85 | _ = @import("crypto/25519/curve25519.zig"); |
| 84 | 86 | _ = @import("crypto/25519/ed25519.zig"); |
| 85 | 87 | _ = @import("crypto/25519/edwards25519.zig"); |
lib/std/crypto/benchmark.zig+4| ... | ... | @@ -60,6 +60,10 @@ const macs = [_]Crypto{ |
| 60 | 60 | Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" }, |
| 61 | 61 | Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" }, |
| 62 | 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 | 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 | 976 | /// MIPS RS3000 Little-endian |
| 977 | 977 | _MIPS_RS3_LE = 10, |
| 978 | 978 | |
| 979 | /// SPU Mark II | |
| 980 | _SPU_2 = 13, | |
| 981 | ||
| 979 | 982 | /// Hewlett-Packard PA-RISC |
| 980 | 983 | _PARISC = 15, |
| 981 | 984 |
lib/std/fs.zig+19-12| ... | ... | @@ -686,21 +686,28 @@ pub const Dir = struct { |
| 686 | 686 | return self.openFileW(path_w.span(), flags); |
| 687 | 687 | } |
| 688 | 688 | |
| 689 | var os_flags: u32 = os.O_CLOEXEC; | |
| 689 | 690 | // Use the O_ locking flags if the os supports them |
| 690 | 691 | // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag) |
| 691 | 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 | os.O_NONBLOCK | os.O_SYNC | |
| 694 | else | |
| 695 | @as(u32, 0); | |
| 696 | const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { | |
| 697 | .None => @as(u32, 0), | |
| 698 | .Shared => os.O_SHLOCK | nonblocking_lock_flag, | |
| 699 | .Exclusive => os.O_EXLOCK | nonblocking_lock_flag, | |
| 700 | } else 0; | |
| 701 | ||
| 702 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | |
| 703 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) | |
| 693 | if (has_flock_open_flags) { | |
| 694 | const nonblocking_lock_flag = if (flags.lock_nonblocking) | |
| 695 | os.O_NONBLOCK | os.O_SYNC | |
| 696 | else | |
| 697 | @as(u32, 0); | |
| 698 | os_flags |= switch (flags.lock) { | |
| 699 | .None => @as(u32, 0), | |
| 700 | .Shared => os.O_SHLOCK | nonblocking_lock_flag, | |
| 701 | .Exclusive => os.O_EXLOCK | nonblocking_lock_flag, | |
| 702 | }; | |
| 703 | } | |
| 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 | 711 | @as(u32, os.O_RDWR) |
| 705 | 712 | else if (flags.write) |
| 706 | 713 | @as(u32, os.O_WRONLY) |
lib/std/fs/file.zig+4| ... | ... | @@ -101,6 +101,10 @@ pub const File = struct { |
| 101 | 101 | /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions |
| 102 | 102 | /// related to opening the file, reading, writing, and locking. |
| 103 | 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 | 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 | 20 | pub const Fnv1a_64 = fnv.Fnv1a_64; |
| 21 | 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 | 24 | pub const SipHash64 = siphash.SipHash64; |
| 25 | 25 | pub const SipHash128 = siphash.SipHash128; |
| 26 | 26 | |
| ... | ... | @@ -42,7 +42,6 @@ test "hash" { |
| 42 | 42 | _ = @import("hash/auto_hash.zig"); |
| 43 | 43 | _ = @import("hash/crc.zig"); |
| 44 | 44 | _ = @import("hash/fnv.zig"); |
| 45 | _ = @import("hash/siphash.zig"); | |
| 46 | 45 | _ = @import("hash/murmur.zig"); |
| 47 | 46 | _ = @import("hash/cityhash.zig"); |
| 48 | 47 | _ = @import("hash/wyhash.zig"); |
lib/std/hash/benchmark.zig-12| ... | ... | @@ -25,24 +25,12 @@ const Hash = struct { |
| 25 | 25 | init_u64: ?u64 = null, |
| 26 | 26 | }; |
| 27 | 27 | |
| 28 | const siphash_key = "0123456789abcdef"; | |
| 29 | ||
| 30 | 28 | const hashes = [_]Hash{ |
| 31 | 29 | Hash{ |
| 32 | 30 | .ty = hash.Wyhash, |
| 33 | 31 | .name = "wyhash", |
| 34 | 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 | 34 | Hash{ |
| 47 | 35 | .ty = hash.Fnv1a_64, |
| 48 | 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 | 433 | const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size]; |
| 434 | 434 | self.backing_allocator.free(bucket_slice); |
| 435 | 435 | } else { |
| 436 | // TODO Set the slot data to undefined. | |
| 437 | // Related: https://github.com/ziglang/zig/issues/4298 | |
| 436 | @memset(bucket.page + slot_index * size_class, undefined, size_class); | |
| 438 | 437 | } |
| 439 | 438 | } |
| 440 | 439 | |
| ... | ... | @@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 567 | 566 | const new_aligned_size = math.max(new_size, old_align); |
| 568 | 567 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); |
| 569 | 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 | 572 | return new_size; |
| 571 | 573 | } |
| 572 | 574 | return error.OutOfMemory; |
lib/std/linked_list.zig-12| ... | ... | @@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type { |
| 28 | 28 | |
| 29 | 29 | pub const Data = T; |
| 30 | 30 | |
| 31 | pub fn init(data: T) Node { | |
| 32 | return Node{ | |
| 33 | .data = data, | |
| 34 | }; | |
| 35 | } | |
| 36 | ||
| 37 | 31 | /// Insert a new node after the current one. |
| 38 | 32 | /// |
| 39 | 33 | /// Arguments: |
| ... | ... | @@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type { |
| 175 | 169 | prev: ?*Node = null, |
| 176 | 170 | next: ?*Node = null, |
| 177 | 171 | data: T, |
| 178 | ||
| 179 | pub fn init(data: T) Node { | |
| 180 | return Node{ | |
| 181 | .data = data, | |
| 182 | }; | |
| 183 | } | |
| 184 | 172 | }; |
| 185 | 173 | |
| 186 | 174 | first: ?*Node = null, |
lib/std/macho.zig+78-1| ... | ... | @@ -40,6 +40,24 @@ pub const uuid_command = extern struct { |
| 40 | 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 | 61 | /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD |
| 44 | 62 | /// "stab" style symbol table information as described in the header files |
| 45 | 63 | /// <nlist.h> and <stab.h>. |
| ... | ... | @@ -65,7 +83,7 @@ pub const symtab_command = extern struct { |
| 65 | 83 | |
| 66 | 84 | /// The linkedit_data_command contains the offsets and sizes of a blob |
| 67 | 85 | /// of data in the __LINKEDIT segment. |
| 68 | const linkedit_data_command = extern struct { | |
| 86 | pub const linkedit_data_command = extern struct { | |
| 69 | 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 | 88 | cmd: u32, |
| 71 | 89 | |
| ... | ... | @@ -79,6 +97,65 @@ const linkedit_data_command = extern struct { |
| 79 | 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 | 159 | /// The segment load command indicates that a part of this file is to be |
| 83 | 160 | /// mapped into the task's address space. The size of this segment in memory, |
| 84 | 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 | 24 | }; |
| 25 | 25 | |
| 26 | 26 | pub usingnamespace @import("linux/netlink.zig"); |
| 27 | pub const BPF = @import("linux/bpf.zig"); | |
| 28 | 27 | |
| 29 | 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 | 29 | }; |
| 30 | 30 | pub usingnamespace @import("bits.zig"); |
| 31 | 31 | pub const tls = @import("linux/tls.zig"); |
| 32 | pub const BPF = @import("linux/bpf.zig"); | |
| 32 | 33 | |
| 33 | 34 | /// Set by startup code, used by `getauxval`. |
| 34 | 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 | // 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 | 1 | const Builder = @import("std").build.Builder; |
| 7 | 2 | |
| 8 | 3 | pub fn build(b: *Builder) void { |
lib/std/special/init-exe/src/main.zig-5| ... | ... | @@ -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 | 1 | const std = @import("std"); |
| 7 | 2 | |
| 8 | 3 | pub fn main() anyerror!void { |
lib/std/special/init-lib/build.zig-5| ... | ... | @@ -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 | 1 | const Builder = @import("std").build.Builder; |
| 7 | 2 | |
| 8 | 3 | pub fn build(b: *Builder) void { |
lib/std/special/init-lib/src/main.zig-5| ... | ... | @@ -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 | 1 | const std = @import("std"); |
| 7 | 2 | const testing = std.testing; |
| 8 | 3 |
lib/std/target.zig+18-8| ... | ... | @@ -96,8 +96,12 @@ pub const Target = struct { |
| 96 | 96 | win10_rs4 = 0x0A000005, |
| 97 | 97 | win10_rs5 = 0x0A000006, |
| 98 | 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 | 105 | pub const Range = struct { |
| 102 | 106 | min: WindowsVersion, |
| 103 | 107 | max: WindowsVersion, |
| ... | ... | @@ -124,18 +128,17 @@ pub const Target = struct { |
| 124 | 128 | out_stream: anytype, |
| 125 | 129 | ) !void { |
| 126 | 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 | 132 | try std.fmt.format(out_stream, ".{}", .{@tagName(self)}); |
| 129 | 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 | 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 | 139 | try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)}); |
| 135 | 140 | } else { |
| 136 | try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())}); | |
| 137 | try std.fmt.format(out_stream, "{}", .{@enumToInt(self)}); | |
| 138 | try out_stream.writeAll(")"); | |
| 141 | try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)}); | |
| 139 | 142 | } |
| 140 | 143 | } |
| 141 | 144 | } |
| ... | ... | @@ -280,7 +283,7 @@ pub const Target = struct { |
| 280 | 283 | .windows => return .{ |
| 281 | 284 | .windows = .{ |
| 282 | 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 | 666 | renderscript32, |
| 664 | 667 | renderscript64, |
| 665 | 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 | 673 | pub fn isARM(arch: Arch) bool { |
| 668 | 674 | return switch (arch) { |
| ... | ... | @@ -761,6 +767,7 @@ pub const Target = struct { |
| 761 | 767 | .sparcv9 => ._SPARCV9, |
| 762 | 768 | .s390x => ._S390, |
| 763 | 769 | .ve => ._NONE, |
| 770 | .spu_2 => ._SPU_2, | |
| 764 | 771 | }; |
| 765 | 772 | } |
| 766 | 773 | |
| ... | ... | @@ -803,6 +810,7 @@ pub const Target = struct { |
| 803 | 810 | .renderscript64, |
| 804 | 811 | .shave, |
| 805 | 812 | .ve, |
| 813 | .spu_2, | |
| 806 | 814 | => .Little, |
| 807 | 815 | |
| 808 | 816 | .arc, |
| ... | ... | @@ -827,6 +835,7 @@ pub const Target = struct { |
| 827 | 835 | switch (arch) { |
| 828 | 836 | .avr, |
| 829 | 837 | .msp430, |
| 838 | .spu_2, | |
| 830 | 839 | => return 16, |
| 831 | 840 | |
| 832 | 841 | .arc, |
| ... | ... | @@ -1317,12 +1326,13 @@ pub const Target = struct { |
| 1317 | 1326 | .bpfeb, |
| 1318 | 1327 | .nvptx, |
| 1319 | 1328 | .nvptx64, |
| 1329 | .spu_2, | |
| 1330 | .avr, | |
| 1320 | 1331 | => return result, |
| 1321 | 1332 | |
| 1322 | 1333 | // TODO go over each item in this list and either move it to the above list, or |
| 1323 | 1334 | // implement the standard dynamic linker path code for it. |
| 1324 | 1335 | .arc, |
| 1325 | .avr, | |
| 1326 | 1336 | .hexagon, |
| 1327 | 1337 | .msp430, |
| 1328 | 1338 | .r600, |
lib/std/zig/system.zig+1-1| ... | ... | @@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct { |
| 249 | 249 | // values |
| 250 | 250 | const known_build_numbers = [_]u32{ |
| 251 | 251 | 10240, 10586, 14393, 15063, 16299, 17134, 17763, |
| 252 | 18362, 18363, | |
| 252 | 18362, 19041, | |
| 253 | 253 | }; |
| 254 | 254 | var last_idx: usize = 0; |
| 255 | 255 | for (known_build_numbers) |build, i| { |
src-self-hosted/Module.zig+42| ... | ... | @@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 80 | 80 | root_name: []u8, |
| 81 | 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 | 86 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 84 | 87 | |
| 85 | 88 | const WorkItem = union(enum) { |
| ... | ... | @@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void { |
| 928 | 931 | |
| 929 | 932 | self.symbol_exports.deinit(gpa); |
| 930 | 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 | 939 | self.* = undefined; |
| 932 | 940 | } |
| 933 | 941 | |
| ... | ... | @@ -2072,6 +2080,18 @@ fn createNewDecl( |
| 2072 | 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 | 2095 | /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. |
| 2076 | 2096 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { |
| 2077 | 2097 | return scope.cast(Scope.Block) orelse |
| ... | ... | @@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_ |
| 3309 | 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 | 3354 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 3313 | 3355 | const zir_module = scope.namespace(); |
| 3314 | 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 | 18 | /// The expression has an inferred type, and it will be evaluated as an rvalue. |
| 19 | 19 | none, |
| 20 | 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. | |
| 22 | lvalue, | |
| 23 | /// The expression must generate a pointer | |
| 21 | /// of an assignment uses this kind of result location. | |
| 24 | 22 | ref, |
| 25 | 23 | /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. |
| 26 | 24 | ty: *zir.Inst, |
| ... | ... | @@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z |
| 46 | 44 | return expr(mod, scope, type_rl, type_node); |
| 47 | 45 | } |
| 48 | 46 | |
| 49 | /// Turn Zig AST into untyped ZIR istructions. | |
| 50 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { | |
| 51 | if (rl == .lvalue) { | |
| 52 | switch (node.tag) { | |
| 53 | .Root => unreachable, | |
| 54 | .Use => unreachable, | |
| 55 | .TestDecl => unreachable, | |
| 56 | .DocComment => unreachable, | |
| 57 | .VarDecl => unreachable, | |
| 58 | .SwitchCase => unreachable, | |
| 59 | .SwitchElse => unreachable, | |
| 60 | .Else => unreachable, | |
| 61 | .Payload => unreachable, | |
| 62 | .PointerPayload => unreachable, | |
| 63 | .PointerIndexPayload => unreachable, | |
| 64 | .ErrorTag => unreachable, | |
| 65 | .FieldInitializer => unreachable, | |
| 66 | .ContainerField => unreachable, | |
| 67 | ||
| 68 | .Assign, | |
| 69 | .AssignBitAnd, | |
| 70 | .AssignBitOr, | |
| 71 | .AssignBitShiftLeft, | |
| 72 | .AssignBitShiftRight, | |
| 73 | .AssignBitXor, | |
| 74 | .AssignDiv, | |
| 75 | .AssignSub, | |
| 76 | .AssignSubWrap, | |
| 77 | .AssignMod, | |
| 78 | .AssignAdd, | |
| 79 | .AssignAddWrap, | |
| 80 | .AssignMul, | |
| 81 | .AssignMulWrap, | |
| 82 | .Add, | |
| 83 | .AddWrap, | |
| 84 | .Sub, | |
| 85 | .SubWrap, | |
| 86 | .Mul, | |
| 87 | .MulWrap, | |
| 88 | .Div, | |
| 89 | .Mod, | |
| 90 | .BitAnd, | |
| 91 | .BitOr, | |
| 92 | .BitShiftLeft, | |
| 93 | .BitShiftRight, | |
| 94 | .BitXor, | |
| 95 | .BangEqual, | |
| 96 | .EqualEqual, | |
| 97 | .GreaterThan, | |
| 98 | .GreaterOrEqual, | |
| 99 | .LessThan, | |
| 100 | .LessOrEqual, | |
| 101 | .ArrayCat, | |
| 102 | .ArrayMult, | |
| 103 | .BoolAnd, | |
| 104 | .BoolOr, | |
| 105 | .Asm, | |
| 106 | .StringLiteral, | |
| 107 | .IntegerLiteral, | |
| 108 | .Call, | |
| 109 | .Unreachable, | |
| 110 | .Return, | |
| 111 | .If, | |
| 112 | .While, | |
| 113 | .BoolNot, | |
| 114 | .AddressOf, | |
| 115 | .FloatLiteral, | |
| 116 | .UndefinedLiteral, | |
| 117 | .BoolLiteral, | |
| 118 | .NullLiteral, | |
| 119 | .OptionalType, | |
| 120 | .Block, | |
| 121 | .LabeledBlock, | |
| 122 | .Break, | |
| 123 | .PtrType, | |
| 124 | .GroupedExpression, | |
| 125 | .ArrayType, | |
| 126 | .ArrayTypeSentinel, | |
| 127 | .EnumLiteral, | |
| 128 | .MultilineStringLiteral, | |
| 129 | .CharLiteral, | |
| 130 | .Defer, | |
| 131 | .Catch, | |
| 132 | .ErrorUnion, | |
| 133 | .MergeErrorSets, | |
| 134 | .Range, | |
| 135 | .OrElse, | |
| 136 | .Await, | |
| 137 | .BitNot, | |
| 138 | .Negation, | |
| 139 | .NegationWrap, | |
| 140 | .Resume, | |
| 141 | .Try, | |
| 142 | .SliceType, | |
| 143 | .Slice, | |
| 144 | .ArrayInitializer, | |
| 145 | .ArrayInitializerDot, | |
| 146 | .StructInitializer, | |
| 147 | .StructInitializerDot, | |
| 148 | .Switch, | |
| 149 | .For, | |
| 150 | .Suspend, | |
| 151 | .Continue, | |
| 152 | .AnyType, | |
| 153 | .ErrorType, | |
| 154 | .FnProto, | |
| 155 | .AnyFrameType, | |
| 156 | .ErrorSetDecl, | |
| 157 | .ContainerDecl, | |
| 158 | .Comptime, | |
| 159 | .Nosuspend, | |
| 160 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | |
| 161 | ||
| 162 | // @field can be assigned to | |
| 163 | .BuiltinCall => { | |
| 164 | const call = node.castTag(.BuiltinCall).?; | |
| 165 | const tree = scope.tree(); | |
| 166 | const builtin_name = tree.tokenSlice(call.builtin_token); | |
| 167 | ||
| 168 | if (!mem.eql(u8, builtin_name, "@field")) { | |
| 169 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | |
| 170 | } | |
| 171 | }, | |
| 47 | fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | |
| 48 | switch (node.tag) { | |
| 49 | .Root => unreachable, | |
| 50 | .Use => unreachable, | |
| 51 | .TestDecl => unreachable, | |
| 52 | .DocComment => unreachable, | |
| 53 | .VarDecl => unreachable, | |
| 54 | .SwitchCase => unreachable, | |
| 55 | .SwitchElse => unreachable, | |
| 56 | .Else => unreachable, | |
| 57 | .Payload => unreachable, | |
| 58 | .PointerPayload => unreachable, | |
| 59 | .PointerIndexPayload => unreachable, | |
| 60 | .ErrorTag => unreachable, | |
| 61 | .FieldInitializer => unreachable, | |
| 62 | .ContainerField => unreachable, | |
| 63 | ||
| 64 | .Assign, | |
| 65 | .AssignBitAnd, | |
| 66 | .AssignBitOr, | |
| 67 | .AssignBitShiftLeft, | |
| 68 | .AssignBitShiftRight, | |
| 69 | .AssignBitXor, | |
| 70 | .AssignDiv, | |
| 71 | .AssignSub, | |
| 72 | .AssignSubWrap, | |
| 73 | .AssignMod, | |
| 74 | .AssignAdd, | |
| 75 | .AssignAddWrap, | |
| 76 | .AssignMul, | |
| 77 | .AssignMulWrap, | |
| 78 | .Add, | |
| 79 | .AddWrap, | |
| 80 | .Sub, | |
| 81 | .SubWrap, | |
| 82 | .Mul, | |
| 83 | .MulWrap, | |
| 84 | .Div, | |
| 85 | .Mod, | |
| 86 | .BitAnd, | |
| 87 | .BitOr, | |
| 88 | .BitShiftLeft, | |
| 89 | .BitShiftRight, | |
| 90 | .BitXor, | |
| 91 | .BangEqual, | |
| 92 | .EqualEqual, | |
| 93 | .GreaterThan, | |
| 94 | .GreaterOrEqual, | |
| 95 | .LessThan, | |
| 96 | .LessOrEqual, | |
| 97 | .ArrayCat, | |
| 98 | .ArrayMult, | |
| 99 | .BoolAnd, | |
| 100 | .BoolOr, | |
| 101 | .Asm, | |
| 102 | .StringLiteral, | |
| 103 | .IntegerLiteral, | |
| 104 | .Call, | |
| 105 | .Unreachable, | |
| 106 | .Return, | |
| 107 | .If, | |
| 108 | .While, | |
| 109 | .BoolNot, | |
| 110 | .AddressOf, | |
| 111 | .FloatLiteral, | |
| 112 | .UndefinedLiteral, | |
| 113 | .BoolLiteral, | |
| 114 | .NullLiteral, | |
| 115 | .OptionalType, | |
| 116 | .Block, | |
| 117 | .LabeledBlock, | |
| 118 | .Break, | |
| 119 | .PtrType, | |
| 120 | .GroupedExpression, | |
| 121 | .ArrayType, | |
| 122 | .ArrayTypeSentinel, | |
| 123 | .EnumLiteral, | |
| 124 | .MultilineStringLiteral, | |
| 125 | .CharLiteral, | |
| 126 | .Defer, | |
| 127 | .Catch, | |
| 128 | .ErrorUnion, | |
| 129 | .MergeErrorSets, | |
| 130 | .Range, | |
| 131 | .OrElse, | |
| 132 | .Await, | |
| 133 | .BitNot, | |
| 134 | .Negation, | |
| 135 | .NegationWrap, | |
| 136 | .Resume, | |
| 137 | .Try, | |
| 138 | .SliceType, | |
| 139 | .Slice, | |
| 140 | .ArrayInitializer, | |
| 141 | .ArrayInitializerDot, | |
| 142 | .StructInitializer, | |
| 143 | .StructInitializerDot, | |
| 144 | .Switch, | |
| 145 | .For, | |
| 146 | .Suspend, | |
| 147 | .Continue, | |
| 148 | .AnyType, | |
| 149 | .ErrorType, | |
| 150 | .FnProto, | |
| 151 | .AnyFrameType, | |
| 152 | .ErrorSetDecl, | |
| 153 | .ContainerDecl, | |
| 154 | .Comptime, | |
| 155 | .Nosuspend, | |
| 156 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | |
| 157 | ||
| 158 | // @field can be assigned to | |
| 159 | .BuiltinCall => { | |
| 160 | const call = node.castTag(.BuiltinCall).?; | |
| 161 | const tree = scope.tree(); | |
| 162 | const builtin_name = tree.tokenSlice(call.builtin_token); | |
| 163 | ||
| 164 | if (!mem.eql(u8, builtin_name, "@field")) { | |
| 165 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | |
| 166 | } | |
| 167 | }, | |
| 172 | 168 | |
| 173 | // can be assigned to | |
| 174 | .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, | |
| 175 | } | |
| 169 | // can be assigned to | |
| 170 | .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, | |
| 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 | 177 | switch (node.tag) { |
| 178 | 178 | .Root => unreachable, // Top-level declaration. |
| 179 | 179 | .Use => unreachable, // Top-level declaration. |
| ... | ... | @@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 232 | 232 | .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), |
| 233 | 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 | 240 | .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), |
| 236 | 241 | .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), |
| 237 | 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 | 247 | .Return => return ret(mod, scope, node.castTag(.Return).?), |
| 243 | 248 | .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), |
| 244 | 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 | 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 | 252 | .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), |
| 249 | 253 | .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), |
| 250 | 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 | 267 | .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), |
| 264 | 268 | .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), |
| 265 | 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 | 276 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), |
| 268 | 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 | 278 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), |
| 272 | 279 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), |
| 273 | 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 | 281 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), |
| 278 | 282 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), |
| 279 | 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 | 291 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 288 | 292 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 289 | 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 | 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 | 295 | .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), |
| 295 | 296 | .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), |
| 296 | 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 | 317 | // proper type inference requires peer type resolution on the block's |
| 317 | 318 | // break operand expressions. |
| 318 | 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 | 321 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, |
| 321 | 322 | }; |
| 322 | 323 | const operand = try expr(mod, parent_scope, branch_rl, rhs); |
| ... | ... | @@ -458,7 +459,9 @@ fn varDecl( |
| 458 | 459 | const tree = scope.tree(); |
| 459 | 460 | const name_src = tree.token_locs[node.name_token].start; |
| 460 | 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 | 465 | switch (tree.token_ids[node.mut_token]) { |
| 463 | 466 | .Keyword_const => { |
| 464 | 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 | 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 | 528 | _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs); |
| 526 | 529 | } |
| 527 | 530 | |
| ... | ... | @@ -531,7 +534,7 @@ fn assignOp( |
| 531 | 534 | infix_node: *ast.Node.SimpleInfixOp, |
| 532 | 535 | op_inst_tag: zir.Inst.Tag, |
| 533 | 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 | 538 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); |
| 536 | 539 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); |
| 537 | 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 | 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 | 580 | fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { |
| 558 | 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 | 584 | fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { |
| 562 | 585 | const tree = scope.tree(); |
| 563 | 586 | const src = tree.token_locs[node.op_token].start; |
| 564 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | |
| 565 | .ty = Type.initTag(.type), | |
| 566 | .val = Value.initTag(.type_type), | |
| 567 | }); | |
| 568 | const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); | |
| 587 | const operand = try typeExpr(mod, scope, node.rhs); | |
| 569 | 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 | 609 | } |
| 591 | 610 | |
| 592 | 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 | 612 | const simple = ptr_info.allowzero_token == null and |
| 599 | 613 | ptr_info.align_info == null and |
| 600 | 614 | ptr_info.volatile_token == null and |
| 601 | 615 | ptr_info.sentinel == null; |
| 602 | 616 | |
| 603 | 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 | 619 | const mutable = ptr_info.const_token == null; |
| 606 | 620 | // TODO stage1 type inference bug |
| 607 | 621 | const T = zir.Inst.Tag; |
| ... | ... | @@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, |
| 629 | 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 | 647 | if (kw_args.sentinel) |some| { |
| 634 | 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 | 654 | fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { |
| 641 | 655 | const tree = scope.tree(); |
| 642 | 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 | 657 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 648 | 658 | .ty = Type.initTag(.type), |
| 649 | 659 | .val = Value.initTag(.usize_type), |
| ... | ... | @@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst |
| 651 | 661 | |
| 652 | 662 | // TODO check for [_]T |
| 653 | 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 | 669 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { |
| 660 | 670 | const tree = scope.tree(); |
| 661 | 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 | 672 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 667 | 673 | .ty = Type.initTag(.type), |
| 668 | 674 | .val = Value.initTag(.usize_type), |
| ... | ... | @@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti |
| 671 | 677 | // TODO check for [_]T |
| 672 | 678 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); |
| 673 | 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 | 681 | const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); |
| 676 | 682 | |
| 677 | 683 | return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ |
| ... | ... | @@ -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 | 712 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { |
| 685 | 713 | const tree = scope.tree(); |
| 686 | 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 | 722 | const src = tree.token_locs[node.rtoken].start; |
| 695 | 723 | |
| 696 | 724 | const operand = try expr(mod, scope, .ref, node.lhs); |
| 697 | const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); | |
| 698 | if (rl == .lvalue or rl == .ref) return unwrapped_ptr; | |
| 725 | return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand)); | |
| 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 | 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 | 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 { | |
| 741 | // TODO introduce lvalues | |
| 789 | fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { | |
| 742 | 790 | const tree = scope.tree(); |
| 743 | 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 | 794 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 747 | 795 | |
| 748 | 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 | 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 | 1020 | // proper type inference requires peer type resolution on the if's |
| 972 | 1021 | // branches. |
| 973 | 1022 | const branch_rl: ResultLoc = switch (rl) { |
| 974 | .discard, .none, .ty, .ptr, .lvalue, .ref => rl, | |
| 1023 | .discard, .none, .ty, .ptr, .ref => rl, | |
| 975 | 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 | 1150 | // proper type inference requires peer type resolution on the while's |
| 1102 | 1151 | // branches. |
| 1103 | 1152 | const branch_rl: ResultLoc = switch (rl) { |
| 1104 | .discard, .none, .ty, .ptr, .lvalue, .ref => rl, | |
| 1153 | .discard, .none, .ty, .ptr, .ref => rl, | |
| 1105 | 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 | 1281 | .local_ptr => { |
| 1233 | 1282 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 1234 | 1283 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 1235 | if (rl == .lvalue or rl == .ref) { | |
| 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 | } | |
| 1284 | return rlWrapPtr(mod, scope, rl, local_ptr.ptr); | |
| 1241 | 1285 | } |
| 1242 | 1286 | s = local_ptr.parent; |
| 1243 | 1287 | }, |
| ... | ... | @@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 1247 | 1291 | } |
| 1248 | 1292 | |
| 1249 | 1293 | if (mod.lookupDeclName(scope, ident_name)) |decl| { |
| 1250 | const result = 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)); | |
| 1294 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); | |
| 1254 | 1295 | } |
| 1255 | 1296 | |
| 1256 | 1297 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); |
| ... | ... | @@ -1466,12 +1507,8 @@ fn simpleCast( |
| 1466 | 1507 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 1467 | 1508 | const tree = scope.tree(); |
| 1468 | 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 | 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 | 1512 | const rhs = try expr(mod, scope, .none, params[1]); |
| 1476 | 1513 | const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); |
| 1477 | 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 | 1535 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1499 | 1536 | return result; |
| 1500 | 1537 | }, |
| 1501 | .lvalue => unreachable, | |
| 1502 | 1538 | .ref => { |
| 1503 | 1539 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); |
| 1504 | 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 | 1569 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 1534 | 1570 | const tree = scope.tree(); |
| 1535 | 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 | 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 | 1574 | switch (rl) { |
| 1543 | 1575 | .none => { |
| 1544 | 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 | 1582 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1551 | 1583 | return result; |
| 1552 | 1584 | }, |
| 1553 | .lvalue => unreachable, | |
| 1554 | 1585 | .ref => { |
| 1555 | 1586 | const operand = try expr(mod, scope, .ref, params[1]); |
| 1556 | 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 | 1849 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 1819 | 1850 | return result; |
| 1820 | 1851 | }, |
| 1821 | .lvalue, .ref => { | |
| 1852 | .ref => { | |
| 1822 | 1853 | // We need a pointer but we have a value. |
| 1823 | 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 | 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 | 1892 | pub fn addZIRInstSpecial( |
| 1856 | 1893 | mod: *Module, |
| 1857 | 1894 | scope: *Scope, |
src-self-hosted/codegen.zig+361-64| ... | ... | @@ -14,6 +14,7 @@ const Allocator = mem.Allocator; |
| 14 | 14 | const trace = @import("tracy.zig").trace; |
| 15 | 15 | const DW = std.dwarf; |
| 16 | 16 | const leb128 = std.debug.leb; |
| 17 | const log = std.log.scoped(.codegen); | |
| 17 | 18 | |
| 18 | 19 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. |
| 19 | 20 | // zig fmt: off |
| ... | ... | @@ -75,8 +76,8 @@ pub fn generateSymbol( |
| 75 | 76 | switch (bin_file.options.target.cpu.arch) { |
| 76 | 77 | .wasm32 => unreachable, // has its own code path |
| 77 | 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 | //.armeb => return Function(.armeb).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), | |
| 80 | .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), | |
| 80 | 81 | //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 81 | 82 | //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 82 | 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 | 102 | //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 102 | 103 | //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 103 | 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 | 106 | //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 105 | 107 | //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs), |
| 106 | 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 | 346 | |
| 345 | 347 | const Branch = struct { |
| 346 | 348 | inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{}, |
| 349 | /// The key must be canonical register. | |
| 347 | 350 | registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{}, |
| 348 | 351 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| 349 | 352 | |
| ... | ... | @@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 381 | 384 | self.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 382 | 385 | const reg = callee_preserved_regs[free_index]; |
| 383 | 386 | self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst }); |
| 387 | log.debug("alloc {} => {*}", .{reg, inst}); | |
| 384 | 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 | 400 | fn deinit(self: *Branch, gpa: *Allocator) void { |
| 388 | 401 | self.inst_table.deinit(gpa); |
| 389 | 402 | self.registers.deinit(gpa); |
| ... | ... | @@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 570 | 583 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 571 | 584 | const inst_table = &branch.inst_table; |
| 572 | 585 | for (body.instructions) |inst| { |
| 573 | const new_inst = try self.genFuncInst(inst); | |
| 574 | try inst_table.putNoClobber(self.gpa, inst, new_inst); | |
| 586 | const mcv = try self.genFuncInst(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 | 591 | var i: ir.Inst.DeathsBitIndex = 0; |
| 577 | 592 | while (inst.getOperand(i)) |operand| : (i += 1) { |
| ... | ... | @@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 714 | 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 | 733 | const elem_ty = inst.ty; |
| 719 | 734 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 720 | 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 | 739 | self.stack_align = abi_align; |
| 725 | 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. | |
| 728 | const ptr_bits = arch.ptrBitWidth(); | |
| 729 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 730 | if (abi_size <= ptr_bytes) { | |
| 731 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); | |
| 732 | if (branch.allocReg(inst)) |reg| { | |
| 733 | return MCValue{ .register = registerAlias(reg, abi_size) }; | |
| 742 | if (reg_ok) { | |
| 743 | // Make sure the type can fit in a register before we try to allocate one. | |
| 744 | const ptr_bits = arch.ptrBitWidth(); | |
| 745 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 746 | if (abi_size <= ptr_bytes) { | |
| 747 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); | |
| 748 | if (branch.allocReg(inst)) |reg| { | |
| 749 | return MCValue{ .register = registerAlias(reg, abi_size) }; | |
| 750 | } | |
| 734 | 751 | } |
| 735 | 752 | } |
| 736 | 753 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 737 | 754 | return MCValue{ .stack_offset = stack_offset }; |
| 738 | 755 | } |
| 739 | 756 | |
| 740 | /// Does not "move" the instruction. | |
| 741 | fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue { | |
| 757 | /// Copies a value to a register without tracking the register. The register is not considered | |
| 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 | 787 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 743 | 788 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); |
| 744 | 789 | |
| 745 | const reg = branch.allocReg(inst) orelse | |
| 746 | return self.fail(inst.src, "TODO implement spilling register to stack", .{}); | |
| 747 | const old_mcv = branch.inst_table.get(inst).?; | |
| 748 | const new_mcv: MCValue = .{ .register = reg }; | |
| 749 | try self.genSetReg(inst.src, reg, old_mcv); | |
| 750 | return new_mcv; | |
| 790 | const reg = branch.allocReg(reg_owner) orelse b: { | |
| 791 | // We'll take over the first register. Move the instruction that was previously | |
| 792 | // there to a stack allocation. | |
| 793 | const reg = callee_preserved_regs[0]; | |
| 794 | const regs_entry = branch.registers.getEntry(reg).?; | |
| 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 | 811 | fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue { |
| ... | ... | @@ -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 { | |
| 872 | if (!inst.operandDies(op_index) or !mcv.isMutable()) | |
| 929 | fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool { | |
| 930 | if (!inst.operandDies(op_index)) | |
| 873 | 931 | return false; |
| 874 | 932 | |
| 875 | // OK we're going to do it, but we need to clear the operand death bit so that | |
| 876 | // it stays allocated. | |
| 933 | switch (mcv) { | |
| 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 | 951 | inst.clearOperandDeath(op_index); |
| 952 | ||
| 878 | 953 | return true; |
| 879 | 954 | } |
| 880 | 955 | |
| ... | ... | @@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 887 | 962 | if (inst.base.isUnused() and !is_volatile) |
| 888 | 963 | return MCValue.dead; |
| 889 | 964 | const dst_mcv: MCValue = blk: { |
| 890 | if (reuseOperand(&inst.base, 0, ptr)) { | |
| 965 | if (self.reuseOperand(&inst.base, 0, ptr)) { | |
| 891 | 966 | // The MCValue that holds the pointer can be re-used as the value. |
| 892 | 967 | break :blk ptr; |
| 893 | 968 | } else { |
| 894 | break :blk try self.allocRegOrMem(&inst.base); | |
| 969 | break :blk try self.allocRegOrMem(&inst.base, true); | |
| 895 | 970 | } |
| 896 | 971 | }; |
| 897 | 972 | switch (ptr) { |
| ... | ... | @@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 985 | 1060 | var dst_mcv: MCValue = undefined; |
| 986 | 1061 | var src_mcv: MCValue = undefined; |
| 987 | 1062 | var src_inst: *ir.Inst = undefined; |
| 988 | if (reuseOperand(inst, 0, lhs)) { | |
| 1063 | if (self.reuseOperand(inst, 0, lhs)) { | |
| 989 | 1064 | // LHS dies; use it as the destination. |
| 990 | 1065 | // Both operands cannot be memory. |
| 991 | 1066 | src_inst = op_rhs; |
| 992 | 1067 | if (lhs.isMemory() and rhs.isMemory()) { |
| 993 | dst_mcv = try self.copyToNewRegister(op_lhs); | |
| 1068 | dst_mcv = try self.copyToNewRegister(inst, lhs); | |
| 994 | 1069 | src_mcv = rhs; |
| 995 | 1070 | } else { |
| 996 | 1071 | dst_mcv = lhs; |
| 997 | 1072 | src_mcv = rhs; |
| 998 | 1073 | } |
| 999 | } else if (reuseOperand(inst, 1, rhs)) { | |
| 1074 | } else if (self.reuseOperand(inst, 1, rhs)) { | |
| 1000 | 1075 | // RHS dies; use it as the destination. |
| 1001 | 1076 | // Both operands cannot be memory. |
| 1002 | 1077 | src_inst = op_lhs; |
| 1003 | 1078 | if (lhs.isMemory() and rhs.isMemory()) { |
| 1004 | dst_mcv = try self.copyToNewRegister(op_rhs); | |
| 1079 | dst_mcv = try self.copyToNewRegister(inst, rhs); | |
| 1005 | 1080 | src_mcv = lhs; |
| 1006 | 1081 | } else { |
| 1007 | 1082 | dst_mcv = rhs; |
| ... | ... | @@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1009 | 1084 | } |
| 1010 | 1085 | } else { |
| 1011 | 1086 | if (lhs.isMemory()) { |
| 1012 | dst_mcv = try self.copyToNewRegister(op_lhs); | |
| 1087 | dst_mcv = try self.copyToNewRegister(inst, lhs); | |
| 1013 | 1088 | src_mcv = rhs; |
| 1014 | 1089 | src_inst = op_rhs; |
| 1015 | 1090 | } else { |
| 1016 | dst_mcv = try self.copyToNewRegister(op_rhs); | |
| 1091 | dst_mcv = try self.copyToNewRegister(inst, rhs); | |
| 1017 | 1092 | src_mcv = lhs; |
| 1018 | 1093 | src_inst = op_lhs; |
| 1019 | 1094 | } |
| ... | ... | @@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1026 | 1101 | switch (src_mcv) { |
| 1027 | 1102 | .immediate => |imm| { |
| 1028 | 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 | 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 | 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 | 1124 | switch (dst_mcv) { |
| 1042 | 1125 | .none => unreachable, |
| 1043 | 1126 | .undef => unreachable, |
| ... | ... | @@ -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 | 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 | 1227 | fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue { |
| 1097 | 1228 | if (FreeRegInt == u0) { |
| 1098 | 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 | 1240 | const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1]; |
| 1110 | 1241 | switch (result) { |
| 1111 | 1242 | .register => |reg| { |
| 1112 | branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base }); | |
| 1243 | branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base }); | |
| 1113 | 1244 | branch.markRegUsed(reg); |
| 1114 | 1245 | |
| 1115 | 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 | 1265 | .riscv64 => { |
| 1135 | 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 | 1279 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), |
| 1138 | 1280 | } |
| 1139 | 1281 | return .none; |
| ... | ... | @@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1219 | 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 | 1427 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), |
| 1223 | 1428 | } |
| 1224 | 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 | 1435 | } else { |
| 1227 | 1436 | unreachable; |
| 1228 | 1437 | } |
| ... | ... | @@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1275 | 1484 | .riscv64 => { |
| 1276 | 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 | 1490 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 1279 | 1491 | } |
| 1280 | 1492 | return .unreach; |
| ... | ... | @@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1304 | 1516 | // Either one, but not both, can be a memory operand. |
| 1305 | 1517 | // Source operand can be an immediate, 8 bits or 32 bits. |
| 1306 | 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 | 1520 | else |
| 1309 | 1521 | lhs; |
| 1310 | 1522 | // This instruction supports only signed 32-bit immediates at most. |
| 1311 | 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 | 1526 | const info = inst.lhs.ty.intInfo(self.target.*); |
| 1315 | 1527 | if (info.signed) { |
| 1316 | 1528 | return MCValue{ .compare_flags_signed = op }; |
| ... | ... | @@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1512 | 1724 | if (!inst.is_volatile and inst.base.isUnused()) |
| 1513 | 1725 | return MCValue.dead; |
| 1514 | 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 | 1770 | .riscv64 => { |
| 1516 | 1771 | for (inst.inputs) |input, i| { |
| 1517 | 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 | 1839 | /// resulting REX is meaningful, but will remain the same if it is not. |
| 1585 | 1840 | /// * Deliberately inserting a "meaningless REX" requires explicit usage of |
| 1586 | 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 | 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 | 1848 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. |
| 1589 | 1849 | var value: u8 = 0x40; |
| 1590 | 1850 | if (arg.b) { |
| ... | ... | @@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1681 | 1941 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); |
| 1682 | 1942 | }, |
| 1683 | 1943 | .register => |reg| { |
| 1684 | const abi_size = ty.abiSize(self.target.*); | |
| 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 | } | |
| 1944 | try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); | |
| 1705 | 1945 | }, |
| 1706 | 1946 | .memory => |vaddr| { |
| 1707 | 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 | 1949 | .stack_offset => |off| { |
| 1710 | 1950 | if (stack_offset == off) |
| 1711 | 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 | 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 | 1960 | |
| 1719 | 1961 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { |
| 1720 | 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 | 2015 | .riscv64 => switch (mcv) { |
| 1722 | 2016 | .dead => unreachable, |
| 1723 | 2017 | .ptr_stack_offset => unreachable, |
| ... | ... | @@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2027 | 2321 | }, |
| 2028 | 2322 | }); |
| 2029 | 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 | 2327 | else => {}, |
| ... | ... | @@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2150 | 2444 | result.stack_byte_count = next_stack_offset; |
| 2151 | 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 | 2450 | else => if (param_types.len != 0) |
| ... | ... | @@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2197 | 2491 | .i386 => @import("codegen/x86.zig"), |
| 2198 | 2492 | .x86_64 => @import("codegen/x86_64.zig"), |
| 2199 | 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 | 2497 | else => struct { |
| 2201 | 2498 | pub const Register = enum { |
| 2202 | 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 | 5 | const trace = @import("tracy.zig").trace; |
| 6 | 6 | const Package = @import("Package.zig"); |
| 7 | 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 | 12 | pub const Options = struct { |
| 10 | 13 | target: std.Target, |
| ... | ... | @@ -20,6 +23,7 @@ pub const Options = struct { |
| 20 | 23 | /// Used for calculating how much space to reserve for executable program code in case |
| 21 | 24 | /// the binary file deos not already have such a section. |
| 22 | 25 | program_code_size_hint: u64 = 256 * 1024, |
| 26 | entry_addr: ?u64 = null, | |
| 23 | 27 | }; |
| 24 | 28 | |
| 25 | 29 | pub const File = struct { |
src-self-hosted/link/Elf.zig+60-27| ... | ... | @@ -14,12 +14,10 @@ const leb128 = std.debug.leb; |
| 14 | 14 | const Package = @import("../Package.zig"); |
| 15 | 15 | const Value = @import("../value.zig").Value; |
| 16 | 16 | const Type = @import("../type.zig").Type; |
| 17 | const build_options = @import("build_options"); | |
| 18 | 17 | const link = @import("../link.zig"); |
| 19 | 18 | const File = link.File; |
| 20 | 19 | const Elf = @This(); |
| 21 | 20 | |
| 22 | const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; | |
| 23 | 21 | const default_entry_addr = 0x8000000; |
| 24 | 22 | |
| 25 | 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 | 247 | .allocator = allocator, |
| 250 | 248 | }, |
| 251 | 249 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 252 | 32 => .p32, | |
| 253 | 64 => .p64, | |
| 250 | 0 ... 32 => .p32, | |
| 251 | 33 ... 64 => .p64, | |
| 254 | 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 | 276 | .file = file, |
| 279 | 277 | }, |
| 280 | 278 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 281 | 32 => .p32, | |
| 282 | 64 => .p64, | |
| 279 | 0 ... 32 => .p32, | |
| 280 | 33 ... 64 => .p64, | |
| 283 | 281 | else => return error.UnsupportedELFArchitecture, |
| 284 | 282 | }, |
| 285 | 283 | .shdr_table_dirty = true, |
| ... | ... | @@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 { |
| 346 | 344 | |
| 347 | 345 | /// Returns end pos of collision, if any. |
| 348 | 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 | 348 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); |
| 351 | 349 | if (start < ehdr_size) |
| 352 | 350 | return ehdr_size; |
| ... | ... | @@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 462 | 460 | const p_align = 0x1000; |
| 463 | 461 | const off = self.findFreeSpace(file_size, p_align); |
| 464 | 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 | 464 | try self.program_headers.append(self.base.allocator, .{ |
| 466 | 465 | .p_type = elf.PT_LOAD, |
| 467 | 466 | .p_offset = off, |
| 468 | 467 | .p_filesz = file_size, |
| 469 | .p_vaddr = default_entry_addr, | |
| 470 | .p_paddr = default_entry_addr, | |
| 468 | .p_vaddr = entry_addr, | |
| 469 | .p_paddr = entry_addr, | |
| 471 | 470 | .p_memsz = file_size, |
| 472 | 471 | .p_align = p_align, |
| 473 | 472 | .p_flags = elf.PF_X | elf.PF_R, |
| ... | ... | @@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 486 | 485 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 487 | 486 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 488 | 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 | 489 | try self.program_headers.append(self.base.allocator, .{ |
| 491 | 490 | .p_type = elf.PT_LOAD, |
| 492 | 491 | .p_offset = off, |
| 493 | 492 | .p_filesz = file_size, |
| 494 | .p_vaddr = default_got_addr, | |
| 495 | .p_paddr = default_got_addr, | |
| 493 | .p_vaddr = got_addr, | |
| 494 | .p_paddr = got_addr, | |
| 496 | 495 | .p_memsz = file_size, |
| 497 | 496 | .p_align = p_align, |
| 498 | 497 | .p_flags = elf.PF_R, |
| ... | ... | @@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 863 | 862 | // Write the form for the compile unit, which must match the abbrev table above. |
| 864 | 863 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); |
| 865 | 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 | 866 | // Currently only one compilation unit is supported, so the address range is simply |
| 868 | 867 | // identical to the main program header virtual address and memory size. |
| 869 | 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 | 1348 | var already_have_free_list_node = false; |
| 1350 | 1349 | { |
| 1351 | 1350 | var i: usize = 0; |
| 1351 | // TODO turn text_block_free_list into a hash map | |
| 1352 | 1352 | while (i < self.text_block_free_list.items.len) { |
| 1353 | 1353 | if (self.text_block_free_list.items[i] == text_block) { |
| 1354 | 1354 | _ = self.text_block_free_list.swapRemove(i); |
| ... | ... | @@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { |
| 1360 | 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 | 1365 | if (self.last_text_block == text_block) { |
| 1365 | 1366 | // TODO shrink the .text section size here |
| 1366 | 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 | 1377 | if (text_block.prev) |prev| { |
| 1370 | 1378 | prev.next = text_block.next; |
| ... | ... | @@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { |
| 1383 | 1391 | } else { |
| 1384 | 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 | 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 | 1606 | next.prev = null; |
| 1585 | 1607 | } |
| 1586 | 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 | 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 | 2173 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| 2152 | 2174 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 2153 | 2175 | const offset = self.program_headers.items[index].p_offset; |
| 2154 | switch (self.base.options.target.cpu.arch.ptrBitWidth()) { | |
| 2155 | 32 => { | |
| 2176 | switch (self.ptr_width) { | |
| 2177 | .p32 => { | |
| 2156 | 2178 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; |
| 2157 | 2179 | if (foreign_endian) { |
| 2158 | 2180 | bswapAllFields(elf.Elf32_Phdr, &phdr[0]); |
| 2159 | 2181 | } |
| 2160 | 2182 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); |
| 2161 | 2183 | }, |
| 2162 | 64 => { | |
| 2184 | .p64 => { | |
| 2163 | 2185 | var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; |
| 2164 | 2186 | if (foreign_endian) { |
| 2165 | 2187 | bswapAllFields(elf.Elf64_Phdr, &phdr[0]); |
| 2166 | 2188 | } |
| 2167 | 2189 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); |
| 2168 | 2190 | }, |
| 2169 | else => return error.UnsupportedArchitecture, | |
| 2170 | 2191 | } |
| 2171 | 2192 | } |
| 2172 | 2193 | |
| 2173 | 2194 | fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2174 | 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()) { | |
| 2176 | 32 => { | |
| 2196 | switch (self.ptr_width) { | |
| 2197 | .p32 => { | |
| 2177 | 2198 | var shdr: [1]elf.Elf32_Shdr = undefined; |
| 2178 | 2199 | shdr[0] = sectHeaderTo32(self.sections.items[index]); |
| 2179 | 2200 | if (foreign_endian) { |
| ... | ... | @@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2182 | 2203 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); |
| 2183 | 2204 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2184 | 2205 | }, |
| 2185 | 64 => { | |
| 2206 | .p64 => { | |
| 2186 | 2207 | var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; |
| 2187 | 2208 | if (foreign_endian) { |
| 2188 | 2209 | bswapAllFields(elf.Elf64_Shdr, &shdr[0]); |
| ... | ... | @@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2190 | 2211 | const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); |
| 2191 | 2212 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2192 | 2213 | }, |
| 2193 | else => return error.UnsupportedArchitecture, | |
| 2194 | 2214 | } |
| 2195 | 2215 | } |
| 2196 | 2216 | |
| 2197 | 2217 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2198 | 2218 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 2199 | 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 | 2221 | if (self.offset_table_count_dirty) { |
| 2202 | 2222 | // TODO Also detect virtual address collisions. |
| 2203 | 2223 | const allocated_size = self.allocatedSize(shdr.sh_offset); |
| ... | ... | @@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2221 | 2241 | } |
| 2222 | 2242 | const endian = self.base.options.target.cpu.arch.endian(); |
| 2223 | 2243 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2224 | switch (self.ptr_width) { | |
| 2225 | .p32 => { | |
| 2244 | switch (entry_size) { | |
| 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 | 2251 | var buf: [4]u8 = undefined; |
| 2227 | 2252 | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); |
| 2228 | 2253 | try self.base.file.?.pwriteAll(&buf, off); |
| 2229 | 2254 | }, |
| 2230 | .p64 => { | |
| 2255 | 8 => { | |
| 2231 | 2256 | var buf: [8]u8 = undefined; |
| 2232 | 2257 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 2233 | 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 | 2370 | } |
| 2345 | 2371 | } |
| 2346 | 2372 | |
| 2373 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. | |
| 2347 | 2374 | fn ptrWidthBytes(self: Elf) u8 { |
| 2348 | 2375 | return switch (self.ptr_width) { |
| 2349 | 2376 | .p32 => 4, |
| ... | ... | @@ -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 | 2387 | /// The reloc offset for the virtual address of a function in its Line Number Program. |
| 2355 | 2388 | /// Size is a virtual address integer. |
| 2356 | 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 | 6 | const fs = std.fs; |
| 7 | 7 | const log = std.log.scoped(.link); |
| 8 | 8 | const macho = std.macho; |
| 9 | const codegen = @import("../codegen.zig"); | |
| 9 | 10 | const math = std.math; |
| 10 | 11 | const mem = std.mem; |
| 12 | const trace = @import("../tracy.zig").trace; | |
| 13 | const Type = @import("../type.zig").Type; | |
| 11 | 14 | |
| 12 | 15 | const Module = @import("../Module.zig"); |
| 13 | 16 | const link = @import("../link.zig"); |
| 14 | 17 | const File = link.File; |
| 15 | 18 | |
| 19 | const is_darwin = std.Target.current.os.tag.isDarwin(); | |
| 20 | ||
| 16 | 21 | pub const base_tag: File.Tag = File.Tag.macho; |
| 17 | 22 | |
| 18 | 23 | base: File, |
| 19 | 24 | |
| 20 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | |
| 21 | /// Same order as in the file. | |
| 22 | segment_cmds: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, | |
| 25 | /// List of all load command headers that are in the file. | |
| 26 | /// We use it to track number and size of all commands needed by the header. | |
| 27 | commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){}, | |
| 28 | command_file_offset: ?u64 = null, | |
| 23 | 29 | |
| 24 | 30 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 25 | 31 | /// Same order as in the file. |
| 32 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, | |
| 26 | 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 | 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 | 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 | 69 | pub const TextBlock = struct { |
| 33 | 70 | pub const empty = TextBlock{}; |
| 34 | 71 | }; |
| ... | ... | @@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO |
| 80 | 117 | /// Truncates the existing file contents and overwrites the contents. |
| 81 | 118 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 82 | 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 | 120 | var self: MachO = .{ |
| 90 | 121 | .base = .{ |
| 91 | 122 | .file = file, |
| ... | ... | @@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach |
| 96 | 127 | }; |
| 97 | 128 | errdefer self.deinit(); |
| 98 | 129 | |
| 99 | if (options.output_mode == .Exe) { | |
| 100 | // The first segment command for executables is always a __PAGEZERO segment. | |
| 101 | try self.segment_cmds.append(allocator, .{ | |
| 102 | .cmd = macho.LC_SEGMENT_64, | |
| 103 | .cmdsize = @sizeOf(macho.segment_command_64), | |
| 104 | .segname = self.makeString("__PAGEZERO"), | |
| 105 | .vmaddr = 0, | |
| 106 | .vmsize = 0, | |
| 107 | .fileoff = 0, | |
| 108 | .filesize = 0, | |
| 109 | .maxprot = 0, | |
| 110 | .initprot = 0, | |
| 111 | .nsects = 0, | |
| 112 | .flags = 0, | |
| 113 | }); | |
| 130 | switch (options.output_mode) { | |
| 131 | .Exe => { | |
| 132 | // The first segment command for executables is always a __PAGEZERO segment. | |
| 133 | const pagezero = .{ | |
| 134 | .cmd = macho.LC_SEGMENT_64, | |
| 135 | .cmdsize = commandSize(@sizeOf(macho.segment_command_64)), | |
| 136 | .segname = makeString("__PAGEZERO"), | |
| 137 | .vmaddr = 0, | |
| 138 | .vmsize = self.vm_start_address, | |
| 139 | .fileoff = 0, | |
| 140 | .filesize = 0, | |
| 141 | .maxprot = 0, | |
| 142 | .initprot = 0, | |
| 143 | .nsects = 0, | |
| 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; | |
| 117 | } | |
| 156 | try self.populateMissingMetadata(); | |
| 118 | 157 | |
| 119 | fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 { | |
| 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; | |
| 158 | return self; | |
| 124 | 159 | } |
| 125 | 160 | |
| 126 | 161 | fn writeMachOHeader(self: *MachO) !void { |
| ... | ... | @@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void { |
| 156 | 191 | }; |
| 157 | 192 | hdr.filetype = filetype; |
| 158 | 193 | |
| 159 | // TODO consider other commands | |
| 160 | const ncmds = try math.cast(u32, self.segment_cmds.items.len); | |
| 194 | const ncmds = try math.cast(u32, self.commands.items.len); | |
| 161 | 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 | 203 | // TODO should these be set to something else? |
| 165 | 204 | hdr.flags = 0; |
| ... | ... | @@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void { |
| 169 | 208 | } |
| 170 | 209 | |
| 171 | 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 | 214 | defer self.base.allocator.free(buf); |
| 176 | 215 | |
| 216 | self.command_file_offset = @sizeOf(macho.mach_header_64); | |
| 217 | ||
| 177 | 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 | 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 | 295 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 185 | 296 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 186 | 297 | self.error_flags.no_entry_point_found = true; |
| ... | ... | @@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 192 | 303 | } |
| 193 | 304 | |
| 194 | 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 | 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 | 326 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 215 | 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 | 583 | |
| 584 | 584 | switch (case.target.getExternalExecutor()) { |
| 585 | 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 | 591 | .qemu => |qemu_bin_name| if (enable_qemu) { |
| 589 | 592 | // TODO Ability for test cases to specify whether to link libc. |
| ... | ... | @@ -635,7 +638,6 @@ pub const TestContext = struct { |
| 635 | 638 | var test_node = update_node.start("test", null); |
| 636 | 639 | test_node.activate(); |
| 637 | 640 | defer test_node.end(); |
| 638 | ||
| 639 | 641 | defer allocator.free(exec_result.stdout); |
| 640 | 642 | defer allocator.free(exec_result.stderr); |
| 641 | 643 | switch (exec_result.term) { |
| ... | ... | @@ -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 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | const Target = std.Target; |
| 6 | const Module = @import("Module.zig"); | |
| 6 | 7 | |
| 7 | 8 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. |
| 8 | 9 | /// It's important for this type to be small. |
| ... | ... | @@ -52,7 +53,7 @@ pub const Type = extern union { |
| 52 | 53 | .bool => return .Bool, |
| 53 | 54 | .void => return .Void, |
| 54 | 55 | .type => return .Type, |
| 55 | .anyerror => return .ErrorSet, | |
| 56 | .error_set, .error_set_single, .anyerror => return .ErrorSet, | |
| 56 | 57 | .comptime_int => return .ComptimeInt, |
| 57 | 58 | .comptime_float => return .ComptimeFloat, |
| 58 | 59 | .noreturn => return .NoReturn, |
| ... | ... | @@ -84,6 +85,10 @@ pub const Type = extern union { |
| 84 | 85 | .optional_single_mut_pointer, |
| 85 | 86 | => return .Optional, |
| 86 | 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 | 156 | .ComptimeInt => return true, |
| 152 | 157 | .Undefined => return true, |
| 153 | 158 | .Null => return true, |
| 159 | .AnyFrame => { | |
| 160 | return a.elemType().eql(b.elemType()); | |
| 161 | }, | |
| 154 | 162 | .Pointer => { |
| 155 | 163 | // Hot path for common case: |
| 156 | 164 | if (a.castPointer()) |a_payload| { |
| ... | ... | @@ -225,7 +233,6 @@ pub const Type = extern union { |
| 225 | 233 | .BoundFn, |
| 226 | 234 | .Opaque, |
| 227 | 235 | .Frame, |
| 228 | .AnyFrame, | |
| 229 | 236 | .Vector, |
| 230 | 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 | 350 | .single_const_pointer_to_comptime_int, |
| 344 | 351 | .const_slice_u8, |
| 345 | 352 | .enum_literal, |
| 353 | .anyerror_void_error_union, | |
| 354 | .@"anyframe", | |
| 346 | 355 | => unreachable, |
| 347 | 356 | |
| 348 | 357 | .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), |
| ... | ... | @@ -397,6 +406,7 @@ pub const Type = extern union { |
| 397 | 406 | .optional_single_mut_pointer, |
| 398 | 407 | .optional_single_const_pointer, |
| 399 | 408 | => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"), |
| 409 | .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"), | |
| 400 | 410 | |
| 401 | 411 | .pointer => { |
| 402 | 412 | const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise); |
| ... | ... | @@ -416,6 +426,19 @@ pub const Type = extern union { |
| 416 | 426 | }; |
| 417 | 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 | 505 | .@"null" => return out_stream.writeAll("@TypeOf(null)"), |
| 483 | 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 | 510 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 486 | 511 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| 487 | 512 | .fn_void_no_args => return out_stream.writeAll("fn() void"), |
| ... | ... | @@ -500,6 +525,12 @@ pub const Type = extern union { |
| 500 | 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 | 534 | .array_u8 => { |
| 504 | 535 | const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise); |
| 505 | 536 | return out_stream.print("[{}]u8", .{payload.len}); |
| ... | ... | @@ -622,6 +653,21 @@ pub const Type = extern union { |
| 622 | 653 | ty = payload.pointee_type; |
| 623 | 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 | 672 | unreachable; |
| 627 | 673 | } |
| ... | ... | @@ -715,6 +761,11 @@ pub const Type = extern union { |
| 715 | 761 | .optional, |
| 716 | 762 | .optional_single_mut_pointer, |
| 717 | 763 | .optional_single_const_pointer, |
| 764 | .@"anyframe", | |
| 765 | .anyframe_T, | |
| 766 | .anyerror_void_error_union, | |
| 767 | .error_set, | |
| 768 | .error_set_single, | |
| 718 | 769 | => true, |
| 719 | 770 | // TODO lazy types |
| 720 | 771 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| ... | ... | @@ -723,6 +774,11 @@ pub const Type = extern union { |
| 723 | 774 | .int_signed => self.cast(Payload.IntSigned).?.bits == 0, |
| 724 | 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 | 782 | .c_void, |
| 727 | 783 | .void, |
| 728 | 784 | .type, |
| ... | ... | @@ -756,6 +812,7 @@ pub const Type = extern union { |
| 756 | 812 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 757 | 813 | .function, // represents machine code; not a pointer |
| 758 | 814 | => return switch (target.cpu.arch) { |
| 815 | .arm => 4, | |
| 759 | 816 | .riscv64 => 2, |
| 760 | 817 | else => 1, |
| 761 | 818 | }, |
| ... | ... | @@ -778,6 +835,8 @@ pub const Type = extern union { |
| 778 | 835 | .mut_slice, |
| 779 | 836 | .optional_single_const_pointer, |
| 780 | 837 | .optional_single_mut_pointer, |
| 838 | .@"anyframe", | |
| 839 | .anyframe_T, | |
| 781 | 840 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 782 | 841 | |
| 783 | 842 | .pointer => { |
| ... | ... | @@ -802,7 +861,11 @@ pub const Type = extern union { |
| 802 | 861 | .f128 => return 16, |
| 803 | 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 | 870 | .array, .array_sentinel => return self.elemType().abiAlignment(target), |
| 808 | 871 | |
| ... | ... | @@ -828,6 +891,16 @@ pub const Type = extern union { |
| 828 | 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 | 904 | .c_void, |
| 832 | 905 | .void, |
| 833 | 906 | .type, |
| ... | ... | @@ -881,12 +954,15 @@ pub const Type = extern union { |
| 881 | 954 | .i32, .u32 => return 4, |
| 882 | 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 | 959 | .const_slice, |
| 887 | 960 | .mut_slice, |
| 888 | .const_slice_u8, | |
| 889 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, | |
| 961 | => { | |
| 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 | 967 | .optional_single_const_pointer, |
| 892 | 968 | .optional_single_mut_pointer, |
| ... | ... | @@ -922,7 +998,11 @@ pub const Type = extern union { |
| 922 | 998 | .f128 => return 16, |
| 923 | 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 | 1007 | .int_signed, .int_unsigned => { |
| 928 | 1008 | const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| |
| ... | ... | @@ -949,6 +1029,18 @@ pub const Type = extern union { |
| 949 | 1029 | // to the child type's ABI alignment. |
| 950 | 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 | 1101 | .c_mut_pointer, |
| 1010 | 1102 | .const_slice, |
| 1011 | 1103 | .mut_slice, |
| 1104 | .error_union, | |
| 1105 | .@"anyframe", | |
| 1106 | .anyframe_T, | |
| 1107 | .anyerror_void_error_union, | |
| 1108 | .error_set, | |
| 1109 | .error_set_single, | |
| 1012 | 1110 | => false, |
| 1013 | 1111 | |
| 1014 | 1112 | .single_const_pointer, |
| ... | ... | @@ -1077,6 +1175,12 @@ pub const Type = extern union { |
| 1077 | 1175 | .optional_single_mut_pointer, |
| 1078 | 1176 | .optional_single_const_pointer, |
| 1079 | 1177 | .enum_literal, |
| 1178 | .error_union, | |
| 1179 | .@"anyframe", | |
| 1180 | .anyframe_T, | |
| 1181 | .anyerror_void_error_union, | |
| 1182 | .error_set, | |
| 1183 | .error_set_single, | |
| 1080 | 1184 | => false, |
| 1081 | 1185 | |
| 1082 | 1186 | .const_slice, |
| ... | ... | @@ -1142,6 +1246,12 @@ pub const Type = extern union { |
| 1142 | 1246 | .optional_single_const_pointer, |
| 1143 | 1247 | .enum_literal, |
| 1144 | 1248 | .mut_slice, |
| 1249 | .error_union, | |
| 1250 | .@"anyframe", | |
| 1251 | .anyframe_T, | |
| 1252 | .anyerror_void_error_union, | |
| 1253 | .error_set, | |
| 1254 | .error_set_single, | |
| 1145 | 1255 | => false, |
| 1146 | 1256 | |
| 1147 | 1257 | .single_const_pointer, |
| ... | ... | @@ -1216,6 +1326,12 @@ pub const Type = extern union { |
| 1216 | 1326 | .optional_single_mut_pointer, |
| 1217 | 1327 | .optional_single_const_pointer, |
| 1218 | 1328 | .enum_literal, |
| 1329 | .error_union, | |
| 1330 | .@"anyframe", | |
| 1331 | .anyframe_T, | |
| 1332 | .anyerror_void_error_union, | |
| 1333 | .error_set, | |
| 1334 | .error_set_single, | |
| 1219 | 1335 | => false, |
| 1220 | 1336 | |
| 1221 | 1337 | .pointer => { |
| ... | ... | @@ -1327,6 +1443,12 @@ pub const Type = extern union { |
| 1327 | 1443 | .optional_single_const_pointer, |
| 1328 | 1444 | .optional_single_mut_pointer, |
| 1329 | 1445 | .enum_literal, |
| 1446 | .error_union, | |
| 1447 | .@"anyframe", | |
| 1448 | .anyframe_T, | |
| 1449 | .anyerror_void_error_union, | |
| 1450 | .error_set, | |
| 1451 | .error_set_single, | |
| 1330 | 1452 | => unreachable, |
| 1331 | 1453 | |
| 1332 | 1454 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | ... | @@ -1448,6 +1570,12 @@ pub const Type = extern union { |
| 1448 | 1570 | .optional_single_mut_pointer, |
| 1449 | 1571 | .optional_single_const_pointer, |
| 1450 | 1572 | .enum_literal, |
| 1573 | .error_union, | |
| 1574 | .@"anyframe", | |
| 1575 | .anyframe_T, | |
| 1576 | .anyerror_void_error_union, | |
| 1577 | .error_set, | |
| 1578 | .error_set_single, | |
| 1451 | 1579 | => unreachable, |
| 1452 | 1580 | |
| 1453 | 1581 | .array => self.cast(Payload.Array).?.len, |
| ... | ... | @@ -1515,6 +1643,12 @@ pub const Type = extern union { |
| 1515 | 1643 | .optional_single_mut_pointer, |
| 1516 | 1644 | .optional_single_const_pointer, |
| 1517 | 1645 | .enum_literal, |
| 1646 | .error_union, | |
| 1647 | .@"anyframe", | |
| 1648 | .anyframe_T, | |
| 1649 | .anyerror_void_error_union, | |
| 1650 | .error_set, | |
| 1651 | .error_set_single, | |
| 1518 | 1652 | => unreachable, |
| 1519 | 1653 | |
| 1520 | 1654 | .array, .array_u8 => return null, |
| ... | ... | @@ -1580,6 +1714,12 @@ pub const Type = extern union { |
| 1580 | 1714 | .optional_single_mut_pointer, |
| 1581 | 1715 | .optional_single_const_pointer, |
| 1582 | 1716 | .enum_literal, |
| 1717 | .error_union, | |
| 1718 | .@"anyframe", | |
| 1719 | .anyframe_T, | |
| 1720 | .anyerror_void_error_union, | |
| 1721 | .error_set, | |
| 1722 | .error_set_single, | |
| 1583 | 1723 | => false, |
| 1584 | 1724 | |
| 1585 | 1725 | .int_signed, |
| ... | ... | @@ -1648,6 +1788,12 @@ pub const Type = extern union { |
| 1648 | 1788 | .optional_single_mut_pointer, |
| 1649 | 1789 | .optional_single_const_pointer, |
| 1650 | 1790 | .enum_literal, |
| 1791 | .error_union, | |
| 1792 | .@"anyframe", | |
| 1793 | .anyframe_T, | |
| 1794 | .anyerror_void_error_union, | |
| 1795 | .error_set, | |
| 1796 | .error_set_single, | |
| 1651 | 1797 | => false, |
| 1652 | 1798 | |
| 1653 | 1799 | .int_unsigned, |
| ... | ... | @@ -1706,6 +1852,12 @@ pub const Type = extern union { |
| 1706 | 1852 | .optional_single_mut_pointer, |
| 1707 | 1853 | .optional_single_const_pointer, |
| 1708 | 1854 | .enum_literal, |
| 1855 | .error_union, | |
| 1856 | .@"anyframe", | |
| 1857 | .anyframe_T, | |
| 1858 | .anyerror_void_error_union, | |
| 1859 | .error_set, | |
| 1860 | .error_set_single, | |
| 1709 | 1861 | => unreachable, |
| 1710 | 1862 | |
| 1711 | 1863 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| ... | ... | @@ -1782,6 +1934,12 @@ pub const Type = extern union { |
| 1782 | 1934 | .optional_single_mut_pointer, |
| 1783 | 1935 | .optional_single_const_pointer, |
| 1784 | 1936 | .enum_literal, |
| 1937 | .error_union, | |
| 1938 | .@"anyframe", | |
| 1939 | .anyframe_T, | |
| 1940 | .anyerror_void_error_union, | |
| 1941 | .error_set, | |
| 1942 | .error_set_single, | |
| 1785 | 1943 | => false, |
| 1786 | 1944 | |
| 1787 | 1945 | .usize, |
| ... | ... | @@ -1887,6 +2045,12 @@ pub const Type = extern union { |
| 1887 | 2045 | .optional_single_mut_pointer, |
| 1888 | 2046 | .optional_single_const_pointer, |
| 1889 | 2047 | .enum_literal, |
| 2048 | .error_union, | |
| 2049 | .@"anyframe", | |
| 2050 | .anyframe_T, | |
| 2051 | .anyerror_void_error_union, | |
| 2052 | .error_set, | |
| 2053 | .error_set_single, | |
| 1890 | 2054 | => unreachable, |
| 1891 | 2055 | }; |
| 1892 | 2056 | } |
| ... | ... | @@ -1958,6 +2122,12 @@ pub const Type = extern union { |
| 1958 | 2122 | .optional_single_mut_pointer, |
| 1959 | 2123 | .optional_single_const_pointer, |
| 1960 | 2124 | .enum_literal, |
| 2125 | .error_union, | |
| 2126 | .@"anyframe", | |
| 2127 | .anyframe_T, | |
| 2128 | .anyerror_void_error_union, | |
| 2129 | .error_set, | |
| 2130 | .error_set_single, | |
| 1961 | 2131 | => unreachable, |
| 1962 | 2132 | } |
| 1963 | 2133 | } |
| ... | ... | @@ -2028,6 +2198,12 @@ pub const Type = extern union { |
| 2028 | 2198 | .optional_single_mut_pointer, |
| 2029 | 2199 | .optional_single_const_pointer, |
| 2030 | 2200 | .enum_literal, |
| 2201 | .error_union, | |
| 2202 | .@"anyframe", | |
| 2203 | .anyframe_T, | |
| 2204 | .anyerror_void_error_union, | |
| 2205 | .error_set, | |
| 2206 | .error_set_single, | |
| 2031 | 2207 | => unreachable, |
| 2032 | 2208 | } |
| 2033 | 2209 | } |
| ... | ... | @@ -2098,6 +2274,12 @@ pub const Type = extern union { |
| 2098 | 2274 | .optional_single_mut_pointer, |
| 2099 | 2275 | .optional_single_const_pointer, |
| 2100 | 2276 | .enum_literal, |
| 2277 | .error_union, | |
| 2278 | .@"anyframe", | |
| 2279 | .anyframe_T, | |
| 2280 | .anyerror_void_error_union, | |
| 2281 | .error_set, | |
| 2282 | .error_set_single, | |
| 2101 | 2283 | => unreachable, |
| 2102 | 2284 | }; |
| 2103 | 2285 | } |
| ... | ... | @@ -2165,6 +2347,12 @@ pub const Type = extern union { |
| 2165 | 2347 | .optional_single_mut_pointer, |
| 2166 | 2348 | .optional_single_const_pointer, |
| 2167 | 2349 | .enum_literal, |
| 2350 | .error_union, | |
| 2351 | .@"anyframe", | |
| 2352 | .anyframe_T, | |
| 2353 | .anyerror_void_error_union, | |
| 2354 | .error_set, | |
| 2355 | .error_set_single, | |
| 2168 | 2356 | => unreachable, |
| 2169 | 2357 | }; |
| 2170 | 2358 | } |
| ... | ... | @@ -2232,6 +2420,12 @@ pub const Type = extern union { |
| 2232 | 2420 | .optional_single_mut_pointer, |
| 2233 | 2421 | .optional_single_const_pointer, |
| 2234 | 2422 | .enum_literal, |
| 2423 | .error_union, | |
| 2424 | .@"anyframe", | |
| 2425 | .anyframe_T, | |
| 2426 | .anyerror_void_error_union, | |
| 2427 | .error_set, | |
| 2428 | .error_set_single, | |
| 2235 | 2429 | => unreachable, |
| 2236 | 2430 | }; |
| 2237 | 2431 | } |
| ... | ... | @@ -2299,6 +2493,12 @@ pub const Type = extern union { |
| 2299 | 2493 | .optional_single_mut_pointer, |
| 2300 | 2494 | .optional_single_const_pointer, |
| 2301 | 2495 | .enum_literal, |
| 2496 | .error_union, | |
| 2497 | .@"anyframe", | |
| 2498 | .anyframe_T, | |
| 2499 | .anyerror_void_error_union, | |
| 2500 | .error_set, | |
| 2501 | .error_set_single, | |
| 2302 | 2502 | => false, |
| 2303 | 2503 | }; |
| 2304 | 2504 | } |
| ... | ... | @@ -2350,6 +2550,12 @@ pub const Type = extern union { |
| 2350 | 2550 | .optional_single_mut_pointer, |
| 2351 | 2551 | .optional_single_const_pointer, |
| 2352 | 2552 | .enum_literal, |
| 2553 | .anyerror_void_error_union, | |
| 2554 | .anyframe_T, | |
| 2555 | .@"anyframe", | |
| 2556 | .error_union, | |
| 2557 | .error_set, | |
| 2558 | .error_set_single, | |
| 2353 | 2559 | => return null, |
| 2354 | 2560 | |
| 2355 | 2561 | .void => return Value.initTag(.void_value), |
| ... | ... | @@ -2453,6 +2659,12 @@ pub const Type = extern union { |
| 2453 | 2659 | .optional_single_mut_pointer, |
| 2454 | 2660 | .optional_single_const_pointer, |
| 2455 | 2661 | .enum_literal, |
| 2662 | .error_union, | |
| 2663 | .@"anyframe", | |
| 2664 | .anyframe_T, | |
| 2665 | .anyerror_void_error_union, | |
| 2666 | .error_set, | |
| 2667 | .error_set_single, | |
| 2456 | 2668 | => return false, |
| 2457 | 2669 | |
| 2458 | 2670 | .c_const_pointer, |
| ... | ... | @@ -2510,6 +2722,8 @@ pub const Type = extern union { |
| 2510 | 2722 | fn_naked_noreturn_no_args, |
| 2511 | 2723 | fn_ccc_void_no_args, |
| 2512 | 2724 | single_const_pointer_to_comptime_int, |
| 2725 | anyerror_void_error_union, | |
| 2726 | @"anyframe", | |
| 2513 | 2727 | const_slice_u8, // See last_no_payload_tag below. |
| 2514 | 2728 | // After this, the tag requires a payload. |
| 2515 | 2729 | |
| ... | ... | @@ -2532,6 +2746,10 @@ pub const Type = extern union { |
| 2532 | 2746 | optional, |
| 2533 | 2747 | optional_single_mut_pointer, |
| 2534 | 2748 | optional_single_const_pointer, |
| 2749 | error_union, | |
| 2750 | anyframe_T, | |
| 2751 | error_set, | |
| 2752 | error_set_single, | |
| 2535 | 2753 | |
| 2536 | 2754 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 2537 | 2755 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -2613,6 +2831,32 @@ pub const Type = extern union { |
| 2613 | 2831 | @"volatile": bool, |
| 2614 | 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 | 61 | single_const_pointer_to_comptime_int_type, |
| 62 | 62 | const_slice_u8_type, |
| 63 | 63 | enum_literal_type, |
| 64 | anyframe_type, | |
| 64 | 65 | |
| 65 | 66 | undef, |
| 66 | 67 | zero, |
| ... | ... | @@ -90,6 +91,8 @@ pub const Value = extern union { |
| 90 | 91 | float_64, |
| 91 | 92 | float_128, |
| 92 | 93 | enum_literal, |
| 94 | error_set, | |
| 95 | @"error", | |
| 93 | 96 | |
| 94 | 97 | pub const last_no_payload_tag = Tag.bool_false; |
| 95 | 98 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -168,6 +171,7 @@ pub const Value = extern union { |
| 168 | 171 | .single_const_pointer_to_comptime_int_type, |
| 169 | 172 | .const_slice_u8_type, |
| 170 | 173 | .enum_literal_type, |
| 174 | .anyframe_type, | |
| 171 | 175 | .undef, |
| 172 | 176 | .zero, |
| 173 | 177 | .void_value, |
| ... | ... | @@ -241,6 +245,10 @@ pub const Value = extern union { |
| 241 | 245 | }; |
| 242 | 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 | 308 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 301 | 309 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 302 | 310 | .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), |
| 311 | .anyframe_type => return out_stream.writeAll("anyframe"), | |
| 303 | 312 | |
| 304 | 313 | .null_value => return out_stream.writeAll("null"), |
| 305 | 314 | .undef => return out_stream.writeAll("undefined"), |
| ... | ... | @@ -343,6 +352,15 @@ pub const Value = extern union { |
| 343 | 352 | .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), |
| 344 | 353 | .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), |
| 345 | 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 | 381 | } |
| 364 | 382 | |
| 365 | 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 | 385 | return switch (self.tag()) { |
| 368 | 386 | .ty => self.cast(Payload.Ty).?.ty, |
| 369 | .int_type => @panic("TODO int type to type"), | |
| 370 | ||
| 371 | 387 | .u8_type => Type.initTag(.u8), |
| 372 | 388 | .i8_type => Type.initTag(.i8), |
| 373 | 389 | .u16_type => Type.initTag(.u16), |
| ... | ... | @@ -408,6 +424,26 @@ pub const Value = extern union { |
| 408 | 424 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 409 | 425 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 410 | 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 | 448 | .undef, |
| 413 | 449 | .zero, |
| ... | ... | @@ -433,6 +469,7 @@ pub const Value = extern union { |
| 433 | 469 | .float_64, |
| 434 | 470 | .float_128, |
| 435 | 471 | .enum_literal, |
| 472 | .@"error", | |
| 436 | 473 | => unreachable, |
| 437 | 474 | }; |
| 438 | 475 | } |
| ... | ... | @@ -482,6 +519,7 @@ pub const Value = extern union { |
| 482 | 519 | .single_const_pointer_to_comptime_int_type, |
| 483 | 520 | .const_slice_u8_type, |
| 484 | 521 | .enum_literal_type, |
| 522 | .anyframe_type, | |
| 485 | 523 | .null_value, |
| 486 | 524 | .function, |
| 487 | 525 | .variable, |
| ... | ... | @@ -498,6 +536,8 @@ pub const Value = extern union { |
| 498 | 536 | .unreachable_value, |
| 499 | 537 | .empty_array, |
| 500 | 538 | .enum_literal, |
| 539 | .error_set, | |
| 540 | .@"error", | |
| 501 | 541 | => unreachable, |
| 502 | 542 | |
| 503 | 543 | .undef => unreachable, |
| ... | ... | @@ -560,6 +600,7 @@ pub const Value = extern union { |
| 560 | 600 | .single_const_pointer_to_comptime_int_type, |
| 561 | 601 | .const_slice_u8_type, |
| 562 | 602 | .enum_literal_type, |
| 603 | .anyframe_type, | |
| 563 | 604 | .null_value, |
| 564 | 605 | .function, |
| 565 | 606 | .variable, |
| ... | ... | @@ -576,6 +617,8 @@ pub const Value = extern union { |
| 576 | 617 | .unreachable_value, |
| 577 | 618 | .empty_array, |
| 578 | 619 | .enum_literal, |
| 620 | .error_set, | |
| 621 | .@"error", | |
| 579 | 622 | => unreachable, |
| 580 | 623 | |
| 581 | 624 | .undef => unreachable, |
| ... | ... | @@ -638,6 +681,7 @@ pub const Value = extern union { |
| 638 | 681 | .single_const_pointer_to_comptime_int_type, |
| 639 | 682 | .const_slice_u8_type, |
| 640 | 683 | .enum_literal_type, |
| 684 | .anyframe_type, | |
| 641 | 685 | .null_value, |
| 642 | 686 | .function, |
| 643 | 687 | .variable, |
| ... | ... | @@ -654,6 +698,8 @@ pub const Value = extern union { |
| 654 | 698 | .unreachable_value, |
| 655 | 699 | .empty_array, |
| 656 | 700 | .enum_literal, |
| 701 | .error_set, | |
| 702 | .@"error", | |
| 657 | 703 | => unreachable, |
| 658 | 704 | |
| 659 | 705 | .undef => unreachable, |
| ... | ... | @@ -742,6 +788,7 @@ pub const Value = extern union { |
| 742 | 788 | .single_const_pointer_to_comptime_int_type, |
| 743 | 789 | .const_slice_u8_type, |
| 744 | 790 | .enum_literal_type, |
| 791 | .anyframe_type, | |
| 745 | 792 | .null_value, |
| 746 | 793 | .function, |
| 747 | 794 | .variable, |
| ... | ... | @@ -759,6 +806,8 @@ pub const Value = extern union { |
| 759 | 806 | .unreachable_value, |
| 760 | 807 | .empty_array, |
| 761 | 808 | .enum_literal, |
| 809 | .error_set, | |
| 810 | .@"error", | |
| 762 | 811 | => unreachable, |
| 763 | 812 | |
| 764 | 813 | .zero, |
| ... | ... | @@ -825,6 +874,7 @@ pub const Value = extern union { |
| 825 | 874 | .single_const_pointer_to_comptime_int_type, |
| 826 | 875 | .const_slice_u8_type, |
| 827 | 876 | .enum_literal_type, |
| 877 | .anyframe_type, | |
| 828 | 878 | .null_value, |
| 829 | 879 | .function, |
| 830 | 880 | .variable, |
| ... | ... | @@ -841,6 +891,8 @@ pub const Value = extern union { |
| 841 | 891 | .unreachable_value, |
| 842 | 892 | .empty_array, |
| 843 | 893 | .enum_literal, |
| 894 | .error_set, | |
| 895 | .@"error", | |
| 844 | 896 | => unreachable, |
| 845 | 897 | |
| 846 | 898 | .zero, |
| ... | ... | @@ -988,6 +1040,7 @@ pub const Value = extern union { |
| 988 | 1040 | .single_const_pointer_to_comptime_int_type, |
| 989 | 1041 | .const_slice_u8_type, |
| 990 | 1042 | .enum_literal_type, |
| 1043 | .anyframe_type, | |
| 991 | 1044 | .bool_true, |
| 992 | 1045 | .bool_false, |
| 993 | 1046 | .null_value, |
| ... | ... | @@ -1007,6 +1060,8 @@ pub const Value = extern union { |
| 1007 | 1060 | .void_value, |
| 1008 | 1061 | .unreachable_value, |
| 1009 | 1062 | .enum_literal, |
| 1063 | .error_set, | |
| 1064 | .@"error", | |
| 1010 | 1065 | => unreachable, |
| 1011 | 1066 | |
| 1012 | 1067 | .zero => false, |
| ... | ... | @@ -1063,6 +1118,7 @@ pub const Value = extern union { |
| 1063 | 1118 | .single_const_pointer_to_comptime_int_type, |
| 1064 | 1119 | .const_slice_u8_type, |
| 1065 | 1120 | .enum_literal_type, |
| 1121 | .anyframe_type, | |
| 1066 | 1122 | .null_value, |
| 1067 | 1123 | .function, |
| 1068 | 1124 | .variable, |
| ... | ... | @@ -1076,6 +1132,8 @@ pub const Value = extern union { |
| 1076 | 1132 | .unreachable_value, |
| 1077 | 1133 | .empty_array, |
| 1078 | 1134 | .enum_literal, |
| 1135 | .error_set, | |
| 1136 | .@"error", | |
| 1079 | 1137 | => unreachable, |
| 1080 | 1138 | |
| 1081 | 1139 | .zero, |
| ... | ... | @@ -1197,6 +1255,7 @@ pub const Value = extern union { |
| 1197 | 1255 | .single_const_pointer_to_comptime_int_type, |
| 1198 | 1256 | .const_slice_u8_type, |
| 1199 | 1257 | .enum_literal_type, |
| 1258 | .anyframe_type, | |
| 1200 | 1259 | .zero, |
| 1201 | 1260 | .bool_true, |
| 1202 | 1261 | .bool_false, |
| ... | ... | @@ -1218,6 +1277,8 @@ pub const Value = extern union { |
| 1218 | 1277 | .unreachable_value, |
| 1219 | 1278 | .empty_array, |
| 1220 | 1279 | .enum_literal, |
| 1280 | .error_set, | |
| 1281 | .@"error", | |
| 1221 | 1282 | => unreachable, |
| 1222 | 1283 | |
| 1223 | 1284 | .ref_val => self.cast(Payload.RefVal).?.val, |
| ... | ... | @@ -1276,6 +1337,7 @@ pub const Value = extern union { |
| 1276 | 1337 | .single_const_pointer_to_comptime_int_type, |
| 1277 | 1338 | .const_slice_u8_type, |
| 1278 | 1339 | .enum_literal_type, |
| 1340 | .anyframe_type, | |
| 1279 | 1341 | .zero, |
| 1280 | 1342 | .bool_true, |
| 1281 | 1343 | .bool_false, |
| ... | ... | @@ -1297,6 +1359,8 @@ pub const Value = extern union { |
| 1297 | 1359 | .void_value, |
| 1298 | 1360 | .unreachable_value, |
| 1299 | 1361 | .enum_literal, |
| 1362 | .error_set, | |
| 1363 | .@"error", | |
| 1300 | 1364 | => unreachable, |
| 1301 | 1365 | |
| 1302 | 1366 | .empty_array => unreachable, // out of bounds array index |
| ... | ... | @@ -1372,6 +1436,7 @@ pub const Value = extern union { |
| 1372 | 1436 | .single_const_pointer_to_comptime_int_type, |
| 1373 | 1437 | .const_slice_u8_type, |
| 1374 | 1438 | .enum_literal_type, |
| 1439 | .anyframe_type, | |
| 1375 | 1440 | .zero, |
| 1376 | 1441 | .empty_array, |
| 1377 | 1442 | .bool_true, |
| ... | ... | @@ -1393,6 +1458,8 @@ pub const Value = extern union { |
| 1393 | 1458 | .float_128, |
| 1394 | 1459 | .void_value, |
| 1395 | 1460 | .enum_literal, |
| 1461 | .error_set, | |
| 1462 | .@"error", | |
| 1396 | 1463 | => false, |
| 1397 | 1464 | |
| 1398 | 1465 | .undef => unreachable, |
| ... | ... | @@ -1522,6 +1589,24 @@ pub const Value = extern union { |
| 1522 | 1589 | base: Payload = .{ .tag = .float_128 }, |
| 1523 | 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 | 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 | 43 | alloc, |
| 44 | 44 | /// Same as `alloc` except the type is inferred. |
| 45 | 45 | alloc_inferred, |
| 46 | /// Create an `anyframe->T`. | |
| 47 | anyframe_type, | |
| 46 | 48 | /// Array concatenation. `a ++ b` |
| 47 | 49 | array_cat, |
| 48 | 50 | /// Array multiplication `a ** b` |
| ... | ... | @@ -70,6 +72,8 @@ pub const Inst = struct { |
| 70 | 72 | /// A typed result location pointer is bitcasted to a new result location pointer. |
| 71 | 73 | /// The new result location pointer has an inferred type. |
| 72 | 74 | bitcast_result_ptr, |
| 75 | /// Bitwise NOT. `~` | |
| 76 | bitnot, | |
| 73 | 77 | /// Bitwise OR. `|` |
| 74 | 78 | bitor, |
| 75 | 79 | /// A labeled block of code, which can return a value. |
| ... | ... | @@ -133,6 +137,10 @@ pub const Inst = struct { |
| 133 | 137 | ensure_result_used, |
| 134 | 138 | /// Emits a compile error if an error is ignored. |
| 135 | 139 | ensure_result_non_error, |
| 140 | /// Create a `E!T` type. | |
| 141 | error_union_type, | |
| 142 | /// Create an error set. | |
| 143 | error_set, | |
| 136 | 144 | /// Export the provided Decl as the provided name in the compilation's output object file. |
| 137 | 145 | @"export", |
| 138 | 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 | 168 | /// A labeled block of code that loops forever. At the end of the body it is implied |
| 161 | 169 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. |
| 162 | 170 | loop, |
| 171 | /// Merge two error sets into one, `E1 || E2`. | |
| 172 | merge_error_sets, | |
| 163 | 173 | /// Ambiguously remainder division or modulus. If the computation would possibly have |
| 164 | 174 | /// a different value depending on whether the operation is remainder division or modulus, |
| 165 | 175 | /// a compile error is emitted. Otherwise the computation is performed. |
| ... | ... | @@ -286,6 +296,8 @@ pub const Inst = struct { |
| 286 | 296 | .unwrap_err_safe, |
| 287 | 297 | .unwrap_err_unsafe, |
| 288 | 298 | .ensure_err_payload_void, |
| 299 | .anyframe_type, | |
| 300 | .bitnot, | |
| 289 | 301 | => UnOp, |
| 290 | 302 | |
| 291 | 303 | .add, |
| ... | ... | @@ -316,6 +328,8 @@ pub const Inst = struct { |
| 316 | 328 | .bitcast, |
| 317 | 329 | .coerce_result_ptr, |
| 318 | 330 | .xor, |
| 331 | .error_union_type, | |
| 332 | .merge_error_sets, | |
| 319 | 333 | => BinOp, |
| 320 | 334 | |
| 321 | 335 | .arg => Arg, |
| ... | ... | @@ -347,6 +361,7 @@ pub const Inst = struct { |
| 347 | 361 | .condbr => CondBr, |
| 348 | 362 | .ptr_type => PtrType, |
| 349 | 363 | .enum_literal => EnumLiteral, |
| 364 | .error_set => ErrorSet, | |
| 350 | 365 | }; |
| 351 | 366 | } |
| 352 | 367 | |
| ... | ... | @@ -438,6 +453,11 @@ pub const Inst = struct { |
| 438 | 453 | .ptr_type, |
| 439 | 454 | .ensure_err_payload_void, |
| 440 | 455 | .enum_literal, |
| 456 | .merge_error_sets, | |
| 457 | .anyframe_type, | |
| 458 | .error_union_type, | |
| 459 | .bitnot, | |
| 460 | .error_set, | |
| 441 | 461 | => false, |
| 442 | 462 | |
| 443 | 463 | .@"break", |
| ... | ... | @@ -908,6 +928,16 @@ pub const Inst = struct { |
| 908 | 928 | }, |
| 909 | 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 | 943 | pub const ErrorMsg = struct { |
| ... | ... | @@ -1142,6 +1172,16 @@ const Writer = struct { |
| 1142 | 1172 | const name = self.loop_table.get(param).?; |
| 1143 | 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 | 1185 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 1146 | 1186 | } |
| 1147 | 1187 | } |
| ... | ... | @@ -1539,6 +1579,21 @@ const Parser = struct { |
| 1539 | 1579 | const name = try self.parseStringLiteral(); |
| 1540 | 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 | 1597 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 1543 | 1598 | } |
| 1544 | 1599 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | ... | @@ -1961,7 +2016,7 @@ const EmitZIR = struct { |
| 1961 | 2016 | return self.emitUnnamedDecl(&as_inst.base); |
| 1962 | 2017 | }, |
| 1963 | 2018 | .Type => { |
| 1964 | const ty = typed_value.val.toType(); | |
| 2019 | const ty = try typed_value.val.toType(&self.arena.allocator); | |
| 1965 | 2020 | return self.emitType(src, ty); |
| 1966 | 2021 | }, |
| 1967 | 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 | 97 | .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), |
| 98 | 98 | .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), |
| 99 | 99 | .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), |
| 100 | .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?), | |
| 100 | 101 | .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), |
| 101 | 102 | .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), |
| 102 | 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 | 123 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), |
| 123 | 124 | .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), |
| 124 | 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 | 150 | for (block_scope.instructions.items) |inst| { |
| 146 | 151 | if (inst.castTag(.ret)) |ret| { |
| 147 | 152 | const val = try mod.resolveConstValue(&block_scope.base, ret.operand); |
| 148 | return val.toType(); | |
| 153 | return val.toType(block_scope.base.arena()); | |
| 149 | 154 | } else { |
| 150 | 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 | 275 | const wanted_type = Type.initTag(.@"type"); |
| 271 | 276 | const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); |
| 272 | 277 | const val = try mod.resolveConstValue(scope, coerced_inst); |
| 273 | return val.toType(); | |
| 278 | return val.toType(scope.arena()); | |
| 274 | 279 | } |
| 275 | 280 | |
| 276 | 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 | 436 | // The bytes references memory inside the ZIR module, which can get deallocated |
| 432 | 437 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. |
| 433 | 438 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 439 | errdefer new_decl_arena.deinit(); | |
| 434 | 440 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); |
| 435 | 441 | |
| 436 | 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 | 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 | 773 | fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { |
| 720 | 774 | const payload = try scope.arena().create(Value.Payload.Bytes); |
| 721 | 775 | payload.* = .{ |
| ... | ... | @@ -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 | 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 | 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 | 1108 | fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 987 | 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 | 1470 | |
| 1349 | 1471 | if (host_size != 0 and bit_offset >= host_size * 8) |
| 1350 | 1472 | return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{}); |
| 1351 | ||
| 1473 | ||
| 1352 | 1474 | const sentinel = if (inst.kw_args.sentinel) |some| |
| 1353 | 1475 | (try resolveInstConst(mod, scope, some)).val |
| 1354 | 1476 | else |
src/analyze.cpp+122-103| ... | ... | @@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2586 | 2586 | return ErrorNone; |
| 2587 | 2587 | |
| 2588 | 2588 | AstNode *decl_node = enum_type->data.enumeration.decl_node; |
| 2589 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 2590 | 2589 | |
| 2591 | 2590 | if (enum_type->data.enumeration.resolve_loop_flag) { |
| 2592 | 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 | 2599 | |
| 2601 | 2600 | enum_type->data.enumeration.resolve_loop_flag = true; |
| 2602 | 2601 | |
| 2603 | assert(!enum_type->data.enumeration.fields); | |
| 2604 | uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; | |
| 2605 | if (field_count == 0) { | |
| 2606 | add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); | |
| 2602 | uint32_t field_count; | |
| 2603 | if (decl_node->type == NodeTypeContainerDecl) { | |
| 2604 | assert(!enum_type->data.enumeration.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.fields = nullptr; | |
| 2610 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | |
| 2611 | return ErrorSemanticAnalyzeFail; | |
| 2609 | enum_type->data.enumeration.src_field_count = field_count; | |
| 2610 | enum_type->data.enumeration.fields = nullptr; | |
| 2611 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | |
| 2612 | return ErrorSemanticAnalyzeFail; | |
| 2613 | } | |
| 2614 | } else { | |
| 2615 | field_count = enum_type->data.enumeration.src_field_count; | |
| 2612 | 2616 | } |
| 2613 | 2617 | |
| 2614 | 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 | 2628 | enum_type->abi_size = tag_int_type->abi_size; |
| 2625 | 2629 | enum_type->abi_align = tag_int_type->abi_align; |
| 2626 | 2630 | |
| 2627 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { | |
| 2628 | ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); | |
| 2631 | ZigType *wanted_tag_int_type = nullptr; | |
| 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 | 2641 | if (type_is_invalid(wanted_tag_int_type)) { |
| 2630 | 2642 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2631 | 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 | 2666 | } |
| 2655 | 2667 | } |
| 2656 | 2668 | |
| 2657 | enum_type->data.enumeration.non_exhaustive = false; | |
| 2658 | 2669 | enum_type->data.enumeration.tag_int_type = tag_int_type; |
| 2659 | 2670 | enum_type->size_in_bits = tag_int_type->size_in_bits; |
| 2660 | 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 | 2674 | BigInt bi_one; |
| 2664 | 2675 | bigint_init_unsigned(&bi_one, 1); |
| 2665 | 2676 | |
| 2666 | AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); | |
| 2667 | if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { | |
| 2677 | if (decl_node->type == NodeTypeContainerDecl) { | |
| 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 | 2695 | field_count -= 1; |
| 2669 | 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 | 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; | |
| 2685 | enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count); | |
| 2686 | enum_type->data.enumeration.fields_by_name.init(field_count); | |
| 2687 | ||
| 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; | |
| 2702 | if (decl_node->type == NodeTypeContainerDecl) { | |
| 2703 | enum_type->data.enumeration.src_field_count = field_count; | |
| 2704 | enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count); | |
| 2705 | enum_type->data.enumeration.fields_by_name.init(field_count); | |
| 2699 | 2706 | |
| 2700 | if (field_node->data.struct_field.type != nullptr) { | |
| 2701 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, | |
| 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 | } | |
| 2707 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; | |
| 2708 | occupied_tag_values.init(field_count); | |
| 2711 | 2709 | |
| 2712 | if (buf_eql_str(type_enum_field->name, "_")) { | |
| 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 | } | |
| 2710 | TypeEnumField *last_enum_field = nullptr; | |
| 2716 | 2711 | |
| 2717 | auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); | |
| 2718 | if (field_entry != nullptr) { | |
| 2719 | ErrorMsg *msg = add_node_error(g, field_node, | |
| 2720 | buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); | |
| 2721 | add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); | |
| 2722 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; | |
| 2723 | continue; | |
| 2724 | } | |
| 2725 | ||
| 2726 | AstNode *tag_value = field_node->data.struct_field.value; | |
| 2712 | for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { | |
| 2713 | AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); | |
| 2714 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; | |
| 2715 | type_enum_field->name = field_node->data.struct_field.name; | |
| 2716 | type_enum_field->decl_index = field_i; | |
| 2717 | type_enum_field->decl_node = field_node; | |
| 2718 | ||
| 2719 | if (field_node->data.struct_field.type != nullptr) { | |
| 2720 | ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, | |
| 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) { | |
| 2729 | // A user-specified value is available | |
| 2730 | ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, | |
| 2731 | nullptr, UndefBad); | |
| 2732 | if (type_is_invalid(result->type)) { | |
| 2736 | auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); | |
| 2737 | if (field_entry != nullptr) { | |
| 2738 | ErrorMsg *msg = add_node_error(g, field_node, | |
| 2739 | buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); | |
| 2740 | add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); | |
| 2733 | 2741 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2734 | 2742 | continue; |
| 2735 | 2743 | } |
| 2736 | 2744 | |
| 2737 | assert(result->special != ConstValSpecialRuntime); | |
| 2738 | assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); | |
| 2745 | AstNode *tag_value = field_node->data.struct_field.value; | |
| 2739 | 2746 | |
| 2740 | bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); | |
| 2741 | } else { | |
| 2742 | // No value was explicitly specified: allocate the last value + 1 | |
| 2743 | // or, if this is the first element, zero | |
| 2744 | if (last_enum_field != nullptr) { | |
| 2745 | bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); | |
| 2747 | if (tag_value != nullptr) { | |
| 2748 | // A user-specified value is available | |
| 2749 | ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, | |
| 2750 | nullptr, UndefBad); | |
| 2751 | if (type_is_invalid(result->type)) { | |
| 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 | 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 | |
| 2751 | if (!bigint_fits_in_bits(&type_enum_field->value, | |
| 2752 | tag_int_type->size_in_bits, | |
| 2753 | tag_int_type->data.integral.is_signed)) { | |
| 2785 | // Make sure the value is unique | |
| 2786 | auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); | |
| 2787 | if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { | |
| 2754 | 2788 | enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; |
| 2755 | 2789 | |
| 2756 | 2790 | Buf *val_buf = buf_alloc(); |
| 2757 | 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, | |
| 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")); | |
| 2799 | last_enum_field = type_enum_field; | |
| 2778 | 2800 | } |
| 2779 | ||
| 2780 | last_enum_field = type_enum_field; | |
| 2801 | occupied_tag_values.deinit(); | |
| 2781 | 2802 | } |
| 2782 | 2803 | |
| 2783 | 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 | 2807 | enum_type->data.enumeration.resolve_loop_flag = false; |
| 2787 | 2808 | enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; |
| 2788 | 2809 | |
| 2789 | occupied_tag_values.deinit(); | |
| 2790 | ||
| 2791 | 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 | 2147 | IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node); |
| 2148 | 2148 | ir_instruction_append(irb->current_basic_block, &const_instruction->base); |
| 2149 | 2149 | const_instruction->value = irb->codegen->intern.for_undefined(); |
| 2150 | const_instruction->value->special = ConstValSpecialUndef; | |
| 2150 | 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 | 14918 | field_val->parent.data.p_struct.struct_val = const_result->value; |
| 14918 | 14919 | field_val->parent.data.p_struct.field_index = dst_field->src_index; |
| 14919 | 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 | 14924 | } else { |
| 14921 | 14925 | is_comptime = false; |
| 14922 | 14926 | } |
| ... | ... | @@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, |
| 15649 | 15653 | wanted_type->data.array.len == field_count) |
| 15650 | 15654 | { |
| 15651 | 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 | 15657 | (!is_array_init || field_count == 0)) |
| 15654 | 15658 | { |
| 15655 | 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 | 20696 | if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) && |
| 20693 | 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, | |
| 20696 | ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error")); | |
| 20699 | if (call_result_loc->id == ResultLocIdReturn) { | |
| 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 | 20707 | return ira->codegen->invalid_inst_gen; |
| 20699 | 20708 | } |
| ... | ... | @@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 22302 | 22311 | |
| 22303 | 22312 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { |
| 22304 | 22313 | if (field->init_val != nullptr) return; |
| 22314 | if (field->decl_node == nullptr) return; | |
| 22305 | 22315 | if (field->decl_node->type != NodeTypeStructField) return; |
| 22306 | 22316 | AstNode *init_node = field->decl_node->data.struct_field.value; |
| 22307 | 22317 | if (init_node == nullptr) return; |
| ... | ... | @@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25495 | 25505 | error_val->special = ConstValSpecialStatic; |
| 25496 | 25506 | error_val->type = type_info_error_type; |
| 25497 | 25507 | |
| 25498 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); | |
| 25499 | inner_fields[1]->special = ConstValSpecialStatic; | |
| 25500 | inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 25508 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1); | |
| 25501 | 25509 | |
| 25502 | 25510 | ZigValue *name = nullptr; |
| 25503 | 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 | 25513 | if (name == nullptr) |
| 25506 | 25514 | name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; |
| 25507 | 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 | 25517 | error_val->data.x_struct.fields = inner_fields; |
| 25511 | 25518 | error_val->parent.id = ConstParentIdArray; |
| ... | ... | @@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26020 | 26027 | assert(payload->special == ConstValSpecialStatic); |
| 26021 | 26028 | assert(payload->type == type_info_pointer_type); |
| 26022 | 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 | 26033 | assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); |
| 26024 | 26034 | BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); |
| 26025 | 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 | 26113 | assert(payload->special == ConstValSpecialStatic); |
| 26104 | 26114 | assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); |
| 26105 | 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 | 26118 | return get_optional_type(ira->codegen, child_type); |
| 26107 | 26119 | } |
| 26108 | 26120 | case ZigTypeIdErrorUnion: { |
| 26109 | 26121 | assert(payload->special == ConstValSpecialStatic); |
| 26110 | 26122 | assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); |
| 26111 | 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 | 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 | 26131 | return get_error_union_type(ira->codegen, err_set_type, payload_type); |
| 26114 | 26132 | } |
| 26115 | 26133 | case ZigTypeIdOpaque: { |
| ... | ... | @@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26123 | 26141 | assert(payload->special == ConstValSpecialStatic); |
| 26124 | 26142 | assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); |
| 26125 | 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 | 26147 | ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); |
| 26127 | Error err; | |
| 26128 | 26148 | if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { |
| 26129 | 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 | 26154 | assert(payload->special == ConstValSpecialStatic); |
| 26135 | 26155 | assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); |
| 26136 | 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 | 26160 | return get_any_frame_type(ira->codegen, child_type); |
| 26138 | 26161 | } |
| 26139 | 26162 | case ZigTypeIdEnumLiteral: |
| ... | ... | @@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26142 | 26165 | assert(payload->special == ConstValSpecialStatic); |
| 26143 | 26166 | assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr)); |
| 26144 | 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 | 26171 | assert(function->type->id == ZigTypeIdFn); |
| 26146 | 26172 | ZigFn *fn = function->data.x_ptr.data.fn.fn_entry; |
| 26147 | 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 | 26202 | assert(error->type == ir_type_info_get_type(ira, "Error", nullptr)); |
| 26177 | 26203 | ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>(); |
| 26178 | 26204 | err_entry->decl_node = source_instr->source_node; |
| 26179 | Error err; | |
| 26180 | 26205 | if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name))) |
| 26181 | 26206 | return ira->codegen->invalid_inst_gen->value->type; |
| 26182 | 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 | 26228 | assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr)); |
| 26204 | 26229 | |
| 26205 | 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 | 26233 | assert(layout_value->special == ConstValSpecialStatic); |
| 26207 | 26234 | assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); |
| 26208 | 26235 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); |
| 26209 | 26236 | |
| 26210 | 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 | 26240 | assert(fields_value->special == ConstValSpecialStatic); |
| 26212 | 26241 | assert(is_slice(fields_value->type)); |
| 26213 | 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 | 26244 | size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); |
| 26216 | 26245 | |
| 26217 | 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 | 26249 | assert(decls_value->special == ConstValSpecialStatic); |
| 26219 | 26250 | assert(is_slice(decls_value->type)); |
| 26220 | 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 | 26256 | } |
| 26226 | 26257 | |
| 26227 | 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 | 26262 | ZigType *entry = new_type_table_entry(ZigTypeIdStruct); |
| 26231 | 26263 | buf_init_from_buf(&entry->name, |
| ... | ... | @@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26253 | 26285 | return ira->codegen->invalid_inst_gen->value->type; |
| 26254 | 26286 | field->decl_node = source_instr->source_node; |
| 26255 | 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 | 26290 | field->type_val = type_value; |
| 26257 | 26291 | field->type_entry = type_value->data.x_type; |
| 26258 | 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 | 26294 | return ira->codegen->invalid_inst_gen->value->type; |
| 26261 | 26295 | } |
| 26262 | 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 | 26299 | if (default_value->type->id == ZigTypeIdNull) { |
| 26264 | 26300 | field->init_val = nullptr; |
| 26265 | 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 | 26313 | |
| 26278 | 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 | 26397 | case ZigTypeIdUnion: |
| 26282 | 26398 | ir_add_error(ira, source_instr, buf_sprintf( |
| 26283 | 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 | 2 | const std = @import("std"); |
| 3 | 3 | |
| 4 | 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 | 40 | cases.addTest("reject extern variables with initializers", |
| 6 | 41 | \\extern var foo: int = 2; |
| 7 | 42 | , &[_][]const u8{ |
| ... | ... | @@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 123 | 158 | \\export fn baz() void { |
| 124 | 159 | \\ try bar(); |
| 125 | 160 | \\} |
| 126 | \\export fn quux() u32 { | |
| 161 | \\export fn qux() u32 { | |
| 127 | 162 | \\ return bar(); |
| 128 | 163 | \\} |
| 164 | \\export fn quux() u32 { | |
| 165 | \\ var buf: u32 = 0; | |
| 166 | \\ buf = bar(); | |
| 167 | \\} | |
| 129 | 168 | , &[_][]const u8{ |
| 130 | 169 | "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'", |
| 131 | 170 | "tmp.zig:1:17: note: function cannot return an error", |
| 132 | 171 | "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'", |
| 133 | 172 | "tmp.zig:7:17: note: function cannot return an error", |
| 134 | 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 | 179 | cases.addTest("int/float conversion to comptime_int/float", |
| ... | ... | @@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 598 | 639 | \\ _ = C; |
| 599 | 640 | \\} |
| 600 | 641 | , &[_][]const u8{ |
| 601 | "tmp.zig:4:5: error: non-exhaustive enum must specify size", | |
| 602 | "error: value assigned to '_' field of non-exhaustive enum", | |
| 642 | "tmp.zig:4:5: error: value assigned to '_' field of non-exhaustive enum", | |
| 643 | "error: non-exhaustive enum must specify size", | |
| 603 | 644 | "error: non-exhaustive enum specifies every value", |
| 604 | 645 | "error: '_' field of non-exhaustive enum must be last", |
| 605 | 646 | }); |
| ... | ... | @@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1400 | 1441 | , &[_][]const u8{ |
| 1401 | 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 | 1444 | cases.add("wrong type for argument tuple to @asyncCall", |
| 1413 | 1445 | \\export fn entry1() void { |
| 1414 | 1446 | \\ var frame: @Frame(foo) = undefined; |
test/stage1/behavior/type.zig+34| ... | ... | @@ -280,3 +280,37 @@ test "Type.Struct" { |
| 280 | 280 | testing.expectEqual(@as(usize, 0), infoC.decls.len); |
| 281 | 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 | 153 | expect(error_set_info == .ErrorSet); |
| 154 | 154 | expect(error_set_info.ErrorSet.?.len == 3); |
| 155 | 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 | 157 | const error_union_info = @typeInfo(TestErrorSet!usize); |
| 159 | 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 | 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 | 26 | const wasi = std.zig.CrossTarget{ |
| 22 | 27 | .cpu_arch = .wasm32, |
| 23 | 28 | .os_tag = .wasi, |
| ... | ... | @@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{ |
| 26 | 31 | pub fn addCases(ctx: *TestContext) !void { |
| 27 | 32 | try @import("zir.zig").addCases(ctx); |
| 28 | 33 | try @import("cbe.zig").addCases(ctx); |
| 34 | try @import("spu-ii.zig").addCases(ctx); | |
| 35 | ||
| 29 | 36 | { |
| 30 | 37 | var case = ctx.exe("hello world with updates", linux_x64); |
| 31 | 38 | |
| ... | ... | @@ -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 | 225 | var case = ctx.exe("adding numbers at comptime", linux_x64); |
| 184 | 226 | case.addCompareOutput( |
| ... | ... | @@ -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 | 697 | // Character literals and multiline strings. |
| 604 | 698 | case.addCompareOutput( |
| 605 | 699 | \\export fn _start() noreturn { |
tools/process_headers.zig+3-2| ... | ... | @@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch; |
| 15 | 15 | const Abi = std.Target.Abi; |
| 16 | 16 | const OsTag = std.Target.Os.Tag; |
| 17 | 17 | const assert = std.debug.assert; |
| 18 | const Sha256 = std.crypto.hash.sha2.Sha256; | |
| 18 | 19 | |
| 19 | 20 | const LibCTarget = struct { |
| 20 | 21 | name: []const u8, |
| ... | ... | @@ -313,7 +314,7 @@ pub fn main() !void { |
| 313 | 314 | var max_bytes_saved: usize = 0; |
| 314 | 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 | 319 | for (libc_targets) |libc_target| { |
| 319 | 320 | const dest_target = DestTarget{ |
| ... | ... | @@ -359,7 +360,7 @@ pub fn main() !void { |
| 359 | 360 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); |
| 360 | 361 | total_bytes += raw_bytes.len; |
| 361 | 362 | const hash = try allocator.alloc(u8, 32); |
| 362 | hasher.reset(); | |
| 363 | hasher = Sha256.init(.{}); | |
| 363 | 364 | hasher.update(rel_path); |
| 364 | 365 | hasher.update(trimmed); |
| 365 | 366 | hasher.final(hash); |
tools/update_glibc.zig+20-20| ... | ... | @@ -148,12 +148,12 @@ pub fn main() !void { |
| 148 | 148 | for (abi_lists) |*abi_list| { |
| 149 | 149 | const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list)); |
| 150 | 150 | if (!target_funcs_gop.found_existing) { |
| 151 | target_funcs_gop.kv.value = FunctionSet{ | |
| 151 | target_funcs_gop.entry.value = FunctionSet{ | |
| 152 | 152 | .list = std.ArrayList(VersionedFn).init(allocator), |
| 153 | 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 | 158 | for (lib_names) |lib_name, lib_name_index| { |
| 159 | 159 | const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; |
| ... | ... | @@ -203,11 +203,11 @@ pub fn main() !void { |
| 203 | 203 | _ = try global_ver_set.put(ver, undefined); |
| 204 | 204 | const gop = try global_fn_set.getOrPut(name); |
| 205 | 205 | if (gop.found_existing) { |
| 206 | if (!std.mem.eql(u8, gop.kv.value.lib, "c")) { | |
| 207 | gop.kv.value.lib = lib_name; | |
| 206 | if (!std.mem.eql(u8, gop.entry.value.lib, "c")) { | |
| 207 | gop.entry.value.lib = lib_name; | |
| 208 | 208 | } |
| 209 | 209 | } else { |
| 210 | gop.kv.value = Function{ | |
| 210 | gop.entry.value = Function{ | |
| 211 | 211 | .name = name, |
| 212 | 212 | .lib = lib_name, |
| 213 | 213 | .index = undefined, |
| ... | ... | @@ -224,14 +224,14 @@ pub fn main() !void { |
| 224 | 224 | const global_fn_list = blk: { |
| 225 | 225 | var list = std.ArrayList([]const u8).init(allocator); |
| 226 | 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 | 228 | std.sort.sort([]const u8, list.span(), {}, strCmpLessThan); |
| 229 | 229 | break :blk list.span(); |
| 230 | 230 | }; |
| 231 | 231 | const global_ver_list = blk: { |
| 232 | 232 | var list = std.ArrayList([]const u8).init(allocator); |
| 233 | 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 | 235 | std.sort.sort([]const u8, list.span(), {}, versionLessThan); |
| 236 | 236 | break :blk list.span(); |
| 237 | 237 | }; |
| ... | ... | @@ -254,9 +254,9 @@ pub fn main() !void { |
| 254 | 254 | var buffered = std.io.bufferedOutStream(fns_txt_file.outStream()); |
| 255 | 255 | const fns_txt = buffered.outStream(); |
| 256 | 256 | for (global_fn_list) |name, i| { |
| 257 | const kv = global_fn_set.get(name).?; | |
| 258 | kv.value.index = i; | |
| 259 | try fns_txt.print("{} {}\n", .{ name, kv.value.lib }); | |
| 257 | const entry = global_fn_set.getEntry(name).?; | |
| 258 | entry.value.index = i; | |
| 259 | try fns_txt.print("{} {}\n", .{ name, entry.value.lib }); | |
| 260 | 260 | } |
| 261 | 261 | try buffered.flush(); |
| 262 | 262 | } |
| ... | ... | @@ -264,16 +264,16 @@ pub fn main() !void { |
| 264 | 264 | // Now the mapping of version and function to integer index is complete. |
| 265 | 265 | // Here we create a mapping of function name to list of versions. |
| 266 | 266 | for (abi_lists) |*abi_list, abi_index| { |
| 267 | const kv = target_functions.get(@ptrToInt(abi_list)).?; | |
| 268 | const fn_vers_list = &kv.value.fn_vers_list; | |
| 269 | for (kv.value.list.span()) |*ver_fn| { | |
| 267 | const entry = target_functions.getEntry(@ptrToInt(abi_list)).?; | |
| 268 | const fn_vers_list = &entry.value.fn_vers_list; | |
| 269 | for (entry.value.list.span()) |*ver_fn| { | |
| 270 | 270 | const gop = try fn_vers_list.getOrPut(ver_fn.name); |
| 271 | 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; | |
| 275 | if (std.mem.indexOfScalar(usize, gop.kv.value.span(), ver_index) == null) { | |
| 276 | try gop.kv.value.append(ver_index); | |
| 274 | const ver_index = global_ver_set.getEntry(ver_fn.ver).?.value; | |
| 275 | if (std.mem.indexOfScalar(usize, gop.entry.value.span(), ver_index) == null) { | |
| 276 | try gop.entry.value.append(ver_index); | |
| 277 | 277 | } |
| 278 | 278 | } |
| 279 | 279 | } |
| ... | ... | @@ -287,7 +287,7 @@ pub fn main() !void { |
| 287 | 287 | |
| 288 | 288 | // first iterate over the abi lists |
| 289 | 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 | 291 | for (abi_list.targets) |target, it_i| { |
| 292 | 292 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 293 | 293 | try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) }); |
| ... | ... | @@ -295,11 +295,11 @@ pub fn main() !void { |
| 295 | 295 | try abilist_txt.writeByte('\n'); |
| 296 | 296 | // next, each line implicitly corresponds to a function |
| 297 | 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 | 299 | try abilist_txt.writeByte('\n'); |
| 300 | 300 | continue; |
| 301 | 301 | }; |
| 302 | for (kv.value.span()) |ver_index, it_i| { | |
| 302 | for (entry.value.span()) |ver_index, it_i| { | |
| 303 | 303 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 304 | 304 | try abilist_txt.print("{d}", .{ver_index}); |
| 305 | 305 | } |