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) {
289289 /// therefore must be kept in sync with the compiler implementation.
290290 pub const Error = struct {
291291 name: []const u8,
292 /// This field is ignored when using @Type().
293 value: comptime_int,
294292 };
295293
296294 /// 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;
1111usingnamespace @import("../os/bits.zig");
1212
1313extern "c" fn __error() *c_int;
14pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;
1415pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
1516pub extern "c" fn _dyld_image_count() u32;
1617pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
lib/std/cache_hash.zig+38-32
......@@ -4,7 +4,8 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66const 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
89const fs = std.fs;
910const base64 = std.base64;
1011const ArrayList = std.ArrayList;
......@@ -16,9 +17,8 @@ const Allocator = std.mem.Allocator;
1617
1718const base64_encoder = fs.base64_encoder;
1819const base64_decoder = fs.base64_decoder;
19/// This is 70 more bits than UUIDs. For an analysis of probability of collisions, see:
20/// https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions
21const BIN_DIGEST_LEN = 24;
20/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
21const BIN_DIGEST_LEN = 16;
2222const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);
2323
2424const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;
......@@ -43,9 +43,13 @@ pub const File = struct {
4343 }
4444};
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.
4649pub const CacheHash = struct {
4750 allocator: *Allocator,
48 blake3: Blake3,
51 hasher_init: Hasher, // initial state, that can be copied
52 hasher: Hasher, // current state for incremental hashing
4953 manifest_dir: fs.Dir,
5054 manifest_file: ?fs.File,
5155 manifest_dirty: bool,
......@@ -54,9 +58,11 @@ pub const CacheHash = struct {
5458
5559 /// Be sure to call release after successful initialization.
5660 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);
5762 return CacheHash{
5863 .allocator = allocator,
59 .blake3 = Blake3.init(.{}),
64 .hasher_init = hasher_init,
65 .hasher = hasher_init,
6066 .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),
6167 .manifest_file = null,
6268 .manifest_dirty = false,
......@@ -69,8 +75,8 @@ pub const CacheHash = struct {
6975 pub fn addSlice(self: *CacheHash, val: []const u8) void {
7076 assert(self.manifest_file == null);
7177
72 self.blake3.update(val);
73 self.blake3.update(&[_]u8{0});
78 self.hasher.update(val);
79 self.hasher.update(&[_]u8{0});
7480 }
7581
7682 /// Convert the input value into bytes and record it as a dependency of the
......@@ -133,12 +139,12 @@ pub const CacheHash = struct {
133139 assert(self.manifest_file == null);
134140
135141 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
136 self.blake3.final(&bin_digest);
142 self.hasher.final(&bin_digest);
137143
138144 base64_encoder.encode(self.b64_digest[0..], &bin_digest);
139145
140 self.blake3 = Blake3.init(.{});
141 self.blake3.update(&bin_digest);
146 self.hasher = self.hasher_init;
147 self.hasher.update(&bin_digest);
142148
143149 const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});
144150 defer self.allocator.free(manifest_file_path);
......@@ -238,7 +244,7 @@ pub const CacheHash = struct {
238244 }
239245
240246 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
243249 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
244250 cache_hash_file.bin_digest = actual_digest;
......@@ -248,7 +254,7 @@ pub const CacheHash = struct {
248254 }
249255
250256 if (!any_file_changed) {
251 self.blake3.update(&cache_hash_file.bin_digest);
257 self.hasher.update(&cache_hash_file.bin_digest);
252258 }
253259 }
254260
......@@ -256,8 +262,8 @@ pub const CacheHash = struct {
256262 // cache miss
257263 // keep the manifest file open
258264 // reset the hash
259 self.blake3 = Blake3.init(.{});
260 self.blake3.update(&bin_digest);
265 self.hasher = self.hasher_init;
266 self.hasher.update(&bin_digest);
261267
262268 // Remove files not in the initial hash
263269 for (self.files.items[input_file_count..]) |*file| {
......@@ -266,7 +272,7 @@ pub const CacheHash = struct {
266272 self.files.shrink(input_file_count);
267273
268274 for (self.files.items) |file| {
269 self.blake3.update(&file.bin_digest);
275 self.hasher.update(&file.bin_digest);
270276 }
271277 return null;
272278 }
......@@ -304,23 +310,23 @@ pub const CacheHash = struct {
304310
305311 // Hash while reading from disk, to keep the contents in the cpu cache while
306312 // doing hashing.
307 var blake3 = Blake3.init(.{});
313 var hasher = self.hasher_init;
308314 var off: usize = 0;
309315 while (true) {
310316 // give me everything you've got, captain
311317 const bytes_read = try file.read(contents[off..]);
312318 if (bytes_read == 0) break;
313 blake3.update(contents[off..][0..bytes_read]);
319 hasher.update(contents[off..][0..bytes_read]);
314320 off += bytes_read;
315321 }
316 blake3.final(&ch_file.bin_digest);
322 hasher.final(&ch_file.bin_digest);
317323
318324 ch_file.contents = contents;
319325 } else {
320 try hashFile(file, &ch_file.bin_digest);
326 try hashFile(file, &ch_file.bin_digest, self.hasher_init);
321327 }
322328
323 self.blake3.update(&ch_file.bin_digest);
329 self.hasher.update(&ch_file.bin_digest);
324330 }
325331
326332 /// Add a file as a dependency of process being cached, after the initial hash has been
......@@ -382,7 +388,7 @@ pub const CacheHash = struct {
382388 // the artifacts to cache.
383389
384390 var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
385 self.blake3.final(&bin_digest);
391 self.hasher.final(&bin_digest);
386392
387393 var out_digest: [BASE64_DIGEST_LEN]u8 = undefined;
388394 base64_encoder.encode(&out_digest, &bin_digest);
......@@ -433,17 +439,17 @@ pub const CacheHash = struct {
433439 }
434440};
435441
436fn hashFile(file: fs.File, bin_digest: []u8) !void {
437 var blake3 = Blake3.init(.{});
442fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void {
438443 var buf: [1024]u8 = undefined;
439444
445 var hasher = hasher_init;
440446 while (true) {
441447 const bytes_read = try file.read(&buf);
442448 if (bytes_read == 0) break;
443 blake3.update(buf[0..bytes_read]);
449 hasher.update(buf[0..bytes_read]);
444450 }
445451
446 blake3.final(bin_digest);
452 hasher.final(bin_digest);
447453}
448454
449455/// If the wall clock time, rounded to the same precision as the
......@@ -507,7 +513,7 @@ test "cache file and then recall it" {
507513 _ = try ch.addFile(temp_file, null);
508514
509515 // 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
512518 digest1 = ch.final();
513519 }
......@@ -575,7 +581,7 @@ test "check that changing a file makes cache fail" {
575581 const temp_file_idx = try ch.addFile(temp_file, 100);
576582
577583 // 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
580586 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" {
592598 const temp_file_idx = try ch.addFile(temp_file, 100);
593599
594600 // 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
597603 // The cache system does not keep the contents of re-hashed input files.
598604 testing.expect(ch.files.items[temp_file_idx].contents == null);
......@@ -625,7 +631,7 @@ test "no file inputs" {
625631 ch.add("1234");
626632
627633 // 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
630636 digest1 = ch.final();
631637 }
......@@ -672,7 +678,7 @@ test "CacheHashes with files added after initial hash work" {
672678 _ = try ch.addFile(temp_file1, null);
673679
674680 // 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
677683 _ = try ch.addFilePost(temp_file2);
678684
......@@ -705,7 +711,7 @@ test "CacheHashes with files added after initial hash work" {
705711 _ = try ch.addFile(temp_file1, null);
706712
707713 // 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
710716 _ = try ch.addFilePost(temp_file2);
711717
lib/std/crypto.zig+2
......@@ -18,6 +18,7 @@ pub const hash = struct {
1818/// Authentication (MAC) functions.
1919pub const auth = struct {
2020 pub const hmac = @import("crypto/hmac.zig");
21 pub const siphash = @import("crypto/siphash.zig");
2122};
2223
2324/// Authenticated Encryption with Associated Data
......@@ -80,6 +81,7 @@ test "crypto" {
8081 _ = @import("crypto/sha1.zig");
8182 _ = @import("crypto/sha2.zig");
8283 _ = @import("crypto/sha3.zig");
84 _ = @import("crypto/siphash.zig");
8385 _ = @import("crypto/25519/curve25519.zig");
8486 _ = @import("crypto/25519/ed25519.zig");
8587 _ = @import("crypto/25519/edwards25519.zig");
lib/std/crypto/benchmark.zig+4
......@@ -60,6 +60,10 @@ const macs = [_]Crypto{
6060 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
6161 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" },
6262 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" },
6367};
6468
6569pub 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) {
976976 /// MIPS RS3000 Little-endian
977977 _MIPS_RS3_LE = 10,
978978
979 /// SPU Mark II
980 _SPU_2 = 13,
981
979982 /// Hewlett-Packard PA-RISC
980983 _PARISC = 15,
981984
lib/std/fs.zig+19-12
......@@ -686,21 +686,28 @@ pub const Dir = struct {
686686 return self.openFileW(path_w.span(), flags);
687687 }
688688
689 var os_flags: u32 = os.O_CLOEXEC;
689690 // Use the O_ locking flags if the os supports them
690691 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
691692 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
692 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking)
693 os.O_NONBLOCK | os.O_SYNC
694 else
695 @as(u32, 0);
696 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
697 .None => @as(u32, 0),
698 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
699 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
700 } else 0;
701
702 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
703 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
693 if (has_flock_open_flags) {
694 const nonblocking_lock_flag = if (flags.lock_nonblocking)
695 os.O_NONBLOCK | os.O_SYNC
696 else
697 @as(u32, 0);
698 os_flags |= switch (flags.lock) {
699 .None => @as(u32, 0),
700 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
701 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
702 };
703 }
704 if (@hasDecl(os, "O_LARGEFILE")) {
705 os_flags |= os.O_LARGEFILE;
706 }
707 if (!flags.allow_ctty) {
708 os_flags |= os.O_NOCTTY;
709 }
710 os_flags |= if (flags.write and flags.read)
704711 @as(u32, os.O_RDWR)
705712 else if (flags.write)
706713 @as(u32, os.O_WRONLY)
lib/std/fs/file.zig+4
......@@ -101,6 +101,10 @@ pub const File = struct {
101101 /// if `std.io.is_async`. It allows the use of `nosuspend` when calling functions
102102 /// related to opening the file, reading, writing, and locking.
103103 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,
104108 };
105109
106110 /// 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;
2020pub const Fnv1a_64 = fnv.Fnv1a_64;
2121pub const Fnv1a_128 = fnv.Fnv1a_128;
2222
23const siphash = @import("hash/siphash.zig");
23const siphash = @import("crypto/siphash.zig");
2424pub const SipHash64 = siphash.SipHash64;
2525pub const SipHash128 = siphash.SipHash128;
2626
......@@ -42,7 +42,6 @@ test "hash" {
4242 _ = @import("hash/auto_hash.zig");
4343 _ = @import("hash/crc.zig");
4444 _ = @import("hash/fnv.zig");
45 _ = @import("hash/siphash.zig");
4645 _ = @import("hash/murmur.zig");
4746 _ = @import("hash/cityhash.zig");
4847 _ = @import("hash/wyhash.zig");
lib/std/hash/benchmark.zig-12
......@@ -25,24 +25,12 @@ const Hash = struct {
2525 init_u64: ?u64 = null,
2626};
2727
28const siphash_key = "0123456789abcdef";
29
3028const hashes = [_]Hash{
3129 Hash{
3230 .ty = hash.Wyhash,
3331 .name = "wyhash",
3432 .init_u64 = 0,
3533 },
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 },
4634 Hash{
4735 .ty = hash.Fnv1a_64,
4836 .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 {
433433 const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size];
434434 self.backing_allocator.free(bucket_slice);
435435 } else {
436 // TODO Set the slot data to undefined.
437 // Related: https://github.com/ziglang/zig/issues/4298
436 @memset(bucket.page + slot_index * size_class, undefined, size_class);
438437 }
439438 }
440439
......@@ -567,6 +566,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
567566 const new_aligned_size = math.max(new_size, old_align);
568567 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
569568 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 }
570572 return new_size;
571573 }
572574 return error.OutOfMemory;
lib/std/linked_list.zig-12
......@@ -28,12 +28,6 @@ pub fn SinglyLinkedList(comptime T: type) type {
2828
2929 pub const Data = T;
3030
31 pub fn init(data: T) Node {
32 return Node{
33 .data = data,
34 };
35 }
36
3731 /// Insert a new node after the current one.
3832 ///
3933 /// Arguments:
......@@ -175,12 +169,6 @@ pub fn TailQueue(comptime T: type) type {
175169 prev: ?*Node = null,
176170 next: ?*Node = null,
177171 data: T,
178
179 pub fn init(data: T) Node {
180 return Node{
181 .data = data,
182 };
183 }
184172 };
185173
186174 first: ?*Node = null,
lib/std/macho.zig+78-1
......@@ -40,6 +40,24 @@ pub const uuid_command = extern struct {
4040 uuid: [16]u8,
4141};
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
4361/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
4462/// "stab" style symbol table information as described in the header files
4563/// <nlist.h> and <stab.h>.
......@@ -65,7 +83,7 @@ pub const symtab_command = extern struct {
6583
6684/// The linkedit_data_command contains the offsets and sizes of a blob
6785/// of data in the __LINKEDIT segment.
68const linkedit_data_command = extern struct {
86pub const linkedit_data_command = extern struct {
6987 /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
7088 cmd: u32,
7189
......@@ -79,6 +97,65 @@ const linkedit_data_command = extern struct {
7997 datasize: u32,
8098};
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
82159/// The segment load command indicates that a part of this file is to be
83160/// mapped into the task's address space. The size of this segment in memory,
84161/// 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) {
2424};
2525
2626pub usingnamespace @import("linux/netlink.zig");
27pub const BPF = @import("linux/bpf.zig");
2827
2928const 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) {
2929};
3030pub usingnamespace @import("bits.zig");
3131pub const tls = @import("linux/tls.zig");
32pub const BPF = @import("linux/bpf.zig");
3233
3334/// Set by startup code, used by `getauxval`.
3435pub 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// 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.
61const Builder = @import("std").build.Builder;
72
83pub fn build(b: *Builder) void {
lib/std/special/init-exe/src/main.zig-5
......@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
61const std = @import("std");
72
83pub fn main() anyerror!void {
lib/std/special/init-lib/build.zig-5
......@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
61const Builder = @import("std").build.Builder;
72
83pub fn build(b: *Builder) void {
lib/std/special/init-lib/src/main.zig-5
......@@ -1,8 +1,3 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
61const std = @import("std");
72const testing = std.testing;
83
lib/std/target.zig+18-8
......@@ -96,8 +96,12 @@ pub const Target = struct {
9696 win10_rs4 = 0x0A000005,
9797 win10_rs5 = 0x0A000006,
9898 win10_19h1 = 0x0A000007,
99 win10_20h1 = 0x0A000008,
99100 _,
100101
102 /// Latest Windows version that the Zig Standard Library is aware of
103 pub const latest = WindowsVersion.win10_20h1;
104
101105 pub const Range = struct {
102106 min: WindowsVersion,
103107 max: WindowsVersion,
......@@ -124,18 +128,17 @@ pub const Target = struct {
124128 out_stream: anytype,
125129 ) !void {
126130 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)) {
128132 try std.fmt.format(out_stream, ".{}", .{@tagName(self)});
129133 } 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)});
131136 }
132137 } 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)) {
134139 try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});
135140 } else {
136 try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())});
137 try std.fmt.format(out_stream, "{}", .{@enumToInt(self)});
138 try out_stream.writeAll(")");
141 try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)});
139142 }
140143 }
141144 }
......@@ -280,7 +283,7 @@ pub const Target = struct {
280283 .windows => return .{
281284 .windows = .{
282285 .min = .win8_1,
283 .max = .win10_19h1,
286 .max = WindowsVersion.latest,
284287 },
285288 },
286289 }
......@@ -663,6 +666,9 @@ pub const Target = struct {
663666 renderscript32,
664667 renderscript64,
665668 ve,
669 // Stage1 currently assumes that architectures above this comment
670 // map one-to-one with the ZigLLVM_ArchType enum.
671 spu_2,
666672
667673 pub fn isARM(arch: Arch) bool {
668674 return switch (arch) {
......@@ -761,6 +767,7 @@ pub const Target = struct {
761767 .sparcv9 => ._SPARCV9,
762768 .s390x => ._S390,
763769 .ve => ._NONE,
770 .spu_2 => ._SPU_2,
764771 };
765772 }
766773
......@@ -803,6 +810,7 @@ pub const Target = struct {
803810 .renderscript64,
804811 .shave,
805812 .ve,
813 .spu_2,
806814 => .Little,
807815
808816 .arc,
......@@ -827,6 +835,7 @@ pub const Target = struct {
827835 switch (arch) {
828836 .avr,
829837 .msp430,
838 .spu_2,
830839 => return 16,
831840
832841 .arc,
......@@ -1317,12 +1326,13 @@ pub const Target = struct {
13171326 .bpfeb,
13181327 .nvptx,
13191328 .nvptx64,
1329 .spu_2,
1330 .avr,
13201331 => return result,
13211332
13221333 // TODO go over each item in this list and either move it to the above list, or
13231334 // implement the standard dynamic linker path code for it.
13241335 .arc,
1325 .avr,
13261336 .hexagon,
13271337 .msp430,
13281338 .r600,
lib/std/zig/system.zig+1-1
......@@ -249,7 +249,7 @@ pub const NativeTargetInfo = struct {
249249 // values
250250 const known_build_numbers = [_]u32{
251251 10240, 10586, 14393, 15063, 16299, 17134, 17763,
252 18362, 18363,
252 18362, 19041,
253253 };
254254 var last_idx: usize = 0;
255255 for (known_build_numbers) |build, i| {
src-self-hosted/Module.zig+42
......@@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
8080root_name: []u8,
8181keep_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
8386pub const InnerError = error{ OutOfMemory, AnalysisFail };
8487
8588const WorkItem = union(enum) {
......@@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void {
928931
929932 self.symbol_exports.deinit(gpa);
930933 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);
931939 self.* = undefined;
932940}
933941
......@@ -2072,6 +2080,18 @@ fn createNewDecl(
20722080 return new_decl;
20732081}
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
20752095/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
20762096pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
20772097 return scope.cast(Scope.Block) orelse
......@@ -3309,6 +3329,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_
33093329 return Type.initPayload(&payload.base);
33103330}
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
33123354pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
33133355 const zir_module = scope.namespace();
33143356 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) {
1818 /// The expression has an inferred type, and it will be evaluated as an rvalue.
1919 none,
2020 /// The expression must generate a pointer rather than a value. For example, the left hand side
21 /// of an assignment uses an "LValue" result location.
22 lvalue,
23 /// The expression must generate a pointer
21 /// of an assignment uses this kind of result location.
2422 ref,
2523 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
2624 ty: *zir.Inst,
......@@ -46,134 +44,136 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
4644 return expr(mod, scope, type_rl, type_node);
4745}
4846
49/// Turn Zig AST into untyped ZIR istructions.
50pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
51 if (rl == .lvalue) {
52 switch (node.tag) {
53 .Root => unreachable,
54 .Use => unreachable,
55 .TestDecl => unreachable,
56 .DocComment => unreachable,
57 .VarDecl => unreachable,
58 .SwitchCase => unreachable,
59 .SwitchElse => unreachable,
60 .Else => unreachable,
61 .Payload => unreachable,
62 .PointerPayload => unreachable,
63 .PointerIndexPayload => unreachable,
64 .ErrorTag => unreachable,
65 .FieldInitializer => unreachable,
66 .ContainerField => unreachable,
67
68 .Assign,
69 .AssignBitAnd,
70 .AssignBitOr,
71 .AssignBitShiftLeft,
72 .AssignBitShiftRight,
73 .AssignBitXor,
74 .AssignDiv,
75 .AssignSub,
76 .AssignSubWrap,
77 .AssignMod,
78 .AssignAdd,
79 .AssignAddWrap,
80 .AssignMul,
81 .AssignMulWrap,
82 .Add,
83 .AddWrap,
84 .Sub,
85 .SubWrap,
86 .Mul,
87 .MulWrap,
88 .Div,
89 .Mod,
90 .BitAnd,
91 .BitOr,
92 .BitShiftLeft,
93 .BitShiftRight,
94 .BitXor,
95 .BangEqual,
96 .EqualEqual,
97 .GreaterThan,
98 .GreaterOrEqual,
99 .LessThan,
100 .LessOrEqual,
101 .ArrayCat,
102 .ArrayMult,
103 .BoolAnd,
104 .BoolOr,
105 .Asm,
106 .StringLiteral,
107 .IntegerLiteral,
108 .Call,
109 .Unreachable,
110 .Return,
111 .If,
112 .While,
113 .BoolNot,
114 .AddressOf,
115 .FloatLiteral,
116 .UndefinedLiteral,
117 .BoolLiteral,
118 .NullLiteral,
119 .OptionalType,
120 .Block,
121 .LabeledBlock,
122 .Break,
123 .PtrType,
124 .GroupedExpression,
125 .ArrayType,
126 .ArrayTypeSentinel,
127 .EnumLiteral,
128 .MultilineStringLiteral,
129 .CharLiteral,
130 .Defer,
131 .Catch,
132 .ErrorUnion,
133 .MergeErrorSets,
134 .Range,
135 .OrElse,
136 .Await,
137 .BitNot,
138 .Negation,
139 .NegationWrap,
140 .Resume,
141 .Try,
142 .SliceType,
143 .Slice,
144 .ArrayInitializer,
145 .ArrayInitializerDot,
146 .StructInitializer,
147 .StructInitializerDot,
148 .Switch,
149 .For,
150 .Suspend,
151 .Continue,
152 .AnyType,
153 .ErrorType,
154 .FnProto,
155 .AnyFrameType,
156 .ErrorSetDecl,
157 .ContainerDecl,
158 .Comptime,
159 .Nosuspend,
160 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
161
162 // @field can be assigned to
163 .BuiltinCall => {
164 const call = node.castTag(.BuiltinCall).?;
165 const tree = scope.tree();
166 const builtin_name = tree.tokenSlice(call.builtin_token);
167
168 if (!mem.eql(u8, builtin_name, "@field")) {
169 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
170 }
171 },
47fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
48 switch (node.tag) {
49 .Root => unreachable,
50 .Use => unreachable,
51 .TestDecl => unreachable,
52 .DocComment => unreachable,
53 .VarDecl => unreachable,
54 .SwitchCase => unreachable,
55 .SwitchElse => unreachable,
56 .Else => unreachable,
57 .Payload => unreachable,
58 .PointerPayload => unreachable,
59 .PointerIndexPayload => unreachable,
60 .ErrorTag => unreachable,
61 .FieldInitializer => unreachable,
62 .ContainerField => unreachable,
63
64 .Assign,
65 .AssignBitAnd,
66 .AssignBitOr,
67 .AssignBitShiftLeft,
68 .AssignBitShiftRight,
69 .AssignBitXor,
70 .AssignDiv,
71 .AssignSub,
72 .AssignSubWrap,
73 .AssignMod,
74 .AssignAdd,
75 .AssignAddWrap,
76 .AssignMul,
77 .AssignMulWrap,
78 .Add,
79 .AddWrap,
80 .Sub,
81 .SubWrap,
82 .Mul,
83 .MulWrap,
84 .Div,
85 .Mod,
86 .BitAnd,
87 .BitOr,
88 .BitShiftLeft,
89 .BitShiftRight,
90 .BitXor,
91 .BangEqual,
92 .EqualEqual,
93 .GreaterThan,
94 .GreaterOrEqual,
95 .LessThan,
96 .LessOrEqual,
97 .ArrayCat,
98 .ArrayMult,
99 .BoolAnd,
100 .BoolOr,
101 .Asm,
102 .StringLiteral,
103 .IntegerLiteral,
104 .Call,
105 .Unreachable,
106 .Return,
107 .If,
108 .While,
109 .BoolNot,
110 .AddressOf,
111 .FloatLiteral,
112 .UndefinedLiteral,
113 .BoolLiteral,
114 .NullLiteral,
115 .OptionalType,
116 .Block,
117 .LabeledBlock,
118 .Break,
119 .PtrType,
120 .GroupedExpression,
121 .ArrayType,
122 .ArrayTypeSentinel,
123 .EnumLiteral,
124 .MultilineStringLiteral,
125 .CharLiteral,
126 .Defer,
127 .Catch,
128 .ErrorUnion,
129 .MergeErrorSets,
130 .Range,
131 .OrElse,
132 .Await,
133 .BitNot,
134 .Negation,
135 .NegationWrap,
136 .Resume,
137 .Try,
138 .SliceType,
139 .Slice,
140 .ArrayInitializer,
141 .ArrayInitializerDot,
142 .StructInitializer,
143 .StructInitializerDot,
144 .Switch,
145 .For,
146 .Suspend,
147 .Continue,
148 .AnyType,
149 .ErrorType,
150 .FnProto,
151 .AnyFrameType,
152 .ErrorSetDecl,
153 .ContainerDecl,
154 .Comptime,
155 .Nosuspend,
156 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
157
158 // @field can be assigned to
159 .BuiltinCall => {
160 const call = node.castTag(.BuiltinCall).?;
161 const tree = scope.tree();
162 const builtin_name = tree.tokenSlice(call.builtin_token);
163
164 if (!mem.eql(u8, builtin_name, "@field")) {
165 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
166 }
167 },
172168
173 // can be assigned to
174 .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
175 }
169 // can be assigned to
170 .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
176171 }
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 {
177177 switch (node.tag) {
178178 .Root => unreachable, // Top-level declaration.
179179 .Use => unreachable, // Top-level declaration.
......@@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
232232 .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
233233 .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
235240 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
236241 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
237242 .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
242247 .Return => return ret(mod, scope, node.castTag(.Return).?),
243248 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
244249 .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).?),
246251 .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).?)),
248252 .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
249253 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
250254 .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
263267 .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
264268 .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
265269 .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
267276 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
268277 .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", .{}),
271278 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
272279 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
273280 .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", .{}),
277281 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
278282 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
279283 .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
287291 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
288292 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
289293 .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", .{}),
291294 .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", .{}),
294295 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
295296 .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),
296297 .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
316317 // proper type inference requires peer type resolution on the block's
317318 // break operand expressions.
318319 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,
320321 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
321322 };
322323 const operand = try expr(mod, parent_scope, branch_rl, rhs);
......@@ -458,7 +459,9 @@ fn varDecl(
458459 const tree = scope.tree();
459460 const name_src = tree.token_locs[node.name_token].start;
460461 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
462465 switch (tree.token_ids[node.mut_token]) {
463466 .Keyword_const => {
464467 // 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
521524 return;
522525 }
523526 }
524 const lvalue = try expr(mod, scope, .lvalue, infix_node.lhs);
527 const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
525528 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
526529}
527530
......@@ -531,7 +534,7 @@ fn assignOp(
531534 infix_node: *ast.Node.SimpleInfixOp,
532535 op_inst_tag: zir.Inst.Tag,
533536) 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);
535538 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
536539 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
537540 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
554557 return addZIRUnOp(mod, scope, src, .boolnot, operand);
555558}
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
557580fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
558581 return expr(mod, scope, .ref, node.rhs);
559582}
......@@ -561,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE
561584fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
562585 const tree = scope.tree();
563586 const src = tree.token_locs[node.op_token].start;
564 const meta_type = try addZIRInstConst(mod, scope, src, .{
565 .ty = Type.initTag(.type),
566 .val = Value.initTag(.type_type),
567 });
568 const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
587 const operand = try typeExpr(mod, scope, node.rhs);
569588 return addZIRUnOp(mod, scope, src, .optional_type, operand);
570589}
571590
......@@ -590,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir
590609}
591610
592611fn 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
598612 const simple = ptr_info.allowzero_token == null and
599613 ptr_info.align_info == null and
600614 ptr_info.volatile_token == null and
601615 ptr_info.sentinel == null;
602616
603617 if (simple) {
604 const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);
618 const child_type = try typeExpr(mod, scope, rhs);
605619 const mutable = ptr_info.const_token == null;
606620 // TODO stage1 type inference bug
607621 const T = zir.Inst.Tag;
......@@ -629,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
629643 kw_args.sentinel = try expr(mod, scope, .none, some);
630644 }
631645
632 const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);
646 const child_type = try typeExpr(mod, scope, rhs);
633647 if (kw_args.sentinel) |some| {
634648 kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
635649 }
......@@ -640,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
640654fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
641655 const tree = scope.tree();
642656 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 });
647657 const usize_type = try addZIRInstConst(mod, scope, src, .{
648658 .ty = Type.initTag(.type),
649659 .val = Value.initTag(.usize_type),
......@@ -651,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst
651661
652662 // TODO check for [_]T
653663 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);
657667}
658668
659669fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
660670 const tree = scope.tree();
661671 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 });
666672 const usize_type = try addZIRInstConst(mod, scope, src, .{
667673 .ty = Type.initTag(.type),
668674 .val = Value.initTag(.usize_type),
......@@ -671,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti
671677 // TODO check for [_]T
672678 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
673679 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);
675681 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
676682
677683 return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
......@@ -681,6 +687,28 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti
681687 }, .{});
682688}
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
684712fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
685713 const tree = scope.tree();
686714 const src = tree.token_locs[node.name].start;
......@@ -694,10 +722,31 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si
694722 const src = tree.token_locs[node.rtoken].start;
695723
696724 const operand = try expr(mod, scope, .ref, node.lhs);
697 const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand);
698 if (rl == .lvalue or rl == .ref) return unwrapped_ptr;
725 return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
726}
727
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 });
701750}
702751
703752/// 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
737786 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
738787}
739788
740fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
741 // TODO introduce lvalues
789fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
742790 const tree = scope.tree();
743791 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);
746794 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
747795
748796 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));
750799}
751800
752801fn 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
9711020 // proper type inference requires peer type resolution on the if's
9721021 // branches.
9731022 const branch_rl: ResultLoc = switch (rl) {
974 .discard, .none, .ty, .ptr, .lvalue, .ref => rl,
1023 .discard, .none, .ty, .ptr, .ref => rl,
9751024 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
9761025 };
9771026
......@@ -1101,7 +1150,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
11011150 // proper type inference requires peer type resolution on the while's
11021151 // branches.
11031152 const branch_rl: ResultLoc = switch (rl) {
1104 .discard, .none, .ty, .ptr, .lvalue, .ref => rl,
1153 .discard, .none, .ty, .ptr, .ref => rl,
11051154 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
11061155 };
11071156
......@@ -1232,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
12321281 .local_ptr => {
12331282 const local_ptr = s.cast(Scope.LocalPtr).?;
12341283 if (mem.eql(u8, local_ptr.name, ident_name)) {
1235 if (rl == .lvalue or rl == .ref) {
1236 return local_ptr.ptr;
1237 } else {
1238 const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
1239 return rlWrap(mod, scope, rl, result);
1240 }
1284 return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
12411285 }
12421286 s = local_ptr.parent;
12431287 },
......@@ -1247,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
12471291 }
12481292
12491293 if (mod.lookupDeclName(scope, ident_name)) |decl| {
1250 const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
1251 if (rl == .lvalue or rl == .ref)
1252 return result;
1253 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, result));
1294 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
12541295 }
12551296
12561297 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
......@@ -1466,12 +1507,8 @@ fn simpleCast(
14661507 try ensureBuiltinParamCount(mod, scope, call, 2);
14671508 const tree = scope.tree();
14681509 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 });
14731510 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]);
14751512 const rhs = try expr(mod, scope, .none, params[1]);
14761513 const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
14771514 return rlWrap(mod, scope, rl, result);
......@@ -1498,7 +1535,6 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
14981535 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
14991536 return result;
15001537 },
1501 .lvalue => unreachable,
15021538 .ref => {
15031539 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
15041540 return addZIRUnOp(mod, scope, result.src, .ref, result);
......@@ -1533,12 +1569,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
15331569 try ensureBuiltinParamCount(mod, scope, call, 2);
15341570 const tree = scope.tree();
15351571 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 });
15401572 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]);
15421574 switch (rl) {
15431575 .none => {
15441576 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
15501582 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
15511583 return result;
15521584 },
1553 .lvalue => unreachable,
15541585 .ref => {
15551586 const operand = try expr(mod, scope, .ref, params[1]);
15561587 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
18181849 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
18191850 return result;
18201851 },
1821 .lvalue, .ref => {
1852 .ref => {
18221853 // We need a pointer but we have a value.
18231854 return addZIRUnOp(mod, scope, result.src, .ref, result);
18241855 },
......@@ -1852,6 +1883,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul
18521883 return rlWrap(mod, scope, rl, void_inst);
18531884}
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
18551892pub fn addZIRInstSpecial(
18561893 mod: *Module,
18571894 scope: *Scope,
src-self-hosted/codegen.zig+361-64
......@@ -14,6 +14,7 @@ const Allocator = mem.Allocator;
1414const trace = @import("tracy.zig").trace;
1515const DW = std.dwarf;
1616const leb128 = std.debug.leb;
17const log = std.log.scoped(.codegen);
1718
1819// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
1920// zig fmt: off
......@@ -75,8 +76,8 @@ pub fn generateSymbol(
7576 switch (bin_file.options.target.cpu.arch) {
7677 .wasm32 => unreachable, // has its own code path
7778 .wasm64 => unreachable, // has its own code path
78 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
79 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
79 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
8081 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
8182 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
8283 //.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(
101102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
102103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103104 //.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),
104106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106108 //.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 {
344346
345347 const Branch = struct {
346348 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
349 /// The key must be canonical register.
347350 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
348351 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
349352
......@@ -381,9 +384,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
381384 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
382385 const reg = callee_preserved_regs[free_index];
383386 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
387 log.debug("alloc {} => {*}", .{reg, inst});
384388 return reg;
385389 }
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
387400 fn deinit(self: *Branch, gpa: *Allocator) void {
388401 self.inst_table.deinit(gpa);
389402 self.registers.deinit(gpa);
......@@ -570,8 +583,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
570583 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
571584 const inst_table = &branch.inst_table;
572585 for (body.instructions) |inst| {
573 const new_inst = try self.genFuncInst(inst);
574 try inst_table.putNoClobber(self.gpa, inst, new_inst);
586 const mcv = try self.genFuncInst(inst);
587 log.debug("{*} => {}", .{inst, mcv});
588 // TODO don't put void or dead things in here
589 try inst_table.putNoClobber(self.gpa, inst, mcv);
575590
576591 var i: ir.Inst.DeathsBitIndex = 0;
577592 while (inst.getOperand(i)) |operand| : (i += 1) {
......@@ -714,7 +729,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
714729 return self.allocMem(inst, abi_size, abi_align);
715730 }
716731
717 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {
732 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
718733 const elem_ty = inst.ty;
719734 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
720735 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 {
724739 self.stack_align = abi_align;
725740 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.
728 const ptr_bits = arch.ptrBitWidth();
729 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
730 if (abi_size <= ptr_bytes) {
731 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
732 if (branch.allocReg(inst)) |reg| {
733 return MCValue{ .register = registerAlias(reg, abi_size) };
742 if (reg_ok) {
743 // Make sure the type can fit in a register before we try to allocate one.
744 const ptr_bits = arch.ptrBitWidth();
745 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
746 if (abi_size <= ptr_bytes) {
747 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
748 if (branch.allocReg(inst)) |reg| {
749 return MCValue{ .register = registerAlias(reg, abi_size) };
750 }
734751 }
735752 }
736753 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
737754 return MCValue{ .stack_offset = stack_offset };
738755 }
739756
740 /// Does not "move" the instruction.
741 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
757 /// Copies a value to a register without tracking the register. The register is not considered
758 /// allocated. A second call to `copyToTmpRegister` may return the same register.
759 /// This can have a side effect of spilling instructions to the stack to free up a register.
760 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
762
763 const reg = branch.findUnusedReg() orelse b: {
764 // We'll take over the first register. Move the instruction that was previously
765 // there to a stack allocation.
766 const reg = callee_preserved_regs[0];
767 const regs_entry = branch.registers.remove(reg).?;
768 const spilled_inst = regs_entry.value.inst;
769
770 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
771 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
772 const reg_mcv = inst_entry.value;
773 assert(reg == toCanonicalReg(reg_mcv.register));
774 inst_entry.value = stack_mcv;
775 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
776
777 break :b reg;
778 };
779 try self.genSetReg(src, reg, mcv);
780 return reg;
781 }
782
783 /// Allocates a new register and copies `mcv` into it.
784 /// `reg_owner` is the instruction that gets associated with the register in the register table.
785 /// This can have a side effect of spilling instructions to the stack to free up a register.
786 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
742787 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
743788 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
744789
745 const reg = branch.allocReg(inst) orelse
746 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
747 const old_mcv = branch.inst_table.get(inst).?;
748 const new_mcv: MCValue = .{ .register = reg };
749 try self.genSetReg(inst.src, reg, old_mcv);
750 return new_mcv;
790 const reg = branch.allocReg(reg_owner) orelse b: {
791 // We'll take over the first register. Move the instruction that was previously
792 // there to a stack allocation.
793 const reg = callee_preserved_regs[0];
794 const regs_entry = branch.registers.getEntry(reg).?;
795 const spilled_inst = regs_entry.value.inst;
796 regs_entry.value = .{ .inst = reg_owner };
797
798 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
799 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
800 const reg_mcv = inst_entry.value;
801 assert(reg == toCanonicalReg(reg_mcv.register));
802 inst_entry.value = stack_mcv;
803 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
804
805 break :b reg;
806 };
807 try self.genSetReg(reg_owner.src, reg, mcv);
808 return MCValue{ .register = reg };
751809 }
752810
753811 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
......@@ -868,13 +926,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
868926 }
869927 }
870928
871 fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
872 if (!inst.operandDies(op_index) or !mcv.isMutable())
929 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
930 if (!inst.operandDies(op_index))
873931 return false;
874932
875 // OK we're going to do it, but we need to clear the operand death bit so that
876 // it stays allocated.
933 switch (mcv) {
934 .register => |reg| {
935 // If it's in the registers table, need to associate the register with the
936 // new instruction.
937 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
938 if (branch.registers.getEntry(toCanonicalReg(reg))) |entry| {
939 entry.value = .{ .inst = inst };
940 }
941 log.debug("reusing {} => {*}", .{reg, inst});
942 },
943 .stack_offset => |off| {
944 log.debug("reusing stack offset {} => {*}", .{off, inst});
945 return true;
946 },
947 else => return false,
948 }
949
950 // Prevent the operand deaths processing code from deallocating it.
877951 inst.clearOperandDeath(op_index);
952
878953 return true;
879954 }
880955
......@@ -887,11 +962,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
887962 if (inst.base.isUnused() and !is_volatile)
888963 return MCValue.dead;
889964 const dst_mcv: MCValue = blk: {
890 if (reuseOperand(&inst.base, 0, ptr)) {
965 if (self.reuseOperand(&inst.base, 0, ptr)) {
891966 // The MCValue that holds the pointer can be re-used as the value.
892967 break :blk ptr;
893968 } else {
894 break :blk try self.allocRegOrMem(&inst.base);
969 break :blk try self.allocRegOrMem(&inst.base, true);
895970 }
896971 };
897972 switch (ptr) {
......@@ -985,23 +1060,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9851060 var dst_mcv: MCValue = undefined;
9861061 var src_mcv: MCValue = undefined;
9871062 var src_inst: *ir.Inst = undefined;
988 if (reuseOperand(inst, 0, lhs)) {
1063 if (self.reuseOperand(inst, 0, lhs)) {
9891064 // LHS dies; use it as the destination.
9901065 // Both operands cannot be memory.
9911066 src_inst = op_rhs;
9921067 if (lhs.isMemory() and rhs.isMemory()) {
993 dst_mcv = try self.copyToNewRegister(op_lhs);
1068 dst_mcv = try self.copyToNewRegister(inst, lhs);
9941069 src_mcv = rhs;
9951070 } else {
9961071 dst_mcv = lhs;
9971072 src_mcv = rhs;
9981073 }
999 } else if (reuseOperand(inst, 1, rhs)) {
1074 } else if (self.reuseOperand(inst, 1, rhs)) {
10001075 // RHS dies; use it as the destination.
10011076 // Both operands cannot be memory.
10021077 src_inst = op_lhs;
10031078 if (lhs.isMemory() and rhs.isMemory()) {
1004 dst_mcv = try self.copyToNewRegister(op_rhs);
1079 dst_mcv = try self.copyToNewRegister(inst, rhs);
10051080 src_mcv = lhs;
10061081 } else {
10071082 dst_mcv = rhs;
......@@ -1009,11 +1084,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10091084 }
10101085 } else {
10111086 if (lhs.isMemory()) {
1012 dst_mcv = try self.copyToNewRegister(op_lhs);
1087 dst_mcv = try self.copyToNewRegister(inst, lhs);
10131088 src_mcv = rhs;
10141089 src_inst = op_rhs;
10151090 } else {
1016 dst_mcv = try self.copyToNewRegister(op_rhs);
1091 dst_mcv = try self.copyToNewRegister(inst, rhs);
10171092 src_mcv = lhs;
10181093 src_inst = op_lhs;
10191094 }
......@@ -1026,18 +1101,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10261101 switch (src_mcv) {
10271102 .immediate => |imm| {
10281103 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) };
10301105 }
10311106 },
10321107 else => {},
10331108 }
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
10371112 return dst_mcv;
10381113 }
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 {
10411124 switch (dst_mcv) {
10421125 .none => unreachable,
10431126 .undef => unreachable,
......@@ -1087,12 +1170,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10871170 },
10881171 }
10891172 },
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 => {
10911198 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
10921199 },
10931200 }
10941201 }
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
10961227 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
10971228 if (FreeRegInt == u0) {
10981229 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 {
11091240 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
11101241 switch (result) {
11111242 .register => |reg| {
1112 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
1243 branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base });
11131244 branch.markRegUsed(reg);
11141245
11151246 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 {
11341265 .riscv64 => {
11351266 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
11361267 },
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 },
11371279 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
11381280 }
11391281 return .none;
......@@ -1219,10 +1361,77 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12191361 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
12201362 }
12211363 },
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 },
12221427 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
12231428 }
12241429 } 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 }
12261435 } else {
12271436 unreachable;
12281437 }
......@@ -1275,6 +1484,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12751484 .riscv64 => {
12761485 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
12771486 },
1487 .arm => {
1488 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1489 },
12781490 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
12791491 }
12801492 return .unreach;
......@@ -1304,13 +1516,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13041516 // Either one, but not both, can be a memory operand.
13051517 // Source operand can be an immediate, 8 bits or 32 bits.
13061518 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)
13081520 else
13091521 lhs;
13101522 // This instruction supports only signed 32-bit immediates at most.
13111523 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);
13141526 const info = inst.lhs.ty.intInfo(self.target.*);
13151527 if (info.signed) {
13161528 return MCValue{ .compare_flags_signed = op };
......@@ -1512,6 +1724,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15121724 if (!inst.is_volatile and inst.base.isUnused())
15131725 return MCValue.dead;
15141726 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 },
15151770 .riscv64 => {
15161771 for (inst.inputs) |input, i| {
15171772 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 {
15841839 /// resulting REX is meaningful, but will remain the same if it is not.
15851840 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
15861841 /// 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
15871846 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);
15881848 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
15891849 var value: u8 = 0x40;
15901850 if (arg.b) {
......@@ -1681,27 +1941,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16811941 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
16821942 },
16831943 .register => |reg| {
1684 const abi_size = ty.abiSize(self.target.*);
1685 const adj_off = stack_offset + abi_size;
1686 try self.code.ensureCapacity(self.code.items.len + 7);
1687 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1688 const reg_id: u8 = @truncate(u3, reg.id());
1689 if (adj_off <= 128) {
1690 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1691 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1692 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1693 const twos_comp = @bitCast(u8, negative_offset);
1694 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp });
1695 } else if (adj_off <= 2147483648) {
1696 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1697 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1698 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1699 const twos_comp = @bitCast(u32, negative_offset);
1700 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM });
1701 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1702 } else {
1703 return self.fail(src, "stack offset too large", .{});
1704 }
1944 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
17051945 },
17061946 .memory => |vaddr| {
17071947 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 {
17091949 .stack_offset => |off| {
17101950 if (stack_offset == off)
17111951 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 });
17131955 },
17141956 },
17151957 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 {
17181960
17191961 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
17201962 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 },
17212015 .riscv64 => switch (mcv) {
17222016 .dead => unreachable,
17232017 .ptr_stack_offset => unreachable,
......@@ -2027,7 +2321,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20272321 },
20282322 });
20292323 if (imm >= math.maxInt(U)) {
2030 return self.copyToNewRegister(inst);
2324 return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
20312325 }
20322326 },
20332327 else => {},
......@@ -2150,7 +2444,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21502444 result.stack_byte_count = next_stack_offset;
21512445 result.stack_align = 16;
21522446 },
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}),
21542448 }
21552449 },
21562450 else => if (param_types.len != 0)
......@@ -2197,6 +2491,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21972491 .i386 => @import("codegen/x86.zig"),
21982492 .x86_64 => @import("codegen/x86_64.zig"),
21992493 .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"),
22002497 else => struct {
22012498 pub const Register = enum {
22022499 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;
55const trace = @import("tracy.zig").trace;
66const Package = @import("Package.zig");
77const 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
912pub const Options = struct {
1013 target: std.Target,
......@@ -20,6 +23,7 @@ pub const Options = struct {
2023 /// Used for calculating how much space to reserve for executable program code in case
2124 /// the binary file deos not already have such a section.
2225 program_code_size_hint: u64 = 256 * 1024,
26 entry_addr: ?u64 = null,
2327};
2428
2529pub const File = struct {
src-self-hosted/link/Elf.zig+60-27
......@@ -14,12 +14,10 @@ const leb128 = std.debug.leb;
1414const Package = @import("../Package.zig");
1515const Value = @import("../value.zig").Value;
1616const Type = @import("../type.zig").Type;
17const build_options = @import("build_options");
1817const link = @import("../link.zig");
1918const File = link.File;
2019const Elf = @This();
2120
22const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
2321const default_entry_addr = 0x8000000;
2422
2523// 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 {
249247 .allocator = allocator,
250248 },
251249 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
252 32 => .p32,
253 64 => .p64,
250 0 ... 32 => .p32,
251 33 ... 64 => .p64,
254252 else => return error.UnsupportedELFArchitecture,
255253 },
256254 };
......@@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
278276 .file = file,
279277 },
280278 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
281 32 => .p32,
282 64 => .p64,
279 0 ... 32 => .p32,
280 33 ... 64 => .p64,
283281 else => return error.UnsupportedELFArchitecture,
284282 },
285283 .shdr_table_dirty = true,
......@@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 {
346344
347345/// Returns end pos of collision, if any.
348346fn 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;
350348 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
351349 if (start < ehdr_size)
352350 return ehdr_size;
......@@ -462,12 +460,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
462460 const p_align = 0x1000;
463461 const off = self.findFreeSpace(file_size, p_align);
464462 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;
465464 try self.program_headers.append(self.base.allocator, .{
466465 .p_type = elf.PT_LOAD,
467466 .p_offset = off,
468467 .p_filesz = file_size,
469 .p_vaddr = default_entry_addr,
470 .p_paddr = default_entry_addr,
468 .p_vaddr = entry_addr,
469 .p_paddr = entry_addr,
471470 .p_memsz = file_size,
472471 .p_align = p_align,
473472 .p_flags = elf.PF_X | elf.PF_R,
......@@ -486,13 +485,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
486485 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
487486 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
488487 // 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;
490489 try self.program_headers.append(self.base.allocator, .{
491490 .p_type = elf.PT_LOAD,
492491 .p_offset = off,
493492 .p_filesz = file_size,
494 .p_vaddr = default_got_addr,
495 .p_paddr = default_got_addr,
493 .p_vaddr = got_addr,
494 .p_paddr = got_addr,
496495 .p_memsz = file_size,
497496 .p_align = p_align,
498497 .p_flags = elf.PF_R,
......@@ -863,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
863862 // Write the form for the compile unit, which must match the abbrev table above.
864863 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
865864 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);
867866 // Currently only one compilation unit is supported, so the address range is simply
868867 // identical to the main program header virtual address and memory size.
869868 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
......@@ -1349,6 +1348,7 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
13491348 var already_have_free_list_node = false;
13501349 {
13511350 var i: usize = 0;
1351 // TODO turn text_block_free_list into a hash map
13521352 while (i < self.text_block_free_list.items.len) {
13531353 if (self.text_block_free_list.items[i] == text_block) {
13541354 _ = self.text_block_free_list.swapRemove(i);
......@@ -1360,11 +1360,19 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
13601360 i += 1;
13611361 }
13621362 }
1363 // TODO process free list for dbg info just like we do above for vaddrs
13631364
13641365 if (self.last_text_block == text_block) {
13651366 // TODO shrink the .text section size here
13661367 self.last_text_block = text_block.prev;
13671368 }
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
13691377 if (text_block.prev) |prev| {
13701378 prev.next = text_block.next;
......@@ -1383,6 +1391,20 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
13831391 } else {
13841392 text_block.next = null;
13851393 }
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 }
13861408}
13871409
13881410fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
......@@ -1584,10 +1606,10 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
15841606 next.prev = null;
15851607 }
15861608 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;
15881610 }
15891611 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;
15911613 }
15921614}
15931615
......@@ -2151,29 +2173,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void {
21512173fn writeProgHeader(self: *Elf, index: usize) !void {
21522174 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
21532175 const offset = self.program_headers.items[index].p_offset;
2154 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2155 32 => {
2176 switch (self.ptr_width) {
2177 .p32 => {
21562178 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
21572179 if (foreign_endian) {
21582180 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
21592181 }
21602182 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
21612183 },
2162 64 => {
2184 .p64 => {
21632185 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
21642186 if (foreign_endian) {
21652187 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
21662188 }
21672189 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
21682190 },
2169 else => return error.UnsupportedArchitecture,
21702191 }
21712192}
21722193
21732194fn writeSectHeader(self: *Elf, index: usize) !void {
21742195 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2175 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
2176 32 => {
2196 switch (self.ptr_width) {
2197 .p32 => {
21772198 var shdr: [1]elf.Elf32_Shdr = undefined;
21782199 shdr[0] = sectHeaderTo32(self.sections.items[index]);
21792200 if (foreign_endian) {
......@@ -2182,7 +2203,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
21822203 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
21832204 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
21842205 },
2185 64 => {
2206 .p64 => {
21862207 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
21872208 if (foreign_endian) {
21882209 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
......@@ -2190,14 +2211,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
21902211 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
21912212 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
21922213 },
2193 else => return error.UnsupportedArchitecture,
21942214 }
21952215}
21962216
21972217fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
21982218 const shdr = &self.sections.items[self.got_section_index.?];
21992219 const phdr = &self.program_headers.items[self.phdr_got_index.?];
2200 const entry_size: u16 = self.ptrWidthBytes();
2220 const entry_size: u16 = self.archPtrWidthBytes();
22012221 if (self.offset_table_count_dirty) {
22022222 // TODO Also detect virtual address collisions.
22032223 const allocated_size = self.allocatedSize(shdr.sh_offset);
......@@ -2221,17 +2241,23 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
22212241 }
22222242 const endian = self.base.options.target.cpu.arch.endian();
22232243 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2224 switch (self.ptr_width) {
2225 .p32 => {
2244 switch (entry_size) {
2245 2 => {
2246 var buf: [2]u8 = undefined;
2247 mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
2248 try self.base.file.?.pwriteAll(&buf, off);
2249 },
2250 4 => {
22262251 var buf: [4]u8 = undefined;
22272252 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
22282253 try self.base.file.?.pwriteAll(&buf, off);
22292254 },
2230 .p64 => {
2255 8 => {
22312256 var buf: [8]u8 = undefined;
22322257 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
22332258 try self.base.file.?.pwriteAll(&buf, off);
22342259 },
2260 else => unreachable,
22352261 }
22362262}
22372263
......@@ -2344,6 +2370,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void {
23442370 }
23452371}
23462372
2373/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
23472374fn ptrWidthBytes(self: Elf) u8 {
23482375 return switch (self.ptr_width) {
23492376 .p32 => 4,
......@@ -2351,6 +2378,12 @@ fn ptrWidthBytes(self: Elf) u8 {
23512378 };
23522379}
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
23542387/// The reloc offset for the virtual address of a function in its Line Number Program.
23552388/// Size is a virtual address integer.
23562389const dbg_line_vaddr_reloc_index = 3;
src-self-hosted/link/MachO.zig+177-38
......@@ -6,29 +6,66 @@ const assert = std.debug.assert;
66const fs = std.fs;
77const log = std.log.scoped(.link);
88const macho = std.macho;
9const codegen = @import("../codegen.zig");
910const math = std.math;
1011const mem = std.mem;
12const trace = @import("../tracy.zig").trace;
13const Type = @import("../type.zig").Type;
1114
1215const Module = @import("../Module.zig");
1316const link = @import("../link.zig");
1417const File = link.File;
1518
19const is_darwin = std.Target.current.os.tag.isDarwin();
20
1621pub const base_tag: File.Tag = File.Tag.macho;
1722
1823base: File,
1924
20/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
21/// Same order as in the file.
22segment_cmds: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
25/// List of all load command headers that are in the file.
26/// We use it to track number and size of all commands needed by the header.
27commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){},
28command_file_offset: ?u64 = null,
2329
2430/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
2531/// Same order as in the file.
32segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
2633sections: 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,
2838entry_addr: ?u64 = null,
2939
40/// Default VM start address set at 4GB
41vm_start_address: u64 = 0x100000000,
42
43seg_table_dirty: bool = false,
44
3045error_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
3269pub const TextBlock = struct {
3370 pub const empty = TextBlock{};
3471};
......@@ -80,12 +117,6 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO
80117/// Truncates the existing file contents and overwrites the contents.
81118/// Returns an error if `file` is not already open with +read +write +seek abilities.
82119fn 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
89120 var self: MachO = .{
90121 .base = .{
91122 .file = file,
......@@ -96,31 +127,35 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
96127 };
97128 errdefer self.deinit();
98129
99 if (options.output_mode == .Exe) {
100 // The first segment command for executables is always a __PAGEZERO segment.
101 try self.segment_cmds.append(allocator, .{
102 .cmd = macho.LC_SEGMENT_64,
103 .cmdsize = @sizeOf(macho.segment_command_64),
104 .segname = self.makeString("__PAGEZERO"),
105 .vmaddr = 0,
106 .vmsize = 0,
107 .fileoff = 0,
108 .filesize = 0,
109 .maxprot = 0,
110 .initprot = 0,
111 .nsects = 0,
112 .flags = 0,
113 });
130 switch (options.output_mode) {
131 .Exe => {
132 // The first segment command for executables is always a __PAGEZERO segment.
133 const pagezero = .{
134 .cmd = macho.LC_SEGMENT_64,
135 .cmdsize = commandSize(@sizeOf(macho.segment_command_64)),
136 .segname = makeString("__PAGEZERO"),
137 .vmaddr = 0,
138 .vmsize = self.vm_start_address,
139 .fileoff = 0,
140 .filesize = 0,
141 .maxprot = 0,
142 .initprot = 0,
143 .nsects = 0,
144 .flags = 0,
145 };
146 try self.commands.append(allocator, .{
147 .cmd = pagezero.cmd,
148 .cmdsize = pagezero.cmdsize,
149 });
150 try self.segments.append(allocator, pagezero);
151 },
152 .Obj => return error.TODOImplementWritingObjFiles,
153 .Lib => return error.TODOImplementWritingLibFiles,
114154 }
115155
116 return self;
117}
156 try self.populateMissingMetadata();
118157
119fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 {
120 var buf: [16]u8 = undefined;
121 if (bytes.len > buf.len) @compileError("MachO segment/section name too long");
122 mem.copy(u8, buf[0..], bytes);
123 return buf;
158 return self;
124159}
125160
126161fn writeMachOHeader(self: *MachO) !void {
......@@ -156,10 +191,14 @@ fn writeMachOHeader(self: *MachO) !void {
156191 };
157192 hdr.filetype = filetype;
158193
159 // TODO consider other commands
160 const ncmds = try math.cast(u32, self.segment_cmds.items.len);
194 const ncmds = try math.cast(u32, self.commands.items.len);
161195 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
164203 // TODO should these be set to something else?
165204 hdr.flags = 0;
......@@ -169,18 +208,90 @@ fn writeMachOHeader(self: *MachO) !void {
169208}
170209
171210pub fn flush(self: *MachO, module: *Module) !void {
172 // TODO implement flush
211 // Save segments first
173212 {
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);
175214 defer self.base.allocator.free(buf);
176215
216 self.command_file_offset = @sizeOf(macho.mach_header_64);
217
177218 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;
179221 }
180222
181223 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64));
182224 }
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
184295 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
185296 log.debug("flushing. no_entry_point_found = true\n", .{});
186297 self.error_flags.no_entry_point_found = true;
......@@ -192,7 +303,8 @@ pub fn flush(self: *MachO, module: *Module) !void {
192303}
193304
194305pub 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);
196308 self.sections.deinit(self.base.allocator);
197309}
198310
......@@ -214,3 +326,30 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
214326pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
215327 @panic("TODO implement getDeclVAddr for MachO");
216328}
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 {
583583
584584 switch (case.target.getExternalExecutor()) {
585585 .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
588591 .qemu => |qemu_bin_name| if (enable_qemu) {
589592 // TODO Ability for test cases to specify whether to link libc.
......@@ -635,7 +638,6 @@ pub const TestContext = struct {
635638 var test_node = update_node.start("test", null);
636639 test_node.activate();
637640 defer test_node.end();
638
639641 defer allocator.free(exec_result.stdout);
640642 defer allocator.free(exec_result.stderr);
641643 switch (exec_result.term) {
......@@ -657,4 +659,115 @@ pub const TestContext = struct {
657659 }
658660 }
659661 }
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 }
660773};
src-self-hosted/type.zig+251-7
......@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const Target = std.Target;
6const Module = @import("Module.zig");
67
78/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
89/// It's important for this type to be small.
......@@ -52,7 +53,7 @@ pub const Type = extern union {
5253 .bool => return .Bool,
5354 .void => return .Void,
5455 .type => return .Type,
55 .anyerror => return .ErrorSet,
56 .error_set, .error_set_single, .anyerror => return .ErrorSet,
5657 .comptime_int => return .ComptimeInt,
5758 .comptime_float => return .ComptimeFloat,
5859 .noreturn => return .NoReturn,
......@@ -84,6 +85,10 @@ pub const Type = extern union {
8485 .optional_single_mut_pointer,
8586 => return .Optional,
8687 .enum_literal => return .EnumLiteral,
88
89 .anyerror_void_error_union, .error_union => return .ErrorUnion,
90
91 .anyframe_T, .@"anyframe" => return .AnyFrame,
8792 }
8893 }
8994
......@@ -151,6 +156,9 @@ pub const Type = extern union {
151156 .ComptimeInt => return true,
152157 .Undefined => return true,
153158 .Null => return true,
159 .AnyFrame => {
160 return a.elemType().eql(b.elemType());
161 },
154162 .Pointer => {
155163 // Hot path for common case:
156164 if (a.castPointer()) |a_payload| {
......@@ -225,7 +233,6 @@ pub const Type = extern union {
225233 .BoundFn,
226234 .Opaque,
227235 .Frame,
228 .AnyFrame,
229236 .Vector,
230237 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
231238 }
......@@ -343,6 +350,8 @@ pub const Type = extern union {
343350 .single_const_pointer_to_comptime_int,
344351 .const_slice_u8,
345352 .enum_literal,
353 .anyerror_void_error_union,
354 .@"anyframe",
346355 => unreachable,
347356
348357 .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
......@@ -397,6 +406,7 @@ pub const Type = extern union {
397406 .optional_single_mut_pointer,
398407 .optional_single_const_pointer,
399408 => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
409 .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
400410
401411 .pointer => {
402412 const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
......@@ -416,6 +426,19 @@ pub const Type = extern union {
416426 };
417427 return Type{ .ptr_otherwise = &new_payload.base };
418428 },
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),
419442 }
420443 }
421444
......@@ -482,6 +505,8 @@ pub const Type = extern union {
482505 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
483506 .@"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"),
485510 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
486511 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
487512 .fn_void_no_args => return out_stream.writeAll("fn() void"),
......@@ -500,6 +525,12 @@ pub const Type = extern union {
500525 continue;
501526 },
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 },
503534 .array_u8 => {
504535 const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
505536 return out_stream.print("[{}]u8", .{payload.len});
......@@ -622,6 +653,21 @@ pub const Type = extern union {
622653 ty = payload.pointee_type;
623654 continue;
624655 },
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 },
625671 }
626672 unreachable;
627673 }
......@@ -715,6 +761,11 @@ pub const Type = extern union {
715761 .optional,
716762 .optional_single_mut_pointer,
717763 .optional_single_const_pointer,
764 .@"anyframe",
765 .anyframe_T,
766 .anyerror_void_error_union,
767 .error_set,
768 .error_set_single,
718769 => true,
719770 // TODO lazy types
720771 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
......@@ -723,6 +774,11 @@ pub const Type = extern union {
723774 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,
724775 .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
726782 .c_void,
727783 .void,
728784 .type,
......@@ -756,6 +812,7 @@ pub const Type = extern union {
756812 .fn_ccc_void_no_args, // represents machine code; not a pointer
757813 .function, // represents machine code; not a pointer
758814 => return switch (target.cpu.arch) {
815 .arm => 4,
759816 .riscv64 => 2,
760817 else => 1,
761818 },
......@@ -778,6 +835,8 @@ pub const Type = extern union {
778835 .mut_slice,
779836 .optional_single_const_pointer,
780837 .optional_single_mut_pointer,
838 .@"anyframe",
839 .anyframe_T,
781840 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
782841
783842 .pointer => {
......@@ -802,7 +861,11 @@ pub const Type = extern union {
802861 .f128 => return 16,
803862 .c_longdouble => return 16,
804863
805 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
864 .error_set,
865 .error_set_single,
866 .anyerror_void_error_union,
867 .anyerror,
868 => return 2, // TODO revisit this when we have the concept of the error tag type
806869
807870 .array, .array_sentinel => return self.elemType().abiAlignment(target),
808871
......@@ -828,6 +891,16 @@ pub const Type = extern union {
828891 return child_type.abiAlignment(target);
829892 },
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
831904 .c_void,
832905 .void,
833906 .type,
......@@ -881,12 +954,15 @@ pub const Type = extern union {
881954 .i32, .u32 => return 4,
882955 .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
886959 .const_slice,
887960 .mut_slice,
888 .const_slice_u8,
889 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
961 => {
962 if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2;
963 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
964 },
965 .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
890966
891967 .optional_single_const_pointer,
892968 .optional_single_mut_pointer,
......@@ -922,7 +998,11 @@ pub const Type = extern union {
922998 .f128 => return 16,
923999 .c_longdouble => return 16,
9241000
925 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
1001 .error_set,
1002 .error_set_single,
1003 .anyerror_void_error_union,
1004 .anyerror,
1005 => return 2, // TODO revisit this when we have the concept of the error tag type
9261006
9271007 .int_signed, .int_unsigned => {
9281008 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
......@@ -949,6 +1029,18 @@ pub const Type = extern union {
9491029 // to the child type's ABI alignment.
9501030 return child_type.abiAlignment(target) + child_type.abiSize(target);
9511031 },
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 },
9521044 };
9531045 }
9541046
......@@ -1009,6 +1101,12 @@ pub const Type = extern union {
10091101 .c_mut_pointer,
10101102 .const_slice,
10111103 .mut_slice,
1104 .error_union,
1105 .@"anyframe",
1106 .anyframe_T,
1107 .anyerror_void_error_union,
1108 .error_set,
1109 .error_set_single,
10121110 => false,
10131111
10141112 .single_const_pointer,
......@@ -1077,6 +1175,12 @@ pub const Type = extern union {
10771175 .optional_single_mut_pointer,
10781176 .optional_single_const_pointer,
10791177 .enum_literal,
1178 .error_union,
1179 .@"anyframe",
1180 .anyframe_T,
1181 .anyerror_void_error_union,
1182 .error_set,
1183 .error_set_single,
10801184 => false,
10811185
10821186 .const_slice,
......@@ -1142,6 +1246,12 @@ pub const Type = extern union {
11421246 .optional_single_const_pointer,
11431247 .enum_literal,
11441248 .mut_slice,
1249 .error_union,
1250 .@"anyframe",
1251 .anyframe_T,
1252 .anyerror_void_error_union,
1253 .error_set,
1254 .error_set_single,
11451255 => false,
11461256
11471257 .single_const_pointer,
......@@ -1216,6 +1326,12 @@ pub const Type = extern union {
12161326 .optional_single_mut_pointer,
12171327 .optional_single_const_pointer,
12181328 .enum_literal,
1329 .error_union,
1330 .@"anyframe",
1331 .anyframe_T,
1332 .anyerror_void_error_union,
1333 .error_set,
1334 .error_set_single,
12191335 => false,
12201336
12211337 .pointer => {
......@@ -1327,6 +1443,12 @@ pub const Type = extern union {
13271443 .optional_single_const_pointer,
13281444 .optional_single_mut_pointer,
13291445 .enum_literal,
1446 .error_union,
1447 .@"anyframe",
1448 .anyframe_T,
1449 .anyerror_void_error_union,
1450 .error_set,
1451 .error_set_single,
13301452 => unreachable,
13311453
13321454 .array => self.cast(Payload.Array).?.elem_type,
......@@ -1448,6 +1570,12 @@ pub const Type = extern union {
14481570 .optional_single_mut_pointer,
14491571 .optional_single_const_pointer,
14501572 .enum_literal,
1573 .error_union,
1574 .@"anyframe",
1575 .anyframe_T,
1576 .anyerror_void_error_union,
1577 .error_set,
1578 .error_set_single,
14511579 => unreachable,
14521580
14531581 .array => self.cast(Payload.Array).?.len,
......@@ -1515,6 +1643,12 @@ pub const Type = extern union {
15151643 .optional_single_mut_pointer,
15161644 .optional_single_const_pointer,
15171645 .enum_literal,
1646 .error_union,
1647 .@"anyframe",
1648 .anyframe_T,
1649 .anyerror_void_error_union,
1650 .error_set,
1651 .error_set_single,
15181652 => unreachable,
15191653
15201654 .array, .array_u8 => return null,
......@@ -1580,6 +1714,12 @@ pub const Type = extern union {
15801714 .optional_single_mut_pointer,
15811715 .optional_single_const_pointer,
15821716 .enum_literal,
1717 .error_union,
1718 .@"anyframe",
1719 .anyframe_T,
1720 .anyerror_void_error_union,
1721 .error_set,
1722 .error_set_single,
15831723 => false,
15841724
15851725 .int_signed,
......@@ -1648,6 +1788,12 @@ pub const Type = extern union {
16481788 .optional_single_mut_pointer,
16491789 .optional_single_const_pointer,
16501790 .enum_literal,
1791 .error_union,
1792 .@"anyframe",
1793 .anyframe_T,
1794 .anyerror_void_error_union,
1795 .error_set,
1796 .error_set_single,
16511797 => false,
16521798
16531799 .int_unsigned,
......@@ -1706,6 +1852,12 @@ pub const Type = extern union {
17061852 .optional_single_mut_pointer,
17071853 .optional_single_const_pointer,
17081854 .enum_literal,
1855 .error_union,
1856 .@"anyframe",
1857 .anyframe_T,
1858 .anyerror_void_error_union,
1859 .error_set,
1860 .error_set_single,
17091861 => unreachable,
17101862
17111863 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
......@@ -1782,6 +1934,12 @@ pub const Type = extern union {
17821934 .optional_single_mut_pointer,
17831935 .optional_single_const_pointer,
17841936 .enum_literal,
1937 .error_union,
1938 .@"anyframe",
1939 .anyframe_T,
1940 .anyerror_void_error_union,
1941 .error_set,
1942 .error_set_single,
17851943 => false,
17861944
17871945 .usize,
......@@ -1887,6 +2045,12 @@ pub const Type = extern union {
18872045 .optional_single_mut_pointer,
18882046 .optional_single_const_pointer,
18892047 .enum_literal,
2048 .error_union,
2049 .@"anyframe",
2050 .anyframe_T,
2051 .anyerror_void_error_union,
2052 .error_set,
2053 .error_set_single,
18902054 => unreachable,
18912055 };
18922056 }
......@@ -1958,6 +2122,12 @@ pub const Type = extern union {
19582122 .optional_single_mut_pointer,
19592123 .optional_single_const_pointer,
19602124 .enum_literal,
2125 .error_union,
2126 .@"anyframe",
2127 .anyframe_T,
2128 .anyerror_void_error_union,
2129 .error_set,
2130 .error_set_single,
19612131 => unreachable,
19622132 }
19632133 }
......@@ -2028,6 +2198,12 @@ pub const Type = extern union {
20282198 .optional_single_mut_pointer,
20292199 .optional_single_const_pointer,
20302200 .enum_literal,
2201 .error_union,
2202 .@"anyframe",
2203 .anyframe_T,
2204 .anyerror_void_error_union,
2205 .error_set,
2206 .error_set_single,
20312207 => unreachable,
20322208 }
20332209 }
......@@ -2098,6 +2274,12 @@ pub const Type = extern union {
20982274 .optional_single_mut_pointer,
20992275 .optional_single_const_pointer,
21002276 .enum_literal,
2277 .error_union,
2278 .@"anyframe",
2279 .anyframe_T,
2280 .anyerror_void_error_union,
2281 .error_set,
2282 .error_set_single,
21012283 => unreachable,
21022284 };
21032285 }
......@@ -2165,6 +2347,12 @@ pub const Type = extern union {
21652347 .optional_single_mut_pointer,
21662348 .optional_single_const_pointer,
21672349 .enum_literal,
2350 .error_union,
2351 .@"anyframe",
2352 .anyframe_T,
2353 .anyerror_void_error_union,
2354 .error_set,
2355 .error_set_single,
21682356 => unreachable,
21692357 };
21702358 }
......@@ -2232,6 +2420,12 @@ pub const Type = extern union {
22322420 .optional_single_mut_pointer,
22332421 .optional_single_const_pointer,
22342422 .enum_literal,
2423 .error_union,
2424 .@"anyframe",
2425 .anyframe_T,
2426 .anyerror_void_error_union,
2427 .error_set,
2428 .error_set_single,
22352429 => unreachable,
22362430 };
22372431 }
......@@ -2299,6 +2493,12 @@ pub const Type = extern union {
22992493 .optional_single_mut_pointer,
23002494 .optional_single_const_pointer,
23012495 .enum_literal,
2496 .error_union,
2497 .@"anyframe",
2498 .anyframe_T,
2499 .anyerror_void_error_union,
2500 .error_set,
2501 .error_set_single,
23022502 => false,
23032503 };
23042504 }
......@@ -2350,6 +2550,12 @@ pub const Type = extern union {
23502550 .optional_single_mut_pointer,
23512551 .optional_single_const_pointer,
23522552 .enum_literal,
2553 .anyerror_void_error_union,
2554 .anyframe_T,
2555 .@"anyframe",
2556 .error_union,
2557 .error_set,
2558 .error_set_single,
23532559 => return null,
23542560
23552561 .void => return Value.initTag(.void_value),
......@@ -2453,6 +2659,12 @@ pub const Type = extern union {
24532659 .optional_single_mut_pointer,
24542660 .optional_single_const_pointer,
24552661 .enum_literal,
2662 .error_union,
2663 .@"anyframe",
2664 .anyframe_T,
2665 .anyerror_void_error_union,
2666 .error_set,
2667 .error_set_single,
24562668 => return false,
24572669
24582670 .c_const_pointer,
......@@ -2510,6 +2722,8 @@ pub const Type = extern union {
25102722 fn_naked_noreturn_no_args,
25112723 fn_ccc_void_no_args,
25122724 single_const_pointer_to_comptime_int,
2725 anyerror_void_error_union,
2726 @"anyframe",
25132727 const_slice_u8, // See last_no_payload_tag below.
25142728 // After this, the tag requires a payload.
25152729
......@@ -2532,6 +2746,10 @@ pub const Type = extern union {
25322746 optional,
25332747 optional_single_mut_pointer,
25342748 optional_single_const_pointer,
2749 error_union,
2750 anyframe_T,
2751 error_set,
2752 error_set_single,
25352753
25362754 pub const last_no_payload_tag = Tag.const_slice_u8;
25372755 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -2613,6 +2831,32 @@ pub const Type = extern union {
26132831 @"volatile": bool,
26142832 size: std.builtin.TypeInfo.Pointer.Size,
26152833 };
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 };
26162860 };
26172861};
26182862
src-self-hosted/value.zig+88-3
......@@ -61,6 +61,7 @@ pub const Value = extern union {
6161 single_const_pointer_to_comptime_int_type,
6262 const_slice_u8_type,
6363 enum_literal_type,
64 anyframe_type,
6465
6566 undef,
6667 zero,
......@@ -90,6 +91,8 @@ pub const Value = extern union {
9091 float_64,
9192 float_128,
9293 enum_literal,
94 error_set,
95 @"error",
9396
9497 pub const last_no_payload_tag = Tag.bool_false;
9598 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -168,6 +171,7 @@ pub const Value = extern union {
168171 .single_const_pointer_to_comptime_int_type,
169172 .const_slice_u8_type,
170173 .enum_literal_type,
174 .anyframe_type,
171175 .undef,
172176 .zero,
173177 .void_value,
......@@ -241,6 +245,10 @@ pub const Value = extern union {
241245 };
242246 return Value{ .ptr_otherwise = &new_payload.base };
243247 },
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),
244252 }
245253 }
246254
......@@ -300,6 +308,7 @@ pub const Value = extern union {
300308 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
301309 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
302310 .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
311 .anyframe_type => return out_stream.writeAll("anyframe"),
303312
304313 .null_value => return out_stream.writeAll("null"),
305314 .undef => return out_stream.writeAll("undefined"),
......@@ -343,6 +352,15 @@ pub const Value = extern union {
343352 .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
344353 .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
345354 .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}),
346364 };
347365 }
348366
......@@ -363,11 +381,9 @@ pub const Value = extern union {
363381 }
364382
365383 /// 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 {
367385 return switch (self.tag()) {
368386 .ty => self.cast(Payload.Ty).?.ty,
369 .int_type => @panic("TODO int type to type"),
370
371387 .u8_type => Type.initTag(.u8),
372388 .i8_type => Type.initTag(.i8),
373389 .u16_type => Type.initTag(.u16),
......@@ -408,6 +424,26 @@ pub const Value = extern union {
408424 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
409425 .const_slice_u8_type => Type.initTag(.const_slice_u8),
410426 .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
412448 .undef,
413449 .zero,
......@@ -433,6 +469,7 @@ pub const Value = extern union {
433469 .float_64,
434470 .float_128,
435471 .enum_literal,
472 .@"error",
436473 => unreachable,
437474 };
438475 }
......@@ -482,6 +519,7 @@ pub const Value = extern union {
482519 .single_const_pointer_to_comptime_int_type,
483520 .const_slice_u8_type,
484521 .enum_literal_type,
522 .anyframe_type,
485523 .null_value,
486524 .function,
487525 .variable,
......@@ -498,6 +536,8 @@ pub const Value = extern union {
498536 .unreachable_value,
499537 .empty_array,
500538 .enum_literal,
539 .error_set,
540 .@"error",
501541 => unreachable,
502542
503543 .undef => unreachable,
......@@ -560,6 +600,7 @@ pub const Value = extern union {
560600 .single_const_pointer_to_comptime_int_type,
561601 .const_slice_u8_type,
562602 .enum_literal_type,
603 .anyframe_type,
563604 .null_value,
564605 .function,
565606 .variable,
......@@ -576,6 +617,8 @@ pub const Value = extern union {
576617 .unreachable_value,
577618 .empty_array,
578619 .enum_literal,
620 .error_set,
621 .@"error",
579622 => unreachable,
580623
581624 .undef => unreachable,
......@@ -638,6 +681,7 @@ pub const Value = extern union {
638681 .single_const_pointer_to_comptime_int_type,
639682 .const_slice_u8_type,
640683 .enum_literal_type,
684 .anyframe_type,
641685 .null_value,
642686 .function,
643687 .variable,
......@@ -654,6 +698,8 @@ pub const Value = extern union {
654698 .unreachable_value,
655699 .empty_array,
656700 .enum_literal,
701 .error_set,
702 .@"error",
657703 => unreachable,
658704
659705 .undef => unreachable,
......@@ -742,6 +788,7 @@ pub const Value = extern union {
742788 .single_const_pointer_to_comptime_int_type,
743789 .const_slice_u8_type,
744790 .enum_literal_type,
791 .anyframe_type,
745792 .null_value,
746793 .function,
747794 .variable,
......@@ -759,6 +806,8 @@ pub const Value = extern union {
759806 .unreachable_value,
760807 .empty_array,
761808 .enum_literal,
809 .error_set,
810 .@"error",
762811 => unreachable,
763812
764813 .zero,
......@@ -825,6 +874,7 @@ pub const Value = extern union {
825874 .single_const_pointer_to_comptime_int_type,
826875 .const_slice_u8_type,
827876 .enum_literal_type,
877 .anyframe_type,
828878 .null_value,
829879 .function,
830880 .variable,
......@@ -841,6 +891,8 @@ pub const Value = extern union {
841891 .unreachable_value,
842892 .empty_array,
843893 .enum_literal,
894 .error_set,
895 .@"error",
844896 => unreachable,
845897
846898 .zero,
......@@ -988,6 +1040,7 @@ pub const Value = extern union {
9881040 .single_const_pointer_to_comptime_int_type,
9891041 .const_slice_u8_type,
9901042 .enum_literal_type,
1043 .anyframe_type,
9911044 .bool_true,
9921045 .bool_false,
9931046 .null_value,
......@@ -1007,6 +1060,8 @@ pub const Value = extern union {
10071060 .void_value,
10081061 .unreachable_value,
10091062 .enum_literal,
1063 .error_set,
1064 .@"error",
10101065 => unreachable,
10111066
10121067 .zero => false,
......@@ -1063,6 +1118,7 @@ pub const Value = extern union {
10631118 .single_const_pointer_to_comptime_int_type,
10641119 .const_slice_u8_type,
10651120 .enum_literal_type,
1121 .anyframe_type,
10661122 .null_value,
10671123 .function,
10681124 .variable,
......@@ -1076,6 +1132,8 @@ pub const Value = extern union {
10761132 .unreachable_value,
10771133 .empty_array,
10781134 .enum_literal,
1135 .error_set,
1136 .@"error",
10791137 => unreachable,
10801138
10811139 .zero,
......@@ -1197,6 +1255,7 @@ pub const Value = extern union {
11971255 .single_const_pointer_to_comptime_int_type,
11981256 .const_slice_u8_type,
11991257 .enum_literal_type,
1258 .anyframe_type,
12001259 .zero,
12011260 .bool_true,
12021261 .bool_false,
......@@ -1218,6 +1277,8 @@ pub const Value = extern union {
12181277 .unreachable_value,
12191278 .empty_array,
12201279 .enum_literal,
1280 .error_set,
1281 .@"error",
12211282 => unreachable,
12221283
12231284 .ref_val => self.cast(Payload.RefVal).?.val,
......@@ -1276,6 +1337,7 @@ pub const Value = extern union {
12761337 .single_const_pointer_to_comptime_int_type,
12771338 .const_slice_u8_type,
12781339 .enum_literal_type,
1340 .anyframe_type,
12791341 .zero,
12801342 .bool_true,
12811343 .bool_false,
......@@ -1297,6 +1359,8 @@ pub const Value = extern union {
12971359 .void_value,
12981360 .unreachable_value,
12991361 .enum_literal,
1362 .error_set,
1363 .@"error",
13001364 => unreachable,
13011365
13021366 .empty_array => unreachable, // out of bounds array index
......@@ -1372,6 +1436,7 @@ pub const Value = extern union {
13721436 .single_const_pointer_to_comptime_int_type,
13731437 .const_slice_u8_type,
13741438 .enum_literal_type,
1439 .anyframe_type,
13751440 .zero,
13761441 .empty_array,
13771442 .bool_true,
......@@ -1393,6 +1458,8 @@ pub const Value = extern union {
13931458 .float_128,
13941459 .void_value,
13951460 .enum_literal,
1461 .error_set,
1462 .@"error",
13961463 => false,
13971464
13981465 .undef => unreachable,
......@@ -1522,6 +1589,24 @@ pub const Value = extern union {
15221589 base: Payload = .{ .tag = .float_128 },
15231590 val: f128,
15241591 };
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 };
15251610 };
15261611
15271612 /// Big enough to fit any non-BigInt value
src-self-hosted/zir.zig+56-1
......@@ -43,6 +43,8 @@ pub const Inst = struct {
4343 alloc,
4444 /// Same as `alloc` except the type is inferred.
4545 alloc_inferred,
46 /// Create an `anyframe->T`.
47 anyframe_type,
4648 /// Array concatenation. `a ++ b`
4749 array_cat,
4850 /// Array multiplication `a ** b`
......@@ -70,6 +72,8 @@ pub const Inst = struct {
7072 /// A typed result location pointer is bitcasted to a new result location pointer.
7173 /// The new result location pointer has an inferred type.
7274 bitcast_result_ptr,
75 /// Bitwise NOT. `~`
76 bitnot,
7377 /// Bitwise OR. `|`
7478 bitor,
7579 /// A labeled block of code, which can return a value.
......@@ -133,6 +137,10 @@ pub const Inst = struct {
133137 ensure_result_used,
134138 /// Emits a compile error if an error is ignored.
135139 ensure_result_non_error,
140 /// Create a `E!T` type.
141 error_union_type,
142 /// Create an error set.
143 error_set,
136144 /// Export the provided Decl as the provided name in the compilation's output object file.
137145 @"export",
138146 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
......@@ -160,6 +168,8 @@ pub const Inst = struct {
160168 /// A labeled block of code that loops forever. At the end of the body it is implied
161169 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
162170 loop,
171 /// Merge two error sets into one, `E1 || E2`.
172 merge_error_sets,
163173 /// Ambiguously remainder division or modulus. If the computation would possibly have
164174 /// a different value depending on whether the operation is remainder division or modulus,
165175 /// a compile error is emitted. Otherwise the computation is performed.
......@@ -286,6 +296,8 @@ pub const Inst = struct {
286296 .unwrap_err_safe,
287297 .unwrap_err_unsafe,
288298 .ensure_err_payload_void,
299 .anyframe_type,
300 .bitnot,
289301 => UnOp,
290302
291303 .add,
......@@ -316,6 +328,8 @@ pub const Inst = struct {
316328 .bitcast,
317329 .coerce_result_ptr,
318330 .xor,
331 .error_union_type,
332 .merge_error_sets,
319333 => BinOp,
320334
321335 .arg => Arg,
......@@ -347,6 +361,7 @@ pub const Inst = struct {
347361 .condbr => CondBr,
348362 .ptr_type => PtrType,
349363 .enum_literal => EnumLiteral,
364 .error_set => ErrorSet,
350365 };
351366 }
352367
......@@ -438,6 +453,11 @@ pub const Inst = struct {
438453 .ptr_type,
439454 .ensure_err_payload_void,
440455 .enum_literal,
456 .merge_error_sets,
457 .anyframe_type,
458 .error_union_type,
459 .bitnot,
460 .error_set,
441461 => false,
442462
443463 .@"break",
......@@ -908,6 +928,16 @@ pub const Inst = struct {
908928 },
909929 kw_args: struct {},
910930 };
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 };
911941};
912942
913943pub const ErrorMsg = struct {
......@@ -1142,6 +1172,16 @@ const Writer = struct {
11421172 const name = self.loop_table.get(param).?;
11431173 return std.zig.renderStringLiteral(name, stream);
11441174 },
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 },
11451185 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
11461186 }
11471187 }
......@@ -1539,6 +1579,21 @@ const Parser = struct {
15391579 const name = try self.parseStringLiteral();
15401580 return self.loop_table.get(name).?;
15411581 },
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 },
15421597 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
15431598 }
15441599 return self.fail("TODO parse parameter {}", .{@typeName(T)});
......@@ -1961,7 +2016,7 @@ const EmitZIR = struct {
19612016 return self.emitUnnamedDecl(&as_inst.base);
19622017 },
19632018 .Type => {
1964 const ty = typed_value.val.toType();
2019 const ty = try typed_value.val.toType(&self.arena.allocator);
19652020 return self.emitType(src, ty);
19662021 },
19672022 .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!
9797 .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
9898 .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
9999 .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),
100 .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?),
100101 .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),
101102 .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),
102103 .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!
122123 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
123124 .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
124125 .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).?),
125130 }
126131}
127132
......@@ -145,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir
145150 for (block_scope.instructions.items) |inst| {
146151 if (inst.castTag(.ret)) |ret| {
147152 const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
148 return val.toType();
153 return val.toType(block_scope.base.arena());
149154 } else {
150155 return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
151156 }
......@@ -270,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
270275 const wanted_type = Type.initTag(.@"type");
271276 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
272277 const val = try mod.resolveConstValue(scope, coerced_inst);
273 return val.toType();
278 return val.toType(scope.arena());
274279}
275280
276281fn 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
431436 // The bytes references memory inside the ZIR module, which can get deallocated
432437 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
433438 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
439 errdefer new_decl_arena.deinit();
434440 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
435441
436442 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
716722 return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));
717723}
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
719773fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
720774 const payload = try scope.arena().create(Value.Payload.Bytes);
721775 payload.* = .{
......@@ -858,8 +912,72 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
858912 );
859913 }
860914 },
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 => {},
862979 }
980 return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty});
863981}
864982
865983fn 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
9831101 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
9841102}
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
9861108fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
9871109 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
9881110}
......@@ -1348,7 +1470,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne
13481470
13491471 if (host_size != 0 and bit_offset >= host_size * 8)
13501472 return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
1351
1473
13521474 const sentinel = if (inst.kw_args.sentinel) |some|
13531475 (try resolveInstConst(mod, scope, some)).val
13541476 else
src/analyze.cpp+122-103
......@@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
25862586 return ErrorNone;
25872587
25882588 AstNode *decl_node = enum_type->data.enumeration.decl_node;
2589 assert(decl_node->type == NodeTypeContainerDecl);
25902589
25912590 if (enum_type->data.enumeration.resolve_loop_flag) {
25922591 if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) {
......@@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26002599
26012600 enum_type->data.enumeration.resolve_loop_flag = true;
26022601
2603 assert(!enum_type->data.enumeration.fields);
2604 uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
2605 if (field_count == 0) {
2606 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
2602 uint32_t field_count;
2603 if (decl_node->type == NodeTypeContainerDecl) {
2604 assert(!enum_type->data.enumeration.fields);
2605 field_count = (uint32_t)decl_node->data.container_decl.fields.length;
2606 if (field_count == 0) {
2607 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
26072608
2608 enum_type->data.enumeration.src_field_count = field_count;
2609 enum_type->data.enumeration.fields = nullptr;
2610 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2611 return ErrorSemanticAnalyzeFail;
2609 enum_type->data.enumeration.src_field_count = field_count;
2610 enum_type->data.enumeration.fields = nullptr;
2611 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2612 return ErrorSemanticAnalyzeFail;
2613 }
2614 } else {
2615 field_count = enum_type->data.enumeration.src_field_count;
26122616 }
26132617
26142618 Scope *scope = &enum_type->data.enumeration.decls_scope->base;
......@@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26242628 enum_type->abi_size = tag_int_type->abi_size;
26252629 enum_type->abi_align = tag_int_type->abi_align;
26262630
2627 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
2628 ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
2631 ZigType *wanted_tag_int_type = nullptr;
2632 if (decl_node->type == NodeTypeContainerDecl) {
2633 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
2634 wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
2635 }
2636 } else {
2637 wanted_tag_int_type = enum_type->data.enumeration.tag_int_type;
2638 }
2639
2640 if (wanted_tag_int_type != nullptr) {
26292641 if (type_is_invalid(wanted_tag_int_type)) {
26302642 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
26312643 } else if (wanted_tag_int_type->id != ZigTypeIdInt &&
......@@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26542666 }
26552667 }
26562668
2657 enum_type->data.enumeration.non_exhaustive = false;
26582669 enum_type->data.enumeration.tag_int_type = tag_int_type;
26592670 enum_type->size_in_bits = tag_int_type->size_in_bits;
26602671 enum_type->abi_size = tag_int_type->abi_size;
......@@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26632674 BigInt bi_one;
26642675 bigint_init_unsigned(&bi_one, 1);
26652676
2666 AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
2667 if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
2677 if (decl_node->type == NodeTypeContainerDecl) {
2678 AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
2679 if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
2680 if (last_field_node->data.struct_field.value != nullptr) {
2681 add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
2682 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2683 }
2684 if (decl_node->data.container_decl.init_arg_expr == nullptr) {
2685 add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size"));
2686 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2687 }
2688 enum_type->data.enumeration.non_exhaustive = true;
2689 } else {
2690 enum_type->data.enumeration.non_exhaustive = false;
2691 }
2692 }
2693
2694 if (enum_type->data.enumeration.non_exhaustive) {
26682695 field_count -= 1;
26692696 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"));
26712698 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
26722699 }
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;
26822700 }
26832701
2684 enum_type->data.enumeration.src_field_count = field_count;
2685 enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);
2686 enum_type->data.enumeration.fields_by_name.init(field_count);
2687
2688 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2689 occupied_tag_values.init(field_count);
2690
2691 TypeEnumField *last_enum_field = nullptr;
2692
2693 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2694 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2695 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2696 type_enum_field->name = field_node->data.struct_field.name;
2697 type_enum_field->decl_index = field_i;
2698 type_enum_field->decl_node = field_node;
2702 if (decl_node->type == NodeTypeContainerDecl) {
2703 enum_type->data.enumeration.src_field_count = field_count;
2704 enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);
2705 enum_type->data.enumeration.fields_by_name.init(field_count);
26992706
2700 if (field_node->data.struct_field.type != nullptr) {
2701 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
2702 buf_sprintf("structs and unions, not enums, support field types"));
2703 add_error_note(g, msg, decl_node,
2704 buf_sprintf("consider 'union(enum)' here"));
2705 } else if (field_node->data.struct_field.align_expr != nullptr) {
2706 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
2707 buf_sprintf("structs and unions, not enums, support field alignment"));
2708 add_error_note(g, msg, decl_node,
2709 buf_sprintf("consider 'union(enum)' here"));
2710 }
2707 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2708 occupied_tag_values.init(field_count);
27112709
2712 if (buf_eql_str(type_enum_field->name, "_")) {
2713 add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
2714 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2715 }
2710 TypeEnumField *last_enum_field = nullptr;
27162711
2717 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
2718 if (field_entry != nullptr) {
2719 ErrorMsg *msg = add_node_error(g, field_node,
2720 buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
2721 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
2722 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2723 continue;
2724 }
2725
2726 AstNode *tag_value = field_node->data.struct_field.value;
2712 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2713 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2714 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2715 type_enum_field->name = field_node->data.struct_field.name;
2716 type_enum_field->decl_index = field_i;
2717 type_enum_field->decl_node = field_node;
2718
2719 if (field_node->data.struct_field.type != nullptr) {
2720 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
2721 buf_sprintf("structs and unions, not enums, support field types"));
2722 add_error_note(g, msg, decl_node,
2723 buf_sprintf("consider 'union(enum)' here"));
2724 } else if (field_node->data.struct_field.align_expr != nullptr) {
2725 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
2726 buf_sprintf("structs and unions, not enums, support field alignment"));
2727 add_error_note(g, msg, decl_node,
2728 buf_sprintf("consider 'union(enum)' here"));
2729 }
2730
2731 if (buf_eql_str(type_enum_field->name, "_")) {
2732 add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
2733 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2734 }
27272735
2728 if (tag_value != nullptr) {
2729 // A user-specified value is available
2730 ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
2731 nullptr, UndefBad);
2732 if (type_is_invalid(result->type)) {
2736 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
2737 if (field_entry != nullptr) {
2738 ErrorMsg *msg = add_node_error(g, field_node,
2739 buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
2740 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
27332741 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
27342742 continue;
27352743 }
27362744
2737 assert(result->special != ConstValSpecialRuntime);
2738 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
2745 AstNode *tag_value = field_node->data.struct_field.value;
27392746
2740 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
2741 } else {
2742 // No value was explicitly specified: allocate the last value + 1
2743 // or, if this is the first element, zero
2744 if (last_enum_field != nullptr) {
2745 bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
2747 if (tag_value != nullptr) {
2748 // A user-specified value is available
2749 ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
2750 nullptr, UndefBad);
2751 if (type_is_invalid(result->type)) {
2752 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2753 continue;
2754 }
2755
2756 assert(result->special != ConstValSpecialRuntime);
2757 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
2758
2759 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
27462760 } 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 }
27482783 }
27492784
2750 // Make sure we can represent this number with tag_int_type
2751 if (!bigint_fits_in_bits(&type_enum_field->value,
2752 tag_int_type->size_in_bits,
2753 tag_int_type->data.integral.is_signed)) {
2785 // Make sure the value is unique
2786 auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
2787 if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) {
27542788 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
27552789
27562790 Buf *val_buf = buf_alloc();
27572791 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"));
27632797 }
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,
2775 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
2776 add_error_note(g, msg, entry->value,
2777 buf_sprintf("other occurrence here"));
2799 last_enum_field = type_enum_field;
27782800 }
2779
2780 last_enum_field = type_enum_field;
2801 occupied_tag_values.deinit();
27812802 }
27822803
27832804 if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
......@@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
27862807 enum_type->data.enumeration.resolve_loop_flag = false;
27872808 enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
27882809
2789 occupied_tag_values.deinit();
2790
27912810 return ErrorNone;
27922811}
27932812
src/ir.cpp+127-11
......@@ -2147,6 +2147,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN
21472147 IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
21482148 ir_instruction_append(irb->current_basic_block, &const_instruction->base);
21492149 const_instruction->value = irb->codegen->intern.for_undefined();
2150 const_instruction->value->special = ConstValSpecialUndef;
21502151 return &const_instruction->base;
21512152}
21522153
......@@ -14917,6 +14918,9 @@ static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* so
1491714918 field_val->parent.data.p_struct.struct_val = const_result->value;
1491814919 field_val->parent.data.p_struct.field_index = dst_field->src_index;
1491914920 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 }
1492014924 } else {
1492114925 is_comptime = false;
1492214926 }
......@@ -15649,7 +15653,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1564915653 wanted_type->data.array.len == field_count)
1565015654 {
1565115655 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) &&
1565315657 (!is_array_init || field_count == 0))
1565415658 {
1565515659 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,
2069220696 if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) &&
2069320697 expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet)
2069420698 {
20695 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
20696 ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));
20699 if (call_result_loc->id == ResultLocIdReturn) {
20700 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
20701 ira->explicit_return_type_source_node, buf_sprintf("function cannot return an error"));
20702 } else {
20703 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, result_loc->base.source_node,
20704 buf_sprintf("cannot store an error in type '%s'", buf_ptr(&expected_return_type->name)));
20705 }
2069720706 }
2069820707 return ira->codegen->invalid_inst_gen;
2069920708 }
......@@ -22302,6 +22311,7 @@ static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira,
2230222311
2230322312static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) {
2230422313 if (field->init_val != nullptr) return;
22314 if (field->decl_node == nullptr) return;
2230522315 if (field->decl_node->type != NodeTypeStructField) return;
2230622316 AstNode *init_node = field->decl_node->data.struct_field.value;
2230722317 if (init_node == nullptr) return;
......@@ -25495,9 +25505,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2549525505 error_val->special = ConstValSpecialStatic;
2549625506 error_val->type = type_info_error_type;
2549725507
25498 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);
25499 inner_fields[1]->special = ConstValSpecialStatic;
25500 inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
25508 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1);
2550125509
2550225510 ZigValue *name = nullptr;
2550325511 if (error->cached_error_name_val != nullptr)
......@@ -25505,7 +25513,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2550525513 if (name == nullptr)
2550625514 name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee;
2550725515 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
2551025517 error_val->data.x_struct.fields = inner_fields;
2551125518 error_val->parent.id = ConstParentIdArray;
......@@ -26020,6 +26027,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2602026027 assert(payload->special == ConstValSpecialStatic);
2602126028 assert(payload->type == type_info_pointer_type);
2602226029 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
2602326033 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
2602426034 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
2602526035 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
2610326113 assert(payload->special == ConstValSpecialStatic);
2610426114 assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr));
2610526115 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;
2610626118 return get_optional_type(ira->codegen, child_type);
2610726119 }
2610826120 case ZigTypeIdErrorUnion: {
2610926121 assert(payload->special == ConstValSpecialStatic);
2611026122 assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr));
2611126123 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
2611226127 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
2611326131 return get_error_union_type(ira->codegen, err_set_type, payload_type);
2611426132 }
2611526133 case ZigTypeIdOpaque: {
......@@ -26123,8 +26141,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2612326141 assert(payload->special == ConstValSpecialStatic);
2612426142 assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr));
2612526143 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
2612626147 ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1);
26127 Error err;
2612826148 if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) {
2612926149 return ira->codegen->invalid_inst_gen->value->type;
2613026150 }
......@@ -26134,6 +26154,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2613426154 assert(payload->special == ConstValSpecialStatic);
2613526155 assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr));
2613626156 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
2613726160 return get_any_frame_type(ira->codegen, child_type);
2613826161 }
2613926162 case ZigTypeIdEnumLiteral:
......@@ -26142,6 +26165,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2614226165 assert(payload->special == ConstValSpecialStatic);
2614326166 assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));
2614426167 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
2614526171 assert(function->type->id == ZigTypeIdFn);
2614626172 ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
2614726173 return get_fn_frame_type(ira->codegen, fn);
......@@ -26176,7 +26202,6 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2617626202 assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
2617726203 ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
2617826204 err_entry->decl_node = source_instr->source_node;
26179 Error err;
2618026205 if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name)))
2618126206 return ira->codegen->invalid_inst_gen->value->type;
2618226207 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
2620326228 assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr));
2620426229
2620526230 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;
2620626233 assert(layout_value->special == ConstValSpecialStatic);
2620726234 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
2620826235 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
2620926236
2621026237 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;
2621126240 assert(fields_value->special == ConstValSpecialStatic);
2621226241 assert(is_slice(fields_value->type));
2621326242 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
2621526244 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
2621626245
2621726246 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;
2621826249 assert(decls_value->special == ConstValSpecialStatic);
2621926250 assert(is_slice(decls_value->type));
2622026251 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
2622526256 }
2622626257
2622726258 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
2623026262 ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
2623126263 buf_init_from_buf(&entry->name,
......@@ -26253,6 +26285,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2625326285 return ira->codegen->invalid_inst_gen->value->type;
2625426286 field->decl_node = source_instr->source_node;
2625526287 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;
2625626290 field->type_val = type_value;
2625726291 field->type_entry = type_value->data.x_type;
2625826292 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
2626026294 return ira->codegen->invalid_inst_gen->value->type;
2626126295 }
2626226296 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;
2626326299 if (default_value->type->id == ZigTypeIdNull) {
2626426300 field->init_val = nullptr;
2626526301 } 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
2627726313
2627826314 return entry;
2627926315 }
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 }
2628126397 case ZigTypeIdUnion:
2628226398 ir_add_error(ira, source_instr, buf_sprintf(
2628326399 "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");
22const std = @import("std");
33
44pub 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
540 cases.addTest("reject extern variables with initializers",
641 \\extern var foo: int = 2;
742 , &[_][]const u8{
......@@ -123,16 +158,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
123158 \\export fn baz() void {
124159 \\ try bar();
125160 \\}
126 \\export fn quux() u32 {
161 \\export fn qux() u32 {
127162 \\ return bar();
128163 \\}
164 \\export fn quux() u32 {
165 \\ var buf: u32 = 0;
166 \\ buf = bar();
167 \\}
129168 , &[_][]const u8{
130169 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
131170 "tmp.zig:1:17: note: function cannot return an error",
132171 "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",
133172 "tmp.zig:7:17: note: function cannot return an error",
134173 "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'",
136177 });
137178
138179 cases.addTest("int/float conversion to comptime_int/float",
......@@ -598,8 +639,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
598639 \\ _ = C;
599640 \\}
600641 , &[_][]const u8{
601 "tmp.zig:4:5: error: non-exhaustive enum must specify size",
602 "error: value assigned to '_' field of non-exhaustive enum",
642 "tmp.zig:4:5: error: value assigned to '_' field of non-exhaustive enum",
643 "error: non-exhaustive enum must specify size",
603644 "error: non-exhaustive enum specifies every value",
604645 "error: '_' field of non-exhaustive enum must be last",
605646 });
......@@ -1400,15 +1441,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
14001441 , &[_][]const u8{
14011442 "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'",
14021443 });
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
14121444 cases.add("wrong type for argument tuple to @asyncCall",
14131445 \\export fn entry1() void {
14141446 \\ var frame: @Frame(foo) = undefined;
test/stage1/behavior/type.zig+34
......@@ -280,3 +280,37 @@ test "Type.Struct" {
280280 testing.expectEqual(@as(usize, 0), infoC.decls.len);
281281 testing.expectEqual(@as(bool, false), infoC.is_tuple);
282282}
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 {
153153 expect(error_set_info == .ErrorSet);
154154 expect(error_set_info.ErrorSet.?.len == 3);
155155 expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First"));
156 expect(error_set_info.ErrorSet.?[2].value == @errorToInt(TestErrorSet.Third));
157156
158157 const error_union_info = @typeInfo(TestErrorSet!usize);
159158 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{
1818 .os_tag = .linux,
1919};
2020
21const linux_arm = std.zig.CrossTarget{
22 .cpu_arch = .arm,
23 .os_tag = .linux,
24};
25
2126const wasi = std.zig.CrossTarget{
2227 .cpu_arch = .wasm32,
2328 .os_tag = .wasi,
......@@ -26,6 +31,8 @@ const wasi = std.zig.CrossTarget{
2631pub fn addCases(ctx: *TestContext) !void {
2732 try @import("zir.zig").addCases(ctx);
2833 try @import("cbe.zig").addCases(ctx);
34 try @import("spu-ii.zig").addCases(ctx);
35
2936 {
3037 var case = ctx.exe("hello world with updates", linux_x64);
3138
......@@ -179,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void {
179186 );
180187 }
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
182224 {
183225 var case = ctx.exe("adding numbers at comptime", linux_x64);
184226 case.addCompareOutput(
......@@ -600,6 +642,58 @@ pub fn addCases(ctx: *TestContext) !void {
600642 "",
601643 );
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
603697 // Character literals and multiline strings.
604698 case.addCompareOutput(
605699 \\export fn _start() noreturn {
tools/process_headers.zig+3-2
......@@ -15,6 +15,7 @@ const Arch = std.Target.Cpu.Arch;
1515const Abi = std.Target.Abi;
1616const OsTag = std.Target.Os.Tag;
1717const assert = std.debug.assert;
18const Sha256 = std.crypto.hash.sha2.Sha256;
1819
1920const LibCTarget = struct {
2021 name: []const u8,
......@@ -313,7 +314,7 @@ pub fn main() !void {
313314 var max_bytes_saved: usize = 0;
314315 var total_bytes: usize = 0;
315316
316 var hasher = std.crypto.hash.sha2.Sha256.init(.{});
317 var hasher = Sha256.init(.{});
317318
318319 for (libc_targets) |libc_target| {
319320 const dest_target = DestTarget{
......@@ -359,7 +360,7 @@ pub fn main() !void {
359360 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
360361 total_bytes += raw_bytes.len;
361362 const hash = try allocator.alloc(u8, 32);
362 hasher.reset();
363 hasher = Sha256.init(.{});
363364 hasher.update(rel_path);
364365 hasher.update(trimmed);
365366 hasher.final(hash);
tools/update_glibc.zig+20-20
......@@ -148,12 +148,12 @@ pub fn main() !void {
148148 for (abi_lists) |*abi_list| {
149149 const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list));
150150 if (!target_funcs_gop.found_existing) {
151 target_funcs_gop.kv.value = FunctionSet{
151 target_funcs_gop.entry.value = FunctionSet{
152152 .list = std.ArrayList(VersionedFn).init(allocator),
153153 .fn_vers_list = FnVersionList.init(allocator),
154154 };
155155 }
156 const fn_set = &target_funcs_gop.kv.value.list;
156 const fn_set = &target_funcs_gop.entry.value.list;
157157
158158 for (lib_names) |lib_name, lib_name_index| {
159159 const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib";
......@@ -203,11 +203,11 @@ pub fn main() !void {
203203 _ = try global_ver_set.put(ver, undefined);
204204 const gop = try global_fn_set.getOrPut(name);
205205 if (gop.found_existing) {
206 if (!std.mem.eql(u8, gop.kv.value.lib, "c")) {
207 gop.kv.value.lib = lib_name;
206 if (!std.mem.eql(u8, gop.entry.value.lib, "c")) {
207 gop.entry.value.lib = lib_name;
208208 }
209209 } else {
210 gop.kv.value = Function{
210 gop.entry.value = Function{
211211 .name = name,
212212 .lib = lib_name,
213213 .index = undefined,
......@@ -224,14 +224,14 @@ pub fn main() !void {
224224 const global_fn_list = blk: {
225225 var list = std.ArrayList([]const u8).init(allocator);
226226 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);
228228 std.sort.sort([]const u8, list.span(), {}, strCmpLessThan);
229229 break :blk list.span();
230230 };
231231 const global_ver_list = blk: {
232232 var list = std.ArrayList([]const u8).init(allocator);
233233 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);
235235 std.sort.sort([]const u8, list.span(), {}, versionLessThan);
236236 break :blk list.span();
237237 };
......@@ -254,9 +254,9 @@ pub fn main() !void {
254254 var buffered = std.io.bufferedOutStream(fns_txt_file.outStream());
255255 const fns_txt = buffered.outStream();
256256 for (global_fn_list) |name, i| {
257 const kv = global_fn_set.get(name).?;
258 kv.value.index = i;
259 try fns_txt.print("{} {}\n", .{ name, kv.value.lib });
257 const entry = global_fn_set.getEntry(name).?;
258 entry.value.index = i;
259 try fns_txt.print("{} {}\n", .{ name, entry.value.lib });
260260 }
261261 try buffered.flush();
262262 }
......@@ -264,16 +264,16 @@ pub fn main() !void {
264264 // Now the mapping of version and function to integer index is complete.
265265 // Here we create a mapping of function name to list of versions.
266266 for (abi_lists) |*abi_list, abi_index| {
267 const kv = target_functions.get(@ptrToInt(abi_list)).?;
268 const fn_vers_list = &kv.value.fn_vers_list;
269 for (kv.value.list.span()) |*ver_fn| {
267 const entry = target_functions.getEntry(@ptrToInt(abi_list)).?;
268 const fn_vers_list = &entry.value.fn_vers_list;
269 for (entry.value.list.span()) |*ver_fn| {
270270 const gop = try fn_vers_list.getOrPut(ver_fn.name);
271271 if (!gop.found_existing) {
272 gop.kv.value = std.ArrayList(usize).init(allocator);
272 gop.entry.value = std.ArrayList(usize).init(allocator);
273273 }
274 const ver_index = global_ver_set.get(ver_fn.ver).?.value;
275 if (std.mem.indexOfScalar(usize, gop.kv.value.span(), ver_index) == null) {
276 try gop.kv.value.append(ver_index);
274 const ver_index = global_ver_set.getEntry(ver_fn.ver).?.value;
275 if (std.mem.indexOfScalar(usize, gop.entry.value.span(), ver_index) == null) {
276 try gop.entry.value.append(ver_index);
277277 }
278278 }
279279 }
......@@ -287,7 +287,7 @@ pub fn main() !void {
287287
288288 // first iterate over the abi lists
289289 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;
291291 for (abi_list.targets) |target, it_i| {
292292 if (it_i != 0) try abilist_txt.writeByte(' ');
293293 try abilist_txt.print("{}-linux-{}", .{ @tagName(target.arch), @tagName(target.abi) });
......@@ -295,11 +295,11 @@ pub fn main() !void {
295295 try abilist_txt.writeByte('\n');
296296 // next, each line implicitly corresponds to a function
297297 for (global_fn_list) |name| {
298 const kv = fn_vers_list.get(name) orelse {
298 const entry = fn_vers_list.getEntry(name) orelse {
299299 try abilist_txt.writeByte('\n');
300300 continue;
301301 };
302 for (kv.value.span()) |ver_index, it_i| {
302 for (entry.value.span()) |ver_index, it_i| {
303303 if (it_i != 0) try abilist_txt.writeByte(' ');
304304 try abilist_txt.print("{d}", .{ver_index});
305305 }