authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-25 14:12:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-25 14:12:48-07:00
loga8f0f37adb3bae8ad3d1f344fdaf1f1051551d21
treec433b384cc2dd72495c71d7820a69cc2ddd649d7
parentf9bd049c89e4d2b4d3f51a937ec2114c3cac9176
parent6fb105fdd7798dc988de09a7b6709c5168355dfa

Merge remote-tracking branch 'origin/master' into llvm11


48 files changed, 4504 insertions(+), 1967 deletions(-)

lib/std/builtin.zig-2
...@@ -289,8 +289,6 @@ pub const TypeInfo = union(enum) {...@@ -289,8 +289,6 @@ pub const TypeInfo = union(enum) {
289 /// therefore must be kept in sync with the compiler implementation.289 /// therefore must be kept in sync with the compiler implementation.
290 pub const Error = struct {290 pub const Error = struct {
291 name: []const u8,291 name: []const u8,
292 /// This field is ignored when using @Type().
293 value: comptime_int,
294 };292 };
295293
296 /// This data structure is used by the Zig language code generation and294 /// This data structure is used by the Zig language code generation and
lib/std/c/darwin.zig+1
...@@ -11,6 +11,7 @@ const macho = std.macho;...@@ -11,6 +11,7 @@ const macho = std.macho;
11usingnamespace @import("../os/bits.zig");11usingnamespace @import("../os/bits.zig");
1212
13extern "c" fn __error() *c_int;13extern "c" fn __error() *c_int;
14pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;
14pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;15pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
15pub extern "c" fn _dyld_image_count() u32;16pub extern "c" fn _dyld_image_count() u32;
16pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;17pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
lib/std/cache_hash.zig+38-32
...@@ -4,7 +4,8 @@...@@ -4,7 +4,8 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std.zig");6const std = @import("std.zig");
7const Blake3 = std.crypto.hash.Blake3;7const crypto = std.crypto;
8const 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
8const fs = std.fs;9const fs = std.fs;
9const base64 = std.base64;10const base64 = std.base64;
10const ArrayList = std.ArrayList;11const ArrayList = std.ArrayList;
...@@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator;...@@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator;
1617
17const base64_encoder = fs.base64_encoder;18const base64_encoder = fs.base64_encoder;
18const base64_decoder = fs.base64_decoder;19const base64_decoder = fs.base64_decoder;
19/// This is 70 more bits than UUIDs. For an analysis of probability of collisions, see:20/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
20/// https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions21const BIN_DIGEST_LEN = 16;
21const BIN_DIGEST_LEN = 24;
22const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);22const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);
2323
24const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;24const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;
...@@ -43,9 +43,13 @@ pub const File = struct {...@@ -43,9 +43,13 @@ pub const File = struct {
43 }43 }
44};44};
4545
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.
46pub const CacheHash = struct {49pub const CacheHash = struct {
47 allocator: *Allocator,50 allocator: *Allocator,
48 blake3: Blake3,51 hasher_init: Hasher, // initial state, that can be copied
52 hasher: Hasher, // current state for incremental hashing
49 manifest_dir: fs.Dir,53 manifest_dir: fs.Dir,
50 manifest_file: ?fs.File,54 manifest_file: ?fs.File,
51 manifest_dirty: bool,55 manifest_dirty: bool,
...@@ -54,9 +58,11 @@ pub const CacheHash = struct {...@@ -54,9 +58,11 @@ pub const CacheHash = struct {
5458
55 /// Be sure to call release after successful initialization.59 /// Be sure to call release after successful initialization.
56 pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash {60 pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash {
61 const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
57 return CacheHash{62 return CacheHash{
58 .allocator = allocator,63 .allocator = allocator,
59 .blake3 = Blake3.init(.{}),64 .hasher_init = hasher_init,
65 .hasher = hasher_init,
60 .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),66 .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),
61 .manifest_file = null,67 .manifest_file = null,
62 .manifest_dirty = false,68 .manifest_dirty = false,
...@@ -69,8 +75,8 @@ pub const CacheHash = struct {...@@ -69,8 +75,8 @@ pub const CacheHash = struct {
69 pub fn addSlice(self: *CacheHash, val: []const u8) void {75 pub fn addSlice(self: *CacheHash, val: []const u8) void {
70 assert(self.manifest_file == null);76 assert(self.manifest_file == null);
7177
72 self.blake3.update(val);78 self.hasher.update(val);
73 self.blake3.update(&[_]u8{0});79 self.hasher.update(&[_]u8{0});
74 }80 }
7581
76 /// Convert the input value into bytes and record it as a dependency of the82 /// Convert the input value into bytes and record it as a dependency of the
...@@ -133,12 +139,12 @@ pub const CacheHash = struct {...@@ -133,12 +139,12 @@ pub const CacheHash = struct {
133 assert(self.manifest_file == null);139 assert(self.manifest_file == null);
134140
135 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;141 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
136 self.blake3.final(&bin_digest);142 self.hasher.final(&bin_digest);
137143
138 base64_encoder.encode(self.b64_digest[0..], &bin_digest);144 base64_encoder.encode(self.b64_digest[0..], &bin_digest);
139145
140 self.blake3 = Blake3.init(.{});146 self.hasher = self.hasher_init;
141 self.blake3.update(&bin_digest);147 self.hasher.update(&bin_digest);
142148
143 const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});149 const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});
144 defer self.allocator.free(manifest_file_path);150 defer self.allocator.free(manifest_file_path);
...@@ -238,7 +244,7 @@ pub const CacheHash = struct {...@@ -238,7 +244,7 @@ pub const CacheHash = struct {
238 }244 }
239245
240 var actual_digest: [BIN_DIGEST_LEN]u8 = undefined;246 var actual_digest: [BIN_DIGEST_LEN]u8 = undefined;
241 try hashFile(this_file, &actual_digest);247 try hashFile(this_file, &actual_digest, self.hasher_init);
242248
243 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {249 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
244 cache_hash_file.bin_digest = actual_digest;250 cache_hash_file.bin_digest = actual_digest;
...@@ -248,7 +254,7 @@ pub const CacheHash = struct {...@@ -248,7 +254,7 @@ pub const CacheHash = struct {
248 }254 }
249255
250 if (!any_file_changed) {256 if (!any_file_changed) {
251 self.blake3.update(&cache_hash_file.bin_digest);257 self.hasher.update(&cache_hash_file.bin_digest);
252 }258 }
253 }259 }
254260
...@@ -256,8 +262,8 @@ pub const CacheHash = struct {...@@ -256,8 +262,8 @@ pub const CacheHash = struct {
256 // cache miss262 // cache miss
257 // keep the manifest file open263 // keep the manifest file open
258 // reset the hash264 // reset the hash
259 self.blake3 = Blake3.init(.{});265 self.hasher = self.hasher_init;
260 self.blake3.update(&bin_digest);266 self.hasher.update(&bin_digest);
261267
262 // Remove files not in the initial hash268 // Remove files not in the initial hash
263 for (self.files.items[input_file_count..]) |*file| {269 for (self.files.items[input_file_count..]) |*file| {
...@@ -266,7 +272,7 @@ pub const CacheHash = struct {...@@ -266,7 +272,7 @@ pub const CacheHash = struct {
266 self.files.shrink(input_file_count);272 self.files.shrink(input_file_count);
267273
268 for (self.files.items) |file| {274 for (self.files.items) |file| {
269 self.blake3.update(&file.bin_digest);275 self.hasher.update(&file.bin_digest);
270 }276 }
271 return null;277 return null;
272 }278 }
...@@ -304,23 +310,23 @@ pub const CacheHash = struct {...@@ -304,23 +310,23 @@ pub const CacheHash = struct {
304310
305 // Hash while reading from disk, to keep the contents in the cpu cache while311 // Hash while reading from disk, to keep the contents in the cpu cache while
306 // doing hashing.312 // doing hashing.
307 var blake3 = Blake3.init(.{});313 var hasher = self.hasher_init;
308 var off: usize = 0;314 var off: usize = 0;
309 while (true) {315 while (true) {
310 // give me everything you've got, captain316 // give me everything you've got, captain
311 const bytes_read = try file.read(contents[off..]);317 const bytes_read = try file.read(contents[off..]);
312 if (bytes_read == 0) break;318 if (bytes_read == 0) break;
313 blake3.update(contents[off..][0..bytes_read]);319 hasher.update(contents[off..][0..bytes_read]);
314 off += bytes_read;320 off += bytes_read;
315 }321 }
316 blake3.final(&ch_file.bin_digest);322 hasher.final(&ch_file.bin_digest);
317323
318 ch_file.contents = contents;324 ch_file.contents = contents;
319 } else {325 } else {
320 try hashFile(file, &ch_file.bin_digest);326 try hashFile(file, &ch_file.bin_digest, self.hasher_init);
321 }327 }
322328
323 self.blake3.update(&ch_file.bin_digest);329 self.hasher.update(&ch_file.bin_digest);
324 }330 }
325331
326 /// Add a file as a dependency of process being cached, after the initial hash has been332 /// Add a file as a dependency of process being cached, after the initial hash has been
...@@ -382,7 +388,7 @@ pub const CacheHash = struct {...@@ -382,7 +388,7 @@ pub const CacheHash = struct {
382 // the artifacts to cache.388 // the artifacts to cache.
383389
384 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;390 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
385 self.blake3.final(&bin_digest);391 self.hasher.final(&bin_digest);
386392
387 var out_digest: [BASE64_DIGEST_LEN]u8 = undefined;393 var out_digest: [BASE64_DIGEST_LEN]u8 = undefined;
388 base64_encoder.encode(&out_digest, &bin_digest);394 base64_encoder.encode(&out_digest, &bin_digest);
...@@ -433,17 +439,17 @@ pub const CacheHash = struct {...@@ -433,17 +439,17 @@ pub const CacheHash = struct {
433 }439 }
434};440};
435441
436fn hashFile(file: fs.File, bin_digest: []u8) !void {442fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void {
437 var blake3 = Blake3.init(.{});
438 var buf: [1024]u8 = undefined;443 var buf: [1024]u8 = undefined;
439444
445 var hasher = hasher_init;
440 while (true) {446 while (true) {
441 const bytes_read = try file.read(&buf);447 const bytes_read = try file.read(&buf);
442 if (bytes_read == 0) break;448 if (bytes_read == 0) break;
443 blake3.update(buf[0..bytes_read]);449 hasher.update(buf[0..bytes_read]);
444 }450 }
445451
446 blake3.final(bin_digest);452 hasher.final(bin_digest);
447}453}
448454
449/// If the wall clock time, rounded to the same precision as the455/// If the wall clock time, rounded to the same precision as the
...@@ -507,7 +513,7 @@ test "cache file and then recall it" {...@@ -507,7 +513,7 @@ test "cache file and then recall it" {
507 _ = try ch.addFile(temp_file, null);513 _ = try ch.addFile(temp_file, null);
508514
509 // There should be nothing in the cache515 // 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());
511517
512 digest1 = ch.final();518 digest1 = ch.final();
513 }519 }
...@@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" {...@@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" {
575 const temp_file_idx = try ch.addFile(temp_file, 100);581 const temp_file_idx = try ch.addFile(temp_file, 100);
576582
577 // There should be nothing in the cache583 // 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());
579585
580 testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));586 testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
581587
...@@ -592,7 +598,7 @@ test "check that changing a file makes cache fail" {...@@ -592,7 +598,7 @@ test "check that changing a file makes cache fail" {
592 const temp_file_idx = try ch.addFile(temp_file, 100);598 const temp_file_idx = try ch.addFile(temp_file, 100);
593599
594 // A file that we depend on has been updated, so the cache should not contain an entry for it600 // 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());
596602
597 // The cache system does not keep the contents of re-hashed input files.603 // The cache system does not keep the contents of re-hashed input files.
598 testing.expect(ch.files.items[temp_file_idx].contents == null);604 testing.expect(ch.files.items[temp_file_idx].contents == null);
...@@ -625,7 +631,7 @@ test "no file inputs" {...@@ -625,7 +631,7 @@ test "no file inputs" {
625 ch.add("1234");631 ch.add("1234");
626632
627 // There should be nothing in the cache633 // 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());
629635
630 digest1 = ch.final();636 digest1 = ch.final();
631 }637 }
...@@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" {...@@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" {
672 _ = try ch.addFile(temp_file1, null);678 _ = try ch.addFile(temp_file1, null);
673679
674 // There should be nothing in the cache680 // 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());
676682
677 _ = try ch.addFilePost(temp_file2);683 _ = try ch.addFilePost(temp_file2);
678684
...@@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" {...@@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" {
705 _ = try ch.addFile(temp_file1, null);711 _ = try ch.addFile(temp_file1, null);
706712
707 // A file that we depend on has been updated, so the cache should not contain an entry for it713 // 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());
709715
710 _ = try ch.addFilePost(temp_file2);716 _ = try ch.addFilePost(temp_file2);
711717
lib/std/crypto.zig+2
...@@ -18,6 +18,7 @@ pub const hash = struct {...@@ -18,6 +18,7 @@ pub const hash = struct {
18/// Authentication (MAC) functions.18/// Authentication (MAC) functions.
19pub const auth = struct {19pub const auth = struct {
20 pub const hmac = @import("crypto/hmac.zig");20 pub const hmac = @import("crypto/hmac.zig");
21 pub const siphash = @import("crypto/siphash.zig");
21};22};
2223
23/// Authenticated Encryption with Associated Data24/// Authenticated Encryption with Associated Data
...@@ -80,6 +81,7 @@ test "crypto" {...@@ -80,6 +81,7 @@ test "crypto" {
80 _ = @import("crypto/sha1.zig");81 _ = @import("crypto/sha1.zig");
81 _ = @import("crypto/sha2.zig");82 _ = @import("crypto/sha2.zig");
82 _ = @import("crypto/sha3.zig");83 _ = @import("crypto/sha3.zig");
84 _ = @import("crypto/siphash.zig");
83 _ = @import("crypto/25519/curve25519.zig");85 _ = @import("crypto/25519/curve25519.zig");
84 _ = @import("crypto/25519/ed25519.zig");86 _ = @import("crypto/25519/ed25519.zig");
85 _ = @import("crypto/25519/edwards25519.zig");87 _ = @import("crypto/25519/edwards25519.zig");
lib/std/crypto/benchmark.zig+4
...@@ -60,6 +60,10 @@ const macs = [_]Crypto{...@@ -60,6 +60,10 @@ const macs = [_]Crypto{
60 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },60 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
61 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" },61 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" },
62 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" },62 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" },
63 Crypto{ .ty = crypto.auth.siphash.SipHash64(2, 4), .name = "siphash-2-4" },
64 Crypto{ .ty = crypto.auth.siphash.SipHash64(1, 3), .name = "siphash-1-3" },
65 Crypto{ .ty = crypto.auth.siphash.SipHash128(2, 4), .name = "siphash128-2-4" },
66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
63};67};
6468
65pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {69pub 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/
14const std = @import("../std.zig");
15const assert = std.debug.assert;
16const testing = std.testing;
17const math = std.math;
18const 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.
30pub 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.
44pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
45 return SipHash(u128, c_rounds, d_rounds);
46}
47
48fn 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
177fn 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
247const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
248
249test "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
329test "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
409test "iterative non-divisible update" {
410 var buf: [1024]u8 = undefined;
411 for (buf) |*e, i| {
412 e.* = @truncate(u8, i);
413 }
414
415 const key = "0x128dad08f12307";
416 const Siphash = SipHash64(2, 4);
417
418 var end: usize = 9;
419 while (end < buf.len) : (end += 9) {
420 const non_iterative_hash = Siphash.toInt(buf[0..end], key[0..]);
421
422 var siphash = Siphash.init(key);
423 var i: usize = 0;
424 while (i < end) : (i += 7) {
425 siphash.update(buf[i..std.math.min(i + 7, end)]);
426 }
427 const iterative_hash = siphash.finalInt();
428
429 std.testing.expectEqual(iterative_hash, non_iterative_hash);
430 }
431}
lib/std/elf.zig+3
...@@ -976,6 +976,9 @@ pub const EM = extern enum(u16) {...@@ -976,6 +976,9 @@ pub const EM = extern enum(u16) {
976 /// MIPS RS3000 Little-endian976 /// MIPS RS3000 Little-endian
977 _MIPS_RS3_LE = 10,977 _MIPS_RS3_LE = 10,
978978
979 /// SPU Mark II
980 _SPU_2 = 13,
981
979 /// Hewlett-Packard PA-RISC982 /// Hewlett-Packard PA-RISC
980 _PARISC = 15,983 _PARISC = 15,
981984
lib/std/fs.zig+19-12
...@@ -686,21 +686,28 @@ pub const Dir = struct {...@@ -686,21 +686,28 @@ pub const Dir = struct {
686 return self.openFileW(path_w.span(), flags);686 return self.openFileW(path_w.span(), flags);
687 }687 }
688688
689 var os_flags: u32 = os.O_CLOEXEC;
689 // Use the O_ locking flags if the os supports them690 // Use the O_ locking flags if the os supports them
690 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)691 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
691 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;692 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
692 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking)693 if (has_flock_open_flags) {
693 os.O_NONBLOCK | os.O_SYNC694 const nonblocking_lock_flag = if (flags.lock_nonblocking)
694 else695 os.O_NONBLOCK | os.O_SYNC
695 @as(u32, 0);696 else
696 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {697 @as(u32, 0);
697 .None => @as(u32, 0),698 os_flags |= switch (flags.lock) {
698 .Shared => os.O_SHLOCK | nonblocking_lock_flag,699 .None => @as(u32, 0),
699 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,700 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
700 } else 0;701 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
701702 };
702 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;703 }
703 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)704 if (@hasDecl(os, "O_LARGEFILE")) {
705 os_flags |= os.O_LARGEFILE;
706 }
707 if (!flags.allow_ctty) {
708 os_flags |= os.O_NOCTTY;
709 }
710 os_flags |= if (flags.write and flags.read)
704 @as(u32, os.O_RDWR)711 @as(u32, os.O_RDWR)
705 else if (flags.write)712 else if (flags.write)
706 @as(u32, os.O_WRONLY)713 @as(u32, os.O_WRONLY)
lib/std/fs/file.zig+4
...@@ -101,6 +101,10 @@ pub const File = struct {...@@ -101,6 +101,10 @@ pub const File = struct {
101 /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions101 /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions
102 /// related to opening the file, reading, writing, and locking.102 /// related to opening the file, reading, writing, and locking.
103 intended_io_mode: io.ModeOverride = io.default_mode,103 intended_io_mode: io.ModeOverride = io.default_mode,
104
105 /// Set this to allow the opened file to automatically become the
106 /// controlling TTY for the current process.
107 allow_ctty: bool = false,
104 };108 };
105109
106 /// TODO https://github.com/ziglang/zig/issues/3802110 /// TODO https://github.com/ziglang/zig/issues/3802
lib/std/hash.zig+1-2
...@@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32;...@@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32;
20pub const Fnv1a_64 = fnv.Fnv1a_64;20pub const Fnv1a_64 = fnv.Fnv1a_64;
21pub const Fnv1a_128 = fnv.Fnv1a_128;21pub const Fnv1a_128 = fnv.Fnv1a_128;
2222
23const siphash = @import("hash/siphash.zig");23const siphash = @import("crypto/siphash.zig");
24pub const SipHash64 = siphash.SipHash64;24pub const SipHash64 = siphash.SipHash64;
25pub const SipHash128 = siphash.SipHash128;25pub const SipHash128 = siphash.SipHash128;
2626
...@@ -42,7 +42,6 @@ test "hash" {...@@ -42,7 +42,6 @@ test "hash" {
42 _ = @import("hash/auto_hash.zig");42 _ = @import("hash/auto_hash.zig");
43 _ = @import("hash/crc.zig");43 _ = @import("hash/crc.zig");
44 _ = @import("hash/fnv.zig");44 _ = @import("hash/fnv.zig");
45 _ = @import("hash/siphash.zig");
46 _ = @import("hash/murmur.zig");45 _ = @import("hash/murmur.zig");
47 _ = @import("hash/cityhash.zig");46 _ = @import("hash/cityhash.zig");
48 _ = @import("hash/wyhash.zig");47 _ = @import("hash/wyhash.zig");
lib/std/hash/benchmark.zig-12
...@@ -25,24 +25,12 @@ const Hash = struct {...@@ -25,24 +25,12 @@ const Hash = struct {
25 init_u64: ?u64 = null,25 init_u64: ?u64 = null,
26};26};
2727
28const siphash_key = "0123456789abcdef";
29
30const hashes = [_]Hash{28const hashes = [_]Hash{
31 Hash{29 Hash{
32 .ty = hash.Wyhash,30 .ty = hash.Wyhash,
33 .name = "wyhash",31 .name = "wyhash",
34 .init_u64 = 0,32 .init_u64 = 0,
35 },33 },
36 Hash{
37 .ty = hash.SipHash64(1, 3),
38 .name = "siphash(1,3)",
39 .init_u8s = siphash_key,
40 },
41 Hash{
42 .ty = hash.SipHash64(2, 4),
43 .name = "siphash(2,4)",
44 .init_u8s = siphash_key,
45 },
46 Hash{34 Hash{
47 .ty = hash.Fnv1a_64,35 .ty = hash.Fnv1a_64,
48 .name = "fnv1a",36 .name = "fnv1a",
lib/std/hash/siphash.zig deleted-393
...@@ -1,393 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6// Siphash
7//
8// SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance
9// against hash flooding DoS attacks.
10//
11// https://131002.net/siphash/
12
13const std = @import("../std.zig");
14const assert = std.debug.assert;
15const testing = std.testing;
16const math = std.math;
17const mem = std.mem;
18
19const Endian = std.builtin.Endian;
20
21pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
22 return SipHash(u64, c_rounds, d_rounds);
23}
24
25pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
26 return SipHash(u128, c_rounds, d_rounds);
27}
28
29fn 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
159pub 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
211const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
212
213test "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
292test "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
371test "iterative non-divisible update" {
372 var buf: [1024]u8 = undefined;
373 for (buf) |*e, i| {
374 e.* = @truncate(u8, i);
375 }
376
377 const key = "0x128dad08f12307";
378 const Siphash = SipHash64(2, 4);
379
380 var end: usize = 9;
381 while (end < buf.len) : (end += 9) {
382 const non_iterative_hash = Siphash.hash(key, buf[0..end]);
383
384 var wy = Siphash.init(key);
385 var i: usize = 0;
386 while (i < end) : (i += 7) {
387 wy.update(buf[i..std.math.min(i + 7, end)]);
388 }
389 const iterative_hash = wy.final();
390
391 std.testing.expectEqual(iterative_hash, non_iterative_hash);
392 }
393}
lib/std/heap/general_purpose_allocator.zig+4-2
...@@ -433,8 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -433,8 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
433 const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size];433 const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size];
434 self.backing_allocator.free(bucket_slice);434 self.backing_allocator.free(bucket_slice);
435 } else {435 } else {
436 // TODO Set the slot data to undefined.436 @memset(bucket.page + slot_index * size_class, undefined, size_class);
437 // Related: https://github.com/ziglang/zig/issues/4298
438 }437 }
439 }438 }
440439
...@@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
567 const new_aligned_size = math.max(new_size, old_align);566 const new_aligned_size = math.max(new_size, old_align);
568 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);567 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
569 if (new_size_class <= size_class) {568 if (new_size_class <= size_class) {
569 if (old_mem.len > new_size) {
570 @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);
571 }
570 return new_size;572 return new_size;
571 }573 }
572 return error.OutOfMemory;574 return error.OutOfMemory;
lib/std/linked_list.zig-12
...@@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type {...@@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type {
2828
29 pub const Data = T;29 pub const Data = T;
3030
31 pub fn init(data: T) Node {
32 return Node{
33 .data = data,
34 };
35 }
36
37 /// Insert a new node after the current one.31 /// Insert a new node after the current one.
38 ///32 ///
39 /// Arguments:33 /// Arguments:
...@@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type {...@@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type {
175 prev: ?*Node = null,169 prev: ?*Node = null,
176 next: ?*Node = null,170 next: ?*Node = null,
177 data: T,171 data: T,
178
179 pub fn init(data: T) Node {
180 return Node{
181 .data = data,
182 };
183 }
184 };172 };
185173
186 first: ?*Node = null,174 first: ?*Node = null,
lib/std/macho.zig+78-1
...@@ -40,6 +40,24 @@ pub const uuid_command = extern struct {...@@ -40,6 +40,24 @@ pub const uuid_command = extern struct {
40 uuid: [16]u8,40 uuid: [16]u8,
41};41};
4242
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.
47pub const entry_point_command = struct {
48 /// LC_MAIN only used in MH_EXECUTE filetypes
49 cmd: u32,
50
51 /// sizeof(struct entry_point_command)
52 cmdsize: u32,
53
54 /// file (__TEXT) offset of main()
55 entryoff: u64,
56
57 /// if not zero, initial stack size
58 stacksize: u64,
59};
60
43/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD61/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
44/// "stab" style symbol table information as described in the header files62/// "stab" style symbol table information as described in the header files
45/// <nlist.h> and <stab.h>.63/// <nlist.h> and <stab.h>.
...@@ -65,7 +83,7 @@ pub const symtab_command = extern struct {...@@ -65,7 +83,7 @@ pub const symtab_command = extern struct {
6583
66/// The linkedit_data_command contains the offsets and sizes of a blob84/// The linkedit_data_command contains the offsets and sizes of a blob
67/// of data in the __LINKEDIT segment.85/// of data in the __LINKEDIT segment.
68const linkedit_data_command = extern struct {86pub const linkedit_data_command = extern struct {
69 /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.87 /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
70 cmd: u32,88 cmd: u32,
7189
...@@ -79,6 +97,65 @@ const linkedit_data_command = extern struct {...@@ -79,6 +97,65 @@ const linkedit_data_command = extern struct {
79 datasize: u32,97 datasize: u32,
80};98};
8199
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.
106pub 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.
127pub 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.
145pub const dylib = extern struct {
146 /// library's pathname (offset pointing at the end of dylib_command)
147 name: u32,
148
149 /// library's build timestamp
150 timestamp: u32,
151
152 /// library's current version number
153 current_version: u32,
154
155 /// library's compatibility version number
156 compatibility_version: u32,
157};
158
82/// The segment load command indicates that a part of this file is to be159/// The segment load command indicates that a part of this file is to be
83/// mapped into the task's address space. The size of this segment in memory,160/// mapped into the task's address space. The size of this segment in memory,
84/// vmsize, maybe equal to or larger than the amount to map from this file,161/// vmsize, maybe equal to or larger than the amount to map from this file,
lib/std/os/bits/linux.zig-1
...@@ -24,7 +24,6 @@ pub usingnamespace switch (builtin.arch) {...@@ -24,7 +24,6 @@ pub usingnamespace switch (builtin.arch) {
24};24};
2525
26pub usingnamespace @import("linux/netlink.zig");26pub usingnamespace @import("linux/netlink.zig");
27pub const BPF = @import("linux/bpf.zig");
2827
29const is_mips = builtin.arch.isMIPS();28const is_mips = builtin.arch.isMIPS();
3029
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.
6usingnamespace std.os;
7const std = @import("../../../std.zig");
8const expectEqual = std.testing.expectEqual;
9const fd_t = std.os.fd_t;
10const pid_t = std.os.pid_t;
11
12// instruction classes
13pub const LD = 0x00;
14pub const LDX = 0x01;
15pub const ST = 0x02;
16pub const STX = 0x03;
17pub const ALU = 0x04;
18pub const JMP = 0x05;
19pub const RET = 0x06;
20pub const MISC = 0x07;
21
22/// 32-bit
23pub const W = 0x00;
24/// 16-bit
25pub const H = 0x08;
26/// 8-bit
27pub const B = 0x10;
28/// 64-bit
29pub const DW = 0x18;
30
31pub const IMM = 0x00;
32pub const ABS = 0x20;
33pub const IND = 0x40;
34pub const MEM = 0x60;
35pub const LEN = 0x80;
36pub const MSH = 0xa0;
37
38// alu fields
39pub const ADD = 0x00;
40pub const SUB = 0x10;
41pub const MUL = 0x20;
42pub const DIV = 0x30;
43pub const OR = 0x40;
44pub const AND = 0x50;
45pub const LSH = 0x60;
46pub const RSH = 0x70;
47pub const NEG = 0x80;
48pub const MOD = 0x90;
49pub const XOR = 0xa0;
50
51// jmp fields
52pub const JA = 0x00;
53pub const JEQ = 0x10;
54pub const JGT = 0x20;
55pub const JGE = 0x30;
56pub const JSET = 0x40;
57
58//#define BPF_SRC(code) ((code) & 0x08)
59pub const K = 0x00;
60pub const X = 0x08;
61
62pub const MAXINSNS = 4096;
63
64// instruction classes
65/// jmp mode in word width
66pub const JMP32 = 0x06;
67/// alu mode in double word width
68pub const ALU64 = 0x07;
69
70// ld/ldx fields
71/// exclusive add
72pub const XADD = 0xc0;
73
74// alu/jmp fields
75/// mov reg to reg
76pub const MOV = 0xb0;
77/// sign extending arithmetic shift right */
78pub const ARSH = 0xc0;
79
80// change endianness of a register
81/// flags for endianness conversion:
82pub const END = 0xd0;
83/// convert to little-endian */
84pub const TO_LE = 0x00;
85/// convert to big-endian
86pub const TO_BE = 0x08;
87pub const FROM_LE = TO_LE;
88pub const FROM_BE = TO_BE;
89
90// jmp encodings
91/// jump != *
92pub const JNE = 0x50;
93/// LT is unsigned, '<'
94pub const JLT = 0xa0;
95/// LE is unsigned, '<=' *
96pub const JLE = 0xb0;
97/// SGT is signed '>', GT in x86
98pub const JSGT = 0x60;
99/// SGE is signed '>=', GE in x86
100pub const JSGE = 0x70;
101/// SLT is signed, '<'
102pub const JSLT = 0xc0;
103/// SLE is signed, '<='
104pub const JSLE = 0xd0;
105/// function call
106pub const CALL = 0x80;
107/// function return
108pub 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.
112pub 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.
115pub const F_ALLOW_MULTI = 0x2;
116/// Flag for prog_attach command.
117pub 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.
122pub 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.
133pub 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.
150pub 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
160pub const PSEUDO_MAP_FD = 1;
161pub 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
165pub const PSEUDO_CALL = 1;
166
167/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
168pub const ANY = 0;
169/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
170pub const NOEXIST = 1;
171/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
172pub const EXIST = 2;
173/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
174pub const F_LOCK = 4;
175
176/// flag for BPF_MAP_CREATE command */
177pub 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.
182pub const BPF_F_NO_COMMON_LRU = 0x2;
183/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
184pub const BPF_F_NUMA_NODE = 0x4;
185/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
186/// syscall side
187pub const BPF_F_RDONLY = 0x8;
188/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
189/// syscall side
190pub const BPF_F_WRONLY = 0x10;
191/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
192/// instead of pointer
193pub 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.
196pub const BPF_F_ZERO_SEED = 0x40;
197/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
198/// side.
199pub const BPF_F_RDONLY_PROG = 0x80;
200/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
201/// side.
202pub const BPF_F_WRONLY_PROG = 0x100;
203/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
204/// socket
205pub const BPF_F_CLONE = 0x200;
206/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
207pub const BPF_F_MMAPABLE = 0x400;
208
209/// These values correspond to "syscalls" within the BPF program's environment
210pub 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
334pub 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
513fn expect_insn(insn: Insn, val: u64) void {
514 expectEqual(@bitCast(u64, insn), val);
515}
516
517test "insn bitsize" {
518 expectEqual(@bitSizeOf(Insn), 64);
519}
520
521// mov instructions
522test "mov imm" {
523 expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7);
524}
525
526test "mov reg" {
527 expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf);
528}
529
530// alu instructions
531test "add imm" {
532 expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207);
533}
534
535// ld instructions
536test "ld_abs" {
537 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);
538}
539
540test "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
546test "stx_mem" {
547 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);
548}
549
550test "xadd" {
551 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);
552}
553
554// jmp instructions
555test "jeq imm" {
556 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);
557}
558
559// other instructions
560test "call" {
561 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);
562}
563
564test "exit" {
565 expect_insn(Insn.exit(), 0x0000000000000095);
566}
567
568pub 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
607pub 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
639pub 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
673pub 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
715const obj_name_len = 16;
716/// struct used by Cmd.map_create command
717pub 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
746pub 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
757pub 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
774pub 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
814pub 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
821pub 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
834pub 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
854pub 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
867pub 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
874pub 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
885pub const RawTracepointAttr = extern struct {
886 name: u64,
887 prog_fd: fd_t,
888};
889
890/// struct used by Cmd.btf_load command
891pub 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
899pub 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
924pub 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
935pub 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
947pub const EnableStatsAttr = extern struct {
948 type: u32,
949};
950
951/// struct used by Cmd.iter_create command
952pub const IterCreateAttr = extern struct {
953 link_fd: fd_t,
954 flags: u32,
955};
956
957pub const Attr = extern union {
958 map_create: MapCreateAttr,
959 map_elem: MapElemAttr,
960 map_batch: MapBatchAttr,
961 prog_load: ProgLoadAttr,
962 obj: ObjAttr,
963 prog_attach: ProgAttachAttr,
964 test_run: TestRunAttr,
965 get_id: GetIdAttr,
966 info: InfoAttr,
967 query: QueryAttr,
968 raw_tracepoint: RawTracepointAttr,
969 btf_load: BtfLoadAttr,
970 task_fd_query: TaskFdQueryAttr,
971 link_create: LinkCreateAttr,
972 link_update: LinkUpdateAttr,
973 enable_stats: EnableStatsAttr,
974 iter_create: IterCreateAttr,
975};
lib/std/os/linux.zig+1
...@@ -29,6 +29,7 @@ pub usingnamespace switch (builtin.arch) {...@@ -29,6 +29,7 @@ pub usingnamespace switch (builtin.arch) {
29};29};
30pub usingnamespace @import("bits.zig");30pub usingnamespace @import("bits.zig");
31pub const tls = @import("linux/tls.zig");31pub const tls = @import("linux/tls.zig");
32pub const BPF = @import("linux/bpf.zig");
3233
33/// Set by startup code, used by `getauxval`.34/// Set by startup code, used by `getauxval`.
34pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;35pub 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.
6usingnamespace std.os;
7const std = @import("../../std.zig");
8const expectEqual = std.testing.expectEqual;
9
10// instruction classes
11pub const LD = 0x00;
12pub const LDX = 0x01;
13pub const ST = 0x02;
14pub const STX = 0x03;
15pub const ALU = 0x04;
16pub const JMP = 0x05;
17pub const RET = 0x06;
18pub const MISC = 0x07;
19
20/// 32-bit
21pub const W = 0x00;
22/// 16-bit
23pub const H = 0x08;
24/// 8-bit
25pub const B = 0x10;
26/// 64-bit
27pub const DW = 0x18;
28
29pub const IMM = 0x00;
30pub const ABS = 0x20;
31pub const IND = 0x40;
32pub const MEM = 0x60;
33pub const LEN = 0x80;
34pub const MSH = 0xa0;
35
36// alu fields
37pub const ADD = 0x00;
38pub const SUB = 0x10;
39pub const MUL = 0x20;
40pub const DIV = 0x30;
41pub const OR = 0x40;
42pub const AND = 0x50;
43pub const LSH = 0x60;
44pub const RSH = 0x70;
45pub const NEG = 0x80;
46pub const MOD = 0x90;
47pub const XOR = 0xa0;
48
49// jmp fields
50pub const JA = 0x00;
51pub const JEQ = 0x10;
52pub const JGT = 0x20;
53pub const JGE = 0x30;
54pub const JSET = 0x40;
55
56//#define BPF_SRC(code) ((code) & 0x08)
57pub const K = 0x00;
58pub const X = 0x08;
59
60pub const MAXINSNS = 4096;
61
62// instruction classes
63/// jmp mode in word width
64pub const JMP32 = 0x06;
65/// alu mode in double word width
66pub const ALU64 = 0x07;
67
68// ld/ldx fields
69/// exclusive add
70pub const XADD = 0xc0;
71
72// alu/jmp fields
73/// mov reg to reg
74pub const MOV = 0xb0;
75/// sign extending arithmetic shift right */
76pub const ARSH = 0xc0;
77
78// change endianness of a register
79/// flags for endianness conversion:
80pub const END = 0xd0;
81/// convert to little-endian */
82pub const TO_LE = 0x00;
83/// convert to big-endian
84pub const TO_BE = 0x08;
85pub const FROM_LE = TO_LE;
86pub const FROM_BE = TO_BE;
87
88// jmp encodings
89/// jump != *
90pub const JNE = 0x50;
91/// LT is unsigned, '<'
92pub const JLT = 0xa0;
93/// LE is unsigned, '<=' *
94pub const JLE = 0xb0;
95/// SGT is signed '>', GT in x86
96pub const JSGT = 0x60;
97/// SGE is signed '>=', GE in x86
98pub const JSGE = 0x70;
99/// SLT is signed, '<'
100pub const JSLT = 0xc0;
101/// SLE is signed, '<='
102pub const JSLE = 0xd0;
103/// function call
104pub const CALL = 0x80;
105/// function return
106pub 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.
110pub 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.
113pub const F_ALLOW_MULTI = 0x2;
114/// Flag for prog_attach command.
115pub 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.
120pub 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.
131pub 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.
148pub 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
158pub const PSEUDO_MAP_FD = 1;
159pub 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
163pub const PSEUDO_CALL = 1;
164
165/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
166pub const ANY = 0;
167/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
168pub const NOEXIST = 1;
169/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
170pub const EXIST = 2;
171/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
172pub const F_LOCK = 4;
173
174/// flag for BPF_MAP_CREATE command */
175pub 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.
180pub const BPF_F_NO_COMMON_LRU = 0x2;
181/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
182pub const BPF_F_NUMA_NODE = 0x4;
183/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
184/// syscall side
185pub const BPF_F_RDONLY = 0x8;
186/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
187/// syscall side
188pub const BPF_F_WRONLY = 0x10;
189/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
190/// instead of pointer
191pub 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.
194pub const BPF_F_ZERO_SEED = 0x40;
195/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
196/// side.
197pub const BPF_F_RDONLY_PROG = 0x80;
198/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
199/// side.
200pub const BPF_F_WRONLY_PROG = 0x100;
201/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
202/// socket
203pub const BPF_F_CLONE = 0x200;
204/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
205pub const BPF_F_MMAPABLE = 0x400;
206
207/// These values correspond to "syscalls" within the BPF program's environment
208pub 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
332pub 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
511fn expect_insn(insn: Insn, val: u64) void {
512 expectEqual(@bitCast(u64, insn), val);
513}
514
515test "insn bitsize" {
516 expectEqual(@bitSizeOf(Insn), 64);
517}
518
519// mov instructions
520test "mov imm" {
521 expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7);
522}
523
524test "mov reg" {
525 expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf);
526}
527
528// alu instructions
529test "add imm" {
530 expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207);
531}
532
533// ld instructions
534test "ld_abs" {
535 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);
536}
537
538test "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
544test "stx_mem" {
545 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);
546}
547
548test "xadd" {
549 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);
550}
551
552// jmp instructions
553test "jeq imm" {
554 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);
555}
556
557// other instructions
558test "call" {
559 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);
560}
561
562test "exit" {
563 expect_insn(Insn.exit(), 0x0000000000000095);
564}
565
566pub 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
605pub 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
637pub 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
671pub 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
713const obj_name_len = 16;
714/// struct used by Cmd.map_create command
715pub 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
744pub 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
755pub 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
772pub 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
812pub 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
819pub 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
832pub 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
852pub 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
865pub 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
872pub 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
883pub const RawTracepointAttr = extern struct {
884 name: u64,
885 prog_fd: fd_t,
886};
887
888/// struct used by Cmd.btf_load command
889pub 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
897pub 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
922pub 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
933pub 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
945pub const EnableStatsAttr = extern struct {
946 type: u32,
947};
948
949/// struct used by Cmd.iter_create command
950pub const IterCreateAttr = extern struct {
951 link_fd: fd_t,
952 flags: u32,
953};
954
955pub const Attr = extern union {
956 map_create: MapCreateAttr,
957 map_elem: MapElemAttr,
958 map_batch: MapBatchAttr,
959 prog_load: ProgLoadAttr,
960 obj: ObjAttr,
961 prog_attach: ProgAttachAttr,
962 test_run: TestRunAttr,
963 get_id: GetIdAttr,
964 info: InfoAttr,
965 query: QueryAttr,
966 raw_tracepoint: RawTracepointAttr,
967 btf_load: BtfLoadAttr,
968 task_fd_query: TaskFdQueryAttr,
969 link_create: LinkCreateAttr,
970 link_update: LinkUpdateAttr,
971 enable_stats: EnableStatsAttr,
972 iter_create: IterCreateAttr,
973};
lib/std/special/init-exe/build.zig-5
...@@ -1,8 +1,3 @@...@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
72
8pub fn build(b: *Builder) void {3pub fn build(b: *Builder) void {
lib/std/special/init-exe/src/main.zig-5
...@@ -1,8 +1,3 @@...@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");1const std = @import("std");
72
8pub fn main() anyerror!void {3pub fn main() anyerror!void {
lib/std/special/init-lib/build.zig-5
...@@ -1,8 +1,3 @@...@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
72
8pub fn build(b: *Builder) void {3pub fn build(b: *Builder) void {
lib/std/special/init-lib/src/main.zig-5
...@@ -1,8 +1,3 @@...@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");1const std = @import("std");
7const testing = std.testing;2const testing = std.testing;
83
lib/std/target.zig+18-8
...@@ -96,8 +96,12 @@ pub const Target = struct {...@@ -96,8 +96,12 @@ pub const Target = struct {
96 win10_rs4 = 0x0A000005,96 win10_rs4 = 0x0A000005,
97 win10_rs5 = 0x0A000006,97 win10_rs5 = 0x0A000006,
98 win10_19h1 = 0x0A000007,98 win10_19h1 = 0x0A000007,
99 win10_20h1 = 0x0A000008,
99 _,100 _,
100101
102 /// Latest Windows version that the Zig Standard Library is aware of
103 pub const latest = WindowsVersion.win10_20h1;
104
101 pub const Range = struct {105 pub const Range = struct {
102 min: WindowsVersion,106 min: WindowsVersion,
103 max: WindowsVersion,107 max: WindowsVersion,
...@@ -124,18 +128,17 @@ pub const Target = struct {...@@ -124,18 +128,17 @@ pub const Target = struct {
124 out_stream: anytype,128 out_stream: anytype,
125 ) !void {129 ) !void {
126 if (fmt.len > 0 and fmt[0] == 's') {130 if (fmt.len > 0 and fmt[0] == 's') {
127 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {131 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) {
128 try std.fmt.format(out_stream, ".{}", .{@tagName(self)});132 try std.fmt.format(out_stream, ".{}", .{@tagName(self)});
129 } else {133 } else {
130 try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, {})", .{@enumToInt(self)});134 // TODO this code path breaks zig triples, but it is used in `builtin`
135 try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, 0x{X:0>8})", .{@enumToInt(self)});
131 }136 }
132 } else {137 } else {
133 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {138 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) {
134 try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});139 try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});
135 } else {140 } else {
136 try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())});141 try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)});
137 try std.fmt.format(out_stream, "{}", .{@enumToInt(self)});
138 try out_stream.writeAll(")");
139 }142 }
140 }143 }
141 }144 }
...@@ -280,7 +283,7 @@ pub const Target = struct {...@@ -280,7 +283,7 @@ pub const Target = struct {
280 .windows => return .{283 .windows => return .{
281 .windows = .{284 .windows = .{
282 .min = .win8_1,285 .min = .win8_1,
283 .max = .win10_19h1,286 .max = WindowsVersion.latest,
284 },287 },
285 },288 },
286 }289 }
...@@ -663,6 +666,9 @@ pub const Target = struct {...@@ -663,6 +666,9 @@ pub const Target = struct {
663 renderscript32,666 renderscript32,
664 renderscript64,667 renderscript64,
665 ve,668 ve,
669 // Stage1 currently assumes that architectures above this comment
670 // map one-to-one with the ZigLLVM_ArchType enum.
671 spu_2,
666672
667 pub fn isARM(arch: Arch) bool {673 pub fn isARM(arch: Arch) bool {
668 return switch (arch) {674 return switch (arch) {
...@@ -761,6 +767,7 @@ pub const Target = struct {...@@ -761,6 +767,7 @@ pub const Target = struct {
761 .sparcv9 => ._SPARCV9,767 .sparcv9 => ._SPARCV9,
762 .s390x => ._S390,768 .s390x => ._S390,
763 .ve => ._NONE,769 .ve => ._NONE,
770 .spu_2 => ._SPU_2,
764 };771 };
765 }772 }
766773
...@@ -803,6 +810,7 @@ pub const Target = struct {...@@ -803,6 +810,7 @@ pub const Target = struct {
803 .renderscript64,810 .renderscript64,
804 .shave,811 .shave,
805 .ve,812 .ve,
813 .spu_2,
806 => .Little,814 => .Little,
807815
808 .arc,816 .arc,
...@@ -827,6 +835,7 @@ pub const Target = struct {...@@ -827,6 +835,7 @@ pub const Target = struct {
827 switch (arch) {835 switch (arch) {
828 .avr,836 .avr,
829 .msp430,837 .msp430,
838 .spu_2,
830 => return 16,839 => return 16,
831840
832 .arc,841 .arc,
...@@ -1317,12 +1326,13 @@ pub const Target = struct {...@@ -1317,12 +1326,13 @@ pub const Target = struct {
1317 .bpfeb,1326 .bpfeb,
1318 .nvptx,1327 .nvptx,
1319 .nvptx64,1328 .nvptx64,
1329 .spu_2,
1330 .avr,
1320 => return result,1331 => return result,
13211332
1322 // TODO go over each item in this list and either move it to the above list, or1333 // TODO go over each item in this list and either move it to the above list, or
1323 // implement the standard dynamic linker path code for it.1334 // implement the standard dynamic linker path code for it.
1324 .arc,1335 .arc,
1325 .avr,
1326 .hexagon,1336 .hexagon,
1327 .msp430,1337 .msp430,
1328 .r600,1338 .r600,
lib/std/zig/system.zig+1-1
...@@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct {...@@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct {
249 // values249 // values
250 const known_build_numbers = [_]u32{250 const known_build_numbers = [_]u32{
251 10240, 10586, 14393, 15063, 16299, 17134, 17763,251 10240, 10586, 14393, 15063, 16299, 17134, 17763,
252 18362, 18363,252 18362, 19041,
253 };253 };
254 var last_idx: usize = 0;254 var last_idx: usize = 0;
255 for (known_build_numbers) |build, i| {255 for (known_build_numbers) |build, i| {
src-self-hosted/Module.zig+42
...@@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},...@@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
80root_name: []u8,80root_name: []u8,
81keep_source_files_loaded: bool,81keep_source_files_loaded: bool,
8282
83/// Error tags and their values, tag names are duped with mod.gpa.
84global_error_set: std.StringHashMapUnmanaged(u16) = .{},
85
83pub const InnerError = error{ OutOfMemory, AnalysisFail };86pub const InnerError = error{ OutOfMemory, AnalysisFail };
8487
85const WorkItem = union(enum) {88const WorkItem = union(enum) {
...@@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void {...@@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void {
928931
929 self.symbol_exports.deinit(gpa);932 self.symbol_exports.deinit(gpa);
930 self.root_scope.destroy(gpa);933 self.root_scope.destroy(gpa);
934
935 for (self.global_error_set.items()) |entry| {
936 gpa.free(entry.key);
937 }
938 self.global_error_set.deinit(gpa);
931 self.* = undefined;939 self.* = undefined;
932}940}
933941
...@@ -2072,6 +2080,18 @@ fn createNewDecl(...@@ -2072,6 +2080,18 @@ fn createNewDecl(
2072 return new_decl;2080 return new_decl;
2073}2081}
20742082
2083/// Get error value for error tag `name`.
2084pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
2085 const gop = try self.global_error_set.getOrPut(self.gpa, name);
2086 if (gop.found_existing)
2087 return gop.entry.*;
2088 errdefer self.global_error_set.removeAssertDiscard(name);
2089
2090 gop.entry.key = try self.gpa.dupe(u8, name);
2091 gop.entry.value = @intCast(u16, self.global_error_set.items().len - 1);
2092 return gop.entry.*;
2093}
2094
2075/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.2095/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
2076pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {2096pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2077 return scope.cast(Scope.Block) orelse2097 return scope.cast(Scope.Block) orelse
...@@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_...@@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_
3309 return Type.initPayload(&payload.base);3329 return Type.initPayload(&payload.base);
3310}3330}
33113331
3332pub 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
3346pub 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
3312pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {3354pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
3313 const zir_module = scope.namespace();3355 const zir_module = scope.namespace();
3314 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");3356 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
src-self-hosted/astgen.zig+235-198
...@@ -18,9 +18,7 @@ pub const ResultLoc = union(enum) {...@@ -18,9 +18,7 @@ pub const ResultLoc = union(enum) {
18 /// The expression has an inferred type, and it will be evaluated as an rvalue.18 /// The expression has an inferred type, and it will be evaluated as an rvalue.
19 none,19 none,
20 /// The expression must generate a pointer rather than a value. For example, the left hand side20 /// The expression must generate a pointer rather than a value. For example, the left hand side
21 /// of an assignment uses an "LValue" result location.21 /// of an assignment uses this kind of result location.
22 lvalue,
23 /// The expression must generate a pointer
24 ref,22 ref,
25 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.23 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
26 ty: *zir.Inst,24 ty: *zir.Inst,
...@@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z...@@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
46 return expr(mod, scope, type_rl, type_node);44 return expr(mod, scope, type_rl, type_node);
47}45}
4846
49/// Turn Zig AST into untyped ZIR istructions.47fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
50pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {48 switch (node.tag) {
51 if (rl == .lvalue) {49 .Root => unreachable,
52 switch (node.tag) {50 .Use => unreachable,
53 .Root => unreachable,51 .TestDecl => unreachable,
54 .Use => unreachable,52 .DocComment => unreachable,
55 .TestDecl => unreachable,53 .VarDecl => unreachable,
56 .DocComment => unreachable,54 .SwitchCase => unreachable,
57 .VarDecl => unreachable,55 .SwitchElse => unreachable,
58 .SwitchCase => unreachable,56 .Else => unreachable,
59 .SwitchElse => unreachable,57 .Payload => unreachable,
60 .Else => unreachable,58 .PointerPayload => unreachable,
61 .Payload => unreachable,59 .PointerIndexPayload => unreachable,
62 .PointerPayload => unreachable,60 .ErrorTag => unreachable,
63 .PointerIndexPayload => unreachable,61 .FieldInitializer => unreachable,
64 .ErrorTag => unreachable,62 .ContainerField => unreachable,
65 .FieldInitializer => unreachable,63
66 .ContainerField => unreachable,64 .Assign,
6765 .AssignBitAnd,
68 .Assign,66 .AssignBitOr,
69 .AssignBitAnd,67 .AssignBitShiftLeft,
70 .AssignBitOr,68 .AssignBitShiftRight,
71 .AssignBitShiftLeft,69 .AssignBitXor,
72 .AssignBitShiftRight,70 .AssignDiv,
73 .AssignBitXor,71 .AssignSub,
74 .AssignDiv,72 .AssignSubWrap,
75 .AssignSub,73 .AssignMod,
76 .AssignSubWrap,74 .AssignAdd,
77 .AssignMod,75 .AssignAddWrap,
78 .AssignAdd,76 .AssignMul,
79 .AssignAddWrap,77 .AssignMulWrap,
80 .AssignMul,78 .Add,
81 .AssignMulWrap,79 .AddWrap,
82 .Add,80 .Sub,
83 .AddWrap,81 .SubWrap,
84 .Sub,82 .Mul,
85 .SubWrap,83 .MulWrap,
86 .Mul,84 .Div,
87 .MulWrap,85 .Mod,
88 .Div,86 .BitAnd,
89 .Mod,87 .BitOr,
90 .BitAnd,88 .BitShiftLeft,
91 .BitOr,89 .BitShiftRight,
92 .BitShiftLeft,90 .BitXor,
93 .BitShiftRight,91 .BangEqual,
94 .BitXor,92 .EqualEqual,
95 .BangEqual,93 .GreaterThan,
96 .EqualEqual,94 .GreaterOrEqual,
97 .GreaterThan,95 .LessThan,
98 .GreaterOrEqual,96 .LessOrEqual,
99 .LessThan,97 .ArrayCat,
100 .LessOrEqual,98 .ArrayMult,
101 .ArrayCat,99 .BoolAnd,
102 .ArrayMult,100 .BoolOr,
103 .BoolAnd,101 .Asm,
104 .BoolOr,102 .StringLiteral,
105 .Asm,103 .IntegerLiteral,
106 .StringLiteral,104 .Call,
107 .IntegerLiteral,105 .Unreachable,
108 .Call,106 .Return,
109 .Unreachable,107 .If,
110 .Return,108 .While,
111 .If,109 .BoolNot,
112 .While,110 .AddressOf,
113 .BoolNot,111 .FloatLiteral,
114 .AddressOf,112 .UndefinedLiteral,
115 .FloatLiteral,113 .BoolLiteral,
116 .UndefinedLiteral,114 .NullLiteral,
117 .BoolLiteral,115 .OptionalType,
118 .NullLiteral,116 .Block,
119 .OptionalType,117 .LabeledBlock,
120 .Block,118 .Break,
121 .LabeledBlock,119 .PtrType,
122 .Break,120 .GroupedExpression,
123 .PtrType,121 .ArrayType,
124 .GroupedExpression,122 .ArrayTypeSentinel,
125 .ArrayType,123 .EnumLiteral,
126 .ArrayTypeSentinel,124 .MultilineStringLiteral,
127 .EnumLiteral,125 .CharLiteral,
128 .MultilineStringLiteral,126 .Defer,
129 .CharLiteral,127 .Catch,
130 .Defer,128 .ErrorUnion,
131 .Catch,129 .MergeErrorSets,
132 .ErrorUnion,130 .Range,
133 .MergeErrorSets,131 .OrElse,
134 .Range,132 .Await,
135 .OrElse,133 .BitNot,
136 .Await,134 .Negation,
137 .BitNot,135 .NegationWrap,
138 .Negation,136 .Resume,
139 .NegationWrap,137 .Try,
140 .Resume,138 .SliceType,
141 .Try,139 .Slice,
142 .SliceType,140 .ArrayInitializer,
143 .Slice,141 .ArrayInitializerDot,
144 .ArrayInitializer,142 .StructInitializer,
145 .ArrayInitializerDot,143 .StructInitializerDot,
146 .StructInitializer,144 .Switch,
147 .StructInitializerDot,145 .For,
148 .Switch,146 .Suspend,
149 .For,147 .Continue,
150 .Suspend,148 .AnyType,
151 .Continue,149 .ErrorType,
152 .AnyType,150 .FnProto,
153 .ErrorType,151 .AnyFrameType,
154 .FnProto,152 .ErrorSetDecl,
155 .AnyFrameType,153 .ContainerDecl,
156 .ErrorSetDecl,154 .Comptime,
157 .ContainerDecl,155 .Nosuspend,
158 .Comptime,156 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
159 .Nosuspend,157
160 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),158 // @field can be assigned to
161159 .BuiltinCall => {
162 // @field can be assigned to160 const call = node.castTag(.BuiltinCall).?;
163 .BuiltinCall => {161 const tree = scope.tree();
164 const call = node.castTag(.BuiltinCall).?;162 const builtin_name = tree.tokenSlice(call.builtin_token);
165 const tree = scope.tree();163
166 const builtin_name = tree.tokenSlice(call.builtin_token);164 if (!mem.eql(u8, builtin_name, "@field")) {
167165 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
168 if (!mem.eql(u8, builtin_name, "@field")) {166 }
169 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});167 },
170 }
171 },
172168
173 // can be assigned to169 // can be assigned to
174 .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},170 .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
175 }
176 }171 }
172 return expr(mod, scope, .ref, node);
173}
174
175/// Turn Zig AST into untyped ZIR istructions.
176pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
177 switch (node.tag) {177 switch (node.tag) {
178 .Root => unreachable, // Top-level declaration.178 .Root => unreachable, // Top-level declaration.
179 .Use => unreachable, // Top-level declaration.179 .Use => unreachable, // Top-level declaration.
...@@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
232 .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),232 .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
233 .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),233 .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
234234
235 .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
236 .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
237 .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
238 .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
239
235 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),240 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
236 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),241 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
237 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),242 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
...@@ -242,9 +247,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -242,9 +247,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
242 .Return => return ret(mod, scope, node.castTag(.Return).?),247 .Return => return ret(mod, scope, node.castTag(.Return).?),
243 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),248 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
244 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),249 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
245 .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)),250 .Period => return field(mod, scope, rl, node.castTag(.Period).?),
246 .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),251 .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
247 .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
248 .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),252 .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
249 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),253 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
250 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),254 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
...@@ -263,17 +267,17 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -263,17 +267,17 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
263 .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),267 .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
264 .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),268 .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
265 .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),269 .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
270 .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
271 .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
272 .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
273 .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
274 .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
266275
267 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),276 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
268 .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),277 .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
269 .ErrorUnion => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorUnion", .{}),
270 .MergeErrorSets => return mod.failNode(scope, node, "TODO implement astgen.expr for .MergeErrorSets", .{}),
271 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),278 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
272 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),279 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
273 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),280 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
274 .BitNot => return mod.failNode(scope, node, "TODO implement astgen.expr for .BitNot", .{}),
275 .Negation => return mod.failNode(scope, node, "TODO implement astgen.expr for .Negation", .{}),
276 .NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}),
277 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),281 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
278 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),282 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
279 .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),283 .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
...@@ -287,10 +291,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -287,10 +291,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
287 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),291 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
288 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),292 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
289 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),293 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
290 .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}),
291 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),294 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
292 .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}),
293 .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}),
294 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),295 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
295 .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),296 .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),
296 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),297 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
...@@ -316,7 +317,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr...@@ -316,7 +317,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
316 // proper type inference requires peer type resolution on the block's317 // proper type inference requires peer type resolution on the block's
317 // break operand expressions.318 // break operand expressions.
318 const branch_rl: ResultLoc = switch (label.result_loc) {319 const branch_rl: ResultLoc = switch (label.result_loc) {
319 .discard, .none, .ty, .ptr, .lvalue, .ref => label.result_loc,320 .discard, .none, .ty, .ptr, .ref => label.result_loc,
320 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },321 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
321 };322 };
322 const operand = try expr(mod, parent_scope, branch_rl, rhs);323 const operand = try expr(mod, parent_scope, branch_rl, rhs);
...@@ -458,7 +459,9 @@ fn varDecl(...@@ -458,7 +459,9 @@ fn varDecl(
458 const tree = scope.tree();459 const tree = scope.tree();
459 const name_src = tree.token_locs[node.name_token].start;460 const name_src = tree.token_locs[node.name_token].start;
460 const ident_name = try identifierTokenString(mod, scope, node.name_token);461 const ident_name = try identifierTokenString(mod, scope, node.name_token);
461 const init_node = node.getTrailer("init_node").?;462 const init_node = node.getTrailer("init_node") orelse
463 return mod.fail(scope, name_src, "variables must be initialized", .{});
464
462 switch (tree.token_ids[node.mut_token]) {465 switch (tree.token_ids[node.mut_token]) {
463 .Keyword_const => {466 .Keyword_const => {
464 // Depending on the type of AST the initialization expression is, we may need an lvalue467 // Depending on the type of AST the initialization expression is, we may need an lvalue
...@@ -521,7 +524,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne...@@ -521,7 +524,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne
521 return;524 return;
522 }525 }
523 }526 }
524 const lvalue = try expr(mod, scope, .lvalue, infix_node.lhs);527 const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
525 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);528 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
526}529}
527530
...@@ -531,7 +534,7 @@ fn assignOp(...@@ -531,7 +534,7 @@ fn assignOp(
531 infix_node: *ast.Node.SimpleInfixOp,534 infix_node: *ast.Node.SimpleInfixOp,
532 op_inst_tag: zir.Inst.Tag,535 op_inst_tag: zir.Inst.Tag,
533) InnerError!void {536) InnerError!void {
534 const lhs_ptr = try expr(mod, scope, .lvalue, infix_node.lhs);537 const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
535 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);538 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
536 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);539 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
537 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);540 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
...@@ -554,6 +557,26 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr...@@ -554,6 +557,26 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr
554 return addZIRUnOp(mod, scope, src, .boolnot, operand);557 return addZIRUnOp(mod, scope, src, .boolnot, operand);
555}558}
556559
560fn 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
567fn 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
557fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {580fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
558 return expr(mod, scope, .ref, node.rhs);581 return expr(mod, scope, .ref, node.rhs);
559}582}
...@@ -561,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE...@@ -561,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE
561fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {584fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
562 const tree = scope.tree();585 const tree = scope.tree();
563 const src = tree.token_locs[node.op_token].start;586 const src = tree.token_locs[node.op_token].start;
564 const meta_type = try addZIRInstConst(mod, scope, src, .{587 const operand = try typeExpr(mod, scope, node.rhs);
565 .ty = Type.initTag(.type),
566 .val = Value.initTag(.type_type),
567 });
568 const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
569 return addZIRUnOp(mod, scope, src, .optional_type, operand);588 return addZIRUnOp(mod, scope, src, .optional_type, operand);
570}589}
571590
...@@ -590,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir...@@ -590,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir
590}609}
591610
592fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {611fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
593 const meta_type = try addZIRInstConst(mod, scope, src, .{
594 .ty = Type.initTag(.type),
595 .val = Value.initTag(.type_type),
596 });
597
598 const simple = ptr_info.allowzero_token == null and612 const simple = ptr_info.allowzero_token == null and
599 ptr_info.align_info == null and613 ptr_info.align_info == null and
600 ptr_info.volatile_token == null and614 ptr_info.volatile_token == null and
601 ptr_info.sentinel == null;615 ptr_info.sentinel == null;
602616
603 if (simple) {617 if (simple) {
604 const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);618 const child_type = try typeExpr(mod, scope, rhs);
605 const mutable = ptr_info.const_token == null;619 const mutable = ptr_info.const_token == null;
606 // TODO stage1 type inference bug620 // TODO stage1 type inference bug
607 const T = zir.Inst.Tag;621 const T = zir.Inst.Tag;
...@@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,...@@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
629 kw_args.sentinel = try expr(mod, scope, .none, some);643 kw_args.sentinel = try expr(mod, scope, .none, some);
630 }644 }
631645
632 const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);646 const child_type = try typeExpr(mod, scope, rhs);
633 if (kw_args.sentinel) |some| {647 if (kw_args.sentinel) |some| {
634 kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);648 kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
635 }649 }
...@@ -640,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,...@@ -640,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
640fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {654fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
641 const tree = scope.tree();655 const tree = scope.tree();
642 const src = tree.token_locs[node.op_token].start;656 const src = tree.token_locs[node.op_token].start;
643 const meta_type = try addZIRInstConst(mod, scope, src, .{
644 .ty = Type.initTag(.type),
645 .val = Value.initTag(.type_type),
646 });
647 const usize_type = try addZIRInstConst(mod, scope, src, .{657 const usize_type = try addZIRInstConst(mod, scope, src, .{
648 .ty = Type.initTag(.type),658 .ty = Type.initTag(.type),
649 .val = Value.initTag(.usize_type),659 .val = Value.initTag(.usize_type),
...@@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst...@@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst
651661
652 // TODO check for [_]T662 // TODO check for [_]T
653 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);663 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
654 const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);664 const elem_type = try typeExpr(mod, scope, node.rhs);
655665
656 return addZIRBinOp(mod, scope, src, .array_type, len, child_type);666 return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
657}667}
658668
659fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {669fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
660 const tree = scope.tree();670 const tree = scope.tree();
661 const src = tree.token_locs[node.op_token].start;671 const src = tree.token_locs[node.op_token].start;
662 const meta_type = try addZIRInstConst(mod, scope, src, .{
663 .ty = Type.initTag(.type),
664 .val = Value.initTag(.type_type),
665 });
666 const usize_type = try addZIRInstConst(mod, scope, src, .{672 const usize_type = try addZIRInstConst(mod, scope, src, .{
667 .ty = Type.initTag(.type),673 .ty = Type.initTag(.type),
668 .val = Value.initTag(.usize_type),674 .val = Value.initTag(.usize_type),
...@@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti...@@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti
671 // TODO check for [_]T677 // TODO check for [_]T
672 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);678 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
673 const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);679 const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
674 const elem_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);680 const elem_type = try typeExpr(mod, scope, node.rhs);
675 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);681 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
676682
677 return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{683 return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
...@@ -681,6 +687,28 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti...@@ -681,6 +687,28 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti
681 }, .{});687 }, .{});
682}688}
683689
690fn 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
704fn 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
684fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {712fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
685 const tree = scope.tree();713 const tree = scope.tree();
686 const src = tree.token_locs[node.name].start;714 const src = tree.token_locs[node.name].start;
...@@ -694,10 +722,31 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si...@@ -694,10 +722,31 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si
694 const src = tree.token_locs[node.rtoken].start;722 const src = tree.token_locs[node.rtoken].start;
695723
696 const operand = try expr(mod, scope, .ref, node.lhs);724 const operand = try expr(mod, scope, .ref, node.lhs);
697 const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand);725 return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
698 if (rl == .lvalue or rl == .ref) return unwrapped_ptr;726}
727
728fn 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 }
699738
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
743fn 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}
702751
703/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.752/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
...@@ -737,16 +786,16 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke...@@ -737,16 +786,16 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke
737 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});786 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
738}787}
739788
740fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {789fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
741 // TODO introduce lvalues
742 const tree = scope.tree();790 const tree = scope.tree();
743 const src = tree.token_locs[node.op_token].start;791 const src = tree.token_locs[node.op_token].start;
744792
745 const lhs = try expr(mod, scope, .none, node.lhs);793 const lhs = try expr(mod, scope, .ref, node.lhs);
746 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);794 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
747795
748 const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});796 const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
749 return addZIRUnOp(mod, scope, src, .deref, pointer);797 if (rl == .ref) return pointer;
798 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer));
750}799}
751800
752fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {801fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
...@@ -971,7 +1020,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -971,7 +1020,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
971 // proper type inference requires peer type resolution on the if's1020 // proper type inference requires peer type resolution on the if's
972 // branches.1021 // branches.
973 const branch_rl: ResultLoc = switch (rl) {1022 const branch_rl: ResultLoc = switch (rl) {
974 .discard, .none, .ty, .ptr, .lvalue, .ref => rl,1023 .discard, .none, .ty, .ptr, .ref => rl,
975 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },1024 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
976 };1025 };
9771026
...@@ -1101,7 +1150,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1101,7 +1150,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1101 // proper type inference requires peer type resolution on the while's1150 // proper type inference requires peer type resolution on the while's
1102 // branches.1151 // branches.
1103 const branch_rl: ResultLoc = switch (rl) {1152 const branch_rl: ResultLoc = switch (rl) {
1104 .discard, .none, .ty, .ptr, .lvalue, .ref => rl,1153 .discard, .none, .ty, .ptr, .ref => rl,
1105 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },1154 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
1106 };1155 };
11071156
...@@ -1232,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -1232,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
1232 .local_ptr => {1281 .local_ptr => {
1233 const local_ptr = s.cast(Scope.LocalPtr).?;1282 const local_ptr = s.cast(Scope.LocalPtr).?;
1234 if (mem.eql(u8, local_ptr.name, ident_name)) {1283 if (mem.eql(u8, local_ptr.name, ident_name)) {
1235 if (rl == .lvalue or rl == .ref) {1284 return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
1236 return local_ptr.ptr;
1237 } else {
1238 const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
1239 return rlWrap(mod, scope, rl, result);
1240 }
1241 }1285 }
1242 s = local_ptr.parent;1286 s = local_ptr.parent;
1243 },1287 },
...@@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
1247 }1291 }
12481292
1249 if (mod.lookupDeclName(scope, ident_name)) |decl| {1293 if (mod.lookupDeclName(scope, ident_name)) |decl| {
1250 const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});1294 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
1251 if (rl == .lvalue or rl == .ref)
1252 return result;
1253 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, result));
1254 }1295 }
12551296
1256 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});1297 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
...@@ -1466,12 +1507,8 @@ fn simpleCast(...@@ -1466,12 +1507,8 @@ fn simpleCast(
1466 try ensureBuiltinParamCount(mod, scope, call, 2);1507 try ensureBuiltinParamCount(mod, scope, call, 2);
1467 const tree = scope.tree();1508 const tree = scope.tree();
1468 const src = tree.token_locs[call.builtin_token].start;1509 const src = tree.token_locs[call.builtin_token].start;
1469 const type_type = try addZIRInstConst(mod, scope, src, .{
1470 .ty = Type.initTag(.type),
1471 .val = Value.initTag(.type_type),
1472 });
1473 const params = call.params();1510 const params = call.params();
1474 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);1511 const dest_type = try typeExpr(mod, scope, params[0]);
1475 const rhs = try expr(mod, scope, .none, params[1]);1512 const rhs = try expr(mod, scope, .none, params[1]);
1476 const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);1513 const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
1477 return rlWrap(mod, scope, rl, result);1514 return rlWrap(mod, scope, rl, result);
...@@ -1498,7 +1535,6 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I...@@ -1498,7 +1535,6 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
1498 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);1535 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
1499 return result;1536 return result;
1500 },1537 },
1501 .lvalue => unreachable,
1502 .ref => {1538 .ref => {
1503 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);1539 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
1504 return addZIRUnOp(mod, scope, result.src, .ref, result);1540 return addZIRUnOp(mod, scope, result.src, .ref, result);
...@@ -1533,12 +1569,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa...@@ -1533,12 +1569,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
1533 try ensureBuiltinParamCount(mod, scope, call, 2);1569 try ensureBuiltinParamCount(mod, scope, call, 2);
1534 const tree = scope.tree();1570 const tree = scope.tree();
1535 const src = tree.token_locs[call.builtin_token].start;1571 const src = tree.token_locs[call.builtin_token].start;
1536 const type_type = try addZIRInstConst(mod, scope, src, .{
1537 .ty = Type.initTag(.type),
1538 .val = Value.initTag(.type_type),
1539 });
1540 const params = call.params();1572 const params = call.params();
1541 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);1573 const dest_type = try typeExpr(mod, scope, params[0]);
1542 switch (rl) {1574 switch (rl) {
1543 .none => {1575 .none => {
1544 const operand = try expr(mod, scope, .none, params[1]);1576 const operand = try expr(mod, scope, .none, params[1]);
...@@ -1550,7 +1582,6 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa...@@ -1550,7 +1582,6 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
1550 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);1582 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
1551 return result;1583 return result;
1552 },1584 },
1553 .lvalue => unreachable,
1554 .ref => {1585 .ref => {
1555 const operand = try expr(mod, scope, .ref, params[1]);1586 const operand = try expr(mod, scope, .ref, params[1]);
1556 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);1587 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
...@@ -1818,7 +1849,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1818,7 +1849,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1818 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);1849 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
1819 return result;1850 return result;
1820 },1851 },
1821 .lvalue, .ref => {1852 .ref => {
1822 // We need a pointer but we have a value.1853 // We need a pointer but we have a value.
1823 return addZIRUnOp(mod, scope, result.src, .ref, result);1854 return addZIRUnOp(mod, scope, result.src, .ref, result);
1824 },1855 },
...@@ -1852,6 +1883,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul...@@ -1852,6 +1883,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul
1852 return rlWrap(mod, scope, rl, void_inst);1883 return rlWrap(mod, scope, rl, void_inst);
1853}1884}
18541885
1886fn 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
1855pub fn addZIRInstSpecial(1892pub fn addZIRInstSpecial(
1856 mod: *Module,1893 mod: *Module,
1857 scope: *Scope,1894 scope: *Scope,
src-self-hosted/codegen.zig+361-64
...@@ -14,6 +14,7 @@ const Allocator = mem.Allocator;...@@ -14,6 +14,7 @@ const Allocator = mem.Allocator;
14const trace = @import("tracy.zig").trace;14const trace = @import("tracy.zig").trace;
15const DW = std.dwarf;15const DW = std.dwarf;
16const leb128 = std.debug.leb;16const leb128 = std.debug.leb;
17const log = std.log.scoped(.codegen);
1718
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.19// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off20// zig fmt: off
...@@ -75,8 +76,8 @@ pub fn generateSymbol(...@@ -75,8 +76,8 @@ pub fn generateSymbol(
75 switch (bin_file.options.target.cpu.arch) {76 switch (bin_file.options.target.cpu.arch) {
76 .wasm32 => unreachable, // has its own code path77 .wasm32 => unreachable, // has its own code path
77 .wasm64 => unreachable, // has its own code path78 .wasm64 => unreachable, // has its own code path
78 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),79 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
79 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
80 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
81 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
82 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
...@@ -101,6 +102,7 @@ pub fn generateSymbol(...@@ -101,6 +102,7 @@ pub fn generateSymbol(
101 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
102 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),104 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
104 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),108 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
...@@ -344,6 +346,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -344,6 +346,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
344346
345 const Branch = struct {347 const Branch = struct {
346 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},348 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
349 /// The key must be canonical register.
347 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},350 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
348 free_registers: FreeRegInt = math.maxInt(FreeRegInt),351 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
349352
...@@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
381 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);384 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
382 const reg = callee_preserved_regs[free_index];385 const reg = callee_preserved_regs[free_index];
383 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });386 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
387 log.debug("alloc {} => {*}", .{reg, inst});
384 return reg;388 return reg;
385 }389 }
386390
391 /// Does not track the register.
392 fn findUnusedReg(self: *Branch) ?Register {
393 const free_index = @ctz(FreeRegInt, self.free_registers);
394 if (free_index >= callee_preserved_regs.len) {
395 return null;
396 }
397 return callee_preserved_regs[free_index];
398 }
399
387 fn deinit(self: *Branch, gpa: *Allocator) void {400 fn deinit(self: *Branch, gpa: *Allocator) void {
388 self.inst_table.deinit(gpa);401 self.inst_table.deinit(gpa);
389 self.registers.deinit(gpa);402 self.registers.deinit(gpa);
...@@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
570 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];583 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
571 const inst_table = &branch.inst_table;584 const inst_table = &branch.inst_table;
572 for (body.instructions) |inst| {585 for (body.instructions) |inst| {
573 const new_inst = try self.genFuncInst(inst);586 const mcv = try self.genFuncInst(inst);
574 try inst_table.putNoClobber(self.gpa, inst, new_inst);587 log.debug("{*} => {}", .{inst, mcv});
588 // TODO don't put void or dead things in here
589 try inst_table.putNoClobber(self.gpa, inst, mcv);
575590
576 var i: ir.Inst.DeathsBitIndex = 0;591 var i: ir.Inst.DeathsBitIndex = 0;
577 while (inst.getOperand(i)) |operand| : (i += 1) {592 while (inst.getOperand(i)) |operand| : (i += 1) {
...@@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
714 return self.allocMem(inst, abi_size, abi_align);729 return self.allocMem(inst, abi_size, abi_align);
715 }730 }
716731
717 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {732 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
718 const elem_ty = inst.ty;733 const elem_ty = inst.ty;
719 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {734 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
720 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});735 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
...@@ -724,30 +739,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -724,30 +739,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
724 self.stack_align = abi_align;739 self.stack_align = abi_align;
725 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];740 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
726741
727 // Make sure the type can fit in a register before we try to allocate one.742 if (reg_ok) {
728 const ptr_bits = arch.ptrBitWidth();743 // Make sure the type can fit in a register before we try to allocate one.
729 const ptr_bytes: u64 = @divExact(ptr_bits, 8);744 const ptr_bits = arch.ptrBitWidth();
730 if (abi_size <= ptr_bytes) {745 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
731 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);746 if (abi_size <= ptr_bytes) {
732 if (branch.allocReg(inst)) |reg| {747 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
733 return MCValue{ .register = registerAlias(reg, abi_size) };748 if (branch.allocReg(inst)) |reg| {
749 return MCValue{ .register = registerAlias(reg, abi_size) };
750 }
734 }751 }
735 }752 }
736 const stack_offset = try self.allocMem(inst, abi_size, abi_align);753 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
737 return MCValue{ .stack_offset = stack_offset };754 return MCValue{ .stack_offset = stack_offset };
738 }755 }
739756
740 /// Does not "move" the instruction.757 /// Copies a value to a register without tracking the register. The register is not considered
741 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {758 /// allocated. A second call to `copyToTmpRegister` may return the same register.
759 /// This can have a side effect of spilling instructions to the stack to free up a register.
760 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
762
763 const reg = branch.findUnusedReg() orelse b: {
764 // We'll take over the first register. Move the instruction that was previously
765 // there to a stack allocation.
766 const reg = callee_preserved_regs[0];
767 const regs_entry = branch.registers.remove(reg).?;
768 const spilled_inst = regs_entry.value.inst;
769
770 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
771 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
772 const reg_mcv = inst_entry.value;
773 assert(reg == toCanonicalReg(reg_mcv.register));
774 inst_entry.value = stack_mcv;
775 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
776
777 break :b reg;
778 };
779 try self.genSetReg(src, reg, mcv);
780 return reg;
781 }
782
783 /// Allocates a new register and copies `mcv` into it.
784 /// `reg_owner` is the instruction that gets associated with the register in the register table.
785 /// This can have a side effect of spilling instructions to the stack to free up a register.
786 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
742 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];787 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
743 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);788 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
744789
745 const reg = branch.allocReg(inst) orelse790 const reg = branch.allocReg(reg_owner) orelse b: {
746 return self.fail(inst.src, "TODO implement spilling register to stack", .{});791 // We'll take over the first register. Move the instruction that was previously
747 const old_mcv = branch.inst_table.get(inst).?;792 // there to a stack allocation.
748 const new_mcv: MCValue = .{ .register = reg };793 const reg = callee_preserved_regs[0];
749 try self.genSetReg(inst.src, reg, old_mcv);794 const regs_entry = branch.registers.getEntry(reg).?;
750 return new_mcv;795 const spilled_inst = regs_entry.value.inst;
796 regs_entry.value = .{ .inst = reg_owner };
797
798 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
799 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
800 const reg_mcv = inst_entry.value;
801 assert(reg == toCanonicalReg(reg_mcv.register));
802 inst_entry.value = stack_mcv;
803 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
804
805 break :b reg;
806 };
807 try self.genSetReg(reg_owner.src, reg, mcv);
808 return MCValue{ .register = reg };
751 }809 }
752810
753 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {811 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
...@@ -868,13 +926,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -868,13 +926,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
868 }926 }
869 }927 }
870928
871 fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {929 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
872 if (!inst.operandDies(op_index) or !mcv.isMutable())930 if (!inst.operandDies(op_index))
873 return false;931 return false;
874932
875 // OK we're going to do it, but we need to clear the operand death bit so that933 switch (mcv) {
876 // it stays allocated.934 .register => |reg| {
935 // If it's in the registers table, need to associate the register with the
936 // new instruction.
937 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
938 if (branch.registers.getEntry(toCanonicalReg(reg))) |entry| {
939 entry.value = .{ .inst = inst };
940 }
941 log.debug("reusing {} => {*}", .{reg, inst});
942 },
943 .stack_offset => |off| {
944 log.debug("reusing stack offset {} => {*}", .{off, inst});
945 return true;
946 },
947 else => return false,
948 }
949
950 // Prevent the operand deaths processing code from deallocating it.
877 inst.clearOperandDeath(op_index);951 inst.clearOperandDeath(op_index);
952
878 return true;953 return true;
879 }954 }
880955
...@@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
887 if (inst.base.isUnused() and !is_volatile)962 if (inst.base.isUnused() and !is_volatile)
888 return MCValue.dead;963 return MCValue.dead;
889 const dst_mcv: MCValue = blk: {964 const dst_mcv: MCValue = blk: {
890 if (reuseOperand(&inst.base, 0, ptr)) {965 if (self.reuseOperand(&inst.base, 0, ptr)) {
891 // The MCValue that holds the pointer can be re-used as the value.966 // The MCValue that holds the pointer can be re-used as the value.
892 break :blk ptr;967 break :blk ptr;
893 } else {968 } else {
894 break :blk try self.allocRegOrMem(&inst.base);969 break :blk try self.allocRegOrMem(&inst.base, true);
895 }970 }
896 };971 };
897 switch (ptr) {972 switch (ptr) {
...@@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
985 var dst_mcv: MCValue = undefined;1060 var dst_mcv: MCValue = undefined;
986 var src_mcv: MCValue = undefined;1061 var src_mcv: MCValue = undefined;
987 var src_inst: *ir.Inst = undefined;1062 var src_inst: *ir.Inst = undefined;
988 if (reuseOperand(inst, 0, lhs)) {1063 if (self.reuseOperand(inst, 0, lhs)) {
989 // LHS dies; use it as the destination.1064 // LHS dies; use it as the destination.
990 // Both operands cannot be memory.1065 // Both operands cannot be memory.
991 src_inst = op_rhs;1066 src_inst = op_rhs;
992 if (lhs.isMemory() and rhs.isMemory()) {1067 if (lhs.isMemory() and rhs.isMemory()) {
993 dst_mcv = try self.copyToNewRegister(op_lhs);1068 dst_mcv = try self.copyToNewRegister(inst, lhs);
994 src_mcv = rhs;1069 src_mcv = rhs;
995 } else {1070 } else {
996 dst_mcv = lhs;1071 dst_mcv = lhs;
997 src_mcv = rhs;1072 src_mcv = rhs;
998 }1073 }
999 } else if (reuseOperand(inst, 1, rhs)) {1074 } else if (self.reuseOperand(inst, 1, rhs)) {
1000 // RHS dies; use it as the destination.1075 // RHS dies; use it as the destination.
1001 // Both operands cannot be memory.1076 // Both operands cannot be memory.
1002 src_inst = op_lhs;1077 src_inst = op_lhs;
1003 if (lhs.isMemory() and rhs.isMemory()) {1078 if (lhs.isMemory() and rhs.isMemory()) {
1004 dst_mcv = try self.copyToNewRegister(op_rhs);1079 dst_mcv = try self.copyToNewRegister(inst, rhs);
1005 src_mcv = lhs;1080 src_mcv = lhs;
1006 } else {1081 } else {
1007 dst_mcv = rhs;1082 dst_mcv = rhs;
...@@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1009 }1084 }
1010 } else {1085 } else {
1011 if (lhs.isMemory()) {1086 if (lhs.isMemory()) {
1012 dst_mcv = try self.copyToNewRegister(op_lhs);1087 dst_mcv = try self.copyToNewRegister(inst, lhs);
1013 src_mcv = rhs;1088 src_mcv = rhs;
1014 src_inst = op_rhs;1089 src_inst = op_rhs;
1015 } else {1090 } else {
1016 dst_mcv = try self.copyToNewRegister(op_rhs);1091 dst_mcv = try self.copyToNewRegister(inst, rhs);
1017 src_mcv = lhs;1092 src_mcv = lhs;
1018 src_inst = op_lhs;1093 src_inst = op_lhs;
1019 }1094 }
...@@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1026 switch (src_mcv) {1101 switch (src_mcv) {
1027 .immediate => |imm| {1102 .immediate => |imm| {
1028 if (imm > math.maxInt(u31)) {1103 if (imm > math.maxInt(u31)) {
1029 src_mcv = try self.copyToNewRegister(src_inst);1104 src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
1030 }1105 }
1031 },1106 },
1032 else => {},1107 else => {},
1033 }1108 }
10341109
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);
10361111
1037 return dst_mcv;1112 return dst_mcv;
1038 }1113 }
10391114
1040 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {1115 fn genX8664BinMathCode(
1116 self: *Self,
1117 src: usize,
1118 dst_ty: Type,
1119 dst_mcv: MCValue,
1120 src_mcv: MCValue,
1121 opx: u8,
1122 mr: u8,
1123 ) !void {
1041 switch (dst_mcv) {1124 switch (dst_mcv) {
1042 .none => unreachable,1125 .none => unreachable,
1043 .undef => unreachable,1126 .undef => unreachable,
...@@ -1087,12 +1170,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1087,12 +1170,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1087 },1170 },
1088 }1171 }
1089 },1172 },
1090 .embedded_in_code, .memory, .stack_offset => {1173 .stack_offset => |off| {
1174 switch (src_mcv) {
1175 .none => unreachable,
1176 .undef => return self.genSetStack(src, dst_ty, off, .undef),
1177 .dead, .unreach => unreachable,
1178 .ptr_stack_offset => unreachable,
1179 .ptr_embedded_in_code => unreachable,
1180 .register => |src_reg| {
1181 try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);
1182 },
1183 .immediate => |imm| {
1184 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});
1185 },
1186 .embedded_in_code, .memory, .stack_offset => {
1187 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
1188 },
1189 .compare_flags_unsigned => {
1190 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
1191 },
1192 .compare_flags_signed => {
1193 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
1194 },
1195 }
1196 },
1197 .embedded_in_code, .memory => {
1091 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});1198 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
1092 },1199 },
1093 }1200 }
1094 }1201 }
10951202
1203 fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
1204 const abi_size = ty.abiSize(self.target.*);
1205 const adj_off = off + abi_size;
1206 try self.code.ensureCapacity(self.code.items.len + 7);
1207 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
1208 const reg_id: u8 = @truncate(u3, reg.id());
1209 if (adj_off <= 128) {
1210 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1211 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1212 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1213 const twos_comp = @bitCast(u8, negative_offset);
1214 self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp });
1215 } else if (adj_off <= 2147483648) {
1216 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1217 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1218 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1219 const twos_comp = @bitCast(u32, negative_offset);
1220 self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM });
1221 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1222 } else {
1223 return self.fail(src, "stack offset too large", .{});
1224 }
1225 }
1226
1096 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {1227 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
1097 if (FreeRegInt == u0) {1228 if (FreeRegInt == u0) {
1098 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1229 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
...@@ -1109,7 +1240,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1109,7 +1240,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1109 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];1240 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
1110 switch (result) {1241 switch (result) {
1111 .register => |reg| {1242 .register => |reg| {
1112 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });1243 branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base });
1113 branch.markRegUsed(reg);1244 branch.markRegUsed(reg);
11141245
1115 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);1246 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
...@@ -1134,6 +1265,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1134,6 +1265,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1134 .riscv64 => {1265 .riscv64 => {
1135 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());1266 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
1136 },1267 },
1268 .spu_2 => {
1269 try self.code.resize(self.code.items.len + 2);
1270 var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 };
1271 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
1272 },
1273 .arm => {
1274 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
1275 },
1276 .armeb => {
1277 mem.writeIntBig(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
1278 },
1137 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1279 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
1138 }1280 }
1139 return .none;1281 return .none;
...@@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1219 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});1361 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1220 }1362 }
1221 },1363 },
1364 .spu_2 => {
1365 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1366 if (info.args.len != 0) {
1367 return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
1368 }
1369 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1370 const func = func_val.func;
1371 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1372 const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
1373 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
1374 // First, push the return address, then jump; if noreturn, don't bother with the first step
1375 // TODO: implement packed struct -> u16 at comptime and move the bitcast here
1376 var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 };
1377 if (return_type.zigTypeTag() == .NoReturn) {
1378 try self.code.resize(self.code.items.len + 4);
1379 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
1380 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
1381 return MCValue.unreach;
1382 } else {
1383 try self.code.resize(self.code.items.len + 8);
1384 var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget };
1385 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push));
1386 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4));
1387 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
1388 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
1389 switch (return_type.zigTypeTag()) {
1390 .Void => return MCValue{ .none = {} },
1391 .NoReturn => unreachable,
1392 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
1393 }
1394 }
1395 } else {
1396 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1397 }
1398 } else {
1399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1400 }
1401 },
1402 .arm => {
1403 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1404
1405 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1406 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1407 const func = func_val.func;
1408 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1409 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1410 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1411 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1412
1413 // TODO only works with leaf functions
1414 // at the moment, which works fine for
1415 // Hello World, but not for real code
1416 // of course. Add pushing lr to stack
1417 // and popping after call
1418 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1419 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1420 } else {
1421 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1422 }
1423 } else {
1424 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1425 }
1426 },
1222 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1427 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1223 }1428 }
1224 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {1429 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
1225 return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO", .{});1430 switch (arch) {
1431 .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}),
1432 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
1433 else => unreachable,
1434 }
1226 } else {1435 } else {
1227 unreachable;1436 unreachable;
1228 }1437 }
...@@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1275 .riscv64 => {1484 .riscv64 => {
1276 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1485 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1277 },1486 },
1487 .arm => {
1488 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1489 },
1278 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1490 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1279 }1491 }
1280 return .unreach;1492 return .unreach;
...@@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1304 // Either one, but not both, can be a memory operand.1516 // Either one, but not both, can be a memory operand.
1305 // Source operand can be an immediate, 8 bits or 32 bits.1517 // Source operand can be an immediate, 8 bits or 32 bits.
1306 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))1518 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
1307 try self.copyToNewRegister(inst.lhs)1519 try self.copyToNewRegister(&inst.base, lhs)
1308 else1520 else
1309 lhs;1521 lhs;
1310 // This instruction supports only signed 32-bit immediates at most.1522 // This instruction supports only signed 32-bit immediates at most.
1311 const src_mcv = try self.limitImmediateType(inst.rhs, i32);1523 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
13121524
1313 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);1525 try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);
1314 const info = inst.lhs.ty.intInfo(self.target.*);1526 const info = inst.lhs.ty.intInfo(self.target.*);
1315 if (info.signed) {1527 if (info.signed) {
1316 return MCValue{ .compare_flags_signed = op };1528 return MCValue{ .compare_flags_signed = op };
...@@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1512 if (!inst.is_volatile and inst.base.isUnused())1724 if (!inst.is_volatile and inst.base.isUnused())
1513 return MCValue.dead;1725 return MCValue.dead;
1514 switch (arch) {1726 switch (arch) {
1727 .spu_2 => {
1728 if (inst.inputs.len > 0 or inst.output != null) {
1729 return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});
1730 }
1731 if (mem.eql(u8, inst.asm_source, "undefined0")) {
1732 try self.code.resize(self.code.items.len + 2);
1733 var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 };
1734 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
1735 return MCValue.none;
1736 } else {
1737 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
1738 }
1739 },
1740 .arm => {
1741 for (inst.inputs) |input, i| {
1742 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1743 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1744 }
1745 const reg_name = input[1 .. input.len - 1];
1746 const reg = parseRegName(reg_name) orelse
1747 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1748 const arg = try self.resolveInst(inst.args[i]);
1749 try self.genSetReg(inst.base.src, reg, arg);
1750 }
1751
1752 if (mem.eql(u8, inst.asm_source, "svc #0")) {
1753 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
1754 } else {
1755 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
1756 }
1757
1758 if (inst.output) |output| {
1759 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1760 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1761 }
1762 const reg_name = output[2 .. output.len - 1];
1763 const reg = parseRegName(reg_name) orelse
1764 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1765 return MCValue{ .register = reg };
1766 } else {
1767 return MCValue.none;
1768 }
1769 },
1515 .riscv64 => {1770 .riscv64 => {
1516 for (inst.inputs) |input, i| {1771 for (inst.inputs) |input, i| {
1517 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1772 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
...@@ -1584,7 +1839,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1584,7 +1839,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1584 /// resulting REX is meaningful, but will remain the same if it is not.1839 /// resulting REX is meaningful, but will remain the same if it is not.
1585 /// * Deliberately inserting a "meaningless REX" requires explicit usage of1840 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
1586 /// 0x40, and cannot be done via this function.1841 /// 0x40, and cannot be done via this function.
1842 /// W => 64 bit mode
1843 /// R => extension to the MODRM.reg field
1844 /// X => extension to the SIB.index field
1845 /// B => extension to the MODRM.rm field or the SIB.base field
1587 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {1846 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
1847 comptime assert(arch == .x86_64);
1588 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.1848 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
1589 var value: u8 = 0x40;1849 var value: u8 = 0x40;
1590 if (arg.b) {1850 if (arg.b) {
...@@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1681 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});1941 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
1682 },1942 },
1683 .register => |reg| {1943 .register => |reg| {
1684 const abi_size = ty.abiSize(self.target.*);1944 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
1685 const adj_off = stack_offset + abi_size;
1686 try self.code.ensureCapacity(self.code.items.len + 7);
1687 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1688 const reg_id: u8 = @truncate(u3, reg.id());
1689 if (adj_off <= 128) {
1690 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1691 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1692 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1693 const twos_comp = @bitCast(u8, negative_offset);
1694 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp });
1695 } else if (adj_off <= 2147483648) {
1696 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1697 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1698 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1699 const twos_comp = @bitCast(u32, negative_offset);
1700 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM });
1701 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1702 } else {
1703 return self.fail(src, "stack offset too large", .{});
1704 }
1705 },1945 },
1706 .memory => |vaddr| {1946 .memory => |vaddr| {
1707 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});1947 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
...@@ -1709,7 +1949,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1709,7 +1949,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1709 .stack_offset => |off| {1949 .stack_offset => |off| {
1710 if (stack_offset == off)1950 if (stack_offset == off)
1711 return; // Copy stack variable to itself; nothing to do.1951 return; // Copy stack variable to itself; nothing to do.
1712 return self.fail(src, "TODO implement copy stack variable to stack variable", .{});1952
1953 const reg = try self.copyToTmpRegister(src, mcv);
1954 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
1713 },1955 },
1714 },1956 },
1715 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),1957 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
...@@ -1718,6 +1960,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1718,6 +1960,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17181960
1719 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {1961 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1720 switch (arch) {1962 switch (arch) {
1963 .arm => switch (mcv) {
1964 .dead => unreachable,
1965 .ptr_stack_offset => unreachable,
1966 .ptr_embedded_in_code => unreachable,
1967 .unreach, .none => return, // Nothing to do.
1968 .undef => {
1969 if (!self.wantSafety())
1970 return; // The already existing value will do just fine.
1971 // Write the debug undefined value.
1972 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
1973 },
1974 .immediate => |x| {
1975 // TODO better analysis of x to determine the
1976 // least amount of necessary instructions (use
1977 // more intelligent rotating)
1978 if (x <= math.maxInt(u8)) {
1979 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1980 return;
1981 } else if (x <= math.maxInt(u16)) {
1982 // TODO Use movw Note: Not supported on
1983 // all ARM targets!
1984
1985 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1986 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
1987 } else if (x <= math.maxInt(u32)) {
1988 // TODO Use movw and movt Note: Not
1989 // supported on all ARM targets! Also TODO
1990 // write constant to code and load
1991 // relative to pc
1992
1993 // immediate: 0xaabbccdd
1994 // mov reg, #0xaa
1995 // orr reg, reg, #0xbb, 24
1996 // orr reg, reg, #0xcc, 16
1997 // orr reg, reg, #0xdd, 8
1998 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1999 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2000 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2001 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2002 return;
2003 } else {
2004 return self.fail(src, "ARM registers are 32-bit wide", .{});
2005 }
2006 },
2007 .memory => |addr| {
2008 // The value is in memory at a hard-coded address.
2009 // If the type is a pointer, it means the pointer address is at this memory location.
2010 try self.genSetReg(src, reg, .{ .immediate = addr });
2011 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
2012 },
2013 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
2014 },
1721 .riscv64 => switch (mcv) {2015 .riscv64 => switch (mcv) {
1722 .dead => unreachable,2016 .dead => unreachable,
1723 .ptr_stack_offset => unreachable,2017 .ptr_stack_offset => unreachable,
...@@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2027 },2321 },
2028 });2322 });
2029 if (imm >= math.maxInt(U)) {2323 if (imm >= math.maxInt(U)) {
2030 return self.copyToNewRegister(inst);2324 return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
2031 }2325 }
2032 },2326 },
2033 else => {},2327 else => {},
...@@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2150 result.stack_byte_count = next_stack_offset;2444 result.stack_byte_count = next_stack_offset;
2151 result.stack_align = 16;2445 result.stack_align = 16;
2152 },2446 },
2153 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),2447 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
2154 }2448 }
2155 },2449 },
2156 else => if (param_types.len != 0)2450 else => if (param_types.len != 0)
...@@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2197 .i386 => @import("codegen/x86.zig"),2491 .i386 => @import("codegen/x86.zig"),
2198 .x86_64 => @import("codegen/x86_64.zig"),2492 .x86_64 => @import("codegen/x86_64.zig"),
2199 .riscv64 => @import("codegen/riscv64.zig"),2493 .riscv64 => @import("codegen/riscv64.zig"),
2494 .spu_2 => @import("codegen/spu-mk2.zig"),
2495 .arm => @import("codegen/arm.zig"),
2496 .armeb => @import("codegen/arm.zig"),
2200 else => struct {2497 else => struct {
2201 pub const Register = enum {2498 pub const Register = enum {
2202 dummy,2499 dummy,
src-self-hosted/codegen/arm.zig created+607
...@@ -0,0 +1,607 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags neccessary for an
6/// Instruction to be executed
7pub 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
41pub 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
111test "Register.id" {
112 testing.expectEqual(@as(u4, 15), Register.r15.id());
113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114}
115
116pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
119
120/// Represents an instruction in the ARM instruction set architecture
121pub 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
554test "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 @@
1const std = @import("std");
2
3pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter;
4
5pub 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
16pub const InputBehaviour = enum(u2) {
17 zero = 0,
18 immediate = 1,
19 peek = 2,
20 pop = 3,
21};
22
23pub const OutputBehaviour = enum(u2) {
24 discard = 0,
25 push = 1,
26 jump = 2,
27 jump_relative = 3,
28};
29
30pub 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
65pub 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
150pub 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
162pub const Register = enum {
163 dummy,
164
165 pub fn allocIndex(self: Register) ?u4 {
166 return null;
167 }
168};
169
170pub const callee_preserved_regs = [_]Register{};
src-self-hosted/codegen/spu-mk2/interpreter.zig created+166
...@@ -0,0 +1,166 @@
1const std = @import("std");
2const log = std.log.scoped(.SPU_2_Interpreter);
3const spu = @import("../spu-mk2.zig");
4const FlagRegister = spu.FlagRegister;
5const Instruction = spu.Instruction;
6const ExecutionCondition = spu.ExecutionCondition;
7
8pub fn Interpreter(comptime Bus: type) type {
9 return struct {
10 ip: u16 = 0,
11 sp: u16 = undefined,
12 bp: u16 = undefined,
13 fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)),
14 /// This is set to true when we hit an undefined0 instruction, allowing it to
15 /// be used as a trap for testing purposes
16 undefined0: bool = false,
17 /// This is set to true when we hit an undefined1 instruction, allowing it to
18 /// be used as a trap for testing purposes. undefined1 is used as a breakpoint.
19 undefined1: bool = false,
20 bus: Bus,
21
22 pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void {
23 var count: usize = 0;
24 while (size == null or count < size.?) {
25 count += 1;
26 var instruction = @bitCast(Instruction, self.bus.read16(self.ip));
27
28 log.debug("Executing {}\n", .{instruction});
29
30 self.ip +%= 2;
31
32 const execute = switch (instruction.condition) {
33 .always => true,
34 .not_zero => !self.fr.zero,
35 .when_zero => self.fr.zero,
36 .overflow => self.fr.carry,
37 ExecutionCondition.greater_or_equal_zero => !self.fr.negative,
38 else => return error.Unimplemented,
39 };
40
41 if (execute) {
42 const val0 = switch (instruction.input0) {
43 .zero => @as(u16, 0),
44 .immediate => i: {
45 const val = self.bus.read16(@intCast(u16, self.ip));
46 self.ip +%= 2;
47 break :i val;
48 },
49 else => |e| e: {
50 // peek or pop; show value at current SP, and if pop, increment sp
51 const val = self.bus.read16(self.sp);
52 if (e == .pop) {
53 self.sp +%= 2;
54 }
55 break :e val;
56 },
57 };
58 const val1 = switch (instruction.input1) {
59 .zero => @as(u16, 0),
60 .immediate => i: {
61 const val = self.bus.read16(@intCast(u16, self.ip));
62 self.ip +%= 2;
63 break :i val;
64 },
65 else => |e| e: {
66 // peek or pop; show value at current SP, and if pop, increment sp
67 const val = self.bus.read16(self.sp);
68 if (e == .pop) {
69 self.sp +%= 2;
70 }
71 break :e val;
72 },
73 };
74
75 const output: u16 = switch (instruction.command) {
76 .get => self.bus.read16(self.bp +% (2 *% val0)),
77 .set => a: {
78 self.bus.write16(self.bp +% 2 *% val0, val1);
79 break :a val1;
80 },
81 .load8 => self.bus.read8(val0),
82 .load16 => self.bus.read16(val0),
83 .store8 => a: {
84 const val = @truncate(u8, val1);
85 self.bus.write8(val0, val);
86 break :a val;
87 },
88 .store16 => a: {
89 self.bus.write16(val0, val1);
90 break :a val1;
91 },
92 .copy => val0,
93 .add => a: {
94 var val: u16 = undefined;
95 self.fr.carry = @addWithOverflow(u16, val0, val1, &val);
96 break :a val;
97 },
98 .sub => a: {
99 var val: u16 = undefined;
100 self.fr.carry = @subWithOverflow(u16, val0, val1, &val);
101 break :a val;
102 },
103 .spset => a: {
104 self.sp = val0;
105 break :a val0;
106 },
107 .bpset => a: {
108 self.bp = val0;
109 break :a val0;
110 },
111 .frset => a: {
112 const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1);
113 self.fr = @bitCast(FlagRegister, val);
114 break :a val;
115 },
116 .bswap => (val0 >> 8) | (val0 << 8),
117 .bpget => self.bp,
118 .spget => self.sp,
119 .ipget => self.ip +% (2 *% val0),
120 .lsl => val0 << 1,
121 .lsr => val0 >> 1,
122 .@"and" => val0 & val1,
123 .@"or" => val0 | val1,
124 .xor => val0 ^ val1,
125 .not => ~val0,
126 .undefined0 => {
127 self.undefined0 = true;
128 // Break out of the loop, and let the caller decide what to do
129 return;
130 },
131 .undefined1 => {
132 self.undefined1 = true;
133 // Break out of the loop, and let the caller decide what to do
134 return;
135 },
136 .signext => if ((val0 & 0x80) != 0)
137 (val0 & 0xFF) | 0xFF00
138 else
139 (val0 & 0xFF),
140 else => return error.Unimplemented,
141 };
142
143 switch (instruction.output) {
144 .discard => {},
145 .push => {
146 self.sp -%= 2;
147 self.bus.write16(self.sp, output);
148 },
149 .jump => {
150 self.ip = output;
151 },
152 else => return error.Unimplemented,
153 }
154 if (instruction.modify_flags) {
155 self.fr.negative = (output & 0x8000) != 0;
156 self.fr.zero = (output == 0x0000);
157 }
158 } else {
159 if (instruction.input0 == .immediate) self.ip +%= 2;
160 if (instruction.input1 == .immediate) self.ip +%= 2;
161 break;
162 }
163 }
164 }
165 };
166}
src-self-hosted/link.zig+4
...@@ -5,6 +5,9 @@ const fs = std.fs;...@@ -5,6 +5,9 @@ const fs = std.fs;
5const trace = @import("tracy.zig").trace;5const trace = @import("tracy.zig").trace;
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const build_options = @import("build_options");
9
10pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
811
9pub const Options = struct {12pub const Options = struct {
10 target: std.Target,13 target: std.Target,
...@@ -20,6 +23,7 @@ pub const Options = struct {...@@ -20,6 +23,7 @@ pub const Options = struct {
20 /// Used for calculating how much space to reserve for executable program code in case23 /// Used for calculating how much space to reserve for executable program code in case
21 /// the binary file deos not already have such a section.24 /// the binary file deos not already have such a section.
22 program_code_size_hint: u64 = 256 * 1024,25 program_code_size_hint: u64 = 256 * 1024,
26 entry_addr: ?u64 = null,
23};27};
2428
25pub const File = struct {29pub const File = struct {
src-self-hosted/link/Elf.zig+60-27
...@@ -14,12 +14,10 @@ const leb128 = std.debug.leb;...@@ -14,12 +14,10 @@ const leb128 = std.debug.leb;
14const Package = @import("../Package.zig");14const Package = @import("../Package.zig");
15const Value = @import("../value.zig").Value;15const Value = @import("../value.zig").Value;
16const Type = @import("../type.zig").Type;16const Type = @import("../type.zig").Type;
17const build_options = @import("build_options");
18const link = @import("../link.zig");17const link = @import("../link.zig");
19const File = link.File;18const File = link.File;
20const Elf = @This();19const Elf = @This();
2120
22const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
23const default_entry_addr = 0x8000000;21const default_entry_addr = 0x8000000;
2422
25// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.23// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
...@@ -249,8 +247,8 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {...@@ -249,8 +247,8 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
249 .allocator = allocator,247 .allocator = allocator,
250 },248 },
251 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {249 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
252 32 => .p32,250 0 ... 32 => .p32,
253 64 => .p64,251 33 ... 64 => .p64,
254 else => return error.UnsupportedELFArchitecture,252 else => return error.UnsupportedELFArchitecture,
255 },253 },
256 };254 };
...@@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf...@@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
278 .file = file,276 .file = file,
279 },277 },
280 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {278 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
281 32 => .p32,279 0 ... 32 => .p32,
282 64 => .p64,280 33 ... 64 => .p64,
283 else => return error.UnsupportedELFArchitecture,281 else => return error.UnsupportedELFArchitecture,
284 },282 },
285 .shdr_table_dirty = true,283 .shdr_table_dirty = true,
...@@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 {...@@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 {
346344
347/// Returns end pos of collision, if any.345/// Returns end pos of collision, if any.
348fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {346fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
349 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;347 const small_ptr = self.ptr_width == .p32;
350 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);348 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
351 if (start < ehdr_size)349 if (start < ehdr_size)
352 return ehdr_size;350 return ehdr_size;
...@@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
462 const p_align = 0x1000;460 const p_align = 0x1000;
463 const off = self.findFreeSpace(file_size, p_align);461 const off = self.findFreeSpace(file_size, p_align);
464 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });462 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
463 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
465 try self.program_headers.append(self.base.allocator, .{464 try self.program_headers.append(self.base.allocator, .{
466 .p_type = elf.PT_LOAD,465 .p_type = elf.PT_LOAD,
467 .p_offset = off,466 .p_offset = off,
468 .p_filesz = file_size,467 .p_filesz = file_size,
469 .p_vaddr = default_entry_addr,468 .p_vaddr = entry_addr,
470 .p_paddr = default_entry_addr,469 .p_paddr = entry_addr,
471 .p_memsz = file_size,470 .p_memsz = file_size,
472 .p_align = p_align,471 .p_align = p_align,
473 .p_flags = elf.PF_X | elf.PF_R,472 .p_flags = elf.PF_X | elf.PF_R,
...@@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
486 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.485 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
487 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something486 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
488 // else in virtual memory.487 // else in virtual memory.
489 const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000;488 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
490 try self.program_headers.append(self.base.allocator, .{489 try self.program_headers.append(self.base.allocator, .{
491 .p_type = elf.PT_LOAD,490 .p_type = elf.PT_LOAD,
492 .p_offset = off,491 .p_offset = off,
493 .p_filesz = file_size,492 .p_filesz = file_size,
494 .p_vaddr = default_got_addr,493 .p_vaddr = got_addr,
495 .p_paddr = default_got_addr,494 .p_paddr = got_addr,
496 .p_memsz = file_size,495 .p_memsz = file_size,
497 .p_align = p_align,496 .p_align = p_align,
498 .p_flags = elf.PF_R,497 .p_flags = elf.PF_R,
...@@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void {...@@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
863 // Write the form for the compile unit, which must match the abbrev table above.862 // Write the form for the compile unit, which must match the abbrev table above.
864 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);863 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
865 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);864 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
866 const producer_strp = try self.makeDebugString(producer_string);865 const producer_strp = try self.makeDebugString(link.producer_string);
867 // Currently only one compilation unit is supported, so the address range is simply866 // Currently only one compilation unit is supported, so the address range is simply
868 // identical to the main program header virtual address and memory size.867 // identical to the main program header virtual address and memory size.
869 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];868 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
...@@ -1349,6 +1348,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {...@@ -1349,6 +1348,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1349 var already_have_free_list_node = false;1348 var already_have_free_list_node = false;
1350 {1349 {
1351 var i: usize = 0;1350 var i: usize = 0;
1351 // TODO turn text_block_free_list into a hash map
1352 while (i < self.text_block_free_list.items.len) {1352 while (i < self.text_block_free_list.items.len) {
1353 if (self.text_block_free_list.items[i] == text_block) {1353 if (self.text_block_free_list.items[i] == text_block) {
1354 _ = self.text_block_free_list.swapRemove(i);1354 _ = self.text_block_free_list.swapRemove(i);
...@@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {...@@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1360 i += 1;1360 i += 1;
1361 }1361 }
1362 }1362 }
1363 // TODO process free list for dbg info just like we do above for vaddrs
13631364
1364 if (self.last_text_block == text_block) {1365 if (self.last_text_block == text_block) {
1365 // TODO shrink the .text section size here1366 // TODO shrink the .text section size here
1366 self.last_text_block = text_block.prev;1367 self.last_text_block = text_block.prev;
1367 }1368 }
1369 if (self.dbg_info_decl_first == text_block) {
1370 self.dbg_info_decl_first = text_block.dbg_info_next;
1371 }
1372 if (self.dbg_info_decl_last == text_block) {
1373 // TODO shrink the .debug_info section size here
1374 self.dbg_info_decl_last = text_block.dbg_info_prev;
1375 }
13681376
1369 if (text_block.prev) |prev| {1377 if (text_block.prev) |prev| {
1370 prev.next = text_block.next;1378 prev.next = text_block.next;
...@@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {...@@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
1383 } else {1391 } else {
1384 text_block.next = null;1392 text_block.next = null;
1385 }1393 }
1394
1395 if (text_block.dbg_info_prev) |prev| {
1396 prev.dbg_info_next = text_block.dbg_info_next;
1397
1398 // TODO the free list logic like we do for text blocks above
1399 } else {
1400 text_block.dbg_info_prev = null;
1401 }
1402
1403 if (text_block.dbg_info_next) |next| {
1404 next.dbg_info_prev = text_block.dbg_info_prev;
1405 } else {
1406 text_block.dbg_info_next = null;
1407 }
1386}1408}
13871409
1388fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {1410fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
...@@ -1584,10 +1606,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {...@@ -1584,10 +1606,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1584 next.prev = null;1606 next.prev = null;
1585 }1607 }
1586 if (self.dbg_line_fn_first == &decl.fn_link.elf) {1608 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
1587 self.dbg_line_fn_first = null;1609 self.dbg_line_fn_first = decl.fn_link.elf.next;
1588 }1610 }
1589 if (self.dbg_line_fn_last == &decl.fn_link.elf) {1611 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
1590 self.dbg_line_fn_last = null;1612 self.dbg_line_fn_last = decl.fn_link.elf.prev;
1591 }1613 }
1592}1614}
15931615
...@@ -2151,29 +2173,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void {...@@ -2151,29 +2173,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void {
2151fn writeProgHeader(self: *Elf, index: usize) !void {2173fn writeProgHeader(self: *Elf, index: usize) !void {
2152 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2174 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2153 const offset = self.program_headers.items[index].p_offset;2175 const offset = self.program_headers.items[index].p_offset;
2154 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {2176 switch (self.ptr_width) {
2155 32 => {2177 .p32 => {
2156 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};2178 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
2157 if (foreign_endian) {2179 if (foreign_endian) {
2158 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);2180 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
2159 }2181 }
2160 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2182 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2161 },2183 },
2162 64 => {2184 .p64 => {
2163 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};2185 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
2164 if (foreign_endian) {2186 if (foreign_endian) {
2165 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);2187 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
2166 }2188 }
2167 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2189 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2168 },2190 },
2169 else => return error.UnsupportedArchitecture,
2170 }2191 }
2171}2192}
21722193
2173fn writeSectHeader(self: *Elf, index: usize) !void {2194fn writeSectHeader(self: *Elf, index: usize) !void {
2174 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2195 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2175 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {2196 switch (self.ptr_width) {
2176 32 => {2197 .p32 => {
2177 var shdr: [1]elf.Elf32_Shdr = undefined;2198 var shdr: [1]elf.Elf32_Shdr = undefined;
2178 shdr[0] = sectHeaderTo32(self.sections.items[index]);2199 shdr[0] = sectHeaderTo32(self.sections.items[index]);
2179 if (foreign_endian) {2200 if (foreign_endian) {
...@@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2182 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);2203 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
2183 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2204 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2184 },2205 },
2185 64 => {2206 .p64 => {
2186 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};2207 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
2187 if (foreign_endian) {2208 if (foreign_endian) {
2188 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);2209 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
...@@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2190 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);2211 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
2191 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2212 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2192 },2213 },
2193 else => return error.UnsupportedArchitecture,
2194 }2214 }
2195}2215}
21962216
2197fn writeOffsetTableEntry(self: *Elf, index: usize) !void {2217fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2198 const shdr = &self.sections.items[self.got_section_index.?];2218 const shdr = &self.sections.items[self.got_section_index.?];
2199 const phdr = &self.program_headers.items[self.phdr_got_index.?];2219 const phdr = &self.program_headers.items[self.phdr_got_index.?];
2200 const entry_size: u16 = self.ptrWidthBytes();2220 const entry_size: u16 = self.archPtrWidthBytes();
2201 if (self.offset_table_count_dirty) {2221 if (self.offset_table_count_dirty) {
2202 // TODO Also detect virtual address collisions.2222 // TODO Also detect virtual address collisions.
2203 const allocated_size = self.allocatedSize(shdr.sh_offset);2223 const allocated_size = self.allocatedSize(shdr.sh_offset);
...@@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {...@@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2221 }2241 }
2222 const endian = self.base.options.target.cpu.arch.endian();2242 const endian = self.base.options.target.cpu.arch.endian();
2223 const off = shdr.sh_offset + @as(u64, entry_size) * index;2243 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2224 switch (self.ptr_width) {2244 switch (entry_size) {
2225 .p32 => {2245 2 => {
2246 var buf: [2]u8 = undefined;
2247 mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
2248 try self.base.file.?.pwriteAll(&buf, off);
2249 },
2250 4 => {
2226 var buf: [4]u8 = undefined;2251 var buf: [4]u8 = undefined;
2227 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);2252 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
2228 try self.base.file.?.pwriteAll(&buf, off);2253 try self.base.file.?.pwriteAll(&buf, off);
2229 },2254 },
2230 .p64 => {2255 8 => {
2231 var buf: [8]u8 = undefined;2256 var buf: [8]u8 = undefined;
2232 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);2257 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2233 try self.base.file.?.pwriteAll(&buf, off);2258 try self.base.file.?.pwriteAll(&buf, off);
2234 },2259 },
2260 else => unreachable,
2235 }2261 }
2236}2262}
22372263
...@@ -2344,6 +2370,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void {...@@ -2344,6 +2370,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void {
2344 }2370 }
2345}2371}
23462372
2373/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
2347fn ptrWidthBytes(self: Elf) u8 {2374fn ptrWidthBytes(self: Elf) u8 {
2348 return switch (self.ptr_width) {2375 return switch (self.ptr_width) {
2349 .p32 => 4,2376 .p32 => 4,
...@@ -2351,6 +2378,12 @@ fn ptrWidthBytes(self: Elf) u8 {...@@ -2351,6 +2378,12 @@ fn ptrWidthBytes(self: Elf) u8 {
2351 };2378 };
2352}2379}
23532380
2381/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
2382/// in a 32-bit ELF file.
2383fn archPtrWidthBytes(self: Elf) u8 {
2384 return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);
2385}
2386
2354/// The reloc offset for the virtual address of a function in its Line Number Program.2387/// The reloc offset for the virtual address of a function in its Line Number Program.
2355/// Size is a virtual address integer.2388/// Size is a virtual address integer.
2356const dbg_line_vaddr_reloc_index = 3;2389const dbg_line_vaddr_reloc_index = 3;
src-self-hosted/link/MachO.zig+177-38
...@@ -6,29 +6,66 @@ const assert = std.debug.assert;...@@ -6,29 +6,66 @@ const assert = std.debug.assert;
6const fs = std.fs;6const fs = std.fs;
7const log = std.log.scoped(.link);7const log = std.log.scoped(.link);
8const macho = std.macho;8const macho = std.macho;
9const codegen = @import("../codegen.zig");
9const math = std.math;10const math = std.math;
10const mem = std.mem;11const mem = std.mem;
12const trace = @import("../tracy.zig").trace;
13const Type = @import("../type.zig").Type;
1114
12const Module = @import("../Module.zig");15const Module = @import("../Module.zig");
13const link = @import("../link.zig");16const link = @import("../link.zig");
14const File = link.File;17const File = link.File;
1518
19const is_darwin = std.Target.current.os.tag.isDarwin();
20
16pub const base_tag: File.Tag = File.Tag.macho;21pub const base_tag: File.Tag = File.Tag.macho;
1722
18base: File,23base: File,
1924
20/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.25/// List of all load command headers that are in the file.
21/// Same order as in the file.26/// We use it to track number and size of all commands needed by the header.
22segment_cmds: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},27commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){},
28command_file_offset: ?u64 = null,
2329
24/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.30/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
25/// Same order as in the file.31/// Same order as in the file.
32segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
26sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){},33sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){},
34segment_table_offset: ?u64 = null,
2735
36/// Entry point load command
37entry_point_cmd: ?macho.entry_point_command = null,
28entry_addr: ?u64 = null,38entry_addr: ?u64 = null,
2939
40/// Default VM start address set at 4GB
41vm_start_address: u64 = 0x100000000,
42
43seg_table_dirty: bool = false,
44
30error_flags: File.ErrorFlags = File.ErrorFlags{},45error_flags: File.ErrorFlags = File.ErrorFlags{},
3146
47/// TODO ultimately this will be propagated down from main() and set (in this form or another)
48/// when user links against system lib.
49link_against_system: bool = false,
50
51/// `alloc_num / alloc_den` is the factor of padding when allocating.
52const alloc_num = 4;
53const 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.
58const 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.
63const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
64
65const 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
67const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
68
32pub const TextBlock = struct {69pub const TextBlock = struct {
33 pub const empty = TextBlock{};70 pub const empty = TextBlock{};
34};71};
...@@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO...@@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO
80/// Truncates the existing file contents and overwrites the contents.117/// Truncates the existing file contents and overwrites the contents.
81/// Returns an error if `file` is not already open with +read +write +seek abilities.118/// Returns an error if `file` is not already open with +read +write +seek abilities.
82fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {119fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
83 switch (options.output_mode) {
84 .Exe => {},
85 .Obj => {},
86 .Lib => return error.TODOImplementWritingLibFiles,
87 }
88
89 var self: MachO = .{120 var self: MachO = .{
90 .base = .{121 .base = .{
91 .file = file,122 .file = file,
...@@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach...@@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
96 };127 };
97 errdefer self.deinit();128 errdefer self.deinit();
98129
99 if (options.output_mode == .Exe) {130 switch (options.output_mode) {
100 // The first segment command for executables is always a __PAGEZERO segment.131 .Exe => {
101 try self.segment_cmds.append(allocator, .{132 // The first segment command for executables is always a __PAGEZERO segment.
102 .cmd = macho.LC_SEGMENT_64,133 const pagezero = .{
103 .cmdsize = @sizeOf(macho.segment_command_64),134 .cmd = macho.LC_SEGMENT_64,
104 .segname = self.makeString("__PAGEZERO"),135 .cmdsize = commandSize(@sizeOf(macho.segment_command_64)),
105 .vmaddr = 0,136 .segname = makeString("__PAGEZERO"),
106 .vmsize = 0,137 .vmaddr = 0,
107 .fileoff = 0,138 .vmsize = self.vm_start_address,
108 .filesize = 0,139 .fileoff = 0,
109 .maxprot = 0,140 .filesize = 0,
110 .initprot = 0,141 .maxprot = 0,
111 .nsects = 0,142 .initprot = 0,
112 .flags = 0,143 .nsects = 0,
113 });144 .flags = 0,
145 };
146 try self.commands.append(allocator, .{
147 .cmd = pagezero.cmd,
148 .cmdsize = pagezero.cmdsize,
149 });
150 try self.segments.append(allocator, pagezero);
151 },
152 .Obj => return error.TODOImplementWritingObjFiles,
153 .Lib => return error.TODOImplementWritingLibFiles,
114 }154 }
115155
116 return self;156 try self.populateMissingMetadata();
117}
118157
119fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 {158 return self;
120 var buf: [16]u8 = undefined;
121 if (bytes.len > buf.len) @compileError("MachO segment/section name too long");
122 mem.copy(u8, buf[0..], bytes);
123 return buf;
124}159}
125160
126fn writeMachOHeader(self: *MachO) !void {161fn writeMachOHeader(self: *MachO) !void {
...@@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void {...@@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void {
156 };191 };
157 hdr.filetype = filetype;192 hdr.filetype = filetype;
158193
159 // TODO consider other commands194 const ncmds = try math.cast(u32, self.commands.items.len);
160 const ncmds = try math.cast(u32, self.segment_cmds.items.len);
161 hdr.ncmds = ncmds;195 hdr.ncmds = ncmds;
162 hdr.sizeofcmds = ncmds * @sizeOf(macho.segment_command_64);196
197 var sizeof_cmds: u32 = 0;
198 for (self.commands.items) |cmd| {
199 sizeof_cmds += cmd.cmdsize;
200 }
201 hdr.sizeofcmds = sizeof_cmds;
163202
164 // TODO should these be set to something else?203 // TODO should these be set to something else?
165 hdr.flags = 0;204 hdr.flags = 0;
...@@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void {...@@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void {
169}208}
170209
171pub fn flush(self: *MachO, module: *Module) !void {210pub fn flush(self: *MachO, module: *Module) !void {
172 // TODO implement flush211 // Save segments first
173 {212 {
174 const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segment_cmds.items.len);213 const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segments.items.len);
175 defer self.base.allocator.free(buf);214 defer self.base.allocator.free(buf);
176215
216 self.command_file_offset = @sizeOf(macho.mach_header_64);
217
177 for (buf) |*seg, i| {218 for (buf) |*seg, i| {
178 seg.* = self.segment_cmds.items[i];219 seg.* = self.segments.items[i];
220 self.command_file_offset.? += self.segments.items[i].cmdsize;
179 }221 }
180222
181 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64));223 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64));
182 }224 }
183225
226 switch (self.base.options.output_mode) {
227 .Exe => {
228 if (self.link_against_system) {
229 if (is_darwin) {
230 {
231 // Specify path to dynamic linker dyld
232 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));
233 const load_dylinker = [1]macho.dylinker_command{
234 .{
235 .cmd = macho.LC_LOAD_DYLINKER,
236 .cmdsize = cmdsize,
237 .name = @sizeOf(macho.dylinker_command),
238 },
239 };
240 try self.commands.append(self.base.allocator, .{
241 .cmd = macho.LC_LOAD_DYLINKER,
242 .cmdsize = cmdsize,
243 });
244
245 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
246
247 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
248 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
249
250 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
251 self.command_file_offset.? += cmdsize;
252 }
253
254 {
255 // Link against libSystem
256 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));
257 // According to Apple's manual, we should obtain current libSystem version using libc call
258 // NSVersionOfRunTimeLibrary.
259 const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);
260 const dylib = .{
261 .name = @sizeOf(macho.dylib_command),
262 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
263 .current_version = version,
264 .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering
265 };
266 const load_dylib = [1]macho.dylib_command{
267 .{
268 .cmd = macho.LC_LOAD_DYLIB,
269 .cmdsize = cmdsize,
270 .dylib = dylib,
271 },
272 };
273 try self.commands.append(self.base.allocator, .{
274 .cmd = macho.LC_LOAD_DYLIB,
275 .cmdsize = cmdsize,
276 });
277
278 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
279
280 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
281 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
282
283 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
284 self.command_file_offset.? += cmdsize;
285 }
286 } else {
287 @panic("linking against libSystem on non-native target is unsupported");
288 }
289 }
290 },
291 .Obj => return error.TODOImplementWritingObjFiles,
292 .Lib => return error.TODOImplementWritingLibFiles,
293 }
294
184 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {295 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
185 log.debug("flushing. no_entry_point_found = true\n", .{});296 log.debug("flushing. no_entry_point_found = true\n", .{});
186 self.error_flags.no_entry_point_found = true;297 self.error_flags.no_entry_point_found = true;
...@@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void {
192}303}
193304
194pub fn deinit(self: *MachO) void {305pub fn deinit(self: *MachO) void {
195 self.segment_cmds.deinit(self.base.allocator);306 self.commands.deinit(self.base.allocator);
307 self.segments.deinit(self.base.allocator);
196 self.sections.deinit(self.base.allocator);308 self.sections.deinit(self.base.allocator);
197}309}
198310
...@@ -214,3 +326,30 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}...@@ -214,3 +326,30 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
214pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {326pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
215 @panic("TODO implement getDeclVAddr for MachO");327 @panic("TODO implement getDeclVAddr for MachO");
216}328}
329
330pub fn populateMissingMetadata(self: *MachO) !void {}
331
332fn 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
339fn 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
346fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {
347 if (size == 0) return;
348
349 const buf = try self.base.allocator.alloc(u8, size);
350 defer self.base.allocator.free(buf);
351
352 mem.set(u8, buf[0..], 0);
353
354 try self.base.file.?.pwriteAll(buf, file_offset);
355}
src-self-hosted/test.zig+115-2
...@@ -583,7 +583,10 @@ pub const TestContext = struct {...@@ -583,7 +583,10 @@ pub const TestContext = struct {
583583
584 switch (case.target.getExternalExecutor()) {584 switch (case.target.getExternalExecutor()) {
585 .native => try argv.append(exe_path),585 .native => try argv.append(exe_path),
586 .unavailable => return, // No executor available; pass test.586 .unavailable => {
587 try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);
588 return; // Pass test.
589 },
587590
588 .qemu => |qemu_bin_name| if (enable_qemu) {591 .qemu => |qemu_bin_name| if (enable_qemu) {
589 // TODO Ability for test cases to specify whether to link libc.592 // TODO Ability for test cases to specify whether to link libc.
...@@ -635,7 +638,6 @@ pub const TestContext = struct {...@@ -635,7 +638,6 @@ pub const TestContext = struct {
635 var test_node = update_node.start("test", null);638 var test_node = update_node.start("test", null);
636 test_node.activate();639 test_node.activate();
637 defer test_node.end();640 defer test_node.end();
638
639 defer allocator.free(exec_result.stdout);641 defer allocator.free(exec_result.stdout);
640 defer allocator.free(exec_result.stderr);642 defer allocator.free(exec_result.stderr);
641 switch (exec_result.term) {643 switch (exec_result.term) {
...@@ -657,4 +659,115 @@ pub const TestContext = struct {...@@ -657,4 +659,115 @@ pub const TestContext = struct {
657 }659 }
658 }660 }
659 }661 }
662
663 fn runInterpreterIfAvailable(
664 self: *TestContext,
665 gpa: *Allocator,
666 node: *std.Progress.Node,
667 case: Case,
668 tmp_dir: std.fs.Dir,
669 bin_name: []const u8,
670 ) !void {
671 const arch = case.target.cpu_arch orelse return;
672 switch (arch) {
673 .spu_2 => return self.runSpu2Interpreter(gpa, node, case, tmp_dir, bin_name),
674 else => return,
675 }
676 }
677
678 fn runSpu2Interpreter(
679 self: *TestContext,
680 gpa: *Allocator,
681 update_node: *std.Progress.Node,
682 case: Case,
683 tmp_dir: std.fs.Dir,
684 bin_name: []const u8,
685 ) !void {
686 const spu = @import("codegen/spu-mk2.zig");
687 if (case.target.os_tag) |os| {
688 if (os != .freestanding) {
689 std.debug.panic("Only freestanding makes sense for SPU-II tests!", .{});
690 }
691 } else {
692 std.debug.panic("SPU_2 has no native OS, check the test!", .{});
693 }
694
695 var interpreter = spu.Interpreter(struct {
696 RAM: [0x10000]u8 = undefined,
697
698 pub fn read8(bus: @This(), addr: u16) u8 {
699 return bus.RAM[addr];
700 }
701 pub fn read16(bus: @This(), addr: u16) u16 {
702 return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);
703 }
704
705 pub fn write8(bus: *@This(), addr: u16, val: u8) void {
706 bus.RAM[addr] = val;
707 }
708
709 pub fn write16(bus: *@This(), addr: u16, val: u16) void {
710 std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);
711 }
712 }){
713 .bus = .{},
714 };
715
716 {
717 var load_node = update_node.start("load", null);
718 load_node.activate();
719 defer load_node.end();
720
721 var file = try tmp_dir.openFile(bin_name, .{ .read = true });
722 defer file.close();
723
724 const header = try std.elf.readHeader(file);
725 var iterator = header.program_header_iterator(file);
726
727 var none_loaded = true;
728
729 while (try iterator.next()) |phdr| {
730 if (phdr.p_type != std.elf.PT_LOAD) {
731 std.debug.print("Encountered unexpected ELF program header: type {}\n", .{phdr.p_type});
732 std.process.exit(1);
733 }
734 if (phdr.p_paddr != phdr.p_vaddr) {
735 std.debug.print("Physical address does not match virtual address in ELF header!\n", .{});
736 std.process.exit(1);
737 }
738 if (phdr.p_filesz != phdr.p_memsz) {
739 std.debug.print("Physical size does not match virtual size in ELF header!\n", .{});
740 std.process.exit(1);
741 }
742 if ((try file.pread(interpreter.bus.RAM[phdr.p_paddr .. phdr.p_paddr + phdr.p_filesz], phdr.p_offset)) != phdr.p_filesz) {
743 std.debug.print("Read less than expected from ELF file!", .{});
744 std.process.exit(1);
745 }
746 std.log.scoped(.spu2_test).debug("Loaded 0x{x} bytes to 0x{x:0<4}\n", .{ phdr.p_filesz, phdr.p_paddr });
747 none_loaded = false;
748 }
749 if (none_loaded) {
750 std.debug.print("No data found in ELF file!\n", .{});
751 std.process.exit(1);
752 }
753 }
754
755 var exec_node = update_node.start("execute", null);
756 exec_node.activate();
757 defer exec_node.end();
758
759 var blocks: u16 = 1000;
760 const block_size = 1000;
761 while (!interpreter.undefined0) {
762 const pre_ip = interpreter.ip;
763 if (blocks > 0) {
764 blocks -= 1;
765 try interpreter.ExecuteBlock(block_size);
766 if (pre_ip == interpreter.ip) {
767 std.debug.print("Infinite loop detected in SPU II test!\n", .{});
768 std.process.exit(1);
769 }
770 }
771 }
772 }
660};773};
src-self-hosted/type.zig+251-7
...@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;...@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const Target = std.Target;5const Target = std.Target;
6const Module = @import("Module.zig");
67
7/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.8/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
8/// It's important for this type to be small.9/// It's important for this type to be small.
...@@ -52,7 +53,7 @@ pub const Type = extern union {...@@ -52,7 +53,7 @@ pub const Type = extern union {
52 .bool => return .Bool,53 .bool => return .Bool,
53 .void => return .Void,54 .void => return .Void,
54 .type => return .Type,55 .type => return .Type,
55 .anyerror => return .ErrorSet,56 .error_set, .error_set_single, .anyerror => return .ErrorSet,
56 .comptime_int => return .ComptimeInt,57 .comptime_int => return .ComptimeInt,
57 .comptime_float => return .ComptimeFloat,58 .comptime_float => return .ComptimeFloat,
58 .noreturn => return .NoReturn,59 .noreturn => return .NoReturn,
...@@ -84,6 +85,10 @@ pub const Type = extern union {...@@ -84,6 +85,10 @@ pub const Type = extern union {
84 .optional_single_mut_pointer,85 .optional_single_mut_pointer,
85 => return .Optional,86 => return .Optional,
86 .enum_literal => return .EnumLiteral,87 .enum_literal => return .EnumLiteral,
88
89 .anyerror_void_error_union, .error_union => return .ErrorUnion,
90
91 .anyframe_T, .@"anyframe" => return .AnyFrame,
87 }92 }
88 }93 }
8994
...@@ -151,6 +156,9 @@ pub const Type = extern union {...@@ -151,6 +156,9 @@ pub const Type = extern union {
151 .ComptimeInt => return true,156 .ComptimeInt => return true,
152 .Undefined => return true,157 .Undefined => return true,
153 .Null => return true,158 .Null => return true,
159 .AnyFrame => {
160 return a.elemType().eql(b.elemType());
161 },
154 .Pointer => {162 .Pointer => {
155 // Hot path for common case:163 // Hot path for common case:
156 if (a.castPointer()) |a_payload| {164 if (a.castPointer()) |a_payload| {
...@@ -225,7 +233,6 @@ pub const Type = extern union {...@@ -225,7 +233,6 @@ pub const Type = extern union {
225 .BoundFn,233 .BoundFn,
226 .Opaque,234 .Opaque,
227 .Frame,235 .Frame,
228 .AnyFrame,
229 .Vector,236 .Vector,
230 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),237 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
231 }238 }
...@@ -343,6 +350,8 @@ pub const Type = extern union {...@@ -343,6 +350,8 @@ pub const Type = extern union {
343 .single_const_pointer_to_comptime_int,350 .single_const_pointer_to_comptime_int,
344 .const_slice_u8,351 .const_slice_u8,
345 .enum_literal,352 .enum_literal,
353 .anyerror_void_error_union,
354 .@"anyframe",
346 => unreachable,355 => unreachable,
347356
348 .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),357 .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
...@@ -397,6 +406,7 @@ pub const Type = extern union {...@@ -397,6 +406,7 @@ pub const Type = extern union {
397 .optional_single_mut_pointer,406 .optional_single_mut_pointer,
398 .optional_single_const_pointer,407 .optional_single_const_pointer,
399 => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),408 => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
409 .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
400410
401 .pointer => {411 .pointer => {
402 const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);412 const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
...@@ -416,6 +426,19 @@ pub const Type = extern union {...@@ -416,6 +426,19 @@ pub const Type = extern union {
416 };426 };
417 return Type{ .ptr_otherwise = &new_payload.base };427 return Type{ .ptr_otherwise = &new_payload.base };
418 },428 },
429 .error_union => {
430 const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise);
431 const new_payload = try allocator.create(Payload.ErrorUnion);
432 new_payload.* = .{
433 .base = payload.base,
434
435 .error_set = try payload.error_set.copy(allocator),
436 .payload = try payload.payload.copy(allocator),
437 };
438 return Type{ .ptr_otherwise = &new_payload.base };
439 },
440 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
441 .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
419 }442 }
420 }443 }
421444
...@@ -482,6 +505,8 @@ pub const Type = extern union {...@@ -482,6 +505,8 @@ pub const Type = extern union {
482 .@"null" => return out_stream.writeAll("@TypeOf(null)"),505 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
483 .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),506 .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),
484507
508 .@"anyframe" => return out_stream.writeAll("anyframe"),
509 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
485 .const_slice_u8 => return out_stream.writeAll("[]const u8"),510 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
486 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),511 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
487 .fn_void_no_args => return out_stream.writeAll("fn() void"),512 .fn_void_no_args => return out_stream.writeAll("fn() void"),
...@@ -500,6 +525,12 @@ pub const Type = extern union {...@@ -500,6 +525,12 @@ pub const Type = extern union {
500 continue;525 continue;
501 },526 },
502527
528 .anyframe_T => {
529 const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise);
530 try out_stream.print("anyframe->", .{});
531 ty = payload.return_type;
532 continue;
533 },
503 .array_u8 => {534 .array_u8 => {
504 const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);535 const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
505 return out_stream.print("[{}]u8", .{payload.len});536 return out_stream.print("[{}]u8", .{payload.len});
...@@ -622,6 +653,21 @@ pub const Type = extern union {...@@ -622,6 +653,21 @@ pub const Type = extern union {
622 ty = payload.pointee_type;653 ty = payload.pointee_type;
623 continue;654 continue;
624 },655 },
656 .error_union => {
657 const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise);
658 try payload.error_set.format("", .{}, out_stream);
659 try out_stream.writeAll("!");
660 ty = payload.payload;
661 continue;
662 },
663 .error_set => {
664 const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
665 return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
666 },
667 .error_set_single => {
668 const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
669 return out_stream.print("error{{{}}}", .{payload.name});
670 },
625 }671 }
626 unreachable;672 unreachable;
627 }673 }
...@@ -715,6 +761,11 @@ pub const Type = extern union {...@@ -715,6 +761,11 @@ pub const Type = extern union {
715 .optional,761 .optional,
716 .optional_single_mut_pointer,762 .optional_single_mut_pointer,
717 .optional_single_const_pointer,763 .optional_single_const_pointer,
764 .@"anyframe",
765 .anyframe_T,
766 .anyerror_void_error_union,
767 .error_set,
768 .error_set_single,
718 => true,769 => true,
719 // TODO lazy types770 // TODO lazy types
720 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,771 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
...@@ -723,6 +774,11 @@ pub const Type = extern union {...@@ -723,6 +774,11 @@ pub const Type = extern union {
723 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,774 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,
724 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,775 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
725776
777 .error_union => {
778 const payload = self.cast(Payload.ErrorUnion).?;
779 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
780 },
781
726 .c_void,782 .c_void,
727 .void,783 .void,
728 .type,784 .type,
...@@ -756,6 +812,7 @@ pub const Type = extern union {...@@ -756,6 +812,7 @@ pub const Type = extern union {
756 .fn_ccc_void_no_args, // represents machine code; not a pointer812 .fn_ccc_void_no_args, // represents machine code; not a pointer
757 .function, // represents machine code; not a pointer813 .function, // represents machine code; not a pointer
758 => return switch (target.cpu.arch) {814 => return switch (target.cpu.arch) {
815 .arm => 4,
759 .riscv64 => 2,816 .riscv64 => 2,
760 else => 1,817 else => 1,
761 },818 },
...@@ -778,6 +835,8 @@ pub const Type = extern union {...@@ -778,6 +835,8 @@ pub const Type = extern union {
778 .mut_slice,835 .mut_slice,
779 .optional_single_const_pointer,836 .optional_single_const_pointer,
780 .optional_single_mut_pointer,837 .optional_single_mut_pointer,
838 .@"anyframe",
839 .anyframe_T,
781 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),840 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
782841
783 .pointer => {842 .pointer => {
...@@ -802,7 +861,11 @@ pub const Type = extern union {...@@ -802,7 +861,11 @@ pub const Type = extern union {
802 .f128 => return 16,861 .f128 => return 16,
803 .c_longdouble => return 16,862 .c_longdouble => return 16,
804863
805 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type864 .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
806869
807 .array, .array_sentinel => return self.elemType().abiAlignment(target),870 .array, .array_sentinel => return self.elemType().abiAlignment(target),
808871
...@@ -828,6 +891,16 @@ pub const Type = extern union {...@@ -828,6 +891,16 @@ pub const Type = extern union {
828 return child_type.abiAlignment(target);891 return child_type.abiAlignment(target);
829 },892 },
830893
894 .error_union => {
895 const payload = self.cast(Payload.ErrorUnion).?;
896 if (!payload.error_set.hasCodeGenBits()) {
897 return payload.payload.abiAlignment(target);
898 } else if (!payload.payload.hasCodeGenBits()) {
899 return payload.error_set.abiAlignment(target);
900 }
901 @panic("TODO abiAlignment error union");
902 },
903
831 .c_void,904 .c_void,
832 .void,905 .void,
833 .type,906 .type,
...@@ -881,12 +954,15 @@ pub const Type = extern union {...@@ -881,12 +954,15 @@ pub const Type = extern union {
881 .i32, .u32 => return 4,954 .i32, .u32 => return 4,
882 .i64, .u64 => return 8,955 .i64, .u64 => return 8,
883956
884 .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),957 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
885958
886 .const_slice,959 .const_slice,
887 .mut_slice,960 .mut_slice,
888 .const_slice_u8,961 => {
889 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,962 if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2;
963 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
964 },
965 .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
890966
891 .optional_single_const_pointer,967 .optional_single_const_pointer,
892 .optional_single_mut_pointer,968 .optional_single_mut_pointer,
...@@ -922,7 +998,11 @@ pub const Type = extern union {...@@ -922,7 +998,11 @@ pub const Type = extern union {
922 .f128 => return 16,998 .f128 => return 16,
923 .c_longdouble => return 16,999 .c_longdouble => return 16,
9241000
925 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type1001 .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
9261006
927 .int_signed, .int_unsigned => {1007 .int_signed, .int_unsigned => {
928 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|1008 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
...@@ -949,6 +1029,18 @@ pub const Type = extern union {...@@ -949,6 +1029,18 @@ pub const Type = extern union {
949 // to the child type's ABI alignment.1029 // to the child type's ABI alignment.
950 return child_type.abiAlignment(target) + child_type.abiSize(target);1030 return child_type.abiAlignment(target) + child_type.abiSize(target);
951 },1031 },
1032
1033 .error_union => {
1034 const payload = self.cast(Payload.ErrorUnion).?;
1035 if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
1036 return 0;
1037 } else if (!payload.error_set.hasCodeGenBits()) {
1038 return payload.payload.abiSize(target);
1039 } else if (!payload.payload.hasCodeGenBits()) {
1040 return payload.error_set.abiSize(target);
1041 }
1042 @panic("TODO abiSize error union");
1043 },
952 };1044 };
953 }1045 }
9541046
...@@ -1009,6 +1101,12 @@ pub const Type = extern union {...@@ -1009,6 +1101,12 @@ pub const Type = extern union {
1009 .c_mut_pointer,1101 .c_mut_pointer,
1010 .const_slice,1102 .const_slice,
1011 .mut_slice,1103 .mut_slice,
1104 .error_union,
1105 .@"anyframe",
1106 .anyframe_T,
1107 .anyerror_void_error_union,
1108 .error_set,
1109 .error_set_single,
1012 => false,1110 => false,
10131111
1014 .single_const_pointer,1112 .single_const_pointer,
...@@ -1077,6 +1175,12 @@ pub const Type = extern union {...@@ -1077,6 +1175,12 @@ pub const Type = extern union {
1077 .optional_single_mut_pointer,1175 .optional_single_mut_pointer,
1078 .optional_single_const_pointer,1176 .optional_single_const_pointer,
1079 .enum_literal,1177 .enum_literal,
1178 .error_union,
1179 .@"anyframe",
1180 .anyframe_T,
1181 .anyerror_void_error_union,
1182 .error_set,
1183 .error_set_single,
1080 => false,1184 => false,
10811185
1082 .const_slice,1186 .const_slice,
...@@ -1142,6 +1246,12 @@ pub const Type = extern union {...@@ -1142,6 +1246,12 @@ pub const Type = extern union {
1142 .optional_single_const_pointer,1246 .optional_single_const_pointer,
1143 .enum_literal,1247 .enum_literal,
1144 .mut_slice,1248 .mut_slice,
1249 .error_union,
1250 .@"anyframe",
1251 .anyframe_T,
1252 .anyerror_void_error_union,
1253 .error_set,
1254 .error_set_single,
1145 => false,1255 => false,
11461256
1147 .single_const_pointer,1257 .single_const_pointer,
...@@ -1216,6 +1326,12 @@ pub const Type = extern union {...@@ -1216,6 +1326,12 @@ pub const Type = extern union {
1216 .optional_single_mut_pointer,1326 .optional_single_mut_pointer,
1217 .optional_single_const_pointer,1327 .optional_single_const_pointer,
1218 .enum_literal,1328 .enum_literal,
1329 .error_union,
1330 .@"anyframe",
1331 .anyframe_T,
1332 .anyerror_void_error_union,
1333 .error_set,
1334 .error_set_single,
1219 => false,1335 => false,
12201336
1221 .pointer => {1337 .pointer => {
...@@ -1327,6 +1443,12 @@ pub const Type = extern union {...@@ -1327,6 +1443,12 @@ pub const Type = extern union {
1327 .optional_single_const_pointer,1443 .optional_single_const_pointer,
1328 .optional_single_mut_pointer,1444 .optional_single_mut_pointer,
1329 .enum_literal,1445 .enum_literal,
1446 .error_union,
1447 .@"anyframe",
1448 .anyframe_T,
1449 .anyerror_void_error_union,
1450 .error_set,
1451 .error_set_single,
1330 => unreachable,1452 => unreachable,
13311453
1332 .array => self.cast(Payload.Array).?.elem_type,1454 .array => self.cast(Payload.Array).?.elem_type,
...@@ -1448,6 +1570,12 @@ pub const Type = extern union {...@@ -1448,6 +1570,12 @@ pub const Type = extern union {
1448 .optional_single_mut_pointer,1570 .optional_single_mut_pointer,
1449 .optional_single_const_pointer,1571 .optional_single_const_pointer,
1450 .enum_literal,1572 .enum_literal,
1573 .error_union,
1574 .@"anyframe",
1575 .anyframe_T,
1576 .anyerror_void_error_union,
1577 .error_set,
1578 .error_set_single,
1451 => unreachable,1579 => unreachable,
14521580
1453 .array => self.cast(Payload.Array).?.len,1581 .array => self.cast(Payload.Array).?.len,
...@@ -1515,6 +1643,12 @@ pub const Type = extern union {...@@ -1515,6 +1643,12 @@ pub const Type = extern union {
1515 .optional_single_mut_pointer,1643 .optional_single_mut_pointer,
1516 .optional_single_const_pointer,1644 .optional_single_const_pointer,
1517 .enum_literal,1645 .enum_literal,
1646 .error_union,
1647 .@"anyframe",
1648 .anyframe_T,
1649 .anyerror_void_error_union,
1650 .error_set,
1651 .error_set_single,
1518 => unreachable,1652 => unreachable,
15191653
1520 .array, .array_u8 => return null,1654 .array, .array_u8 => return null,
...@@ -1580,6 +1714,12 @@ pub const Type = extern union {...@@ -1580,6 +1714,12 @@ pub const Type = extern union {
1580 .optional_single_mut_pointer,1714 .optional_single_mut_pointer,
1581 .optional_single_const_pointer,1715 .optional_single_const_pointer,
1582 .enum_literal,1716 .enum_literal,
1717 .error_union,
1718 .@"anyframe",
1719 .anyframe_T,
1720 .anyerror_void_error_union,
1721 .error_set,
1722 .error_set_single,
1583 => false,1723 => false,
15841724
1585 .int_signed,1725 .int_signed,
...@@ -1648,6 +1788,12 @@ pub const Type = extern union {...@@ -1648,6 +1788,12 @@ pub const Type = extern union {
1648 .optional_single_mut_pointer,1788 .optional_single_mut_pointer,
1649 .optional_single_const_pointer,1789 .optional_single_const_pointer,
1650 .enum_literal,1790 .enum_literal,
1791 .error_union,
1792 .@"anyframe",
1793 .anyframe_T,
1794 .anyerror_void_error_union,
1795 .error_set,
1796 .error_set_single,
1651 => false,1797 => false,
16521798
1653 .int_unsigned,1799 .int_unsigned,
...@@ -1706,6 +1852,12 @@ pub const Type = extern union {...@@ -1706,6 +1852,12 @@ pub const Type = extern union {
1706 .optional_single_mut_pointer,1852 .optional_single_mut_pointer,
1707 .optional_single_const_pointer,1853 .optional_single_const_pointer,
1708 .enum_literal,1854 .enum_literal,
1855 .error_union,
1856 .@"anyframe",
1857 .anyframe_T,
1858 .anyerror_void_error_union,
1859 .error_set,
1860 .error_set_single,
1709 => unreachable,1861 => unreachable,
17101862
1711 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },1863 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
...@@ -1782,6 +1934,12 @@ pub const Type = extern union {...@@ -1782,6 +1934,12 @@ pub const Type = extern union {
1782 .optional_single_mut_pointer,1934 .optional_single_mut_pointer,
1783 .optional_single_const_pointer,1935 .optional_single_const_pointer,
1784 .enum_literal,1936 .enum_literal,
1937 .error_union,
1938 .@"anyframe",
1939 .anyframe_T,
1940 .anyerror_void_error_union,
1941 .error_set,
1942 .error_set_single,
1785 => false,1943 => false,
17861944
1787 .usize,1945 .usize,
...@@ -1887,6 +2045,12 @@ pub const Type = extern union {...@@ -1887,6 +2045,12 @@ pub const Type = extern union {
1887 .optional_single_mut_pointer,2045 .optional_single_mut_pointer,
1888 .optional_single_const_pointer,2046 .optional_single_const_pointer,
1889 .enum_literal,2047 .enum_literal,
2048 .error_union,
2049 .@"anyframe",
2050 .anyframe_T,
2051 .anyerror_void_error_union,
2052 .error_set,
2053 .error_set_single,
1890 => unreachable,2054 => unreachable,
1891 };2055 };
1892 }2056 }
...@@ -1958,6 +2122,12 @@ pub const Type = extern union {...@@ -1958,6 +2122,12 @@ pub const Type = extern union {
1958 .optional_single_mut_pointer,2122 .optional_single_mut_pointer,
1959 .optional_single_const_pointer,2123 .optional_single_const_pointer,
1960 .enum_literal,2124 .enum_literal,
2125 .error_union,
2126 .@"anyframe",
2127 .anyframe_T,
2128 .anyerror_void_error_union,
2129 .error_set,
2130 .error_set_single,
1961 => unreachable,2131 => unreachable,
1962 }2132 }
1963 }2133 }
...@@ -2028,6 +2198,12 @@ pub const Type = extern union {...@@ -2028,6 +2198,12 @@ pub const Type = extern union {
2028 .optional_single_mut_pointer,2198 .optional_single_mut_pointer,
2029 .optional_single_const_pointer,2199 .optional_single_const_pointer,
2030 .enum_literal,2200 .enum_literal,
2201 .error_union,
2202 .@"anyframe",
2203 .anyframe_T,
2204 .anyerror_void_error_union,
2205 .error_set,
2206 .error_set_single,
2031 => unreachable,2207 => unreachable,
2032 }2208 }
2033 }2209 }
...@@ -2098,6 +2274,12 @@ pub const Type = extern union {...@@ -2098,6 +2274,12 @@ pub const Type = extern union {
2098 .optional_single_mut_pointer,2274 .optional_single_mut_pointer,
2099 .optional_single_const_pointer,2275 .optional_single_const_pointer,
2100 .enum_literal,2276 .enum_literal,
2277 .error_union,
2278 .@"anyframe",
2279 .anyframe_T,
2280 .anyerror_void_error_union,
2281 .error_set,
2282 .error_set_single,
2101 => unreachable,2283 => unreachable,
2102 };2284 };
2103 }2285 }
...@@ -2165,6 +2347,12 @@ pub const Type = extern union {...@@ -2165,6 +2347,12 @@ pub const Type = extern union {
2165 .optional_single_mut_pointer,2347 .optional_single_mut_pointer,
2166 .optional_single_const_pointer,2348 .optional_single_const_pointer,
2167 .enum_literal,2349 .enum_literal,
2350 .error_union,
2351 .@"anyframe",
2352 .anyframe_T,
2353 .anyerror_void_error_union,
2354 .error_set,
2355 .error_set_single,
2168 => unreachable,2356 => unreachable,
2169 };2357 };
2170 }2358 }
...@@ -2232,6 +2420,12 @@ pub const Type = extern union {...@@ -2232,6 +2420,12 @@ pub const Type = extern union {
2232 .optional_single_mut_pointer,2420 .optional_single_mut_pointer,
2233 .optional_single_const_pointer,2421 .optional_single_const_pointer,
2234 .enum_literal,2422 .enum_literal,
2423 .error_union,
2424 .@"anyframe",
2425 .anyframe_T,
2426 .anyerror_void_error_union,
2427 .error_set,
2428 .error_set_single,
2235 => unreachable,2429 => unreachable,
2236 };2430 };
2237 }2431 }
...@@ -2299,6 +2493,12 @@ pub const Type = extern union {...@@ -2299,6 +2493,12 @@ pub const Type = extern union {
2299 .optional_single_mut_pointer,2493 .optional_single_mut_pointer,
2300 .optional_single_const_pointer,2494 .optional_single_const_pointer,
2301 .enum_literal,2495 .enum_literal,
2496 .error_union,
2497 .@"anyframe",
2498 .anyframe_T,
2499 .anyerror_void_error_union,
2500 .error_set,
2501 .error_set_single,
2302 => false,2502 => false,
2303 };2503 };
2304 }2504 }
...@@ -2350,6 +2550,12 @@ pub const Type = extern union {...@@ -2350,6 +2550,12 @@ pub const Type = extern union {
2350 .optional_single_mut_pointer,2550 .optional_single_mut_pointer,
2351 .optional_single_const_pointer,2551 .optional_single_const_pointer,
2352 .enum_literal,2552 .enum_literal,
2553 .anyerror_void_error_union,
2554 .anyframe_T,
2555 .@"anyframe",
2556 .error_union,
2557 .error_set,
2558 .error_set_single,
2353 => return null,2559 => return null,
23542560
2355 .void => return Value.initTag(.void_value),2561 .void => return Value.initTag(.void_value),
...@@ -2453,6 +2659,12 @@ pub const Type = extern union {...@@ -2453,6 +2659,12 @@ pub const Type = extern union {
2453 .optional_single_mut_pointer,2659 .optional_single_mut_pointer,
2454 .optional_single_const_pointer,2660 .optional_single_const_pointer,
2455 .enum_literal,2661 .enum_literal,
2662 .error_union,
2663 .@"anyframe",
2664 .anyframe_T,
2665 .anyerror_void_error_union,
2666 .error_set,
2667 .error_set_single,
2456 => return false,2668 => return false,
24572669
2458 .c_const_pointer,2670 .c_const_pointer,
...@@ -2510,6 +2722,8 @@ pub const Type = extern union {...@@ -2510,6 +2722,8 @@ pub const Type = extern union {
2510 fn_naked_noreturn_no_args,2722 fn_naked_noreturn_no_args,
2511 fn_ccc_void_no_args,2723 fn_ccc_void_no_args,
2512 single_const_pointer_to_comptime_int,2724 single_const_pointer_to_comptime_int,
2725 anyerror_void_error_union,
2726 @"anyframe",
2513 const_slice_u8, // See last_no_payload_tag below.2727 const_slice_u8, // See last_no_payload_tag below.
2514 // After this, the tag requires a payload.2728 // After this, the tag requires a payload.
25152729
...@@ -2532,6 +2746,10 @@ pub const Type = extern union {...@@ -2532,6 +2746,10 @@ pub const Type = extern union {
2532 optional,2746 optional,
2533 optional_single_mut_pointer,2747 optional_single_mut_pointer,
2534 optional_single_const_pointer,2748 optional_single_const_pointer,
2749 error_union,
2750 anyframe_T,
2751 error_set,
2752 error_set_single,
25352753
2536 pub const last_no_payload_tag = Tag.const_slice_u8;2754 pub const last_no_payload_tag = Tag.const_slice_u8;
2537 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;2755 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -2613,6 +2831,32 @@ pub const Type = extern union {...@@ -2613,6 +2831,32 @@ pub const Type = extern union {
2613 @"volatile": bool,2831 @"volatile": bool,
2614 size: std.builtin.TypeInfo.Pointer.Size,2832 size: std.builtin.TypeInfo.Pointer.Size,
2615 };2833 };
2834
2835 pub const ErrorUnion = struct {
2836 base: Payload = .{ .tag = .error_union },
2837
2838 error_set: Type,
2839 payload: Type,
2840 };
2841
2842 pub const AnyFrame = struct {
2843 base: Payload = .{ .tag = .anyframe_T },
2844
2845 return_type: Type,
2846 };
2847
2848 pub const ErrorSet = struct {
2849 base: Payload = .{ .tag = .error_set },
2850
2851 decl: *Module.Decl,
2852 };
2853
2854 pub const ErrorSetSingle = struct {
2855 base: Payload = .{ .tag = .error_set_single },
2856
2857 /// memory is owned by `Module`
2858 name: []const u8,
2859 };
2616 };2860 };
2617};2861};
26182862
src-self-hosted/value.zig+88-3
...@@ -61,6 +61,7 @@ pub const Value = extern union {...@@ -61,6 +61,7 @@ pub const Value = extern union {
61 single_const_pointer_to_comptime_int_type,61 single_const_pointer_to_comptime_int_type,
62 const_slice_u8_type,62 const_slice_u8_type,
63 enum_literal_type,63 enum_literal_type,
64 anyframe_type,
6465
65 undef,66 undef,
66 zero,67 zero,
...@@ -90,6 +91,8 @@ pub const Value = extern union {...@@ -90,6 +91,8 @@ pub const Value = extern union {
90 float_64,91 float_64,
91 float_128,92 float_128,
92 enum_literal,93 enum_literal,
94 error_set,
95 @"error",
9396
94 pub const last_no_payload_tag = Tag.bool_false;97 pub const last_no_payload_tag = Tag.bool_false;
95 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;98 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -168,6 +171,7 @@ pub const Value = extern union {...@@ -168,6 +171,7 @@ pub const Value = extern union {
168 .single_const_pointer_to_comptime_int_type,171 .single_const_pointer_to_comptime_int_type,
169 .const_slice_u8_type,172 .const_slice_u8_type,
170 .enum_literal_type,173 .enum_literal_type,
174 .anyframe_type,
171 .undef,175 .undef,
172 .zero,176 .zero,
173 .void_value,177 .void_value,
...@@ -241,6 +245,10 @@ pub const Value = extern union {...@@ -241,6 +245,10 @@ pub const Value = extern union {
241 };245 };
242 return Value{ .ptr_otherwise = &new_payload.base };246 return Value{ .ptr_otherwise = &new_payload.base };
243 },247 },
248 .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
249
250 // memory is managed by the declaration
251 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
244 }252 }
245 }253 }
246254
...@@ -300,6 +308,7 @@ pub const Value = extern union {...@@ -300,6 +308,7 @@ pub const Value = extern union {
300 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),308 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
301 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),309 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
302 .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),310 .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
311 .anyframe_type => return out_stream.writeAll("anyframe"),
303312
304 .null_value => return out_stream.writeAll("null"),313 .null_value => return out_stream.writeAll("null"),
305 .undef => return out_stream.writeAll("undefined"),314 .undef => return out_stream.writeAll("undefined"),
...@@ -343,6 +352,15 @@ pub const Value = extern union {...@@ -343,6 +352,15 @@ pub const Value = extern union {
343 .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),352 .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
344 .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),353 .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
345 .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),354 .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
355 .error_set => {
356 const error_set = val.cast(Payload.ErrorSet).?;
357 try out_stream.writeAll("error{");
358 for (error_set.fields.items()) |entry| {
359 try out_stream.print("{},", .{entry.value});
360 }
361 return out_stream.writeAll("}");
362 },
363 .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}),
346 };364 };
347 }365 }
348366
...@@ -363,11 +381,9 @@ pub const Value = extern union {...@@ -363,11 +381,9 @@ pub const Value = extern union {
363 }381 }
364382
365 /// Asserts that the value is representable as a type.383 /// Asserts that the value is representable as a type.
366 pub fn toType(self: Value) Type {384 pub fn toType(self: Value, allocator: *Allocator) !Type {
367 return switch (self.tag()) {385 return switch (self.tag()) {
368 .ty => self.cast(Payload.Ty).?.ty,386 .ty => self.cast(Payload.Ty).?.ty,
369 .int_type => @panic("TODO int type to type"),
370
371 .u8_type => Type.initTag(.u8),387 .u8_type => Type.initTag(.u8),
372 .i8_type => Type.initTag(.i8),388 .i8_type => Type.initTag(.i8),
373 .u16_type => Type.initTag(.u16),389 .u16_type => Type.initTag(.u16),
...@@ -408,6 +424,26 @@ pub const Value = extern union {...@@ -408,6 +424,26 @@ pub const Value = extern union {
408 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),424 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
409 .const_slice_u8_type => Type.initTag(.const_slice_u8),425 .const_slice_u8_type => Type.initTag(.const_slice_u8),
410 .enum_literal_type => Type.initTag(.enum_literal),426 .enum_literal_type => Type.initTag(.enum_literal),
427 .anyframe_type => Type.initTag(.@"anyframe"),
428
429 .int_type => {
430 const payload = self.cast(Payload.IntType).?;
431 if (payload.signed) {
432 const new = try allocator.create(Type.Payload.IntSigned);
433 new.* = .{ .bits = payload.bits };
434 return Type.initPayload(&new.base);
435 } else {
436 const new = try allocator.create(Type.Payload.IntUnsigned);
437 new.* = .{ .bits = payload.bits };
438 return Type.initPayload(&new.base);
439 }
440 },
441 .error_set => {
442 const payload = self.cast(Payload.ErrorSet).?;
443 const new = try allocator.create(Type.Payload.ErrorSet);
444 new.* = .{ .decl = payload.decl };
445 return Type.initPayload(&new.base);
446 },
411447
412 .undef,448 .undef,
413 .zero,449 .zero,
...@@ -433,6 +469,7 @@ pub const Value = extern union {...@@ -433,6 +469,7 @@ pub const Value = extern union {
433 .float_64,469 .float_64,
434 .float_128,470 .float_128,
435 .enum_literal,471 .enum_literal,
472 .@"error",
436 => unreachable,473 => unreachable,
437 };474 };
438 }475 }
...@@ -482,6 +519,7 @@ pub const Value = extern union {...@@ -482,6 +519,7 @@ pub const Value = extern union {
482 .single_const_pointer_to_comptime_int_type,519 .single_const_pointer_to_comptime_int_type,
483 .const_slice_u8_type,520 .const_slice_u8_type,
484 .enum_literal_type,521 .enum_literal_type,
522 .anyframe_type,
485 .null_value,523 .null_value,
486 .function,524 .function,
487 .variable,525 .variable,
...@@ -498,6 +536,8 @@ pub const Value = extern union {...@@ -498,6 +536,8 @@ pub const Value = extern union {
498 .unreachable_value,536 .unreachable_value,
499 .empty_array,537 .empty_array,
500 .enum_literal,538 .enum_literal,
539 .error_set,
540 .@"error",
501 => unreachable,541 => unreachable,
502542
503 .undef => unreachable,543 .undef => unreachable,
...@@ -560,6 +600,7 @@ pub const Value = extern union {...@@ -560,6 +600,7 @@ pub const Value = extern union {
560 .single_const_pointer_to_comptime_int_type,600 .single_const_pointer_to_comptime_int_type,
561 .const_slice_u8_type,601 .const_slice_u8_type,
562 .enum_literal_type,602 .enum_literal_type,
603 .anyframe_type,
563 .null_value,604 .null_value,
564 .function,605 .function,
565 .variable,606 .variable,
...@@ -576,6 +617,8 @@ pub const Value = extern union {...@@ -576,6 +617,8 @@ pub const Value = extern union {
576 .unreachable_value,617 .unreachable_value,
577 .empty_array,618 .empty_array,
578 .enum_literal,619 .enum_literal,
620 .error_set,
621 .@"error",
579 => unreachable,622 => unreachable,
580623
581 .undef => unreachable,624 .undef => unreachable,
...@@ -638,6 +681,7 @@ pub const Value = extern union {...@@ -638,6 +681,7 @@ pub const Value = extern union {
638 .single_const_pointer_to_comptime_int_type,681 .single_const_pointer_to_comptime_int_type,
639 .const_slice_u8_type,682 .const_slice_u8_type,
640 .enum_literal_type,683 .enum_literal_type,
684 .anyframe_type,
641 .null_value,685 .null_value,
642 .function,686 .function,
643 .variable,687 .variable,
...@@ -654,6 +698,8 @@ pub const Value = extern union {...@@ -654,6 +698,8 @@ pub const Value = extern union {
654 .unreachable_value,698 .unreachable_value,
655 .empty_array,699 .empty_array,
656 .enum_literal,700 .enum_literal,
701 .error_set,
702 .@"error",
657 => unreachable,703 => unreachable,
658704
659 .undef => unreachable,705 .undef => unreachable,
...@@ -742,6 +788,7 @@ pub const Value = extern union {...@@ -742,6 +788,7 @@ pub const Value = extern union {
742 .single_const_pointer_to_comptime_int_type,788 .single_const_pointer_to_comptime_int_type,
743 .const_slice_u8_type,789 .const_slice_u8_type,
744 .enum_literal_type,790 .enum_literal_type,
791 .anyframe_type,
745 .null_value,792 .null_value,
746 .function,793 .function,
747 .variable,794 .variable,
...@@ -759,6 +806,8 @@ pub const Value = extern union {...@@ -759,6 +806,8 @@ pub const Value = extern union {
759 .unreachable_value,806 .unreachable_value,
760 .empty_array,807 .empty_array,
761 .enum_literal,808 .enum_literal,
809 .error_set,
810 .@"error",
762 => unreachable,811 => unreachable,
763812
764 .zero,813 .zero,
...@@ -825,6 +874,7 @@ pub const Value = extern union {...@@ -825,6 +874,7 @@ pub const Value = extern union {
825 .single_const_pointer_to_comptime_int_type,874 .single_const_pointer_to_comptime_int_type,
826 .const_slice_u8_type,875 .const_slice_u8_type,
827 .enum_literal_type,876 .enum_literal_type,
877 .anyframe_type,
828 .null_value,878 .null_value,
829 .function,879 .function,
830 .variable,880 .variable,
...@@ -841,6 +891,8 @@ pub const Value = extern union {...@@ -841,6 +891,8 @@ pub const Value = extern union {
841 .unreachable_value,891 .unreachable_value,
842 .empty_array,892 .empty_array,
843 .enum_literal,893 .enum_literal,
894 .error_set,
895 .@"error",
844 => unreachable,896 => unreachable,
845897
846 .zero,898 .zero,
...@@ -988,6 +1040,7 @@ pub const Value = extern union {...@@ -988,6 +1040,7 @@ pub const Value = extern union {
988 .single_const_pointer_to_comptime_int_type,1040 .single_const_pointer_to_comptime_int_type,
989 .const_slice_u8_type,1041 .const_slice_u8_type,
990 .enum_literal_type,1042 .enum_literal_type,
1043 .anyframe_type,
991 .bool_true,1044 .bool_true,
992 .bool_false,1045 .bool_false,
993 .null_value,1046 .null_value,
...@@ -1007,6 +1060,8 @@ pub const Value = extern union {...@@ -1007,6 +1060,8 @@ pub const Value = extern union {
1007 .void_value,1060 .void_value,
1008 .unreachable_value,1061 .unreachable_value,
1009 .enum_literal,1062 .enum_literal,
1063 .error_set,
1064 .@"error",
1010 => unreachable,1065 => unreachable,
10111066
1012 .zero => false,1067 .zero => false,
...@@ -1063,6 +1118,7 @@ pub const Value = extern union {...@@ -1063,6 +1118,7 @@ pub const Value = extern union {
1063 .single_const_pointer_to_comptime_int_type,1118 .single_const_pointer_to_comptime_int_type,
1064 .const_slice_u8_type,1119 .const_slice_u8_type,
1065 .enum_literal_type,1120 .enum_literal_type,
1121 .anyframe_type,
1066 .null_value,1122 .null_value,
1067 .function,1123 .function,
1068 .variable,1124 .variable,
...@@ -1076,6 +1132,8 @@ pub const Value = extern union {...@@ -1076,6 +1132,8 @@ pub const Value = extern union {
1076 .unreachable_value,1132 .unreachable_value,
1077 .empty_array,1133 .empty_array,
1078 .enum_literal,1134 .enum_literal,
1135 .error_set,
1136 .@"error",
1079 => unreachable,1137 => unreachable,
10801138
1081 .zero,1139 .zero,
...@@ -1197,6 +1255,7 @@ pub const Value = extern union {...@@ -1197,6 +1255,7 @@ pub const Value = extern union {
1197 .single_const_pointer_to_comptime_int_type,1255 .single_const_pointer_to_comptime_int_type,
1198 .const_slice_u8_type,1256 .const_slice_u8_type,
1199 .enum_literal_type,1257 .enum_literal_type,
1258 .anyframe_type,
1200 .zero,1259 .zero,
1201 .bool_true,1260 .bool_true,
1202 .bool_false,1261 .bool_false,
...@@ -1218,6 +1277,8 @@ pub const Value = extern union {...@@ -1218,6 +1277,8 @@ pub const Value = extern union {
1218 .unreachable_value,1277 .unreachable_value,
1219 .empty_array,1278 .empty_array,
1220 .enum_literal,1279 .enum_literal,
1280 .error_set,
1281 .@"error",
1221 => unreachable,1282 => unreachable,
12221283
1223 .ref_val => self.cast(Payload.RefVal).?.val,1284 .ref_val => self.cast(Payload.RefVal).?.val,
...@@ -1276,6 +1337,7 @@ pub const Value = extern union {...@@ -1276,6 +1337,7 @@ pub const Value = extern union {
1276 .single_const_pointer_to_comptime_int_type,1337 .single_const_pointer_to_comptime_int_type,
1277 .const_slice_u8_type,1338 .const_slice_u8_type,
1278 .enum_literal_type,1339 .enum_literal_type,
1340 .anyframe_type,
1279 .zero,1341 .zero,
1280 .bool_true,1342 .bool_true,
1281 .bool_false,1343 .bool_false,
...@@ -1297,6 +1359,8 @@ pub const Value = extern union {...@@ -1297,6 +1359,8 @@ pub const Value = extern union {
1297 .void_value,1359 .void_value,
1298 .unreachable_value,1360 .unreachable_value,
1299 .enum_literal,1361 .enum_literal,
1362 .error_set,
1363 .@"error",
1300 => unreachable,1364 => unreachable,
13011365
1302 .empty_array => unreachable, // out of bounds array index1366 .empty_array => unreachable, // out of bounds array index
...@@ -1372,6 +1436,7 @@ pub const Value = extern union {...@@ -1372,6 +1436,7 @@ pub const Value = extern union {
1372 .single_const_pointer_to_comptime_int_type,1436 .single_const_pointer_to_comptime_int_type,
1373 .const_slice_u8_type,1437 .const_slice_u8_type,
1374 .enum_literal_type,1438 .enum_literal_type,
1439 .anyframe_type,
1375 .zero,1440 .zero,
1376 .empty_array,1441 .empty_array,
1377 .bool_true,1442 .bool_true,
...@@ -1393,6 +1458,8 @@ pub const Value = extern union {...@@ -1393,6 +1458,8 @@ pub const Value = extern union {
1393 .float_128,1458 .float_128,
1394 .void_value,1459 .void_value,
1395 .enum_literal,1460 .enum_literal,
1461 .error_set,
1462 .@"error",
1396 => false,1463 => false,
13971464
1398 .undef => unreachable,1465 .undef => unreachable,
...@@ -1522,6 +1589,24 @@ pub const Value = extern union {...@@ -1522,6 +1589,24 @@ pub const Value = extern union {
1522 base: Payload = .{ .tag = .float_128 },1589 base: Payload = .{ .tag = .float_128 },
1523 val: f128,1590 val: f128,
1524 };1591 };
1592
1593 pub const ErrorSet = struct {
1594 base: Payload = .{ .tag = .error_set },
1595
1596 // TODO revisit this when we have the concept of the error tag type
1597 fields: std.StringHashMapUnmanaged(u16),
1598 decl: *Module.Decl,
1599 };
1600
1601 pub const Error = struct {
1602 base: Payload = .{ .tag = .@"error" },
1603
1604 // TODO revisit this when we have the concept of the error tag type
1605 /// `name` is owned by `Module` and will be valid for the entire
1606 /// duration of the compilation.
1607 name: []const u8,
1608 value: u16,
1609 };
1525 };1610 };
15261611
1527 /// Big enough to fit any non-BigInt value1612 /// Big enough to fit any non-BigInt value
src-self-hosted/zir.zig+56-1
...@@ -43,6 +43,8 @@ pub const Inst = struct {...@@ -43,6 +43,8 @@ pub const Inst = struct {
43 alloc,43 alloc,
44 /// Same as `alloc` except the type is inferred.44 /// Same as `alloc` except the type is inferred.
45 alloc_inferred,45 alloc_inferred,
46 /// Create an `anyframe->T`.
47 anyframe_type,
46 /// Array concatenation. `a ++ b`48 /// Array concatenation. `a ++ b`
47 array_cat,49 array_cat,
48 /// Array multiplication `a ** b`50 /// Array multiplication `a ** b`
...@@ -70,6 +72,8 @@ pub const Inst = struct {...@@ -70,6 +72,8 @@ pub const Inst = struct {
70 /// A typed result location pointer is bitcasted to a new result location pointer.72 /// A typed result location pointer is bitcasted to a new result location pointer.
71 /// The new result location pointer has an inferred type.73 /// The new result location pointer has an inferred type.
72 bitcast_result_ptr,74 bitcast_result_ptr,
75 /// Bitwise NOT. `~`
76 bitnot,
73 /// Bitwise OR. `|`77 /// Bitwise OR. `|`
74 bitor,78 bitor,
75 /// A labeled block of code, which can return a value.79 /// A labeled block of code, which can return a value.
...@@ -133,6 +137,10 @@ pub const Inst = struct {...@@ -133,6 +137,10 @@ pub const Inst = struct {
133 ensure_result_used,137 ensure_result_used,
134 /// Emits a compile error if an error is ignored.138 /// Emits a compile error if an error is ignored.
135 ensure_result_non_error,139 ensure_result_non_error,
140 /// Create a `E!T` type.
141 error_union_type,
142 /// Create an error set.
143 error_set,
136 /// Export the provided Decl as the provided name in the compilation's output object file.144 /// Export the provided Decl as the provided name in the compilation's output object file.
137 @"export",145 @"export",
138 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer146 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
...@@ -160,6 +168,8 @@ pub const Inst = struct {...@@ -160,6 +168,8 @@ pub const Inst = struct {
160 /// A labeled block of code that loops forever. At the end of the body it is implied168 /// A labeled block of code that loops forever. At the end of the body it is implied
161 /// to repeat; no explicit "repeat" instruction terminates loop bodies.169 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
162 loop,170 loop,
171 /// Merge two error sets into one, `E1 || E2`.
172 merge_error_sets,
163 /// Ambiguously remainder division or modulus. If the computation would possibly have173 /// Ambiguously remainder division or modulus. If the computation would possibly have
164 /// a different value depending on whether the operation is remainder division or modulus,174 /// a different value depending on whether the operation is remainder division or modulus,
165 /// a compile error is emitted. Otherwise the computation is performed.175 /// a compile error is emitted. Otherwise the computation is performed.
...@@ -286,6 +296,8 @@ pub const Inst = struct {...@@ -286,6 +296,8 @@ pub const Inst = struct {
286 .unwrap_err_safe,296 .unwrap_err_safe,
287 .unwrap_err_unsafe,297 .unwrap_err_unsafe,
288 .ensure_err_payload_void,298 .ensure_err_payload_void,
299 .anyframe_type,
300 .bitnot,
289 => UnOp,301 => UnOp,
290302
291 .add,303 .add,
...@@ -316,6 +328,8 @@ pub const Inst = struct {...@@ -316,6 +328,8 @@ pub const Inst = struct {
316 .bitcast,328 .bitcast,
317 .coerce_result_ptr,329 .coerce_result_ptr,
318 .xor,330 .xor,
331 .error_union_type,
332 .merge_error_sets,
319 => BinOp,333 => BinOp,
320334
321 .arg => Arg,335 .arg => Arg,
...@@ -347,6 +361,7 @@ pub const Inst = struct {...@@ -347,6 +361,7 @@ pub const Inst = struct {
347 .condbr => CondBr,361 .condbr => CondBr,
348 .ptr_type => PtrType,362 .ptr_type => PtrType,
349 .enum_literal => EnumLiteral,363 .enum_literal => EnumLiteral,
364 .error_set => ErrorSet,
350 };365 };
351 }366 }
352367
...@@ -438,6 +453,11 @@ pub const Inst = struct {...@@ -438,6 +453,11 @@ pub const Inst = struct {
438 .ptr_type,453 .ptr_type,
439 .ensure_err_payload_void,454 .ensure_err_payload_void,
440 .enum_literal,455 .enum_literal,
456 .merge_error_sets,
457 .anyframe_type,
458 .error_union_type,
459 .bitnot,
460 .error_set,
441 => false,461 => false,
442462
443 .@"break",463 .@"break",
...@@ -908,6 +928,16 @@ pub const Inst = struct {...@@ -908,6 +928,16 @@ pub const Inst = struct {
908 },928 },
909 kw_args: struct {},929 kw_args: struct {},
910 };930 };
931
932 pub const ErrorSet = struct {
933 pub const base_tag = Tag.error_set;
934 base: Inst,
935
936 positionals: struct {
937 fields: [][]const u8,
938 },
939 kw_args: struct {},
940 };
911};941};
912942
913pub const ErrorMsg = struct {943pub const ErrorMsg = struct {
...@@ -1142,6 +1172,16 @@ const Writer = struct {...@@ -1142,6 +1172,16 @@ const Writer = struct {
1142 const name = self.loop_table.get(param).?;1172 const name = self.loop_table.get(param).?;
1143 return std.zig.renderStringLiteral(name, stream);1173 return std.zig.renderStringLiteral(name, stream);
1144 },1174 },
1175 [][]const u8 => {
1176 try stream.writeByte('[');
1177 for (param) |str, i| {
1178 if (i != 0) {
1179 try stream.writeAll(", ");
1180 }
1181 try std.zig.renderStringLiteral(str, stream);
1182 }
1183 try stream.writeByte(']');
1184 },
1145 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),1185 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
1146 }1186 }
1147 }1187 }
...@@ -1539,6 +1579,21 @@ const Parser = struct {...@@ -1539,6 +1579,21 @@ const Parser = struct {
1539 const name = try self.parseStringLiteral();1579 const name = try self.parseStringLiteral();
1540 return self.loop_table.get(name).?;1580 return self.loop_table.get(name).?;
1541 },1581 },
1582 [][]const u8 => {
1583 try requireEatBytes(self, "[");
1584 skipSpace(self);
1585 if (eatByte(self, ']')) return &[0][]const u8{};
1586
1587 var strings = std.ArrayList([]const u8).init(&self.arena.allocator);
1588 while (true) {
1589 skipSpace(self);
1590 try strings.append(try self.parseStringLiteral());
1591 skipSpace(self);
1592 if (!eatByte(self, ',')) break;
1593 }
1594 try requireEatBytes(self, "]");
1595 return strings.toOwnedSlice();
1596 },
1542 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),1597 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
1543 }1598 }
1544 return self.fail("TODO parse parameter {}", .{@typeName(T)});1599 return self.fail("TODO parse parameter {}", .{@typeName(T)});
...@@ -1961,7 +2016,7 @@ const EmitZIR = struct {...@@ -1961,7 +2016,7 @@ const EmitZIR = struct {
1961 return self.emitUnnamedDecl(&as_inst.base);2016 return self.emitUnnamedDecl(&as_inst.base);
1962 },2017 },
1963 .Type => {2018 .Type => {
1964 const ty = typed_value.val.toType();2019 const ty = try typed_value.val.toType(&self.arena.allocator);
1965 return self.emitType(src, ty);2020 return self.emitType(src, ty);
1966 },2021 },
1967 .Fn => {2022 .Fn => {
src-self-hosted/zir_sema.zig+126-4
...@@ -97,6 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -97,6 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
97 .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),97 .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
98 .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),98 .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
99 .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),99 .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),
100 .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?),
100 .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),101 .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),
101 .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),102 .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),
102 .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?),103 .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?),
...@@ -122,6 +123,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -122,6 +123,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
122 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),123 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
123 .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),124 .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
124 .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?),125 .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?),
126 .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?),
127 .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
128 .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
129 .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
125 }130 }
126}131}
127132
...@@ -145,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir...@@ -145,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir
145 for (block_scope.instructions.items) |inst| {150 for (block_scope.instructions.items) |inst| {
146 if (inst.castTag(.ret)) |ret| {151 if (inst.castTag(.ret)) |ret| {
147 const val = try mod.resolveConstValue(&block_scope.base, ret.operand);152 const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
148 return val.toType();153 return val.toType(block_scope.base.arena());
149 } else {154 } else {
150 return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});155 return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
151 }156 }
...@@ -270,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {...@@ -270,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
270 const wanted_type = Type.initTag(.@"type");275 const wanted_type = Type.initTag(.@"type");
271 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);276 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
272 const val = try mod.resolveConstValue(scope, coerced_inst);277 const val = try mod.resolveConstValue(scope, coerced_inst);
273 return val.toType();278 return val.toType(scope.arena());
274}279}
275280
276fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {281fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
...@@ -431,6 +436,7 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr...@@ -431,6 +436,7 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr
431 // The bytes references memory inside the ZIR module, which can get deallocated436 // The bytes references memory inside the ZIR module, which can get deallocated
432 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.437 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
433 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);438 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
439 errdefer new_decl_arena.deinit();
434 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);440 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
435441
436 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);442 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
...@@ -716,6 +722,54 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar...@@ -716,6 +722,54 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar
716 return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));722 return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));
717}723}
718724
725fn 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
736fn 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
742fn 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
769fn 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
719fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {773fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
720 const payload = try scope.arena().create(Value.Payload.Bytes);774 const payload = try scope.arena().create(Value.Payload.Bytes);
721 payload.* = .{775 payload.* = .{
...@@ -858,8 +912,72 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr...@@ -858,8 +912,72 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
858 );912 );
859 }913 }
860 },914 },
861 else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),915 .Pointer => {
916 const ptr_child = elem_ty.elemType();
917 switch (ptr_child.zigTypeTag()) {
918 .Array => {
919 if (mem.eql(u8, field_name, "len")) {
920 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
921 len_payload.* = .{ .int = ptr_child.arrayLen() };
922
923 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
924 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
925
926 return mod.constInst(scope, fieldptr.base.src, .{
927 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
928 .val = Value.initPayload(&ref_payload.base),
929 });
930 } else {
931 return mod.fail(
932 scope,
933 fieldptr.positionals.field_name.src,
934 "no member named '{}' in '{}'",
935 .{ field_name, elem_ty },
936 );
937 }
938 },
939 else => {},
940 }
941 },
942 .Type => {
943 _ = try mod.resolveConstValue(scope, object_ptr);
944 const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src);
945 const val = result.value().?;
946 const child_type = try val.toType(scope.arena());
947 switch (child_type.zigTypeTag()) {
948 .ErrorSet => {
949 // TODO resolve inferred error sets
950 const entry = if (val.cast(Value.Payload.ErrorSet)) |payload|
951 (payload.fields.getEntry(field_name) orelse
952 return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).*
953 else try mod.getErrorValue(field_name);
954
955 const error_payload = try scope.arena().create(Value.Payload.Error);
956 error_payload.* = .{
957 .name = entry.key,
958 .value = entry.value,
959 };
960
961 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
962 ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) };
963
964 const result_type = if (child_type.tag() == .anyerror) blk: {
965 const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle);
966 result_payload.* = .{ .name = entry.key };
967 break :blk Type.initPayload(&result_payload.base);
968 } else child_type;
969
970 return mod.constInst(scope, fieldptr.base.src, .{
971 .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
972 .val = Value.initPayload(&ref_payload.base),
973 });
974 },
975 else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}),
976 }
977 },
978 else => {},
862 }979 }
980 return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty});
863}981}
864982
865fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {983fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
...@@ -983,6 +1101,10 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE...@@ -983,6 +1101,10 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE
983 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});1101 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
984}1102}
9851103
1104fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1105 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{});
1106}
1107
986fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {1108fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
987 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});1109 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
988}1110}
...@@ -1348,7 +1470,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne...@@ -1348,7 +1470,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne
13481470
1349 if (host_size != 0 and bit_offset >= host_size * 8)1471 if (host_size != 0 and bit_offset >= host_size * 8)
1350 return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});1472 return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
1351 1473
1352 const sentinel = if (inst.kw_args.sentinel) |some|1474 const sentinel = if (inst.kw_args.sentinel) |some|
1353 (try resolveInstConst(mod, scope, some)).val1475 (try resolveInstConst(mod, scope, some)).val
1354 else1476 else
src/analyze.cpp+122-103
...@@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2586 return ErrorNone;2586 return ErrorNone;
25872587
2588 AstNode *decl_node = enum_type->data.enumeration.decl_node;2588 AstNode *decl_node = enum_type->data.enumeration.decl_node;
2589 assert(decl_node->type == NodeTypeContainerDecl);
25902589
2591 if (enum_type->data.enumeration.resolve_loop_flag) {2590 if (enum_type->data.enumeration.resolve_loop_flag) {
2592 if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) {2591 if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) {
...@@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26002599
2601 enum_type->data.enumeration.resolve_loop_flag = true;2600 enum_type->data.enumeration.resolve_loop_flag = true;
26022601
2603 assert(!enum_type->data.enumeration.fields);2602 uint32_t field_count;
2604 uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;2603 if (decl_node->type == NodeTypeContainerDecl) {
2605 if (field_count == 0) {2604 assert(!enum_type->data.enumeration.fields);
2606 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));2605 field_count = (uint32_t)decl_node->data.container_decl.fields.length;
2606 if (field_count == 0) {
2607 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
26072608
2608 enum_type->data.enumeration.src_field_count = field_count;2609 enum_type->data.enumeration.src_field_count = field_count;
2609 enum_type->data.enumeration.fields = nullptr;2610 enum_type->data.enumeration.fields = nullptr;
2610 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2611 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2611 return ErrorSemanticAnalyzeFail;2612 return ErrorSemanticAnalyzeFail;
2613 }
2614 } else {
2615 field_count = enum_type->data.enumeration.src_field_count;
2612 }2616 }
26132617
2614 Scope *scope = &enum_type->data.enumeration.decls_scope->base;2618 Scope *scope = &enum_type->data.enumeration.decls_scope->base;
...@@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2624 enum_type->abi_size = tag_int_type->abi_size;2628 enum_type->abi_size = tag_int_type->abi_size;
2625 enum_type->abi_align = tag_int_type->abi_align;2629 enum_type->abi_align = tag_int_type->abi_align;
26262630
2627 if (decl_node->data.container_decl.init_arg_expr != nullptr) {2631 ZigType *wanted_tag_int_type = nullptr;
2628 ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);2632 if (decl_node->type == NodeTypeContainerDecl) {
2633 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
2634 wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
2635 }
2636 } else {
2637 wanted_tag_int_type = enum_type->data.enumeration.tag_int_type;
2638 }
2639
2640 if (wanted_tag_int_type != nullptr) {
2629 if (type_is_invalid(wanted_tag_int_type)) {2641 if (type_is_invalid(wanted_tag_int_type)) {
2630 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2642 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2631 } else if (wanted_tag_int_type->id != ZigTypeIdInt &&2643 } else if (wanted_tag_int_type->id != ZigTypeIdInt &&
...@@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2654 }2666 }
2655 }2667 }
26562668
2657 enum_type->data.enumeration.non_exhaustive = false;
2658 enum_type->data.enumeration.tag_int_type = tag_int_type;2669 enum_type->data.enumeration.tag_int_type = tag_int_type;
2659 enum_type->size_in_bits = tag_int_type->size_in_bits;2670 enum_type->size_in_bits = tag_int_type->size_in_bits;
2660 enum_type->abi_size = tag_int_type->abi_size;2671 enum_type->abi_size = tag_int_type->abi_size;
...@@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2663 BigInt bi_one;2674 BigInt bi_one;
2664 bigint_init_unsigned(&bi_one, 1);2675 bigint_init_unsigned(&bi_one, 1);
26652676
2666 AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);2677 if (decl_node->type == NodeTypeContainerDecl) {
2667 if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {2678 AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
2679 if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
2680 if (last_field_node->data.struct_field.value != nullptr) {
2681 add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
2682 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2683 }
2684 if (decl_node->data.container_decl.init_arg_expr == nullptr) {
2685 add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size"));
2686 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2687 }
2688 enum_type->data.enumeration.non_exhaustive = true;
2689 } else {
2690 enum_type->data.enumeration.non_exhaustive = false;
2691 }
2692 }
2693
2694 if (enum_type->data.enumeration.non_exhaustive) {
2668 field_count -= 1;2695 field_count -= 1;
2669 if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {2696 if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {
2670 add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value"));2697 add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value"));
2671 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2698 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2672 }2699 }
2673 if (decl_node->data.container_decl.init_arg_expr == nullptr) {
2674 add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size"));
2675 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2676 }
2677 if (last_field_node->data.struct_field.value != nullptr) {
2678 add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
2679 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2680 }
2681 enum_type->data.enumeration.non_exhaustive = true;
2682 }2700 }
26832701
2684 enum_type->data.enumeration.src_field_count = field_count;2702 if (decl_node->type == NodeTypeContainerDecl) {
2685 enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);2703 enum_type->data.enumeration.src_field_count = field_count;
2686 enum_type->data.enumeration.fields_by_name.init(field_count);2704 enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);
26872705 enum_type->data.enumeration.fields_by_name.init(field_count);
2688 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2689 occupied_tag_values.init(field_count);
2690
2691 TypeEnumField *last_enum_field = nullptr;
2692
2693 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2694 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2695 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2696 type_enum_field->name = field_node->data.struct_field.name;
2697 type_enum_field->decl_index = field_i;
2698 type_enum_field->decl_node = field_node;
26992706
2700 if (field_node->data.struct_field.type != nullptr) {2707 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2701 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,2708 occupied_tag_values.init(field_count);
2702 buf_sprintf("structs and unions, not enums, support field types"));
2703 add_error_note(g, msg, decl_node,
2704 buf_sprintf("consider 'union(enum)' here"));
2705 } else if (field_node->data.struct_field.align_expr != nullptr) {
2706 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
2707 buf_sprintf("structs and unions, not enums, support field alignment"));
2708 add_error_note(g, msg, decl_node,
2709 buf_sprintf("consider 'union(enum)' here"));
2710 }
27112709
2712 if (buf_eql_str(type_enum_field->name, "_")) {2710 TypeEnumField *last_enum_field = nullptr;
2713 add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
2714 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2715 }
27162711
2717 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);2712 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2718 if (field_entry != nullptr) {2713 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2719 ErrorMsg *msg = add_node_error(g, field_node,2714 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2720 buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));2715 type_enum_field->name = field_node->data.struct_field.name;
2721 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));2716 type_enum_field->decl_index = field_i;
2722 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2717 type_enum_field->decl_node = field_node;
2723 continue;2718
2724 }2719 if (field_node->data.struct_field.type != nullptr) {
27252720 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
2726 AstNode *tag_value = field_node->data.struct_field.value;2721 buf_sprintf("structs and unions, not enums, support field types"));
2722 add_error_note(g, msg, decl_node,
2723 buf_sprintf("consider 'union(enum)' here"));
2724 } else if (field_node->data.struct_field.align_expr != nullptr) {
2725 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
2726 buf_sprintf("structs and unions, not enums, support field alignment"));
2727 add_error_note(g, msg, decl_node,
2728 buf_sprintf("consider 'union(enum)' here"));
2729 }
2730
2731 if (buf_eql_str(type_enum_field->name, "_")) {
2732 add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
2733 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2734 }
27272735
2728 if (tag_value != nullptr) {2736 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
2729 // A user-specified value is available2737 if (field_entry != nullptr) {
2730 ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,2738 ErrorMsg *msg = add_node_error(g, field_node,
2731 nullptr, UndefBad);2739 buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
2732 if (type_is_invalid(result->type)) {2740 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
2733 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2741 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2734 continue;2742 continue;
2735 }2743 }
27362744
2737 assert(result->special != ConstValSpecialRuntime);2745 AstNode *tag_value = field_node->data.struct_field.value;
2738 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
27392746
2740 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);2747 if (tag_value != nullptr) {
2741 } else {2748 // A user-specified value is available
2742 // No value was explicitly specified: allocate the last value + 12749 ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
2743 // or, if this is the first element, zero2750 nullptr, UndefBad);
2744 if (last_enum_field != nullptr) {2751 if (type_is_invalid(result->type)) {
2745 bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);2752 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2753 continue;
2754 }
2755
2756 assert(result->special != ConstValSpecialRuntime);
2757 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
2758
2759 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
2746 } else {2760 } else {
2747 bigint_init_unsigned(&type_enum_field->value, 0);2761 // No value was explicitly specified: allocate the last value + 1
2762 // or, if this is the first element, zero
2763 if (last_enum_field != nullptr) {
2764 bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
2765 } else {
2766 bigint_init_unsigned(&type_enum_field->value, 0);
2767 }
2768
2769 // Make sure we can represent this number with tag_int_type
2770 if (!bigint_fits_in_bits(&type_enum_field->value,
2771 tag_int_type->size_in_bits,
2772 tag_int_type->data.integral.is_signed)) {
2773 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2774
2775 Buf *val_buf = buf_alloc();
2776 bigint_append_buf(val_buf, &type_enum_field->value, 10);
2777 add_node_error(g, field_node,
2778 buf_sprintf("enumeration value %s too large for type '%s'",
2779 buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
2780
2781 break;
2782 }
2748 }2783 }
27492784
2750 // Make sure we can represent this number with tag_int_type2785 // Make sure the value is unique
2751 if (!bigint_fits_in_bits(&type_enum_field->value,2786 auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
2752 tag_int_type->size_in_bits,2787 if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) {
2753 tag_int_type->data.integral.is_signed)) {
2754 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;2788 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
27552789
2756 Buf *val_buf = buf_alloc();2790 Buf *val_buf = buf_alloc();
2757 bigint_append_buf(val_buf, &type_enum_field->value, 10);2791 bigint_append_buf(val_buf, &type_enum_field->value, 10);
2758 add_node_error(g, field_node,
2759 buf_sprintf("enumeration value %s too large for type '%s'",
2760 buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
27612792
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);
27732798
2774 ErrorMsg *msg = add_node_error(g, field_node,2799 last_enum_field = type_enum_field;
2775 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
2776 add_error_note(g, msg, entry->value,
2777 buf_sprintf("other occurrence here"));
2778 }2800 }
27792801 occupied_tag_values.deinit();
2780 last_enum_field = type_enum_field;
2781 }2802 }
27822803
2783 if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)2804 if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
...@@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2786 enum_type->data.enumeration.resolve_loop_flag = false;2807 enum_type->data.enumeration.resolve_loop_flag = false;
2787 enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;2808 enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
27882809
2789 occupied_tag_values.deinit();
2790
2791 return ErrorNone;2810 return ErrorNone;
2792}2811}
27932812
src/ir.cpp+127-11
...@@ -2147,6 +2147,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN...@@ -2147,6 +2147,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN
2147 IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);2147 IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
2148 ir_instruction_append(irb->current_basic_block, &const_instruction->base);2148 ir_instruction_append(irb->current_basic_block, &const_instruction->base);
2149 const_instruction->value = irb->codegen->intern.for_undefined();2149 const_instruction->value = irb->codegen->intern.for_undefined();
2150 const_instruction->value->special = ConstValSpecialUndef;
2150 return &const_instruction->base;2151 return &const_instruction->base;
2151}2152}
21522153
...@@ -14917,6 +14918,9 @@ static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* so...@@ -14917,6 +14918,9 @@ static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* so
14917 field_val->parent.data.p_struct.struct_val = const_result->value;14918 field_val->parent.data.p_struct.struct_val = const_result->value;
14918 field_val->parent.data.p_struct.field_index = dst_field->src_index;14919 field_val->parent.data.p_struct.field_index = dst_field->src_index;
14919 field_values[dst_field->src_index] = field_val;14920 field_values[dst_field->src_index] = field_val;
14921 if (field_val->type->id == ZigTypeIdUndefined && dst_field->type_entry->id != ZigTypeIdUndefined) {
14922 field_values[dst_field->src_index]->special = ConstValSpecialUndef;
14923 }
14920 } else {14924 } else {
14921 is_comptime = false;14925 is_comptime = false;
14922 }14926 }
...@@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,...@@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
15649 wanted_type->data.array.len == field_count)15653 wanted_type->data.array.len == field_count)
15650 {15654 {
15651 return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type);15655 return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type);
15652 } else if (wanted_type->id == ZigTypeIdStruct &&15656 } else if (wanted_type->id == ZigTypeIdStruct && !is_slice(wanted_type) &&
15653 (!is_array_init || field_count == 0))15657 (!is_array_init || field_count == 0))
15654 {15658 {
15655 return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type);15659 return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type);
...@@ -20692,8 +20696,13 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20692,8 +20696,13 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20692 if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) &&20696 if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) &&
20693 expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet)20697 expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet)
20694 {20698 {
20695 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,20699 if (call_result_loc->id == ResultLocIdReturn) {
20696 ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));20700 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
20701 ira->explicit_return_type_source_node, buf_sprintf("function cannot return an error"));
20702 } else {
20703 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, result_loc->base.source_node,
20704 buf_sprintf("cannot store an error in type '%s'", buf_ptr(&expected_return_type->name)));
20705 }
20697 }20706 }
20698 return ira->codegen->invalid_inst_gen;20707 return ira->codegen->invalid_inst_gen;
20699 }20708 }
...@@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira,...@@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira,
2230222311
22303static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) {22312static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) {
22304 if (field->init_val != nullptr) return;22313 if (field->init_val != nullptr) return;
22314 if (field->decl_node == nullptr) return;
22305 if (field->decl_node->type != NodeTypeStructField) return;22315 if (field->decl_node->type != NodeTypeStructField) return;
22306 AstNode *init_node = field->decl_node->data.struct_field.value;22316 AstNode *init_node = field->decl_node->data.struct_field.value;
22307 if (init_node == nullptr) return;22317 if (init_node == nullptr) return;
...@@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25495 error_val->special = ConstValSpecialStatic;25505 error_val->special = ConstValSpecialStatic;
25496 error_val->type = type_info_error_type;25506 error_val->type = type_info_error_type;
2549725507
25498 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);25508 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1);
25499 inner_fields[1]->special = ConstValSpecialStatic;
25500 inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
2550125509
25502 ZigValue *name = nullptr;25510 ZigValue *name = nullptr;
25503 if (error->cached_error_name_val != nullptr)25511 if (error->cached_error_name_val != nullptr)
...@@ -25505,7 +25513,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25505,7 +25513,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25505 if (name == nullptr)25513 if (name == nullptr)
25506 name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee;25514 name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee;
25507 init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true);25515 init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true);
25508 bigint_init_unsigned(&inner_fields[1]->data.x_bigint, error->value);
2550925516
25510 error_val->data.x_struct.fields = inner_fields;25517 error_val->data.x_struct.fields = inner_fields;
25511 error_val->parent.id = ConstParentIdArray;25518 error_val->parent.id = ConstParentIdArray;
...@@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26020 assert(payload->special == ConstValSpecialStatic);26027 assert(payload->special == ConstValSpecialStatic);
26021 assert(payload->type == type_info_pointer_type);26028 assert(payload->type == type_info_pointer_type);
26022 ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0);26029 ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0);
26030 if (size_value == nullptr)
26031 return ira->codegen->invalid_inst_gen->value->type;
26032
26023 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));26033 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
26024 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);26034 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
26025 PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);26035 PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);
...@@ -26103,13 +26113,21 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26103,13 +26113,21 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26103 assert(payload->special == ConstValSpecialStatic);26113 assert(payload->special == ConstValSpecialStatic);
26104 assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr));26114 assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr));
26105 ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0);26115 ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0);
26116 if (type_is_invalid(child_type))
26117 return ira->codegen->invalid_inst_gen->value->type;
26106 return get_optional_type(ira->codegen, child_type);26118 return get_optional_type(ira->codegen, child_type);
26107 }26119 }
26108 case ZigTypeIdErrorUnion: {26120 case ZigTypeIdErrorUnion: {
26109 assert(payload->special == ConstValSpecialStatic);26121 assert(payload->special == ConstValSpecialStatic);
26110 assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr));26122 assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr));
26111 ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0);26123 ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0);
26124 if (type_is_invalid(err_set_type))
26125 return ira->codegen->invalid_inst_gen->value->type;
26126
26112 ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1);26127 ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1);
26128 if (type_is_invalid(payload_type))
26129 return ira->codegen->invalid_inst_gen->value->type;
26130
26113 return get_error_union_type(ira->codegen, err_set_type, payload_type);26131 return get_error_union_type(ira->codegen, err_set_type, payload_type);
26114 }26132 }
26115 case ZigTypeIdOpaque: {26133 case ZigTypeIdOpaque: {
...@@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26123 assert(payload->special == ConstValSpecialStatic);26141 assert(payload->special == ConstValSpecialStatic);
26124 assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr));26142 assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr));
26125 BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0);26143 BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0);
26144 if (len == nullptr)
26145 return ira->codegen->invalid_inst_gen->value->type;
26146
26126 ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1);26147 ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1);
26127 Error err;
26128 if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) {26148 if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) {
26129 return ira->codegen->invalid_inst_gen->value->type;26149 return ira->codegen->invalid_inst_gen->value->type;
26130 }26150 }
...@@ -26134,6 +26154,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26134,6 +26154,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26134 assert(payload->special == ConstValSpecialStatic);26154 assert(payload->special == ConstValSpecialStatic);
26135 assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr));26155 assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr));
26136 ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);26156 ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);
26157 if (child_type != nullptr && type_is_invalid(child_type))
26158 return ira->codegen->invalid_inst_gen->value->type;
26159
26137 return get_any_frame_type(ira->codegen, child_type);26160 return get_any_frame_type(ira->codegen, child_type);
26138 }26161 }
26139 case ZigTypeIdEnumLiteral:26162 case ZigTypeIdEnumLiteral:
...@@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26142 assert(payload->special == ConstValSpecialStatic);26165 assert(payload->special == ConstValSpecialStatic);
26143 assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));26166 assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));
26144 ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0);26167 ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0);
26168 if (function == nullptr)
26169 return ira->codegen->invalid_inst_gen->value->type;
26170
26145 assert(function->type->id == ZigTypeIdFn);26171 assert(function->type->id == ZigTypeIdFn);
26146 ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;26172 ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
26147 return get_fn_frame_type(ira->codegen, fn);26173 return get_fn_frame_type(ira->codegen, fn);
...@@ -26176,7 +26202,6 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26176,7 +26202,6 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26176 assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));26202 assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
26177 ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();26203 ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
26178 err_entry->decl_node = source_instr->source_node;26204 err_entry->decl_node = source_instr->source_node;
26179 Error err;
26180 if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name)))26205 if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name)))
26181 return ira->codegen->invalid_inst_gen->value->type;26206 return ira->codegen->invalid_inst_gen->value->type;
26182 auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);26207 auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);
...@@ -26203,11 +26228,15 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26203,11 +26228,15 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26203 assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr));26228 assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr));
2620426229
26205 ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);26230 ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);
26231 if (layout_value == nullptr)
26232 return ira->codegen->invalid_inst_gen->value->type;
26206 assert(layout_value->special == ConstValSpecialStatic);26233 assert(layout_value->special == ConstValSpecialStatic);
26207 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));26234 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
26208 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);26235 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
2620926236
26210 ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1);26237 ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1);
26238 if (fields_value == nullptr)
26239 return ira->codegen->invalid_inst_gen->value->type;
26211 assert(fields_value->special == ConstValSpecialStatic);26240 assert(fields_value->special == ConstValSpecialStatic);
26212 assert(is_slice(fields_value->type));26241 assert(is_slice(fields_value->type));
26213 ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index];26242 ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index];
...@@ -26215,6 +26244,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26215,6 +26244,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26215 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);26244 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
2621626245
26217 ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2);26246 ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2);
26247 if (decls_value == nullptr)
26248 return ira->codegen->invalid_inst_gen->value->type;
26218 assert(decls_value->special == ConstValSpecialStatic);26249 assert(decls_value->special == ConstValSpecialStatic);
26219 assert(is_slice(decls_value->type));26250 assert(is_slice(decls_value->type));
26220 ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index];26251 ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index];
...@@ -26225,7 +26256,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26225,7 +26256,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26225 }26256 }
2622626257
26227 bool is_tuple;26258 bool is_tuple;
26228 get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple);26259 if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple)))
26260 return ira->codegen->invalid_inst_gen->value->type;
2622926261
26230 ZigType *entry = new_type_table_entry(ZigTypeIdStruct);26262 ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
26231 buf_init_from_buf(&entry->name,26263 buf_init_from_buf(&entry->name,
...@@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26253 return ira->codegen->invalid_inst_gen->value->type;26285 return ira->codegen->invalid_inst_gen->value->type;
26254 field->decl_node = source_instr->source_node;26286 field->decl_node = source_instr->source_node;
26255 ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1);26287 ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1);
26288 if (type_value == nullptr)
26289 return ira->codegen->invalid_inst_gen->value->type;
26256 field->type_val = type_value;26290 field->type_val = type_value;
26257 field->type_entry = type_value->data.x_type;26291 field->type_entry = type_value->data.x_type;
26258 if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) {26292 if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) {
...@@ -26260,6 +26294,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26260,6 +26294,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26260 return ira->codegen->invalid_inst_gen->value->type;26294 return ira->codegen->invalid_inst_gen->value->type;
26261 }26295 }
26262 ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2);26296 ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2);
26297 if (default_value == nullptr)
26298 return ira->codegen->invalid_inst_gen->value->type;
26263 if (default_value->type->id == ZigTypeIdNull) {26299 if (default_value->type->id == ZigTypeIdNull) {
26264 field->init_val = nullptr;26300 field->init_val = nullptr;
26265 } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) {26301 } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) {
...@@ -26277,7 +26313,87 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -26277,7 +26313,87 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2627726313
26278 return entry;26314 return entry;
26279 }26315 }
26280 case ZigTypeIdEnum:26316 case ZigTypeIdEnum: {
26317 assert(payload->special == ConstValSpecialStatic);
26318 assert(payload->type == ir_type_info_get_type(ira, "Enum", nullptr));
26319
26320 ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);
26321 if (layout_value == nullptr)
26322 return ira->codegen->invalid_inst_gen->value->type;
26323
26324 assert(layout_value->special == ConstValSpecialStatic);
26325 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
26326 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
26327
26328 ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1);
26329
26330 ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2);
26331 if (fields_value == nullptr)
26332 return ira->codegen->invalid_inst_gen->value->type;
26333
26334 assert(fields_value->special == ConstValSpecialStatic);
26335 assert(is_slice(fields_value->type));
26336 ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index];
26337 ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index];
26338 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
26339
26340 ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3);
26341 if (decls_value == nullptr)
26342 return ira->codegen->invalid_inst_gen->value->type;
26343
26344 assert(decls_value->special == ConstValSpecialStatic);
26345 assert(is_slice(decls_value->type));
26346 ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index];
26347 size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint);
26348 if (decls_len != 0) {
26349 ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Enum.decls must be empty for @Type"));
26350 return ira->codegen->invalid_inst_gen->value->type;
26351 }
26352
26353 Error err;
26354 bool is_exhaustive;
26355 if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_exhaustive", 4, &is_exhaustive)))
26356 return ira->codegen->invalid_inst_gen->value->type;
26357
26358 ZigType *entry = new_type_table_entry(ZigTypeIdEnum);
26359 buf_init_from_buf(&entry->name,
26360 get_anon_type_name(ira->codegen, ira->old_irb.exec, "enum", source_instr->scope, source_instr->source_node, &entry->name));
26361 entry->data.enumeration.decl_node = source_instr->source_node;
26362 entry->data.enumeration.tag_int_type = tag_type;
26363 entry->data.enumeration.decls_scope = create_decls_scope(
26364 ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name);
26365 entry->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(fields_len);
26366 entry->data.enumeration.fields_by_name.init(fields_len);
26367 entry->data.enumeration.src_field_count = fields_len;
26368 entry->data.enumeration.layout = layout;
26369 entry->data.enumeration.non_exhaustive = !is_exhaustive;
26370
26371 assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
26372 assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0);
26373 ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val;
26374 assert(fields_arr->special == ConstValSpecialStatic);
26375 assert(fields_arr->data.x_array.special == ConstArraySpecialNone);
26376 for (size_t i = 0; i < fields_len; i++) {
26377 ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i];
26378 assert(field_value->type == ir_type_info_get_type(ira, "EnumField", nullptr));
26379 TypeEnumField *field = &entry->data.enumeration.fields[i];
26380 field->name = buf_alloc();
26381 if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name)))
26382 return ira->codegen->invalid_inst_gen->value->type;
26383 field->decl_index = i;
26384 field->decl_node = source_instr->source_node;
26385 if (entry->data.enumeration.fields_by_name.put_unique(field->name, field) != nullptr) {
26386 ir_add_error(ira, source_instr, buf_sprintf("duplicate enum field '%s'", buf_ptr(field->name)));
26387 return ira->codegen->invalid_inst_gen->value->type;
26388 }
26389 BigInt *field_int_value = get_const_field_lit_int(ira, source_instr->source_node, field_value, "value", 1);
26390 if (field_int_value == nullptr)
26391 return ira->codegen->invalid_inst_gen->value->type;
26392 field->value = *field_int_value;
26393 }
26394
26395 return entry;
26396 }
26281 case ZigTypeIdUnion:26397 case ZigTypeIdUnion:
26282 ir_add_error(ira, source_instr, buf_sprintf(26398 ir_add_error(ira, source_instr, buf_sprintf(
26283 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));26399 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
test/compile_errors.zig+45-13
...@@ -2,6 +2,41 @@ const tests = @import("tests.zig");...@@ -2,6 +2,41 @@ const tests = @import("tests.zig");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add("@Type with undefined",
6 \\comptime {
7 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
8 \\}
9 \\comptime {
10 \\ _ = @Type(.{
11 \\ .Struct = .{
12 \\ .fields = undefined,
13 \\ .decls = undefined,
14 \\ .is_tuple = false,
15 \\ .layout = .Auto,
16 \\ },
17 \\ });
18 \\}
19 , &[_][]const u8{
20 "tmp.zig:2:16: error: use of undefined value here causes undefined behavior",
21 "tmp.zig:5:16: error: use of undefined value here causes undefined behavior",
22 });
23
24 cases.add("struct with declarations unavailable for @Type",
25 \\export fn entry() void {
26 \\ _ = @Type(@typeInfo(struct { const foo = 1; }));
27 \\}
28 , &[_][]const u8{
29 "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type",
30 });
31
32 cases.add("enum with declarations unavailable for @Type",
33 \\export fn entry() void {
34 \\ _ = @Type(@typeInfo(enum { foo, const bar = 1; }));
35 \\}
36 , &[_][]const u8{
37 "tmp.zig:2:15: error: TypeInfo.Enum.decls must be empty for @Type",
38 });
39
5 cases.addTest("reject extern variables with initializers",40 cases.addTest("reject extern variables with initializers",
6 \\extern var foo: int = 2;41 \\extern var foo: int = 2;
7 , &[_][]const u8{42 , &[_][]const u8{
...@@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
123 \\export fn baz() void {158 \\export fn baz() void {
124 \\ try bar();159 \\ try bar();
125 \\}160 \\}
126 \\export fn quux() u32 {161 \\export fn qux() u32 {
127 \\ return bar();162 \\ return bar();
128 \\}163 \\}
164 \\export fn quux() u32 {
165 \\ var buf: u32 = 0;
166 \\ buf = bar();
167 \\}
129 , &[_][]const u8{168 , &[_][]const u8{
130 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",169 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
131 "tmp.zig:1:17: note: function cannot return an error",170 "tmp.zig:1:17: note: function cannot return an error",
132 "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",171 "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",
133 "tmp.zig:7:17: note: function cannot return an error",172 "tmp.zig:7:17: note: function cannot return an error",
134 "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",173 "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
135 "tmp.zig:10:18: note: function cannot return an error",174 "tmp.zig:10:17: note: function cannot return an error",
175 "tmp.zig:15:14: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
176 "tmp.zig:14:5: note: cannot store an error in type 'u32'",
136 });177 });
137178
138 cases.addTest("int/float conversion to comptime_int/float",179 cases.addTest("int/float conversion to comptime_int/float",
...@@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
598 \\ _ = C;639 \\ _ = C;
599 \\}640 \\}
600 , &[_][]const u8{641 , &[_][]const u8{
601 "tmp.zig:4:5: error: non-exhaustive enum must specify size",642 "tmp.zig:4:5: error: value assigned to '_' field of non-exhaustive enum",
602 "error: value assigned to '_' field of non-exhaustive enum",643 "error: non-exhaustive enum must specify size",
603 "error: non-exhaustive enum specifies every value",644 "error: non-exhaustive enum specifies every value",
604 "error: '_' field of non-exhaustive enum must be last",645 "error: '_' field of non-exhaustive enum must be last",
605 });646 });
...@@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1400 , &[_][]const u8{1441 , &[_][]const u8{
1401 "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'",1442 "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'",
1402 });1443 });
1403
1404 cases.add("struct with declarations unavailable for @Type",
1405 \\export fn entry() void {
1406 \\ _ = @Type(@typeInfo(struct { const foo = 1; }));
1407 \\}
1408 , &[_][]const u8{
1409 "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type",
1410 });
1411
1412 cases.add("wrong type for argument tuple to @asyncCall",1444 cases.add("wrong type for argument tuple to @asyncCall",
1413 \\export fn entry1() void {1445 \\export fn entry1() void {
1414 \\ var frame: @Frame(foo) = undefined;1446 \\ var frame: @Frame(foo) = undefined;
test/stage1/behavior/type.zig+34
...@@ -280,3 +280,37 @@ test "Type.Struct" {...@@ -280,3 +280,37 @@ test "Type.Struct" {
280 testing.expectEqual(@as(usize, 0), infoC.decls.len);280 testing.expectEqual(@as(usize, 0), infoC.decls.len);
281 testing.expectEqual(@as(bool, false), infoC.is_tuple);281 testing.expectEqual(@as(bool, false), infoC.is_tuple);
282}282}
283
284test "Type.Enum" {
285 const Foo = @Type(.{
286 .Enum = .{
287 .layout = .Auto,
288 .tag_type = u8,
289 .fields = &[_]TypeInfo.EnumField{
290 .{ .name = "a", .value = 1 },
291 .{ .name = "b", .value = 5 },
292 },
293 .decls = &[_]TypeInfo.Declaration{},
294 .is_exhaustive = true,
295 },
296 });
297 testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive);
298 testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a));
299 testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b));
300 const Bar = @Type(.{
301 .Enum = .{
302 .layout = .Extern,
303 .tag_type = u32,
304 .fields = &[_]TypeInfo.EnumField{
305 .{ .name = "a", .value = 1 },
306 .{ .name = "b", .value = 5 },
307 },
308 .decls = &[_]TypeInfo.Declaration{},
309 .is_exhaustive = false,
310 },
311 });
312 testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive);
313 testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a));
314 testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b));
315 testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6)));
316}
test/stage1/behavior/type_info.zig-1
...@@ -153,7 +153,6 @@ fn testErrorSet() void {...@@ -153,7 +153,6 @@ fn testErrorSet() void {
153 expect(error_set_info == .ErrorSet);153 expect(error_set_info == .ErrorSet);
154 expect(error_set_info.ErrorSet.?.len == 3);154 expect(error_set_info.ErrorSet.?.len == 3);
155 expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));155 expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));
156 expect(error_set_info.ErrorSet.?[2].value == @errorToInt(TestErrorSet.Third));
157156
158 const error_union_info = @typeInfo(TestErrorSet!usize);157 const error_union_info = @typeInfo(TestErrorSet!usize);
159 expect(error_union_info == .ErrorUnion);158 expect(error_union_info == .ErrorUnion);
test/stage2/spu-ii.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4const spu = std.zig.CrossTarget{
5 .cpu_arch = .spu_2,
6 .os_tag = .freestanding,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("SPU-II Basic Test", spu);
12 case.addCompareOutput(
13 \\fn killEmulator() noreturn {
14 \\ asm volatile ("undefined0");
15 \\ unreachable;
16 \\}
17 \\
18 \\export fn _start() noreturn {
19 \\ killEmulator();
20 \\}
21 , "");
22 }
23}
test/stage2/test.zig+94
...@@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{...@@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{
18 .os_tag = .linux,18 .os_tag = .linux,
19};19};
2020
21const linux_arm = std.zig.CrossTarget{
22 .cpu_arch = .arm,
23 .os_tag = .linux,
24};
25
21const wasi = std.zig.CrossTarget{26const wasi = std.zig.CrossTarget{
22 .cpu_arch = .wasm32,27 .cpu_arch = .wasm32,
23 .os_tag = .wasi,28 .os_tag = .wasi,
...@@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{...@@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{
26pub fn addCases(ctx: *TestContext) !void {31pub fn addCases(ctx: *TestContext) !void {
27 try @import("zir.zig").addCases(ctx);32 try @import("zir.zig").addCases(ctx);
28 try @import("cbe.zig").addCases(ctx);33 try @import("cbe.zig").addCases(ctx);
34 try @import("spu-ii.zig").addCases(ctx);
35
29 {36 {
30 var case = ctx.exe("hello world with updates", linux_x64);37 var case = ctx.exe("hello world with updates", linux_x64);
3138
...@@ -179,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -179,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void {
179 );186 );
180 }187 }
181188
189 {
190 var case = ctx.exe("hello world", linux_arm);
191 // Regular old hello world
192 case.addCompareOutput(
193 \\export fn _start() noreturn {
194 \\ print();
195 \\ exit();
196 \\}
197 \\
198 \\fn print() void {
199 \\ asm volatile ("svc #0"
200 \\ :
201 \\ : [number] "{r7}" (4),
202 \\ [arg1] "{r0}" (1),
203 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
204 \\ [arg3] "{r2}" (14)
205 \\ : "memory"
206 \\ );
207 \\ return;
208 \\}
209 \\
210 \\fn exit() noreturn {
211 \\ asm volatile ("svc #0"
212 \\ :
213 \\ : [number] "{r7}" (1),
214 \\ [arg1] "{r0}" (0)
215 \\ : "memory"
216 \\ );
217 \\ unreachable;
218 \\}
219 ,
220 "Hello, World!\n",
221 );
222 }
223
182 {224 {
183 var case = ctx.exe("adding numbers at comptime", linux_x64);225 var case = ctx.exe("adding numbers at comptime", linux_x64);
184 case.addCompareOutput(226 case.addCompareOutput(
...@@ -600,6 +642,58 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -600,6 +642,58 @@ pub fn addCases(ctx: *TestContext) !void {
600 "",642 "",
601 );643 );
602644
645 // Spilling registers to the stack.
646 case.addCompareOutput(
647 \\export fn _start() noreturn {
648 \\ assert(add(3, 4) == 791);
649 \\
650 \\ exit();
651 \\}
652 \\
653 \\fn add(a: u32, b: u32) u32 {
654 \\ const x: u32 = blk: {
655 \\ const c = a + b; // 7
656 \\ const d = a + c; // 10
657 \\ const e = d + b; // 14
658 \\ const f = d + e; // 24
659 \\ const g = e + f; // 38
660 \\ const h = f + g; // 62
661 \\ const i = g + h; // 100
662 \\ const j = i + d; // 110
663 \\ const k = i + j; // 210
664 \\ const l = k + c; // 217
665 \\ const m = l + d; // 227
666 \\ const n = m + e; // 241
667 \\ const o = n + f; // 265
668 \\ const p = o + g; // 303
669 \\ const q = p + h; // 365
670 \\ const r = q + i; // 465
671 \\ const s = r + j; // 575
672 \\ const t = s + k; // 785
673 \\ break :blk t;
674 \\ };
675 \\ const y = x + a; // 788
676 \\ const z = y + a; // 791
677 \\ return z;
678 \\}
679 \\
680 \\pub fn assert(ok: bool) void {
681 \\ if (!ok) unreachable; // assertion failure
682 \\}
683 \\
684 \\fn exit() noreturn {
685 \\ asm volatile ("syscall"
686 \\ :
687 \\ : [number] "{rax}" (231),
688 \\ [arg1] "{rdi}" (0)
689 \\ : "rcx", "r11", "memory"
690 \\ );
691 \\ unreachable;
692 \\}
693 ,
694 "",
695 );
696
603 // Character literals and multiline strings.697 // Character literals and multiline strings.
604 case.addCompareOutput(698 case.addCompareOutput(
605 \\export fn _start() noreturn {699 \\export fn _start() noreturn {
tools/process_headers.zig+3-2
...@@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch;...@@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch;
15const Abi = std.Target.Abi;15const Abi = std.Target.Abi;
16const OsTag = std.Target.Os.Tag;16const OsTag = std.Target.Os.Tag;
17const assert = std.debug.assert;17const assert = std.debug.assert;
18const Sha256 = std.crypto.hash.sha2.Sha256;
1819
19const LibCTarget = struct {20const LibCTarget = struct {
20 name: []const u8,21 name: []const u8,
...@@ -313,7 +314,7 @@ pub fn main() !void {...@@ -313,7 +314,7 @@ pub fn main() !void {
313 var max_bytes_saved: usize = 0;314 var max_bytes_saved: usize = 0;
314 var total_bytes: usize = 0;315 var total_bytes: usize = 0;
315316
316 var hasher = std.crypto.hash.sha2.Sha256.init(.{});317 var hasher = Sha256.init(.{});
317318
318 for (libc_targets) |libc_target| {319 for (libc_targets) |libc_target| {
319 const dest_target = DestTarget{320 const dest_target = DestTarget{
...@@ -359,7 +360,7 @@ pub fn main() !void {...@@ -359,7 +360,7 @@ pub fn main() !void {
359 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");360 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
360 total_bytes += raw_bytes.len;361 total_bytes += raw_bytes.len;
361 const hash = try allocator.alloc(u8, 32);362 const hash = try allocator.alloc(u8, 32);
362 hasher.reset();363 hasher = Sha256.init(.{});
363 hasher.update(rel_path);364 hasher.update(rel_path);
364 hasher.update(trimmed);365 hasher.update(trimmed);
365 hasher.final(hash);366 hasher.final(hash);
tools/update_glibc.zig+20-20
...@@ -148,12 +148,12 @@ pub fn main() !void {...@@ -148,12 +148,12 @@ pub fn main() !void {
148 for (abi_lists) |*abi_list| {148 for (abi_lists) |*abi_list| {
149 const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list));149 const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list));
150 if (!target_funcs_gop.found_existing) {150 if (!target_funcs_gop.found_existing) {
151 target_funcs_gop.kv.value = FunctionSet{151 target_funcs_gop.entry.value = FunctionSet{
152 .list = std.ArrayList(VersionedFn).init(allocator),152 .list = std.ArrayList(VersionedFn).init(allocator),
153 .fn_vers_list = FnVersionList.init(allocator),153 .fn_vers_list = FnVersionList.init(allocator),
154 };154 };
155 }155 }
156 const fn_set = &target_funcs_gop.kv.value.list;156 const fn_set = &target_funcs_gop.entry.value.list;
157157
158 for (lib_names) |lib_name, lib_name_index| {158 for (lib_names) |lib_name, lib_name_index| {
159 const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib";159 const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib";
...@@ -203,11 +203,11 @@ pub fn main() !void {...@@ -203,11 +203,11 @@ pub fn main() !void {
203 _ = try global_ver_set.put(ver, undefined);203 _ = try global_ver_set.put(ver, undefined);
204 const gop = try global_fn_set.getOrPut(name);204 const gop = try global_fn_set.getOrPut(name);
205 if (gop.found_existing) {205 if (gop.found_existing) {
206 if (!std.mem.eql(u8, gop.kv.value.lib, "c")) {206 if (!std.mem.eql(u8, gop.entry.value.lib, "c")) {
207 gop.kv.value.lib = lib_name;207 gop.entry.value.lib = lib_name;
208 }208 }
209 } else {209 } else {
210 gop.kv.value = Function{210 gop.entry.value = Function{
211 .name = name,211 .name = name,
212 .lib = lib_name,212 .lib = lib_name,
213 .index = undefined,213 .index = undefined,
...@@ -224,14 +224,14 @@ pub fn main() !void {...@@ -224,14 +224,14 @@ pub fn main() !void {
224 const global_fn_list = blk: {224 const global_fn_list = blk: {
225 var list = std.ArrayList([]const u8).init(allocator);225 var list = std.ArrayList([]const u8).init(allocator);
226 var it = global_fn_set.iterator();226 var it = global_fn_set.iterator();
227 while (it.next()) |kv| try list.append(kv.key);227 while (it.next()) |entry| try list.append(entry.key);
228 std.sort.sort([]const u8, list.span(), {}, strCmpLessThan);228 std.sort.sort([]const u8, list.span(), {}, strCmpLessThan);
229 break :blk list.span();229 break :blk list.span();
230 };230 };
231 const global_ver_list = blk: {231 const global_ver_list = blk: {
232 var list = std.ArrayList([]const u8).init(allocator);232 var list = std.ArrayList([]const u8).init(allocator);
233 var it = global_ver_set.iterator();233 var it = global_ver_set.iterator();
234 while (it.next()) |kv| try list.append(kv.key);234 while (it.next()) |entry| try list.append(entry.key);
235 std.sort.sort([]const u8, list.span(), {}, versionLessThan);235 std.sort.sort([]const u8, list.span(), {}, versionLessThan);
236 break :blk list.span();236 break :blk list.span();
237 };237 };
...@@ -254,9 +254,9 @@ pub fn main() !void {...@@ -254,9 +254,9 @@ pub fn main() !void {
254 var buffered = std.io.bufferedOutStream(fns_txt_file.outStream());254 var buffered = std.io.bufferedOutStream(fns_txt_file.outStream());
255 const fns_txt = buffered.outStream();255 const fns_txt = buffered.outStream();
256 for (global_fn_list) |name, i| {256 for (global_fn_list) |name, i| {
257 const kv = global_fn_set.get(name).?;257 const entry = global_fn_set.getEntry(name).?;
258 kv.value.index = i;258 entry.value.index = i;
259 try fns_txt.print("{} {}\n", .{ name, kv.value.lib });259 try fns_txt.print("{} {}\n", .{ name, entry.value.lib });
260 }260 }
261 try buffered.flush();261 try buffered.flush();
262 }262 }
...@@ -264,16 +264,16 @@ pub fn main() !void {...@@ -264,16 +264,16 @@ pub fn main() !void {
264 // Now the mapping of version and function to integer index is complete.264 // Now the mapping of version and function to integer index is complete.
265 // Here we create a mapping of function name to list of versions.265 // Here we create a mapping of function name to list of versions.
266 for (abi_lists) |*abi_list, abi_index| {266 for (abi_lists) |*abi_list, abi_index| {
267 const kv = target_functions.get(@ptrToInt(abi_list)).?;267 const entry = target_functions.getEntry(@ptrToInt(abi_list)).?;
268 const fn_vers_list = &kv.value.fn_vers_list;268 const fn_vers_list = &entry.value.fn_vers_list;
269 for (kv.value.list.span()) |*ver_fn| {269 for (entry.value.list.span()) |*ver_fn| {
270 const gop = try fn_vers_list.getOrPut(ver_fn.name);270 const gop = try fn_vers_list.getOrPut(ver_fn.name);
271 if (!gop.found_existing) {271 if (!gop.found_existing) {
272 gop.kv.value = std.ArrayList(usize).init(allocator);272 gop.entry.value = std.ArrayList(usize).init(allocator);
273 }273 }
274 const ver_index = global_ver_set.get(ver_fn.ver).?.value;274 const ver_index = global_ver_set.getEntry(ver_fn.ver).?.value;
275 if (std.mem.indexOfScalar(usize, gop.kv.value.span(), ver_index) == null) {275 if (std.mem.indexOfScalar(usize, gop.entry.value.span(), ver_index) == null) {
276 try gop.kv.value.append(ver_index);276 try gop.entry.value.append(ver_index);
277 }277 }
278 }278 }
279 }279 }
...@@ -287,7 +287,7 @@ pub fn main() !void {...@@ -287,7 +287,7 @@ pub fn main() !void {
287287
288 // first iterate over the abi lists288 // first iterate over the abi lists
289 for (abi_lists) |*abi_list, abi_index| {289 for (abi_lists) |*abi_list, abi_index| {
290 const fn_vers_list = &target_functions.get(@ptrToInt(abi_list)).?.value.fn_vers_list;290 const fn_vers_list = &target_functions.getEntry(@ptrToInt(abi_list)).?.value.fn_vers_list;
291 for (abi_list.targets) |target, it_i| {291 for (abi_list.targets) |target, it_i| {
292 if (it_i != 0) try abilist_txt.writeByte(' ');292 if (it_i != 0) try abilist_txt.writeByte(' ');
293 try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) });293 try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) });
...@@ -295,11 +295,11 @@ pub fn main() !void {...@@ -295,11 +295,11 @@ pub fn main() !void {
295 try abilist_txt.writeByte('\n');295 try abilist_txt.writeByte('\n');
296 // next, each line implicitly corresponds to a function296 // next, each line implicitly corresponds to a function
297 for (global_fn_list) |name| {297 for (global_fn_list) |name| {
298 const kv = fn_vers_list.get(name) orelse {298 const entry = fn_vers_list.getEntry(name) orelse {
299 try abilist_txt.writeByte('\n');299 try abilist_txt.writeByte('\n');
300 continue;300 continue;
301 };301 };
302 for (kv.value.span()) |ver_index, it_i| {302 for (entry.value.span()) |ver_index, it_i| {
303 if (it_i != 0) try abilist_txt.writeByte(' ');303 if (it_i != 0) try abilist_txt.writeByte(' ');
304 try abilist_txt.print("{d}", .{ver_index});304 try abilist_txt.print("{d}", .{ver_index});
305 }305 }