authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-18 15:32:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-18 15:32:42-07:00
log1de2c647df4758e4250def78068f7e61e638c599
tree22f5724a21a0a153648b9fcb00b6fd14a4ec32cb
parent2139697ce548db8a80cf2999a8196836f2e39aef
parent15bcfcd36865fca75b93dc6ce52c904292b62a81

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


108 files changed, 10262 insertions(+), 3138 deletions(-)

CMakeLists.txt+7
......@@ -326,10 +326,15 @@ set(LIBUNWIND_FILES_DEST "${ZIG_LIB_DIR}/libunwind")
326326set(LIBCXX_FILES_DEST "${ZIG_LIB_DIR}/libcxx")
327327set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std")
328328set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h")
329set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig")
329330configure_file (
330331 "${CMAKE_SOURCE_DIR}/src/config.h.in"
331332 "${ZIG_CONFIG_H_OUT}"
332333)
334configure_file (
335 "${CMAKE_SOURCE_DIR}/src/config.zig.in"
336 "${ZIG_CONFIG_ZIG_OUT}"
337)
333338
334339include_directories(
335340 ${CMAKE_SOURCE_DIR}
......@@ -472,6 +477,8 @@ set(BUILD_LIBSTAGE2_ARGS "build-lib"
472477 --bundle-compiler-rt
473478 -fPIC
474479 -lc
480 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}"
481 --pkg-end
475482)
476483
477484if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
build.zig+24-1
......@@ -10,6 +10,8 @@ const io = std.io;
1010const fs = std.fs;
1111const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
1212
13const zig_version = std.builtin.Version{ .major = 0, .minor = 6, .patch = 0 };
14
1315pub fn build(b: *Builder) !void {
1416 b.setPreferredReleaseMode(.ReleaseFast);
1517 const mode = b.standardReleaseOptions();
......@@ -75,10 +77,31 @@ pub fn build(b: *Builder) !void {
7577 }
7678 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
7779 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
78 if (link_libc) exe.linkLibC();
80 if (link_libc) {
81 exe.linkLibC();
82 test_stage2.linkLibC();
83 }
7984
8085 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
8186
87 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
88 const version = if (opt_version_string) |version| version else v: {
89 var code: u8 = undefined;
90 const version_untrimmed = b.execAllowFail(&[_][]const u8{
91 "git", "-C", b.build_root, "name-rev", "HEAD",
92 "--tags", "--name-only", "--no-undefined", "--always",
93 }, &code, .Ignore) catch |err| {
94 std.debug.print(
95 \\Unable to determine zig version string: {}
96 \\Provide the zig version string explicitly using the `version-string` build option.
97 , .{err});
98 std.process.exit(1);
99 };
100 const trimmed = mem.trim(u8, version_untrimmed, " \n\r");
101 break :v b.fmt("{}.{}.{}+{}", .{ zig_version.major, zig_version.minor, zig_version.patch, trimmed });
102 };
103 exe.addBuildOption([]const u8, "version", version);
104
82105 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
83106 exe.addBuildOption(bool, "enable_tracy", tracy != null);
84107 if (tracy) |tracy_path| {
ci/azure/linux_script+1-1
......@@ -14,7 +14,7 @@ sudo apt-get remove -y llvm-*
1414sudo rm -rf /usr/local/*
1515sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy
1616
17QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"
17QEMUBASE="qemu-linux-x86_64-5.1.0"
1818wget https://ziglang.org/deps/$QEMUBASE.tar.xz
1919tar xf $QEMUBASE.tar.xz
2020PATH=$PWD/$QEMUBASE/bin:$PATH
doc/langref.html.in+33-11
......@@ -248,7 +248,7 @@ pub fn main() !void {
248248 </p>
249249 <p>
250250 Following the <code>hello.zig</code> Zig code sample, the {#link|Zig Build System#} is used
251 to build an executable program from the <code>hello.zig</code> source code. Then, the
251 to build an executable program from the <code>hello.zig</code> source code. Then, the
252252 <code>hello</code> program is executed showing its output <code>Hello, world!</code>. The
253253 lines beginning with <code>$</code> represent command line prompts and a command.
254254 Everything else is program output.
......@@ -293,7 +293,7 @@ pub fn main() !void {
293293 <p>
294294 In Zig, a function's block of statements and expressions are surrounded by <code>{</code> and
295295 <code>}</code> curly-braces. Inside of the <code>main</code> function are expressions that perform
296 the task of outputting <code>Hello, world!</code> to standard output.
296 the task of outputting <code>Hello, world!</code> to standard output.
297297 </p>
298298 <p>
299299 First, a constant identifier, <code>stdout</code>, is initialized to represent standard output's
......@@ -325,7 +325,7 @@ pub fn main() !void {
325325 represents writing data to a file. When the disk is full, a write to the file will fail.
326326 However, we typically do not expect writing text to the standard output to fail. To avoid having
327327 to handle the failure case of printing to standard output, you can use alternate functions: the
328 <code>std.log</code> function for proper logging or the <code>std.debug.print</code> function.
328 functions in <code>std.log</code> for proper logging or the <code>std.debug.print</code> function.
329329 This documentation will use the latter option to print to standard error (stderr) and silently return
330330 on failure. The next code sample, <code>hello_again.zig</code> demonstrates the use of
331331 <code>std.debug.print</code>.
......@@ -5135,6 +5135,22 @@ test "float widening" {
51355135 var c: f64 = b;
51365136 var d: f128 = c;
51375137 assert(d == a);
5138}
5139 {#code_end#}
5140 {#header_close#}
5141 {#header_open|Type Coercion: Coercion Float to Int#}
5142 <p>
5143 A compiler error is appropriate because this ambiguous expression leaves the compiler
5144 two choices about the coercion.
5145 </p>
5146 <ul>
5147 <li> Cast {#syntax#}54.0{#endsyntax#} to {#syntax#}comptime_int{#endsyntax#} resulting in {#syntax#}@as(comptime_int, 10){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10){#endsyntax#}</li>
5148 <li> Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
5149 </ul>
5150 {#code_begin|test_err#}
5151// Compile time coercion of float to int
5152test "implicit cast to comptime_int" {
5153 var f: f32 = 54.0 / 5;
51385154}
51395155 {#code_end#}
51405156 {#header_close#}
......@@ -8179,7 +8195,7 @@ const expect = std.testing.expect;
81798195test "@src" {
81808196 doTheTest();
81818197}
8182
8198
81838199fn doTheTest() void {
81848200 const src = @src();
81858201
......@@ -9299,10 +9315,8 @@ fn concat(allocator: *Allocator, a: []const u8, b: []const u8) ![]u8 {
92999315 which will also do perform basic leak detection.
93009316 </p>
93019317 <p>
9302 Currently Zig has no general purpose allocator, but there is
9303 <a href="https://github.com/andrewrk/zig-general-purpose-allocator/">one under active development</a>.
9304 Once it is merged into the Zig standard library it will become available to import
9305 with {#syntax#}std.heap.default_allocator{#endsyntax#}. However, it will still be recommended to
9318 Zig has a general purpose allocator available to be imported
9319 with {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#}. However, it is still recommended to
93069320 follow the {#link|Choosing an Allocator#} guide.
93079321 </p>
93089322
......@@ -9357,9 +9371,17 @@ pub fn main() !void {
93579371 is handled correctly? In this case, use {#syntax#}std.testing.FailingAllocator{#endsyntax#}.
93589372 </li>
93599373 <li>
9360 Finally, if none of the above apply, you need a general purpose allocator. Zig does not
9361 yet have a general purpose allocator in the standard library,
9362 <a href="https://github.com/andrewrk/zig-general-purpose-allocator/">but one is being actively developed</a>.
9374 Are you writing a test? In this case, use {#syntax#}std.testing.allocator{#endsyntax#}.
9375 </li>
9376 <li>
9377 Finally, if none of the above apply, you need a general purpose allocator.
9378 Zig's general purpose allocator is available as a function that takes a {#link|comptime#}
9379 {#link|struct#} of configuration options and returns a type.
9380 Generally, you will set up one {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#} in
9381 your main function, and then pass it or sub-allocators around to various parts of your
9382 application.
9383 </li>
9384 <li>
93639385 You can also consider {#link|Implementing an Allocator#}.
93649386 </li>
93659387 </ol>
lib/std/array_list.zig+1
......@@ -263,6 +263,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
263263 if (better_capacity >= new_capacity) break;
264264 }
265265
266 // TODO This can be optimized to avoid needlessly copying undefined memory.
266267 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
267268 self.items.ptr = new_memory.ptr;
268269 self.capacity = new_memory.len;
lib/std/atomic/queue.zig+1-1
......@@ -22,7 +22,7 @@ pub fn Queue(comptime T: type) type {
2222 return Self{
2323 .head = null,
2424 .tail = null,
25 .mutex = std.Mutex.init(),
25 .mutex = std.Mutex{},
2626 };
2727 }
2828
lib/std/builtin.zig+27
......@@ -52,6 +52,25 @@ pub const subsystem: ?SubSystem = blk: {
5252pub const StackTrace = struct {
5353 index: usize,
5454 instruction_addresses: []usize,
55
56 pub fn format(
57 self: StackTrace,
58 comptime fmt: []const u8,
59 options: std.fmt.FormatOptions,
60 writer: anytype,
61 ) !void {
62 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
63 defer arena.deinit();
64 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
65 return writer.print("\nUnable to print stack trace: Unable to open debug info: {}\n", .{@errorName(err)});
66 };
67 const tty_config = std.debug.detectTTYConfig();
68 try writer.writeAll("\n");
69 std.debug.writeStackTrace(self, writer, &arena.allocator, debug_info, tty_config) catch |err| {
70 try writer.print("Unable to print stack trace: {}\n", .{@errorName(err)});
71 };
72 try writer.writeAll("\n");
73 }
5574};
5675
5776/// This data structure is used by the Zig language code generation and
......@@ -428,6 +447,14 @@ pub const Version = struct {
428447 if (self.max.order(ver) == .lt) return false;
429448 return true;
430449 }
450
451 /// Checks if system is guaranteed to be at least `version` or older than `version`.
452 /// Returns `null` if a runtime check is required.
453 pub fn isAtLeast(self: Range, ver: Version) ?bool {
454 if (self.min.order(ver) != .lt) return true;
455 if (self.max.order(ver) == .lt) return false;
456 return null;
457 }
431458 };
432459
433460 pub fn order(lhs: Version, rhs: Version) std.math.Order {
lib/std/c/linux.zig+4
......@@ -91,6 +91,10 @@ pub extern "c" fn sendfile(
9191 count: usize,
9292) isize;
9393
94pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: c_uint) isize;
95
96pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
97
9498pub const pthread_attr_t = extern struct {
9599 __size: [56]u8,
96100 __align: c_long,
lib/std/cache_hash.zig+18-12
......@@ -188,12 +188,14 @@ pub const CacheHash = struct {
188188 };
189189
190190 var iter = mem.tokenize(line, " ");
191 const size = iter.next() orelse return error.InvalidFormat;
191192 const inode = iter.next() orelse return error.InvalidFormat;
192193 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
193194 const digest_str = iter.next() orelse return error.InvalidFormat;
194195 const file_path = iter.rest();
195196
196 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, mtime_nsec_str, 10) catch return error.InvalidFormat;
197 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
198 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
197199 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
198200 base64_decoder.decode(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
199201
......@@ -216,10 +218,11 @@ pub const CacheHash = struct {
216218 defer this_file.close();
217219
218220 const actual_stat = try this_file.stat();
221 const size_match = actual_stat.size == cache_hash_file.stat.size;
219222 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
220223 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
221224
222 if (!mtime_match or !inode_match) {
225 if (!size_match or !mtime_match or !inode_match) {
223226 self.manifest_dirty = true;
224227
225228 cache_hash_file.stat = actual_stat;
......@@ -392,7 +395,7 @@ pub const CacheHash = struct {
392395
393396 for (self.files.items) |file| {
394397 base64_encoder.encode(encoded_digest[0..], &file.bin_digest);
395 try outStream.print("{} {} {} {}\n", .{ file.stat.inode, file.stat.mtime, encoded_digest[0..], file.path });
398 try outStream.print("{} {} {} {} {}\n", .{ file.stat.size, file.stat.inode, file.stat.mtime, encoded_digest[0..], file.path });
396399 }
397400
398401 try self.manifest_file.?.pwriteAll(contents.items, 0);
......@@ -479,9 +482,10 @@ test "cache file and then recall it" {
479482 const temp_file = "test.txt";
480483 const temp_manifest_dir = "temp_manifest_dir";
481484
485 const ts = std.time.nanoTimestamp();
482486 try cwd.writeFile(temp_file, "Hello, world!\n");
483487
484 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
488 while (isProblematicTimestamp(ts)) {
485489 std.time.sleep(1);
486490 }
487491
......@@ -545,9 +549,13 @@ test "check that changing a file makes cache fail" {
545549 const original_temp_file_contents = "Hello, world!\n";
546550 const updated_temp_file_contents = "Hello, world; but updated!\n";
547551
552 try cwd.deleteTree(temp_manifest_dir);
553 try cwd.deleteTree(temp_file);
554
555 const ts = std.time.nanoTimestamp();
548556 try cwd.writeFile(temp_file, original_temp_file_contents);
549557
550 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
558 while (isProblematicTimestamp(ts)) {
551559 std.time.sleep(1);
552560 }
553561
......@@ -571,10 +579,6 @@ test "check that changing a file makes cache fail" {
571579
572580 try cwd.writeFile(temp_file, updated_temp_file_contents);
573581
574 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
575 std.time.sleep(1);
576 }
577
578582 {
579583 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
580584 defer ch.release();
......@@ -594,7 +598,7 @@ test "check that changing a file makes cache fail" {
594598 testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
595599
596600 try cwd.deleteTree(temp_manifest_dir);
597 try cwd.deleteFile(temp_file);
601 try cwd.deleteTree(temp_file);
598602}
599603
600604test "no file inputs" {
......@@ -643,10 +647,11 @@ test "CacheHashes with files added after initial hash work" {
643647 const temp_file2 = "cache_hash_post_file_test2.txt";
644648 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
645649
650 const ts1 = std.time.nanoTimestamp();
646651 try cwd.writeFile(temp_file1, "Hello, world!\n");
647652 try cwd.writeFile(temp_file2, "Hello world the second!\n");
648653
649 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
654 while (isProblematicTimestamp(ts1)) {
650655 std.time.sleep(1);
651656 }
652657
......@@ -680,9 +685,10 @@ test "CacheHashes with files added after initial hash work" {
680685 testing.expect(mem.eql(u8, &digest1, &digest2));
681686
682687 // Modify the file added after initial hash
688 const ts2 = std.time.nanoTimestamp();
683689 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
684690
685 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
691 while (isProblematicTimestamp(ts2)) {
686692 std.time.sleep(1);
687693 }
688694
lib/std/crypto.zig+23-5
......@@ -29,17 +29,29 @@ pub const HmacSha1 = hmac.HmacSha1;
2929pub const HmacSha256 = hmac.HmacSha256;
3030pub const HmacBlake2s256 = hmac.HmacBlake2s256;
3131
32const import_chaCha20 = @import("crypto/chacha20.zig");
33pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
34pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
32pub const chacha20 = @import("crypto/chacha20.zig");
33pub const chaCha20IETF = chacha20.chaCha20IETF;
34pub const chaCha20With64BitNonce = chacha20.chaCha20With64BitNonce;
35pub const xChaCha20IETF = chacha20.xChaCha20IETF;
3536
3637pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
37pub const X25519 = @import("crypto/x25519.zig").X25519;
3838
3939const import_aes = @import("crypto/aes.zig");
4040pub const AES128 = import_aes.AES128;
4141pub const AES256 = import_aes.AES256;
4242
43pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
44pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519;
45pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
46pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
47pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
48
49pub const aead = struct {
50 pub const Gimli = gimli.Aead;
51 pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
52 pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
53};
54
4355const std = @import("std.zig");
4456pub const randomBytes = std.os.getrandom;
4557
......@@ -55,7 +67,13 @@ test "crypto" {
5567 _ = @import("crypto/sha1.zig");
5668 _ = @import("crypto/sha2.zig");
5769 _ = @import("crypto/sha3.zig");
58 _ = @import("crypto/x25519.zig");
70 _ = @import("crypto/25519/curve25519.zig");
71 _ = @import("crypto/25519/ed25519.zig");
72 _ = @import("crypto/25519/edwards25519.zig");
73 _ = @import("crypto/25519/field.zig");
74 _ = @import("crypto/25519/scalar.zig");
75 _ = @import("crypto/25519/x25519.zig");
76 _ = @import("crypto/25519/ristretto255.zig");
5977}
6078
6179test "issue #4532: no index out of bounds" {
lib/std/crypto/25519/curve25519.zig created+144
......@@ -0,0 +1,144 @@
1const std = @import("std");
2
3/// Group operations over Curve25519.
4pub const Curve25519 = struct {
5 /// The underlying prime field.
6 pub const Fe = @import("field.zig").Fe;
7 /// Field arithmetic mod the order of the main subgroup.
8 pub const scalar = @import("scalar.zig");
9
10 x: Fe,
11
12 /// Decode a Curve25519 point from its compressed (X) coordinates.
13 pub inline fn fromBytes(s: [32]u8) Curve25519 {
14 return .{ .x = Fe.fromBytes(s) };
15 }
16
17 /// Encode a Curve25519 point.
18 pub inline fn toBytes(p: Curve25519) [32]u8 {
19 return p.x.toBytes();
20 }
21
22 /// The Curve25519 base point.
23 pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
24
25 /// Check that the encoding of a Curve25519 point is canonical.
26 pub fn rejectNonCanonical(s: [32]u8) !void {
27 return Fe.rejectNonCanonical(s, false);
28 }
29
30 /// Reject the neutral element.
31 pub fn rejectIdentity(p: Curve25519) !void {
32 if (p.x.isZero()) {
33 return error.IdentityElement;
34 }
35 }
36
37 fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 {
38 var x1 = p.x;
39 var x2 = Fe.one;
40 var z2 = Fe.zero;
41 var x3 = x1;
42 var z3 = Fe.one;
43 var swap: u8 = 0;
44 var pos: usize = bits - 1;
45 while (true) : (pos -= 1) {
46 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 1;
47 swap ^= bit;
48 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
49 swap = bit;
50 const a = x2.add(z2);
51 const b = x2.sub(z2);
52 const aa = a.sq();
53 const bb = b.sq();
54 x2 = aa.mul(bb);
55 const e = aa.sub(bb);
56 const da = x3.sub(z3).mul(a);
57 const cb = x3.add(z3).mul(b);
58 x3 = da.add(cb).sq();
59 z3 = x1.mul(da.sub(cb).sq());
60 z2 = e.mul(bb.add(e.mul32(121666)));
61 if (pos == 0) break;
62 }
63 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
64 z2 = z2.invert();
65 x2 = x2.mul(z2);
66 if (x2.isZero()) {
67 return error.IdentityElement;
68 }
69 return Curve25519{ .x = x2 };
70 }
71
72 /// Multiply a Curve25519 point by a scalar after "clamping" it.
73 /// Clamping forces the scalar to be a multiple of the cofactor in
74 /// order to prevent small subgroups attacks. This is the standard
75 /// way to use Curve25519 for a DH operation.
76 /// Return error.IdentityElement if the resulting point is
77 /// the identity element.
78 pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 {
79 var t: [32]u8 = s;
80 scalar.clamp(&t);
81 return try ladder(p, t, 255);
82 }
83
84 /// Multiply a Curve25519 point by a scalar without clamping it.
85 /// Return error.IdentityElement if the resulting point is
86 /// the identity element or error.WeakPublicKey if the public
87 /// key is a low-order point.
88 pub fn mul(p: Curve25519, s: [32]u8) !Curve25519 {
89 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
90 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
91 return try ladder(p, s, 256);
92 }
93};
94
95test "curve25519" {
96 var s = [32]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
97 const p = try Curve25519.basePoint.clampedMul(s);
98 try p.rejectIdentity();
99 var buf: [128]u8 = undefined;
100 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145");
101 const q = try p.clampedMul(s);
102 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537");
103
104 try Curve25519.rejectNonCanonical(s);
105 s[31] |= 0x80;
106 std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));
107}
108
109test "curve25519 small order check" {
110 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
111 const small_order_ss: [7][32]u8 = .{
112 .{
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4)
114 },
115 .{
116 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1)
117 },
118 .{
119 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00, // 325606250916557431795983626356110631294008115727848805560023387167927233504 (order 8) */
120 },
121 .{
122 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, // 39382357235489614581723060781553021112529911719440698176882885853963445705823 (order 8)
123 },
124 .{
125 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2)
126 },
127 .{
128 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4)
129 },
130 .{
131 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1)
132 },
133 };
134 for (small_order_ss) |small_order_s| {
135 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s));
136 var extra = small_order_s;
137 extra[31] ^= 0x80;
138 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(extra).mul(s));
139 var valid = small_order_s;
140 valid[31] = 0x40;
141 s[0] = 0;
142 std.testing.expectError(error.IdentityElement, Curve25519.fromBytes(valid).mul(s));
143 }
144}
lib/std/crypto/25519/ed25519.zig created+134
......@@ -0,0 +1,134 @@
1const std = @import("std");
2const fmt = std.fmt;
3const mem = std.mem;
4const Sha512 = std.crypto.Sha512;
5
6/// Ed25519 (EdDSA) signatures.
7pub const Ed25519 = struct {
8 /// The underlying elliptic curve.
9 pub const Curve = @import("edwards25519.zig").Edwards25519;
10 /// Length (in bytes) of a seed required to create a key pair.
11 pub const seed_length = 32;
12 /// Length (in bytes) of a compressed key pair.
13 pub const keypair_length = 64;
14 /// Length (in bytes) of a compressed public key.
15 pub const public_length = 32;
16 /// Length (in bytes) of a signature.
17 pub const signature_length = 64;
18 /// Length (in bytes) of optional random bytes, for non-deterministic signatures.
19 pub const noise_length = 32;
20
21 /// Derive a key pair from a secret seed.
22 ///
23 /// As in RFC 8032, an Ed25519 public key is generated by hashing
24 /// the secret key using the SHA-512 function, and interpreting the
25 /// bit-swapped, clamped lower-half of the output as the secret scalar.
26 ///
27 /// For this reason, an EdDSA secret key is commonly called a seed,
28 /// from which the actual secret is derived.
29 pub fn createKeyPair(seed: [seed_length]u8) ![keypair_length]u8 {
30 var az: [Sha512.digest_length]u8 = undefined;
31 var h = Sha512.init();
32 h.update(&seed);
33 h.final(&az);
34 const p = try Curve.basePoint.clampedMul(az[0..32].*);
35 var keypair: [keypair_length]u8 = undefined;
36 mem.copy(u8, &keypair, &seed);
37 mem.copy(u8, keypair[seed_length..], &p.toBytes());
38 return keypair;
39 }
40
41 /// Return the public key for a given key pair.
42 pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 {
43 var public_key: [public_length]u8 = undefined;
44 mem.copy(u8, public_key[0..], key_pair[seed_length..]);
45 return public_key;
46 }
47
48 /// Sign a message using a key pair, and optional random noise.
49 /// Having noise creates non-standard, non-deterministic signatures,
50 /// but has been proven to increase resilience against fault attacks.
51 pub fn sign(msg: []const u8, key_pair: [keypair_length]u8, noise: ?[noise_length]u8) ![signature_length]u8 {
52 const public_key = key_pair[32..];
53 var az: [Sha512.digest_length]u8 = undefined;
54 var h = Sha512.init();
55 h.update(key_pair[0..seed_length]);
56 h.final(&az);
57
58 h = Sha512.init();
59 if (noise) |*z| {
60 h.update(z);
61 }
62 h.update(az[32..]);
63 h.update(msg);
64 var nonce64: [64]u8 = undefined;
65 h.final(&nonce64);
66 const nonce = Curve.scalar.reduce64(nonce64);
67 const r = try Curve.basePoint.mul(nonce);
68
69 var sig: [signature_length]u8 = undefined;
70 mem.copy(u8, sig[0..32], &r.toBytes());
71 mem.copy(u8, sig[32..], public_key);
72 h = Sha512.init();
73 h.update(&sig);
74 h.update(msg);
75 var hram64: [Sha512.digest_length]u8 = undefined;
76 h.final(&hram64);
77 const hram = Curve.scalar.reduce64(hram64);
78
79 var x = az[0..32];
80 Curve.scalar.clamp(x);
81 const s = Curve.scalar.mulAdd(hram, x.*, nonce);
82 mem.copy(u8, sig[32..], s[0..]);
83 return sig;
84 }
85
86 /// Verify an Ed25519 signature given a message and a public key.
87 /// Returns error.InvalidSignature is the signature verification failed.
88 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void {
89 const r = sig[0..32];
90 const s = sig[32..64];
91 try Curve.scalar.rejectNonCanonical(s.*);
92 try Curve.rejectNonCanonical(public_key);
93 const a = try Curve.fromBytes(public_key);
94 try a.rejectIdentity();
95
96 var h = Sha512.init();
97 h.update(r);
98 h.update(&public_key);
99 h.update(msg);
100 var hram64: [Sha512.digest_length]u8 = undefined;
101 h.final(&hram64);
102 const hram = Curve.scalar.reduce64(hram64);
103
104 const p = try a.neg().mul(hram);
105 const check = (try Curve.basePoint.mul(s.*)).add(p).toBytes();
106 if (mem.eql(u8, &check, r) == false) {
107 return error.InvalidSignature;
108 }
109 }
110};
111
112test "ed25519 key pair creation" {
113 var seed: [32]u8 = undefined;
114 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
115 const key_pair = try Ed25519.createKeyPair(seed);
116 var buf: [256]u8 = undefined;
117 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
118
119 const public_key = Ed25519.publicKey(key_pair);
120 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
121}
122
123test "ed25519 signature" {
124 var seed: [32]u8 = undefined;
125 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
126 const key_pair = try Ed25519.createKeyPair(seed);
127
128 const sig = try Ed25519.sign("test", key_pair, null);
129 var buf: [128]u8 = undefined;
130 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
131 const public_key = Ed25519.publicKey(key_pair);
132 try Ed25519.verify(sig, "test", public_key);
133 std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key));
134}
lib/std/crypto/25519/edwards25519.zig created+214
......@@ -0,0 +1,214 @@
1const std = @import("std");
2const fmt = std.fmt;
3
4/// Group operations over Edwards25519.
5pub const Edwards25519 = struct {
6 /// The underlying prime field.
7 pub const Fe = @import("field.zig").Fe;
8 /// Field arithmetic mod the order of the main subgroup.
9 pub const scalar = @import("scalar.zig");
10
11 x: Fe,
12 y: Fe,
13 z: Fe,
14 t: Fe,
15
16 is_base: bool = false,
17
18 /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.
19 pub fn fromBytes(s: [32]u8) !Edwards25519 {
20 const z = Fe.one;
21 const y = Fe.fromBytes(s);
22 var u = y.sq();
23 var v = u.mul(Fe.edwards25519d);
24 u = u.sub(z);
25 v = v.add(z);
26 const v3 = v.sq().mul(v);
27 var x = v3.sq().mul(v).mul(u).pow2523().mul(v3).mul(u);
28 const vxx = x.sq().mul(v);
29 const has_m_root = vxx.sub(u).isZero();
30 const has_p_root = vxx.add(u).isZero();
31 if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) { // best-effort to avoid two conditional branches
32 return error.InvalidEncoding;
33 }
34 x.cMov(x.mul(Fe.sqrtm1), 1 - @boolToInt(has_m_root));
35 x.cMov(x.neg(), @boolToInt(x.isNegative()) ^ (s[31] >> 7));
36 const t = x.mul(y);
37 return Edwards25519{ .x = x, .y = y, .z = z, .t = t };
38 }
39
40 /// Encode an Edwards25519 point.
41 pub fn toBytes(p: Edwards25519) [32]u8 {
42 const zi = p.z.invert();
43 var s = p.y.mul(zi).toBytes();
44 s[31] ^= @as(u8, @boolToInt(p.x.mul(zi).isNegative())) << 7;
45 return s;
46 }
47
48 /// Check that the encoding of a point is canonical.
49 pub fn rejectNonCanonical(s: [32]u8) !void {
50 return Fe.rejectNonCanonical(s, true);
51 }
52
53 /// The edwards25519 base point.
54 pub const basePoint = Edwards25519{
55 .x = Fe{ .limbs = .{ 3990542415680775, 3398198340507945, 4322667446711068, 2814063955482877, 2839572215813860 } },
56 .y = Fe{ .limbs = .{ 1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198 } },
57 .z = Fe.one,
58 .t = Fe{ .limbs = .{ 1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039 } },
59 .is_base = true,
60 };
61
62 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
63
64 /// Reject the neutral element.
65 pub fn rejectIdentity(p: Edwards25519) !void {
66 if (p.x.isZero()) {
67 return error.IdentityElement;
68 }
69 }
70
71 /// Flip the sign of the X coordinate.
72 pub inline fn neg(p: Edwards25519) Edwards25519 {
73 return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() };
74 }
75
76 /// Double an Edwards25519 point.
77 pub fn dbl(p: Edwards25519) Edwards25519 {
78 const t0 = p.x.add(p.y).sq();
79 var x = p.x.sq();
80 var z = p.y.sq();
81 const y = z.add(x);
82 z = z.sub(x);
83 x = t0.sub(y);
84 const t = p.z.sq2().sub(z);
85 return .{
86 .x = x.mul(t),
87 .y = y.mul(z),
88 .z = z.mul(t),
89 .t = x.mul(y),
90 };
91 }
92
93 /// Add two Edwards25519 points.
94 pub fn add(p: Edwards25519, q: Edwards25519) Edwards25519 {
95 const a = p.y.sub(p.x).mul(q.y.sub(q.x));
96 const b = p.x.add(p.y).mul(q.x.add(q.y));
97 const c = p.t.mul(q.t).mul(Fe.edwards25519d2);
98 var d = p.z.mul(q.z);
99 d = d.add(d);
100 const x = b.sub(a);
101 const y = b.add(a);
102 const z = d.add(c);
103 const t = d.sub(c);
104 return .{
105 .x = x.mul(t),
106 .y = y.mul(z),
107 .z = z.mul(t),
108 .t = x.mul(y),
109 };
110 }
111
112 inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void {
113 p.x.cMov(a.x, c);
114 p.y.cMov(a.y, c);
115 p.z.cMov(a.z, c);
116 p.t.cMov(a.t, c);
117 }
118
119 inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 {
120 var t = Edwards25519.identityElement;
121 comptime var i: u8 = 0;
122 inline while (i < 16) : (i += 1) {
123 t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1);
124 }
125 return t;
126 }
127
128 fn pcMul(pc: [16]Edwards25519, s: [32]u8) !Edwards25519 {
129 var q = Edwards25519.identityElement;
130 var pos: usize = 252;
131 while (true) : (pos -= 4) {
132 q = q.dbl().dbl().dbl().dbl();
133 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
134 q = q.add(pcSelect(pc, bit));
135 if (pos == 0) break;
136 }
137 try q.rejectIdentity();
138 return q;
139 }
140
141 fn precompute(p: Edwards25519) [16]Edwards25519 {
142 var pc: [16]Edwards25519 = undefined;
143 pc[0] = Edwards25519.identityElement;
144 pc[1] = p;
145 var i: usize = 2;
146 while (i < 16) : (i += 1) {
147 pc[i] = pc[i - 1].add(p);
148 }
149 return pc;
150 }
151
152 /// Multiply an Edwards25519 point by a scalar without clamping it.
153 /// Return error.WeakPublicKey if the resulting point is
154 /// the identity element.
155 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
156 var pc: [16]Edwards25519 = undefined;
157 if (p.is_base) {
158 @setEvalBranchQuota(10000);
159 pc = comptime precompute(Edwards25519.basePoint);
160 } else {
161 pc = precompute(p);
162 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
163 }
164 return pcMul(pc, s);
165 }
166
167 /// Multiply an Edwards25519 point by a scalar after "clamping" it.
168 /// Clamping forces the scalar to be a multiple of the cofactor in
169 /// order to prevent small subgroups attacks.
170 /// This is strongly recommended for DH operations.
171 /// Return error.WeakPublicKey if the resulting point is
172 /// the identity element.
173 pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 {
174 var t: [32]u8 = s;
175 scalar.clamp(&t);
176 return mul(p, t);
177 }
178};
179
180test "edwards25519 packing/unpacking" {
181 const s = [_]u8{170} ++ [_]u8{0} ** 31;
182 var b = Edwards25519.basePoint;
183 const pk = try b.mul(s);
184 var buf: [128]u8 = undefined;
185 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6");
186
187 const small_order_ss: [7][32]u8 = .{
188 .{
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4)
190 },
191 .{
192 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1)
193 },
194 .{
195 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05, // 270738550114484064931822528722565878893680426757531351946374360975030340202(order 8)
196 },
197 .{
198 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, // 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8)
199 },
200 .{
201 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2)
202 },
203 .{
204 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4)
205 },
206 .{
207 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1)
208 },
209 };
210 for (small_order_ss) |small_order_s| {
211 const small_p = try Edwards25519.fromBytes(small_order_s);
212 std.testing.expectError(error.WeakPublicKey, small_p.mul(s));
213 }
214}
lib/std/crypto/25519/field.zig created+318
......@@ -0,0 +1,318 @@
1const std = @import("std");
2const readIntLittle = std.mem.readIntLittle;
3const writeIntLittle = std.mem.writeIntLittle;
4
5pub const Fe = struct {
6 limbs: [5]u64,
7
8 const MASK51: u64 = 0x7ffffffffffff;
9
10 pub const zero = Fe{ .limbs = .{ 0, 0, 0, 0, 0 } };
11
12 pub const one = Fe{ .limbs = .{ 1, 0, 0, 0, 0 } };
13
14 pub const sqrtm1 = Fe{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } }; // sqrt(-1)
15
16 pub const curve25519BasePoint = Fe{ .limbs = .{ 9, 0, 0, 0, 0 } };
17
18 pub const edwards25519d = Fe{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } }; // 37095705934669439343138083508754565189542113879843219016388785533085940283555
19
20 pub const edwards25519d2 = Fe{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } }; // 2d
21
22 pub const edwards25519sqrtamd = Fe{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } }; // 1/sqrt(a-d)
23
24 pub const edwards25519eonemsqd = Fe{ .limbs = .{ 1136626929484150, 1998550399581263, 496427632559748, 118527312129759, 45110755273534 } }; // 1-d^2
25
26 pub const edwards25519sqdmone = Fe{ .limbs = .{ 1507062230895904, 1572317787530805, 683053064812840, 317374165784489, 1572899562415810 } }; // (d-1)^2
27
28 pub const edwards25519sqrtadm1 = Fe{ .limbs = .{ 2241493124984347, 425987919032274, 2207028919301688, 1220490630685848, 974799131293748 } };
29
30 pub inline fn isZero(fe: Fe) bool {
31 var reduced = fe;
32 reduced.reduce();
33 const limbs = reduced.limbs;
34 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;
35 }
36
37 pub inline fn equivalent(a: Fe, b: Fe) bool {
38 return a.sub(b).isZero();
39 }
40
41 pub fn fromBytes(s: [32]u8) Fe {
42 var fe: Fe = undefined;
43 fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51;
44 fe.limbs[1] = (readIntLittle(u64, s[6..14]) >> 3) & MASK51;
45 fe.limbs[2] = (readIntLittle(u64, s[12..20]) >> 6) & MASK51;
46 fe.limbs[3] = (readIntLittle(u64, s[19..27]) >> 1) & MASK51;
47 fe.limbs[4] = (readIntLittle(u64, s[24..32]) >> 12) & MASK51;
48
49 return fe;
50 }
51
52 pub fn toBytes(fe: Fe) [32]u8 {
53 var reduced = fe;
54 reduced.reduce();
55 var s: [32]u8 = undefined;
56 writeIntLittle(u64, s[0..8], reduced.limbs[0] | (reduced.limbs[1] << 51));
57 writeIntLittle(u64, s[8..16], (reduced.limbs[1] >> 13) | (reduced.limbs[2] << 38));
58 writeIntLittle(u64, s[16..24], (reduced.limbs[2] >> 26) | (reduced.limbs[3] << 25));
59 writeIntLittle(u64, s[24..32], (reduced.limbs[3] >> 39) | (reduced.limbs[4] << 12));
60
61 return s;
62 }
63
64 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {
65 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
66 comptime var i = 30;
67 inline while (i > 0) : (i -= 1) {
68 c |= s[i] ^ 0xff;
69 }
70 c = (c -% 1) >> 8;
71 const d = (@as(u16, 0xed - 1) -% @as(u16, s[0])) >> 8;
72 const x = if (ignore_extra_bit) 0 else s[31] >> 7;
73 if ((((c & d) | x) & 1) != 0) {
74 return error.NonCanonical;
75 }
76 }
77
78 fn reduce(fe: *Fe) void {
79 comptime var i = 0;
80 comptime var j = 0;
81 const limbs = &fe.limbs;
82 inline while (j < 2) : (j += 1) {
83 i = 0;
84 inline while (i < 4) : (i += 1) {
85 limbs[i + 1] += limbs[i] >> 51;
86 limbs[i] &= MASK51;
87 }
88 limbs[0] += 19 * (limbs[4] >> 51);
89 limbs[4] &= MASK51;
90 }
91 limbs[0] += 19;
92 i = 0;
93 inline while (i < 4) : (i += 1) {
94 limbs[i + 1] += limbs[i] >> 51;
95 limbs[i] &= MASK51;
96 }
97 limbs[0] += 19 * (limbs[4] >> 51);
98 limbs[4] &= MASK51;
99
100 limbs[0] += 0x8000000000000 - 19;
101 limbs[1] += 0x8000000000000 - 1;
102 limbs[2] += 0x8000000000000 - 1;
103 limbs[3] += 0x8000000000000 - 1;
104 limbs[4] += 0x8000000000000 - 1;
105
106 i = 0;
107 inline while (i < 4) : (i += 1) {
108 limbs[i + 1] += limbs[i] >> 51;
109 limbs[i] &= MASK51;
110 }
111 limbs[4] &= MASK51;
112 }
113
114 pub inline fn add(a: Fe, b: Fe) Fe {
115 var fe: Fe = undefined;
116 comptime var i = 0;
117 inline while (i < 5) : (i += 1) {
118 fe.limbs[i] = a.limbs[i] + b.limbs[i];
119 }
120 return fe;
121 }
122
123 pub inline fn sub(a: Fe, b: Fe) Fe {
124 var fe = b;
125 comptime var i = 0;
126 inline while (i < 4) : (i += 1) {
127 fe.limbs[i + 1] += fe.limbs[i] >> 51;
128 fe.limbs[i] &= MASK51;
129 }
130 fe.limbs[0] += 19 * (fe.limbs[4] >> 51);
131 fe.limbs[4] &= MASK51;
132 fe.limbs[0] = (a.limbs[0] + 0xfffffffffffda) - fe.limbs[0];
133 fe.limbs[1] = (a.limbs[1] + 0xffffffffffffe) - fe.limbs[1];
134 fe.limbs[2] = (a.limbs[2] + 0xffffffffffffe) - fe.limbs[2];
135 fe.limbs[3] = (a.limbs[3] + 0xffffffffffffe) - fe.limbs[3];
136 fe.limbs[4] = (a.limbs[4] + 0xffffffffffffe) - fe.limbs[4];
137
138 return fe;
139 }
140
141 pub inline fn neg(a: Fe) Fe {
142 return zero.sub(a);
143 }
144
145 pub inline fn isNegative(a: Fe) bool {
146 return (a.toBytes()[0] & 1) != 0;
147 }
148
149 pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {
150 const mask: u64 = 0 -% c;
151 var x = fe.*;
152 comptime var i = 0;
153 inline while (i < 5) : (i += 1) {
154 x.limbs[i] ^= a.limbs[i];
155 }
156 i = 0;
157 inline while (i < 5) : (i += 1) {
158 x.limbs[i] &= mask;
159 }
160 i = 0;
161 inline while (i < 5) : (i += 1) {
162 fe.limbs[i] ^= x.limbs[i];
163 }
164 }
165
166 pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void {
167 const mask: u64 = 0 -% c;
168 var x0 = a0.*;
169 var x1 = a1.*;
170 comptime var i = 0;
171 inline while (i < 5) : (i += 1) {
172 x0.limbs[i] ^= b0.limbs[i];
173 x1.limbs[i] ^= b1.limbs[i];
174 }
175 i = 0;
176 inline while (i < 5) : (i += 1) {
177 x0.limbs[i] &= mask;
178 x1.limbs[i] &= mask;
179 }
180 i = 0;
181 inline while (i < 5) : (i += 1) {
182 a0.limbs[i] ^= x0.limbs[i];
183 b0.limbs[i] ^= x0.limbs[i];
184 a1.limbs[i] ^= x1.limbs[i];
185 b1.limbs[i] ^= x1.limbs[i];
186 }
187 }
188
189 inline fn _carry128(r: *[5]u128) Fe {
190 var rs: [5]u64 = undefined;
191 comptime var i = 0;
192 inline while (i < 4) : (i += 1) {
193 rs[i] = @truncate(u64, r[i]) & MASK51;
194 r[i + 1] += @intCast(u64, r[i] >> 51);
195 }
196 rs[4] = @truncate(u64, r[4]) & MASK51;
197 var carry = @intCast(u64, r[4] >> 51);
198 rs[0] += 19 * carry;
199 carry = rs[0] >> 51;
200 rs[0] &= MASK51;
201 rs[1] += carry;
202 carry = rs[1] >> 51;
203 rs[1] &= MASK51;
204 rs[2] += carry;
205
206 return .{ .limbs = rs };
207 }
208
209 pub inline fn mul(a: Fe, b: Fe) Fe {
210 var ax: [5]u128 = undefined;
211 var bx: [5]u128 = undefined;
212 var a19: [5]u128 = undefined;
213 var r: [5]u128 = undefined;
214 comptime var i = 0;
215 inline while (i < 5) : (i += 1) {
216 ax[i] = @intCast(u128, a.limbs[i]);
217 bx[i] = @intCast(u128, b.limbs[i]);
218 }
219 i = 1;
220 inline while (i < 5) : (i += 1) {
221 a19[i] = 19 * ax[i];
222 }
223 r[0] = ax[0] * bx[0] + a19[1] * bx[4] + a19[2] * bx[3] + a19[3] * bx[2] + a19[4] * bx[1];
224 r[1] = ax[0] * bx[1] + ax[1] * bx[0] + a19[2] * bx[4] + a19[3] * bx[3] + a19[4] * bx[2];
225 r[2] = ax[0] * bx[2] + ax[1] * bx[1] + ax[2] * bx[0] + a19[3] * bx[4] + a19[4] * bx[3];
226 r[3] = ax[0] * bx[3] + ax[1] * bx[2] + ax[2] * bx[1] + ax[3] * bx[0] + a19[4] * bx[4];
227 r[4] = ax[0] * bx[4] + ax[1] * bx[3] + ax[2] * bx[2] + ax[3] * bx[1] + ax[4] * bx[0];
228
229 return _carry128(&r);
230 }
231
232 inline fn _sq(a: Fe, double: comptime bool) Fe {
233 var ax: [5]u128 = undefined;
234 var r: [5]u128 = undefined;
235 comptime var i = 0;
236 inline while (i < 5) : (i += 1) {
237 ax[i] = @intCast(u128, a.limbs[i]);
238 }
239 const a0_2 = 2 * ax[0];
240 const a1_2 = 2 * ax[1];
241 const a1_38 = 38 * ax[1];
242 const a2_38 = 38 * ax[2];
243 const a3_38 = 38 * ax[3];
244 const a3_19 = 19 * ax[3];
245 const a4_19 = 19 * ax[4];
246 r[0] = ax[0] * ax[0] + a1_38 * ax[4] + a2_38 * ax[3];
247 r[1] = a0_2 * ax[1] + a2_38 * ax[4] + a3_19 * ax[3];
248 r[2] = a0_2 * ax[2] + ax[1] * ax[1] + a3_38 * ax[4];
249 r[3] = a0_2 * ax[3] + a1_2 * ax[2] + a4_19 * ax[4];
250 r[4] = a0_2 * ax[4] + a1_2 * ax[3] + ax[2] * ax[2];
251 if (double) {
252 i = 0;
253 inline while (i < 5) : (i += 1) {
254 r[i] *= 2;
255 }
256 }
257 return _carry128(&r);
258 }
259
260 pub inline fn sq(a: Fe) Fe {
261 return _sq(a, false);
262 }
263
264 pub inline fn sq2(a: Fe) Fe {
265 return _sq(a, true);
266 }
267
268 pub inline fn mul32(a: Fe, comptime n: u32) Fe {
269 const sn = @intCast(u128, n);
270 var fe: Fe = undefined;
271 var x: u128 = 0;
272 comptime var i = 0;
273 inline while (i < 5) : (i += 1) {
274 x = a.limbs[i] * sn + (x >> 51);
275 fe.limbs[i] = @truncate(u64, x) & MASK51;
276 }
277 fe.limbs[0] += @intCast(u64, x >> 51) * 19;
278
279 return fe;
280 }
281
282 inline fn sqn(a: Fe, comptime n: comptime_int) Fe {
283 var i: usize = 0;
284 var fe = a;
285 while (i < n) : (i += 1) {
286 fe = fe.sq();
287 }
288 return fe;
289 }
290
291 pub fn invert(a: Fe) Fe {
292 var t0 = a.sq();
293 var t1 = t0.sqn(2).mul(a);
294 t0 = t0.mul(t1);
295 t1 = t1.mul(t0.sq());
296 t1 = t1.mul(t1.sqn(5));
297 var t2 = t1.sqn(10).mul(t1);
298 t2 = t2.mul(t2.sqn(20)).sqn(10);
299 t1 = t1.mul(t2);
300 t2 = t1.sqn(50).mul(t1);
301 return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0);
302 }
303
304 pub fn pow2523(a: Fe) Fe {
305 var c = a;
306 var i: usize = 0;
307 while (i < 249) : (i += 1) {
308 c = c.sq().mul(a);
309 }
310 return c.sq().sq().mul(a);
311 }
312
313 pub fn abs(a: Fe) Fe {
314 var r = a;
315 r.cMov(a.neg(), @boolToInt(a.isNegative()));
316 return r;
317 }
318};
lib/std/crypto/25519/ristretto255.zig created+183
......@@ -0,0 +1,183 @@
1const std = @import("std");
2const fmt = std.fmt;
3
4/// Group operations over Edwards25519.
5pub const Ristretto255 = struct {
6 /// The underlying elliptic curve.
7 pub const Curve = @import("edwards25519.zig").Edwards25519;
8 /// The underlying prime field.
9 pub const Fe = Curve.Fe;
10 /// Field arithmetic mod the order of the main subgroup.
11 pub const scalar = Curve.scalar;
12
13 p: Curve,
14
15 fn sqrtRatioM1(u: Fe, v: Fe) struct { ratio_is_square: u32, root: Fe } {
16 const v3 = v.sq().mul(v); // v^3
17 var x = v3.sq().mul(u).mul(v).pow2523().mul(v3).mul(u); // uv^3(uv^7)^((q-5)/8)
18 const vxx = x.sq().mul(v); // vx^2
19 const m_root_check = vxx.sub(u); // vx^2-u
20 const p_root_check = vxx.add(u); // vx^2+u
21 const f_root_check = u.mul(Fe.sqrtm1).add(vxx); // vx^2+u*sqrt(-1)
22 const has_m_root = m_root_check.isZero();
23 const has_p_root = p_root_check.isZero();
24 const has_f_root = f_root_check.isZero();
25 const x_sqrtm1 = x.mul(Fe.sqrtm1); // x*sqrt(-1)
26 x.cMov(x_sqrtm1, @boolToInt(has_p_root) | @boolToInt(has_f_root));
27 return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() };
28 }
29
30 fn rejectNonCanonical(s: [32]u8) !void {
31 if ((s[0] & 1) != 0) {
32 return error.NonCanonical;
33 }
34 try Fe.rejectNonCanonical(s, false);
35 }
36
37 /// Reject the neutral element.
38 pub inline fn rejectIdentity(p: Ristretto255) !void {
39 return p.p.rejectIdentity();
40 }
41
42 /// The base point (Ristretto is a curve in desguise).
43 pub const basePoint = Ristretto255{ .p = Curve.basePoint };
44
45 /// Decode a Ristretto255 representative.
46 pub fn fromBytes(s: [32]u8) !Ristretto255 {
47 try rejectNonCanonical(s);
48 const s_ = Fe.fromBytes(s);
49 const ss = s_.sq(); // s^2
50 const u1_ = Fe.one.sub(ss); // (1-s^2)
51 const u1u1 = u1_.sq(); // (1-s^2)^2
52 const u2_ = Fe.one.add(ss); // (1+s^2)
53 const u2u2 = u2_.sq(); // (1+s^2)^2
54 const v = Fe.edwards25519d.mul(u1u1).neg().sub(u2u2); // -(d*u1^2)-u2^2
55 const v_u2u2 = v.mul(u2u2); // v*u2^2
56
57 const inv_sqrt = sqrtRatioM1(Fe.one, v_u2u2);
58 var x = inv_sqrt.root.mul(u2_);
59 const y = inv_sqrt.root.mul(x).mul(v).mul(u1_);
60 x = x.mul(s_);
61 x = x.add(x).abs();
62 const t = x.mul(y);
63 if ((1 - inv_sqrt.ratio_is_square) | @boolToInt(t.isNegative()) | @boolToInt(y.isZero()) != 0) {
64 return error.InvalidEncoding;
65 }
66 const p: Curve = .{
67 .x = x,
68 .y = y,
69 .z = Fe.one,
70 .t = t,
71 };
72 return Ristretto255{ .p = p };
73 }
74
75 /// Encode to a Ristretto255 representative.
76 pub fn toBytes(e: Ristretto255) [32]u8 {
77 const p = &e.p;
78 var u1_ = p.z.add(p.y); // Z+Y
79 const zmy = p.z.sub(p.y); // Z-Y
80 u1_ = u1_.mul(zmy); // (Z+Y)*(Z-Y)
81 const u2_ = p.x.mul(p.y); // X*Y
82 const u1_u2u2 = u2_.sq().mul(u1_); // u1*u2^2
83 const inv_sqrt = sqrtRatioM1(Fe.one, u1_u2u2);
84 const den1 = inv_sqrt.root.mul(u1_);
85 const den2 = inv_sqrt.root.mul(u2_);
86 const z_inv = den1.mul(den2).mul(p.t); // den1*den2*T
87 const ix = p.x.mul(Fe.sqrtm1); // X*sqrt(-1)
88 const iy = p.y.mul(Fe.sqrtm1); // Y*sqrt(-1)
89 const eden = den1.mul(Fe.edwards25519sqrtamd); // den1/sqrt(a-d)
90 const t_z_inv = p.t.mul(z_inv); // T*z_inv
91
92 const rotate = @boolToInt(t_z_inv.isNegative());
93 var x = p.x;
94 var y = p.y;
95 var den_inv = den2;
96 x.cMov(iy, rotate);
97 y.cMov(ix, rotate);
98 den_inv.cMov(eden, rotate);
99
100 const x_z_inv = x.mul(z_inv);
101 const yneg = y.neg();
102 y.cMov(yneg, @boolToInt(x_z_inv.isNegative()));
103
104 return p.z.sub(y).mul(den_inv).abs().toBytes();
105 }
106
107 fn elligator(t: Fe) Curve {
108 const r = t.sq().mul(Fe.sqrtm1); // sqrt(-1)*t^2
109 const u = r.add(Fe.one).mul(Fe.edwards25519eonemsqd); // (r+1)*(1-d^2)
110 var c = comptime Fe.one.neg(); // -1
111 const v = c.sub(r.mul(Fe.edwards25519d)).mul(r.add(Fe.edwards25519d)); // (c-r*d)*(r+d)
112 const ratio_sqrt = sqrtRatioM1(u, v);
113 const wasnt_square = 1 - ratio_sqrt.ratio_is_square;
114 var s = ratio_sqrt.root;
115 const s_prime = s.mul(t).abs().neg(); // -|s*t|
116 s.cMov(s_prime, wasnt_square);
117 c.cMov(r, wasnt_square);
118
119 const n = r.sub(Fe.one).mul(c).mul(Fe.edwards25519sqdmone).sub(v); // c*(r-1)*(d-1)^2-v
120 const w0 = s.add(s).mul(v); // 2s*v
121 const w1 = n.mul(Fe.edwards25519sqrtadm1); // n*sqrt(ad-1)
122 const ss = s.sq(); // s^2
123 const w2 = Fe.one.sub(ss); // 1-s^2
124 const w3 = Fe.one.add(ss); // 1+s^2
125
126 return .{ .x = w0.mul(w3), .y = w2.mul(w1), .z = w1.mul(w3), .t = w0.mul(w2) };
127 }
128
129 /// Map a 64-bit string into a Ristretto255 group element
130 pub fn fromUniform(h: [64]u8) Ristretto255 {
131 const p0 = elligator(Fe.fromBytes(h[0..32].*));
132 const p1 = elligator(Fe.fromBytes(h[32..64].*));
133 return Ristretto255{ .p = p0.add(p1) };
134 }
135
136 /// Double a Ristretto255 element.
137 pub inline fn dbl(p: Ristretto255) Ristretto255 {
138 return .{ .p = p.p.dbl() };
139 }
140
141 /// Add two Ristretto255 elements.
142 pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 {
143 return .{ .p = p.p.add(q.p) };
144 }
145
146 /// Multiply a Ristretto255 element with a scalar.
147 /// Return error.WeakPublicKey if the resulting element is
148 /// the identity element.
149 pub inline fn mul(p: Ristretto255, s: [32]u8) !Ristretto255 {
150 return Ristretto255{ .p = try p.p.mul(s) };
151 }
152
153 /// Return true if two Ristretto255 elements are equivalent
154 pub fn equivalent(p: Ristretto255, q: Ristretto255) bool {
155 const p_ = &p.p;
156 const q_ = &q.p;
157 const a = p_.x.mul(q_.y).equivalent(p_.y.mul(q_.x));
158 const b = p_.y.mul(q_.y).equivalent(p_.x.mul(q_.x));
159 return (@boolToInt(a) | @boolToInt(b)) != 0;
160 }
161};
162
163test "ristretto255" {
164 const p = Ristretto255.basePoint;
165 var buf: [256]u8 = undefined;
166 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
167
168 var r: [32]u8 = undefined;
169 try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
170 var q = try Ristretto255.fromBytes(r);
171 q = q.dbl().add(p);
172 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
173
174 const s = [_]u8{15} ++ [_]u8{0} ** 31;
175 const w = try p.mul(s);
176 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E");
177
178 std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p)));
179
180 const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32;
181 const ph = Ristretto255.fromUniform(h);
182 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ph.toBytes()}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19");
183}
lib/std/crypto/25519/scalar.zig created+177
......@@ -0,0 +1,177 @@
1const std = @import("std");
2const mem = std.mem;
3
4const field_size = [32]u8{
5 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 2^252+27742317777372353535851937790883648493
6};
7
8const ScalarExpanded = struct {
9 limbs: [64]i64 = [_]i64{0} ** 64,
10
11 fn fromBytes(s: [32]u8) ScalarExpanded {
12 var limbs: [64]i64 = undefined;
13 for (s) |x, idx| {
14 limbs[idx] = @as(i64, x);
15 }
16 mem.set(i64, limbs[32..], 0);
17 return .{ .limbs = limbs };
18 }
19
20 fn fromBytes64(s: [64]u8) ScalarExpanded {
21 var limbs: [64]i64 = undefined;
22 for (s) |x, idx| {
23 limbs[idx] = @as(i64, x);
24 }
25 return .{ .limbs = limbs };
26 }
27
28 fn reduce(e: *ScalarExpanded) void {
29 const limbs = &e.limbs;
30 var carry: i64 = undefined;
31 var i: usize = 63;
32 while (i >= 32) : (i -= 1) {
33 carry = 0;
34 const k = i - 12;
35 const xi = limbs[i];
36 var j = i - 32;
37 while (j < k) : (j += 1) {
38 const xj = limbs[j] + carry - 16 * xi * @as(i64, field_size[j - (i - 32)]);
39 carry = (xj + 128) >> 8;
40 limbs[j] = xj - carry * 256;
41 }
42 limbs[k] += carry;
43 limbs[i] = 0;
44 }
45 carry = 0;
46 comptime var j: usize = 0;
47 inline while (j < 32) : (j += 1) {
48 const xi = limbs[j] + carry - (limbs[31] >> 4) * @as(i64, field_size[j]);
49 carry = xi >> 8;
50 limbs[j] = xi & 255;
51 }
52 j = 0;
53 inline while (j < 32) : (j += 1) {
54 limbs[j] -= carry * @as(i64, field_size[j]);
55 }
56 j = 0;
57 inline while (j < 32) : (j += 1) {
58 limbs[j + 1] += limbs[j] >> 8;
59 }
60 }
61
62 fn toBytes(e: *ScalarExpanded) [32]u8 {
63 e.reduce();
64 var r: [32]u8 = undefined;
65 var i: usize = 0;
66 while (i < 32) : (i += 1) {
67 r[i] = @intCast(u8, e.limbs[i]);
68 }
69 return r;
70 }
71
72 fn add(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
73 var r = ScalarExpanded{};
74 comptime var i = 0;
75 inline while (i < 64) : (i += 1) {
76 r.limbs[i] = a.limbs[i] + b.limbs[i];
77 }
78 return r;
79 }
80
81 fn mul(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
82 var r = ScalarExpanded{};
83 var i: usize = 0;
84 while (i < 32) : (i += 1) {
85 const ai = a.limbs[i];
86 comptime var j = 0;
87 inline while (j < 32) : (j += 1) {
88 r.limbs[i + j] += ai * b.limbs[j];
89 }
90 }
91 r.reduce();
92 return r;
93 }
94
95 fn sq(a: ScalarExpanded) ScalarExpanded {
96 return a.mul(a);
97 }
98
99 fn mulAdd(a: ScalarExpanded, b: ScalarExpanded, c: ScalarExpanded) ScalarExpanded {
100 var r: ScalarExpanded = .{ .limbs = c.limbs };
101 var i: usize = 0;
102 while (i < 32) : (i += 1) {
103 const ai = a.limbs[i];
104 comptime var j = 0;
105 inline while (j < 32) : (j += 1) {
106 r.limbs[i + j] += ai * b.limbs[j];
107 }
108 }
109 r.reduce();
110 return r;
111 }
112};
113
114/// Reject a scalar whose encoding is not canonical.
115pub fn rejectNonCanonical(s: [32]u8) !void {
116 var c: u8 = 0;
117 var n: u8 = 1;
118 var i: usize = 31;
119 while (true) : (i -= 1) {
120 const xs = @as(u16, s[i]);
121 const xfield_size = @as(u16, field_size[i]);
122 c |= @intCast(u8, ((xs -% xfield_size) >> 8) & n);
123 n &= @intCast(u8, ((xs ^ xfield_size) -% 1) >> 8);
124 if (i == 0) break;
125 }
126 if (c == 0) {
127 return error.NonCanonical;
128 }
129}
130
131/// Reduce a scalar to the field size.
132pub fn reduce(s: [32]u8) [32]u8 {
133 return ScalarExpanded.fromBytes(s).toBytes();
134}
135
136/// Reduce a 64-bytes scalar to the field size.
137pub fn reduce64(s: [64]u8) [32]u8 {
138 return ScalarExpanded.fromBytes64(s).toBytes();
139}
140
141/// Perform the X25519 "clamping" operation.
142/// The scalar is then guaranteed to be a multiple of the cofactor.
143pub inline fn clamp(s: *[32]u8) void {
144 s[0] &= 248;
145 s[31] = (s[31] & 127) | 64;
146}
147
148/// Return a*b+c (mod L)
149pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 {
150 return ScalarExpanded.fromBytes(a).mulAdd(ScalarExpanded.fromBytes(b), ScalarExpanded.fromBytes(c)).toBytes();
151}
152
153test "scalar25519" {
154 const bytes: [32]u8 = .{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 255 };
155 var x = ScalarExpanded.fromBytes(bytes);
156 var y = x.toBytes();
157 try rejectNonCanonical(y);
158 var buf: [128]u8 = undefined;
159 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F");
160
161 const reduced = reduce(field_size);
162 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{reduced}), "0000000000000000000000000000000000000000000000000000000000000000");
163}
164
165test "non-canonical scalar25519" {
166 const too_targe: [32]u8 = .{ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 };
167 std.testing.expectError(error.NonCanonical, rejectNonCanonical(too_targe));
168}
169
170test "mulAdd overflow check" {
171 const a: [32]u8 = [_]u8{0xff} ** 32;
172 const b: [32]u8 = [_]u8{0xff} ** 32;
173 const c: [32]u8 = [_]u8{0xff} ** 32;
174 const x = mulAdd(a, b, c);
175 var buf: [128]u8 = undefined;
176 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903");
177}
lib/std/crypto/25519/x25519.zig created+146
......@@ -0,0 +1,146 @@
1const std = @import("std");
2const mem = std.mem;
3const fmt = std.fmt;
4
5/// X25519 DH function.
6pub const X25519 = struct {
7 /// The underlying elliptic curve.
8 pub const Curve = @import("curve25519.zig").Curve25519;
9 /// Length (in bytes) of a secret key.
10 pub const secret_length = 32;
11 /// Length (in bytes) of the output of the DH function.
12 pub const minimum_key_length = 32;
13
14 /// Compute the public key for a given private key.
15 pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool {
16 std.debug.assert(private_key.len >= minimum_key_length);
17 std.debug.assert(public_key.len >= minimum_key_length);
18 var s: [32]u8 = undefined;
19 mem.copy(u8, &s, private_key[0..32]);
20 if (Curve.basePoint.clampedMul(s)) |q| {
21 mem.copy(u8, public_key, q.toBytes()[0..]);
22 return true;
23 } else |_| {
24 return false;
25 }
26 }
27
28 /// Compute the scalar product of a public key and a secret scalar.
29 /// Note that the output should not be used as a shared secret without
30 /// hashing it first.
31 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
32 std.debug.assert(out.len >= secret_length);
33 std.debug.assert(private_key.len >= minimum_key_length);
34 std.debug.assert(public_key.len >= minimum_key_length);
35 var s: [32]u8 = undefined;
36 var b: [32]u8 = undefined;
37 mem.copy(u8, &s, private_key[0..32]);
38 mem.copy(u8, &b, public_key[0..32]);
39 if (Curve.fromBytes(b).clampedMul(s)) |q| {
40 mem.copy(u8, out, q.toBytes()[0..]);
41 return true;
42 } else |_| {
43 return false;
44 }
45 }
46};
47
48test "x25519 public key calculation from secret key" {
49 var sk: [32]u8 = undefined;
50 var pk_expected: [32]u8 = undefined;
51 var pk_calculated: [32]u8 = undefined;
52 try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
53 try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
54 std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk));
55 std.testing.expectEqual(pk_calculated, pk_expected);
56}
57
58test "x25519 rfc7748 vector1" {
59 const secret_key = [32]u8{ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4 };
60 const public_key = [32]u8{ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c };
61
62 const expected_output = [32]u8{ 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 };
63
64 var output: [32]u8 = undefined;
65
66 std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..]));
67 std.testing.expectEqual(output, expected_output);
68}
69
70test "x25519 rfc7748 vector2" {
71 const secret_key = [32]u8{ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d };
72 const public_key = [32]u8{ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93 };
73
74 const expected_output = [32]u8{ 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57 };
75
76 var output: [32]u8 = undefined;
77
78 std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..]));
79 std.testing.expectEqual(output, expected_output);
80}
81
82test "x25519 rfc7748 one iteration" {
83 const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
84 const expected_output = [32]u8{ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79 };
85
86 var k: [32]u8 = initial_value;
87 var u: [32]u8 = initial_value;
88
89 var i: usize = 0;
90 while (i < 1) : (i += 1) {
91 var output: [32]u8 = undefined;
92 std.testing.expect(X25519.create(output[0..], &k, &u));
93
94 mem.copy(u8, u[0..], k[0..]);
95 mem.copy(u8, k[0..], output[0..]);
96 }
97
98 std.testing.expectEqual(k, expected_output);
99}
100
101test "x25519 rfc7748 1,000 iterations" {
102 // These iteration tests are slow so we always skip them. Results have been verified.
103 if (true) {
104 return error.SkipZigTest;
105 }
106
107 const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
108 const expected_output = [32]u8{ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51 };
109
110 var k: [32]u8 = initial_value.*;
111 var u: [32]u8 = initial_value.*;
112
113 var i: usize = 0;
114 while (i < 1000) : (i += 1) {
115 var output: [32]u8 = undefined;
116 std.testing.expect(X25519.create(output[0..], &k, &u));
117
118 mem.copy(u8, u[0..], k[0..]);
119 mem.copy(u8, k[0..], output[0..]);
120 }
121
122 std.testing.expectEqual(k, expected_output);
123}
124
125test "x25519 rfc7748 1,000,000 iterations" {
126 if (true) {
127 return error.SkipZigTest;
128 }
129
130 const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
131 const expected_output = [32]u8{ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24 };
132
133 var k: [32]u8 = initial_value.*;
134 var u: [32]u8 = initial_value.*;
135
136 var i: usize = 0;
137 while (i < 1000000) : (i += 1) {
138 var output: [32]u8 = undefined;
139 std.testing.expect(X25519.create(output[0..], &k, &u));
140
141 mem.copy(u8, u[0..], k[0..]);
142 mem.copy(u8, k[0..], output[0..]);
143 }
144
145 std.testing.expectEqual(k[0..], expected_output);
146}
lib/std/crypto/benchmark.zig+31-1
......@@ -90,7 +90,6 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
9090 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
9191 prng.random.bytes(out[0..]);
9292
93 var offset: usize = 0;
9493 var timer = try Timer.start();
9594 const start = timer.lap();
9695 {
......@@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
107106 return throughput;
108107}
109108
109const signatures = [_]Crypto{Crypto{ .ty = crypto.Ed25519, .name = "ed25519" }};
110
111pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
112 var seed: [Signature.seed_length]u8 = undefined;
113 prng.random.bytes(seed[0..]);
114 const msg = [_]u8{0} ** 64;
115 const key_pair = try Signature.createKeyPair(seed);
116
117 var timer = try Timer.start();
118 const start = timer.lap();
119 {
120 var i: usize = 0;
121 while (i < signatures_count) : (i += 1) {
122 _ = try Signature.sign(&msg, key_pair, null);
123 }
124 }
125 const end = timer.read();
126
127 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
128 const throughput = @floatToInt(u64, signatures_count / elapsed_s);
129
130 return throughput;
131}
132
110133fn usage() void {
111134 std.debug.warn(
112135 \\throughput_test [options]
......@@ -183,4 +206,11 @@ pub fn main() !void {
183206 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
184207 }
185208 }
209
210 inline for (signatures) |E| {
211 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
212 const throughput = try benchmarkSignatures(E.ty, mode(1000));
213 try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });
214 }
215 }
186216}
lib/std/crypto/chacha20.zig+225-57
......@@ -25,12 +25,24 @@ fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
2525 };
2626}
2727
28// The chacha family of ciphers are based on the salsa family.
29fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
30 assert(out.len >= 64);
28fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
29 var ctx: [16]u32 = undefined;
30 const c = "expand 32-byte k";
31 const constant_le = comptime [_]u32{
32 mem.readIntLittle(u32, c[0..4]),
33 mem.readIntLittle(u32, c[4..8]),
34 mem.readIntLittle(u32, c[8..12]),
35 mem.readIntLittle(u32, c[12..16]),
36 };
37 mem.copy(u32, ctx[0..], constant_le[0..4]);
38 mem.copy(u32, ctx[4..12], key[0..8]);
39 mem.copy(u32, ctx[12..16], d[0..4]);
3140
32 var x: [16]u32 = undefined;
41 return ctx;
42}
3343
44// The chacha family of ciphers are based on the salsa family.
45fn chacha20Core(x: []u32, input: [16]u32) void {
3446 for (x) |_, i|
3547 x[i] = input[i];
3648
......@@ -59,33 +71,27 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
5971 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
6072 }
6173 }
74}
6275
76fn hashToBytes(out: []u8, x: [16]u32) void {
6377 for (x) |_, i| {
64 mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]);
78 mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i]);
6579 }
6680}
6781
6882fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
69 var ctx: [16]u32 = undefined;
83 var ctx = initContext(key, counter);
7084 var remaining: usize = if (in.len > out.len) in.len else out.len;
7185 var cursor: usize = 0;
7286
73 const c = "expand 32-byte k";
74 const constant_le = [_]u32{
75 mem.readIntLittle(u32, c[0..4]),
76 mem.readIntLittle(u32, c[4..8]),
77 mem.readIntLittle(u32, c[8..12]),
78 mem.readIntLittle(u32, c[12..16]),
79 };
80
81 mem.copy(u32, ctx[0..], constant_le[0..4]);
82 mem.copy(u32, ctx[4..12], key[0..8]);
83 mem.copy(u32, ctx[12..16], counter[0..4]);
84
8587 while (true) {
88 var x: [16]u32 = undefined;
8689 var buf: [64]u8 = undefined;
87 salsa20_wordtobyte(buf[0..], ctx);
88
90 chacha20Core(x[0..], ctx);
91 for (x) |_, i| {
92 x[i] +%= ctx[i];
93 }
94 hashToBytes(buf[0..], x);
8995 if (remaining < 64) {
9096 var i: usize = 0;
9197 while (i < remaining) : (i += 1)
......@@ -104,6 +110,20 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo
104110 }
105111}
106112
113fn keyToWords(key: [32]u8) [8]u32 {
114 var k: [8]u32 = undefined;
115 k[0] = mem.readIntLittle(u32, key[0..4]);
116 k[1] = mem.readIntLittle(u32, key[4..8]);
117 k[2] = mem.readIntLittle(u32, key[8..12]);
118 k[3] = mem.readIntLittle(u32, key[12..16]);
119 k[4] = mem.readIntLittle(u32, key[16..20]);
120 k[5] = mem.readIntLittle(u32, key[20..24]);
121 k[6] = mem.readIntLittle(u32, key[24..28]);
122 k[7] = mem.readIntLittle(u32, key[28..32]);
123
124 return k;
125}
126
107127/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
108128/// on secret key data.
109129///
......@@ -116,23 +136,12 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce:
116136 assert(in.len >= out.len);
117137 assert((in.len >> 6) + counter <= maxInt(u32));
118138
119 var k: [8]u32 = undefined;
120139 var c: [4]u32 = undefined;
121
122 k[0] = mem.readIntLittle(u32, key[0..4]);
123 k[1] = mem.readIntLittle(u32, key[4..8]);
124 k[2] = mem.readIntLittle(u32, key[8..12]);
125 k[3] = mem.readIntLittle(u32, key[12..16]);
126 k[4] = mem.readIntLittle(u32, key[16..20]);
127 k[5] = mem.readIntLittle(u32, key[20..24]);
128 k[6] = mem.readIntLittle(u32, key[24..28]);
129 k[7] = mem.readIntLittle(u32, key[28..32]);
130
131140 c[0] = counter;
132141 c[1] = mem.readIntLittle(u32, nonce[0..4]);
133142 c[2] = mem.readIntLittle(u32, nonce[4..8]);
134143 c[3] = mem.readIntLittle(u32, nonce[8..12]);
135 chaCha20_internal(out, in, k, c);
144 chaCha20_internal(out, in, keyToWords(key), c);
136145}
137146
138147/// This is the original ChaCha20 before RFC 7539, which recommends using the
......@@ -143,18 +152,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
143152 assert(counter +% (in.len >> 6) >= counter);
144153
145154 var cursor: usize = 0;
146 var k: [8]u32 = undefined;
155 const k = keyToWords(key);
147156 var c: [4]u32 = undefined;
148
149 k[0] = mem.readIntLittle(u32, key[0..4]);
150 k[1] = mem.readIntLittle(u32, key[4..8]);
151 k[2] = mem.readIntLittle(u32, key[8..12]);
152 k[3] = mem.readIntLittle(u32, key[12..16]);
153 k[4] = mem.readIntLittle(u32, key[16..20]);
154 k[5] = mem.readIntLittle(u32, key[20..24]);
155 k[6] = mem.readIntLittle(u32, key[24..28]);
156 k[7] = mem.readIntLittle(u32, key[28..32]);
157
158157 c[0] = @truncate(u32, counter);
159158 c[1] = @truncate(u32, counter >> 32);
160159 c[2] = mem.readIntLittle(u32, nonce[0..4]);
......@@ -437,15 +436,15 @@ test "crypto.chacha20 test vector 5" {
437436
438437pub const chacha20poly1305_tag_size = 16;
439438
440pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
441 assert(dst.len >= plaintext.len + chacha20poly1305_tag_size);
439pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
440 assert(ciphertext.len >= plaintext.len);
442441
443442 // derive poly1305 key
444443 var polyKey = [_]u8{0} ** 32;
445444 chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce);
446445
447446 // encrypt plaintext
448 chaCha20IETF(dst[0..plaintext.len], plaintext, 1, key, nonce);
447 chaCha20IETF(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
449448
450449 // construct mac
451450 var mac = Poly1305.init(polyKey[0..]);
......@@ -455,7 +454,7 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,
455454 const padding = 16 - (data.len % 16);
456455 mac.update(zeros[0..padding]);
457456 }
458 mac.update(dst[0..plaintext.len]);
457 mac.update(ciphertext[0..plaintext.len]);
459458 if (plaintext.len % 16 != 0) {
460459 const zeros = [_]u8{0} ** 16;
461460 const padding = 16 - (plaintext.len % 16);
......@@ -465,19 +464,17 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,
465464 mem.writeIntLittle(u64, lens[0..8], data.len);
466465 mem.writeIntLittle(u64, lens[8..16], plaintext.len);
467466 mac.update(lens[0..]);
468 mac.final(dst[plaintext.len..]);
467 mac.final(tag);
469468}
470469
471/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.
472pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
473 if (msgAndTag.len < chacha20poly1305_tag_size) {
474 return error.InvalidMessage;
475 }
470pub fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
471 return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_size], plaintext, data, key, nonce);
472}
476473
474/// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached.
475pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
477476 // split ciphertext and tag
478 assert(dst.len >= msgAndTag.len - chacha20poly1305_tag_size);
479 var ciphertext = msgAndTag[0 .. msgAndTag.len - chacha20poly1305_tag_size];
480 var polyTag = msgAndTag[ciphertext.len..];
477 assert(dst.len >= ciphertext.len);
481478
482479 // derive poly1305 key
483480 var polyKey = [_]u8{0} ** 32;
......@@ -510,7 +507,7 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
510507 // See https://github.com/ziglang/zig/issues/1776
511508 var acc: u8 = 0;
512509 for (computedTag) |_, i| {
513 acc |= (computedTag[i] ^ polyTag[i]);
510 acc |= (computedTag[i] ^ tag[i]);
514511 }
515512 if (acc != 0) {
516513 return error.AuthenticationFailed;
......@@ -520,6 +517,75 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
520517 chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
521518}
522519
520/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.
521pub fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
522 if (ciphertextAndTag.len < chacha20poly1305_tag_size) {
523 return error.InvalidMessage;
524 }
525 const ciphertextLen = ciphertextAndTag.len - chacha20poly1305_tag_size;
526 return try chacha20poly1305OpenDetached(dst, ciphertextAndTag[0..ciphertextLen], ciphertextAndTag[ciphertextLen..][0..chacha20poly1305_tag_size], data, key, nonce);
527}
528
529fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
530 var c: [4]u32 = undefined;
531 for (c) |_, i| {
532 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
533 }
534 const ctx = initContext(keyToWords(key), c);
535 var x: [16]u32 = undefined;
536 chacha20Core(x[0..], ctx);
537 var out: [32]u8 = undefined;
538 mem.writeIntLittle(u32, out[0..4], x[0]);
539 mem.writeIntLittle(u32, out[4..8], x[1]);
540 mem.writeIntLittle(u32, out[8..12], x[2]);
541 mem.writeIntLittle(u32, out[12..16], x[3]);
542 mem.writeIntLittle(u32, out[16..20], x[12]);
543 mem.writeIntLittle(u32, out[20..24], x[13]);
544 mem.writeIntLittle(u32, out[24..28], x[14]);
545 mem.writeIntLittle(u32, out[28..32], x[15]);
546
547 return out;
548}
549
550fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } {
551 var subnonce: [12]u8 = undefined;
552 mem.set(u8, subnonce[0..4], 0);
553 mem.copy(u8, subnonce[4..], nonce[16..24]);
554 return .{
555 .key = hchacha20(nonce[0..16].*, key),
556 .nonce = subnonce,
557 };
558}
559
560pub fn xChaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void {
561 const extended = extend(key, nonce);
562 chaCha20IETF(out, in, counter, extended.key, extended.nonce);
563}
564
565pub const xchacha20poly1305_tag_size = 16;
566
567pub fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
568 const extended = extend(key, nonce);
569 return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce);
570}
571
572pub fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
573 const extended = extend(key, nonce);
574 return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce);
575}
576
577/// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached.
578pub fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
579 const extended = extend(key, nonce);
580 return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce);
581}
582
583/// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal.
584pub fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
585 const extended = extend(key, nonce);
586 return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce);
587}
588
523589test "seal" {
524590 {
525591 const plaintext = "";
......@@ -636,3 +702,105 @@ test "open" {
636702 testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce));
637703 }
638704}
705
706test "crypto.xchacha20" {
707 const key = [_]u8{69} ** 32;
708 const nonce = [_]u8{42} ** 24;
709 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
710 {
711 var ciphertext: [input.len]u8 = undefined;
712 xChaCha20IETF(ciphertext[0..], input[0..], 0, key, nonce);
713 var buf: [2 * ciphertext.len]u8 = undefined;
714 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
715 }
716 {
717 const data = "Additional data";
718 var ciphertext: [input.len + xchacha20poly1305_tag_size]u8 = undefined;
719 xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce);
720 var out: [input.len]u8 = undefined;
721 try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);
722 var buf: [2 * ciphertext.len]u8 = undefined;
723 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
724 testing.expectEqualSlices(u8, out[0..], input);
725 ciphertext[0] += 1;
726 testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce));
727 }
728}
729
730pub const Chacha20Poly1305 = struct {
731 pub const tag_length = 16;
732 pub const nonce_length = 12;
733 pub const key_length = 32;
734
735 /// c: ciphertext: output buffer should be of size m.len
736 /// at: authentication tag: output MAC
737 /// m: message
738 /// ad: Associated Data
739 /// npub: public nonce
740 /// k: private key
741 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
742 assert(c.len == m.len);
743 return chacha20poly1305SealDetached(c, at, m, ad, k, npub);
744 }
745
746 /// m: message: output buffer should be of size c.len
747 /// c: ciphertext
748 /// at: authentication tag
749 /// ad: Associated Data
750 /// npub: public nonce
751 /// k: private key
752 /// NOTE: the check of the authentication tag is currently not done in constant time
753 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
754 assert(c.len == m.len);
755 return try chacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
756 }
757};
758
759pub const XChacha20Poly1305 = struct {
760 pub const tag_length = 16;
761 pub const nonce_length = 24;
762 pub const key_length = 32;
763
764 /// c: ciphertext: output buffer should be of size m.len
765 /// at: authentication tag: output MAC
766 /// m: message
767 /// ad: Associated Data
768 /// npub: public nonce
769 /// k: private key
770 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
771 assert(c.len == m.len);
772 return xchacha20poly1305SealDetached(c, at, m, ad, k, npub);
773 }
774
775 /// m: message: output buffer should be of size c.len
776 /// c: ciphertext
777 /// at: authentication tag
778 /// ad: Associated Data
779 /// npub: public nonce
780 /// k: private key
781 /// NOTE: the check of the authentication tag is currently not done in constant time
782 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
783 assert(c.len == m.len);
784 return try xchacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
785 }
786};
787
788test "chacha20 AEAD API" {
789 const aeads = [_]type{ Chacha20Poly1305, XChacha20Poly1305 };
790 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
791 const data = "Additional data";
792
793 inline for (aeads) |aead| {
794 const key = [_]u8{69} ** aead.key_length;
795 const nonce = [_]u8{42} ** aead.nonce_length;
796 var ciphertext: [input.len]u8 = undefined;
797 var tag: [aead.tag_length]u8 = undefined;
798 var out: [input.len]u8 = undefined;
799
800 aead.encrypt(ciphertext[0..], tag[0..], input, data, nonce, key);
801 try aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key);
802 testing.expectEqualSlices(u8, out[0..], input);
803 ciphertext[0] += 1;
804 testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key));
805 }
806}
lib/std/crypto/gimli.zig+1-1
......@@ -269,7 +269,7 @@ pub const Aead = struct {
269269 /// npub: public nonce
270270 /// k: private key
271271 /// NOTE: the check of the authentication tag is currently not done in constant time
272 pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []u8, npub: [16]u8, k: [32]u8) !void {
272 pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {
273273 assert(c.len == m.len);
274274
275275 var state = Aead.init(ad, npub, k);
lib/std/crypto/x25519.zig deleted-675
......@@ -1,675 +0,0 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../std.zig");
6const builtin = @import("builtin");
7const fmt = std.fmt;
8
9const Endian = builtin.Endian;
10const readIntLittle = std.mem.readIntLittle;
11const writeIntLittle = std.mem.writeIntLittle;
12
13// Based on Supercop's ref10 implementation.
14pub const X25519 = struct {
15 pub const secret_length = 32;
16 pub const minimum_key_length = 32;
17
18 fn trimScalar(s: []u8) void {
19 s[0] &= 248;
20 s[31] &= 127;
21 s[31] |= 64;
22 }
23
24 fn scalarBit(s: []const u8, i: usize) i32 {
25 return (s[i >> 3] >> @intCast(u3, i & 7)) & 1;
26 }
27
28 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
29 std.debug.assert(out.len >= secret_length);
30 std.debug.assert(private_key.len >= minimum_key_length);
31 std.debug.assert(public_key.len >= minimum_key_length);
32
33 var storage: [7]Fe = undefined;
34 var x1 = &storage[0];
35 var x2 = &storage[1];
36 var z2 = &storage[2];
37 var x3 = &storage[3];
38 var z3 = &storage[4];
39 var t0 = &storage[5];
40 var t1 = &storage[6];
41
42 // computes the scalar product
43 Fe.fromBytes(x1, public_key);
44
45 // restrict the possible scalar values
46 var e: [32]u8 = undefined;
47 for (e[0..]) |_, i| {
48 e[i] = private_key[i];
49 }
50 trimScalar(e[0..]);
51
52 // computes the actual scalar product (the result is in x2 and z2)
53
54 // Montgomery ladder
55 // In projective coordinates, to avoid divisions: x = X / Z
56 // We don't care about the y coordinate, it's only 1 bit of information
57 Fe.init1(x2);
58 Fe.init0(z2); // "zero" point
59 Fe.copy(x3, x1);
60 Fe.init1(z3);
61
62 var swap: i32 = 0;
63 var pos: isize = 254;
64 while (pos >= 0) : (pos -= 1) {
65 // constant time conditional swap before ladder step
66 const b = scalarBit(&e, @intCast(usize, pos));
67 swap ^= b; // xor trick avoids swapping at the end of the loop
68 Fe.cswap(x2, x3, swap);
69 Fe.cswap(z2, z3, swap);
70 swap = b; // anticipates one last swap after the loop
71
72 // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
73 // with differential addition
74 Fe.sub(t0, x3, z3);
75 Fe.sub(t1, x2, z2);
76 Fe.add(x2, x2, z2);
77 Fe.add(z2, x3, z3);
78 Fe.mul(z3, t0, x2);
79 Fe.mul(z2, z2, t1);
80 Fe.sq(t0, t1);
81 Fe.sq(t1, x2);
82 Fe.add(x3, z3, z2);
83 Fe.sub(z2, z3, z2);
84 Fe.mul(x2, t1, t0);
85 Fe.sub(t1, t1, t0);
86 Fe.sq(z2, z2);
87 Fe.mulSmall(z3, t1, 121666);
88 Fe.sq(x3, x3);
89 Fe.add(t0, t0, z3);
90 Fe.mul(z3, x1, z2);
91 Fe.mul(z2, t1, t0);
92 }
93
94 // last swap is necessary to compensate for the xor trick
95 // Note: after this swap, P3 == P2 + P1.
96 Fe.cswap(x2, x3, swap);
97 Fe.cswap(z2, z3, swap);
98
99 // normalises the coordinates: x == X / Z
100 Fe.invert(z2, z2);
101 Fe.mul(x2, x2, z2);
102 Fe.toBytes(out, x2);
103
104 x1.secureZero();
105 x2.secureZero();
106 x3.secureZero();
107 t0.secureZero();
108 t1.secureZero();
109 z2.secureZero();
110 z3.secureZero();
111 std.mem.secureZero(u8, e[0..]);
112
113 // Returns false if the output is all zero
114 // (happens with some malicious public keys)
115 return !zerocmp(u8, out);
116 }
117
118 pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool {
119 var base_point = [_]u8{9} ++ [_]u8{0} ** 31;
120 return create(public_key, private_key, &base_point);
121 }
122};
123
124// Constant time compare to zero.
125fn zerocmp(comptime T: type, a: []const T) bool {
126 var s: T = 0;
127 for (a) |b| {
128 s |= b;
129 }
130 return s == 0;
131}
132
133////////////////////////////////////
134/// Arithmetic modulo 2^255 - 19 ///
135////////////////////////////////////
136// Taken from Supercop's ref10 implementation.
137// A bit bigger than TweetNaCl, over 4 times faster.
138
139// field element
140const Fe = struct {
141 b: [10]i32,
142
143 fn secureZero(self: *Fe) void {
144 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]);
145 }
146
147 fn init0(h: *Fe) void {
148 for (h.b) |*e| {
149 e.* = 0;
150 }
151 }
152
153 fn init1(h: *Fe) void {
154 for (h.b[1..]) |*e| {
155 e.* = 0;
156 }
157 h.b[0] = 1;
158 }
159
160 fn copy(h: *Fe, f: *const Fe) void {
161 for (h.b) |_, i| {
162 h.b[i] = f.b[i];
163 }
164 }
165
166 fn neg(h: *Fe, f: *const Fe) void {
167 for (h.b) |_, i| {
168 h.b[i] = -f.b[i];
169 }
170 }
171
172 fn add(h: *Fe, f: *const Fe, g: *const Fe) void {
173 for (h.b) |_, i| {
174 h.b[i] = f.b[i] + g.b[i];
175 }
176 }
177
178 fn sub(h: *Fe, f: *const Fe, g: *const Fe) void {
179 for (h.b) |_, i| {
180 h.b[i] = f.b[i] - g.b[i];
181 }
182 }
183
184 fn cswap(f: *Fe, g: *Fe, b: i32) void {
185 for (f.b) |_, i| {
186 const x = (f.b[i] ^ g.b[i]) & -b;
187 f.b[i] ^= x;
188 g.b[i] ^= x;
189 }
190 }
191
192 fn ccopy(f: *Fe, g: *const Fe, b: i32) void {
193 for (f.b) |_, i| {
194 const x = (f.b[i] ^ g.b[i]) & -b;
195 f.b[i] ^= x;
196 }
197 }
198
199 inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void {
200 const j = (i + 1) % 10;
201
202 c[i] = (t[i] + (@as(i64, 1) << shift)) >> (shift + 1);
203 t[j] += c[i] * mult;
204 t[i] -= c[i] * (@as(i64, 1) << (shift + 1));
205 }
206
207 fn carry1(h: *Fe, t: []i64) void {
208 var c: [10]i64 = undefined;
209
210 var sc = c[0..];
211 var st = t[0..];
212
213 carryRound(sc, st, 9, 24, 19);
214 carryRound(sc, st, 1, 24, 1);
215 carryRound(sc, st, 3, 24, 1);
216 carryRound(sc, st, 5, 24, 1);
217 carryRound(sc, st, 7, 24, 1);
218 carryRound(sc, st, 0, 25, 1);
219 carryRound(sc, st, 2, 25, 1);
220 carryRound(sc, st, 4, 25, 1);
221 carryRound(sc, st, 6, 25, 1);
222 carryRound(sc, st, 8, 25, 1);
223
224 for (h.b) |_, i| {
225 h.b[i] = @intCast(i32, t[i]);
226 }
227 }
228
229 fn carry2(h: *Fe, t: []i64) void {
230 var c: [10]i64 = undefined;
231
232 var sc = c[0..];
233 var st = t[0..];
234
235 carryRound(sc, st, 0, 25, 1);
236 carryRound(sc, st, 4, 25, 1);
237 carryRound(sc, st, 1, 24, 1);
238 carryRound(sc, st, 5, 24, 1);
239 carryRound(sc, st, 2, 25, 1);
240 carryRound(sc, st, 6, 25, 1);
241 carryRound(sc, st, 3, 24, 1);
242 carryRound(sc, st, 7, 24, 1);
243 carryRound(sc, st, 4, 25, 1);
244 carryRound(sc, st, 8, 25, 1);
245 carryRound(sc, st, 9, 24, 19);
246 carryRound(sc, st, 0, 25, 1);
247
248 for (h.b) |_, i| {
249 h.b[i] = @intCast(i32, t[i]);
250 }
251 }
252
253 fn fromBytes(h: *Fe, s: []const u8) void {
254 std.debug.assert(s.len >= 32);
255
256 var t: [10]i64 = undefined;
257
258 t[0] = readIntLittle(u32, s[0..4]);
259 t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6;
260 t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5;
261 t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3;
262 t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2;
263 t[5] = readIntLittle(u32, s[16..20]);
264 t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7;
265 t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5;
266 t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4;
267 t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2;
268
269 carry1(h, t[0..]);
270 }
271
272 fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void {
273 var t: [10]i64 = undefined;
274
275 for (t[0..]) |_, i| {
276 t[i] = @as(i64, f.b[i]) * g;
277 }
278
279 carry1(h, t[0..]);
280 }
281
282 fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void {
283 const f = f1.b;
284 const g = g1.b;
285
286 var F: [10]i32 = undefined;
287 var G: [10]i32 = undefined;
288
289 F[1] = f[1] * 2;
290 F[3] = f[3] * 2;
291 F[5] = f[5] * 2;
292 F[7] = f[7] * 2;
293 F[9] = f[9] * 2;
294
295 G[1] = g[1] * 19;
296 G[2] = g[2] * 19;
297 G[3] = g[3] * 19;
298 G[4] = g[4] * 19;
299 G[5] = g[5] * 19;
300 G[6] = g[6] * 19;
301 G[7] = g[7] * 19;
302 G[8] = g[8] * 19;
303 G[9] = g[9] * 19;
304
305 // t's become h
306 var t: [10]i64 = undefined;
307
308 t[0] = f[0] * @as(i64, g[0]) + F[1] * @as(i64, G[9]) + f[2] * @as(i64, G[8]) + F[3] * @as(i64, G[7]) + f[4] * @as(i64, G[6]) + F[5] * @as(i64, G[5]) + f[6] * @as(i64, G[4]) + F[7] * @as(i64, G[3]) + f[8] * @as(i64, G[2]) + F[9] * @as(i64, G[1]);
309 t[1] = f[0] * @as(i64, g[1]) + f[1] * @as(i64, g[0]) + f[2] * @as(i64, G[9]) + f[3] * @as(i64, G[8]) + f[4] * @as(i64, G[7]) + f[5] * @as(i64, G[6]) + f[6] * @as(i64, G[5]) + f[7] * @as(i64, G[4]) + f[8] * @as(i64, G[3]) + f[9] * @as(i64, G[2]);
310 t[2] = f[0] * @as(i64, g[2]) + F[1] * @as(i64, g[1]) + f[2] * @as(i64, g[0]) + F[3] * @as(i64, G[9]) + f[4] * @as(i64, G[8]) + F[5] * @as(i64, G[7]) + f[6] * @as(i64, G[6]) + F[7] * @as(i64, G[5]) + f[8] * @as(i64, G[4]) + F[9] * @as(i64, G[3]);
311 t[3] = f[0] * @as(i64, g[3]) + f[1] * @as(i64, g[2]) + f[2] * @as(i64, g[1]) + f[3] * @as(i64, g[0]) + f[4] * @as(i64, G[9]) + f[5] * @as(i64, G[8]) + f[6] * @as(i64, G[7]) + f[7] * @as(i64, G[6]) + f[8] * @as(i64, G[5]) + f[9] * @as(i64, G[4]);
312 t[4] = f[0] * @as(i64, g[4]) + F[1] * @as(i64, g[3]) + f[2] * @as(i64, g[2]) + F[3] * @as(i64, g[1]) + f[4] * @as(i64, g[0]) + F[5] * @as(i64, G[9]) + f[6] * @as(i64, G[8]) + F[7] * @as(i64, G[7]) + f[8] * @as(i64, G[6]) + F[9] * @as(i64, G[5]);
313 t[5] = f[0] * @as(i64, g[5]) + f[1] * @as(i64, g[4]) + f[2] * @as(i64, g[3]) + f[3] * @as(i64, g[2]) + f[4] * @as(i64, g[1]) + f[5] * @as(i64, g[0]) + f[6] * @as(i64, G[9]) + f[7] * @as(i64, G[8]) + f[8] * @as(i64, G[7]) + f[9] * @as(i64, G[6]);
314 t[6] = f[0] * @as(i64, g[6]) + F[1] * @as(i64, g[5]) + f[2] * @as(i64, g[4]) + F[3] * @as(i64, g[3]) + f[4] * @as(i64, g[2]) + F[5] * @as(i64, g[1]) + f[6] * @as(i64, g[0]) + F[7] * @as(i64, G[9]) + f[8] * @as(i64, G[8]) + F[9] * @as(i64, G[7]);
315 t[7] = f[0] * @as(i64, g[7]) + f[1] * @as(i64, g[6]) + f[2] * @as(i64, g[5]) + f[3] * @as(i64, g[4]) + f[4] * @as(i64, g[3]) + f[5] * @as(i64, g[2]) + f[6] * @as(i64, g[1]) + f[7] * @as(i64, g[0]) + f[8] * @as(i64, G[9]) + f[9] * @as(i64, G[8]);
316 t[8] = f[0] * @as(i64, g[8]) + F[1] * @as(i64, g[7]) + f[2] * @as(i64, g[6]) + F[3] * @as(i64, g[5]) + f[4] * @as(i64, g[4]) + F[5] * @as(i64, g[3]) + f[6] * @as(i64, g[2]) + F[7] * @as(i64, g[1]) + f[8] * @as(i64, g[0]) + F[9] * @as(i64, G[9]);
317 t[9] = f[0] * @as(i64, g[9]) + f[1] * @as(i64, g[8]) + f[2] * @as(i64, g[7]) + f[3] * @as(i64, g[6]) + f[4] * @as(i64, g[5]) + f[5] * @as(i64, g[4]) + f[6] * @as(i64, g[3]) + f[7] * @as(i64, g[2]) + f[8] * @as(i64, g[1]) + f[9] * @as(i64, g[0]);
318
319 carry2(h, t[0..]);
320 }
321
322 // we could use Fe.mul() for this, but this is significantly faster
323 fn sq(h: *Fe, fz: *const Fe) void {
324 const f0 = fz.b[0];
325 const f1 = fz.b[1];
326 const f2 = fz.b[2];
327 const f3 = fz.b[3];
328 const f4 = fz.b[4];
329 const f5 = fz.b[5];
330 const f6 = fz.b[6];
331 const f7 = fz.b[7];
332 const f8 = fz.b[8];
333 const f9 = fz.b[9];
334
335 const f0_2 = f0 * 2;
336 const f1_2 = f1 * 2;
337 const f2_2 = f2 * 2;
338 const f3_2 = f3 * 2;
339 const f4_2 = f4 * 2;
340 const f5_2 = f5 * 2;
341 const f6_2 = f6 * 2;
342 const f7_2 = f7 * 2;
343 const f5_38 = f5 * 38;
344 const f6_19 = f6 * 19;
345 const f7_38 = f7 * 38;
346 const f8_19 = f8 * 19;
347 const f9_38 = f9 * 38;
348
349 var t: [10]i64 = undefined;
350
351 t[0] = f0 * @as(i64, f0) + f1_2 * @as(i64, f9_38) + f2_2 * @as(i64, f8_19) + f3_2 * @as(i64, f7_38) + f4_2 * @as(i64, f6_19) + f5 * @as(i64, f5_38);
352 t[1] = f0_2 * @as(i64, f1) + f2 * @as(i64, f9_38) + f3_2 * @as(i64, f8_19) + f4 * @as(i64, f7_38) + f5_2 * @as(i64, f6_19);
353 t[2] = f0_2 * @as(i64, f2) + f1_2 * @as(i64, f1) + f3_2 * @as(i64, f9_38) + f4_2 * @as(i64, f8_19) + f5_2 * @as(i64, f7_38) + f6 * @as(i64, f6_19);
354 t[3] = f0_2 * @as(i64, f3) + f1_2 * @as(i64, f2) + f4 * @as(i64, f9_38) + f5_2 * @as(i64, f8_19) + f6 * @as(i64, f7_38);
355 t[4] = f0_2 * @as(i64, f4) + f1_2 * @as(i64, f3_2) + f2 * @as(i64, f2) + f5_2 * @as(i64, f9_38) + f6_2 * @as(i64, f8_19) + f7 * @as(i64, f7_38);
356 t[5] = f0_2 * @as(i64, f5) + f1_2 * @as(i64, f4) + f2_2 * @as(i64, f3) + f6 * @as(i64, f9_38) + f7_2 * @as(i64, f8_19);
357 t[6] = f0_2 * @as(i64, f6) + f1_2 * @as(i64, f5_2) + f2_2 * @as(i64, f4) + f3_2 * @as(i64, f3) + f7_2 * @as(i64, f9_38) + f8 * @as(i64, f8_19);
358 t[7] = f0_2 * @as(i64, f7) + f1_2 * @as(i64, f6) + f2_2 * @as(i64, f5) + f3_2 * @as(i64, f4) + f8 * @as(i64, f9_38);
359 t[8] = f0_2 * @as(i64, f8) + f1_2 * @as(i64, f7_2) + f2_2 * @as(i64, f6) + f3_2 * @as(i64, f5_2) + f4 * @as(i64, f4) + f9 * @as(i64, f9_38);
360 t[9] = f0_2 * @as(i64, f9) + f1_2 * @as(i64, f8) + f2_2 * @as(i64, f7) + f3_2 * @as(i64, f6) + f4 * @as(i64, f5_2);
361
362 carry2(h, t[0..]);
363 }
364
365 fn sq2(h: *Fe, f: *const Fe) void {
366 Fe.sq(h, f);
367 Fe.mul_small(h, h, 2);
368 }
369
370 // This could be simplified, but it would be slower
371 fn invert(out: *Fe, z: *const Fe) void {
372 var i: usize = undefined;
373
374 var t: [4]Fe = undefined;
375 var t0 = &t[0];
376 var t1 = &t[1];
377 var t2 = &t[2];
378 var t3 = &t[3];
379
380 Fe.sq(t0, z);
381 Fe.sq(t1, t0);
382 Fe.sq(t1, t1);
383 Fe.mul(t1, z, t1);
384 Fe.mul(t0, t0, t1);
385
386 Fe.sq(t2, t0);
387 Fe.mul(t1, t1, t2);
388
389 Fe.sq(t2, t1);
390 i = 1;
391 while (i < 5) : (i += 1) Fe.sq(t2, t2);
392 Fe.mul(t1, t2, t1);
393
394 Fe.sq(t2, t1);
395 i = 1;
396 while (i < 10) : (i += 1) Fe.sq(t2, t2);
397 Fe.mul(t2, t2, t1);
398
399 Fe.sq(t3, t2);
400 i = 1;
401 while (i < 20) : (i += 1) Fe.sq(t3, t3);
402 Fe.mul(t2, t3, t2);
403
404 Fe.sq(t2, t2);
405 i = 1;
406 while (i < 10) : (i += 1) Fe.sq(t2, t2);
407 Fe.mul(t1, t2, t1);
408
409 Fe.sq(t2, t1);
410 i = 1;
411 while (i < 50) : (i += 1) Fe.sq(t2, t2);
412 Fe.mul(t2, t2, t1);
413
414 Fe.sq(t3, t2);
415 i = 1;
416 while (i < 100) : (i += 1) Fe.sq(t3, t3);
417 Fe.mul(t2, t3, t2);
418
419 Fe.sq(t2, t2);
420 i = 1;
421 while (i < 50) : (i += 1) Fe.sq(t2, t2);
422 Fe.mul(t1, t2, t1);
423
424 Fe.sq(t1, t1);
425 i = 1;
426 while (i < 5) : (i += 1) Fe.sq(t1, t1);
427 Fe.mul(out, t1, t0);
428
429 t0.secureZero();
430 t1.secureZero();
431 t2.secureZero();
432 t3.secureZero();
433 }
434
435 // This could be simplified, but it would be slower
436 fn pow22523(out: *Fe, z: *const Fe) void {
437 var i: usize = undefined;
438
439 var t: [3]Fe = undefined;
440 var t0 = &t[0];
441 var t1 = &t[1];
442 var t2 = &t[2];
443
444 Fe.sq(t0, z);
445 Fe.sq(t1, t0);
446 Fe.sq(t1, t1);
447 Fe.mul(t1, z, t1);
448 Fe.mul(t0, t0, t1);
449
450 Fe.sq(t0, t0);
451 Fe.mul(t0, t1, t0);
452
453 Fe.sq(t1, t0);
454 i = 1;
455 while (i < 5) : (i += 1) Fe.sq(t1, t1);
456 Fe.mul(t0, t1, t0);
457
458 Fe.sq(t1, t0);
459 i = 1;
460 while (i < 10) : (i += 1) Fe.sq(t1, t1);
461 Fe.mul(t1, t1, t0);
462
463 Fe.sq(t2, t1);
464 i = 1;
465 while (i < 20) : (i += 1) Fe.sq(t2, t2);
466 Fe.mul(t1, t2, t1);
467
468 Fe.sq(t1, t1);
469 i = 1;
470 while (i < 10) : (i += 1) Fe.sq(t1, t1);
471 Fe.mul(t0, t1, t0);
472
473 Fe.sq(t1, t0);
474 i = 1;
475 while (i < 50) : (i += 1) Fe.sq(t1, t1);
476 Fe.mul(t1, t1, t0);
477
478 Fe.sq(t2, t1);
479 i = 1;
480 while (i < 100) : (i += 1) Fe.sq(t2, t2);
481 Fe.mul(t1, t2, t1);
482
483 Fe.sq(t1, t1);
484 i = 1;
485 while (i < 50) : (i += 1) Fe.sq(t1, t1);
486 Fe.mul(t0, t1, t0);
487
488 Fe.sq(t0, t0);
489 i = 1;
490 while (i < 2) : (i += 1) Fe.sq(t0, t0);
491 Fe.mul(out, t0, z);
492
493 t0.secureZero();
494 t1.secureZero();
495 t2.secureZero();
496 }
497
498 inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void {
499 c[i] = t[i] >> shift;
500 if (i + 1 < 10) {
501 t[i + 1] += c[i];
502 }
503 t[i] -= c[i] * (@as(i32, 1) << shift);
504 }
505
506 fn toBytes(s: []u8, h: *const Fe) void {
507 std.debug.assert(s.len >= 32);
508
509 var t: [10]i64 = undefined;
510 for (h.b[0..]) |_, i| {
511 t[i] = h.b[i];
512 }
513
514 var q = (19 * t[9] + ((@as(i32, 1) << 24))) >> 25;
515 {
516 var i: usize = 0;
517 while (i < 5) : (i += 1) {
518 q += t[2 * i];
519 q >>= 26;
520 q += t[2 * i + 1];
521 q >>= 25;
522 }
523 }
524 t[0] += 19 * q;
525
526 var c: [10]i64 = undefined;
527
528 var st = t[0..];
529 var sc = c[0..];
530
531 toBytesRound(sc, st, 0, 26);
532 toBytesRound(sc, st, 1, 25);
533 toBytesRound(sc, st, 2, 26);
534 toBytesRound(sc, st, 3, 25);
535 toBytesRound(sc, st, 4, 26);
536 toBytesRound(sc, st, 5, 25);
537 toBytesRound(sc, st, 6, 26);
538 toBytesRound(sc, st, 7, 25);
539 toBytesRound(sc, st, 8, 26);
540 toBytesRound(sc, st, 9, 25);
541
542 var ut: [10]u32 = undefined;
543 for (ut[0..]) |_, i| {
544 ut[i] = @bitCast(u32, @intCast(i32, t[i]));
545 }
546
547 writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
548 writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
549 writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
550 writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
551 writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
552 writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
553 writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
554 writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6));
555
556 std.mem.secureZero(i64, t[0..]);
557 }
558
559 // Parity check. Returns 0 if even, 1 if odd
560 fn isNegative(f: *const Fe) bool {
561 var s: [32]u8 = undefined;
562 Fe.toBytes(s[0..], f);
563 const isneg = s[0] & 1;
564 s.secureZero();
565 return isneg;
566 }
567
568 fn isNonZero(f: *const Fe) bool {
569 var s: [32]u8 = undefined;
570 Fe.toBytes(s[0..], f);
571 const isnonzero = zerocmp(u8, s[0..]);
572 s.secureZero();
573 return isneg;
574 }
575};
576
577test "x25519 public key calculation from secret key" {
578 var sk: [32]u8 = undefined;
579 var pk_expected: [32]u8 = undefined;
580 var pk_calculated: [32]u8 = undefined;
581 try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
582 try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
583 std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk));
584 std.testing.expect(std.mem.eql(u8, &pk_calculated, &pk_expected));
585}
586
587test "x25519 rfc7748 vector1" {
588 const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
589 const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
590
591 const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52";
592
593 var output: [32]u8 = undefined;
594
595 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
596 std.testing.expect(std.mem.eql(u8, &output, expected_output));
597}
598
599test "x25519 rfc7748 vector2" {
600 const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d";
601 const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93";
602
603 const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57";
604
605 var output: [32]u8 = undefined;
606
607 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
608 std.testing.expect(std.mem.eql(u8, &output, expected_output));
609}
610
611test "x25519 rfc7748 one iteration" {
612 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*;
613 const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79";
614
615 var k: [32]u8 = initial_value;
616 var u: [32]u8 = initial_value;
617
618 var i: usize = 0;
619 while (i < 1) : (i += 1) {
620 var output: [32]u8 = undefined;
621 std.testing.expect(X25519.create(output[0..], &k, &u));
622
623 std.mem.copy(u8, u[0..], k[0..]);
624 std.mem.copy(u8, k[0..], output[0..]);
625 }
626
627 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
628}
629
630test "x25519 rfc7748 1,000 iterations" {
631 // These iteration tests are slow so we always skip them. Results have been verified.
632 if (true) {
633 return error.SkipZigTest;
634 }
635
636 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
637 const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51";
638
639 var k: [32]u8 = initial_value.*;
640 var u: [32]u8 = initial_value.*;
641
642 var i: usize = 0;
643 while (i < 1000) : (i += 1) {
644 var output: [32]u8 = undefined;
645 std.testing.expect(X25519.create(output[0..], &k, &u));
646
647 std.mem.copy(u8, u[0..], k[0..]);
648 std.mem.copy(u8, k[0..], output[0..]);
649 }
650
651 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
652}
653
654test "x25519 rfc7748 1,000,000 iterations" {
655 if (true) {
656 return error.SkipZigTest;
657 }
658
659 const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
660 const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24";
661
662 var k: [32]u8 = initial_value.*;
663 var u: [32]u8 = initial_value.*;
664
665 var i: usize = 0;
666 while (i < 1000000) : (i += 1) {
667 var output: [32]u8 = undefined;
668 std.testing.expect(X25519.create(output[0..], &k, &u));
669
670 std.mem.copy(u8, u[0..], k[0..]);
671 std.mem.copy(u8, k[0..], output[0..]);
672 }
673
674 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
675}
lib/std/debug.zig+2-5
......@@ -19,9 +19,6 @@ const windows = std.os.windows;
1919
2020pub const leb = @import("debug/leb128.zig");
2121
22pub const global_allocator = @compileError("Please switch to std.testing.allocator.");
23pub const failing_allocator = @compileError("Please switch to std.testing.failing_allocator.");
24
2522pub const runtime_safety = switch (builtin.mode) {
2623 .Debug, .ReleaseSafe => true,
2724 .ReleaseFast, .ReleaseSmall => false,
......@@ -50,7 +47,7 @@ pub const LineInfo = struct {
5047 }
5148};
5249
53var stderr_mutex = std.Mutex.init();
50var stderr_mutex = std.Mutex{};
5451
5552/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
5653/// "printf debugging".
......@@ -235,7 +232,7 @@ pub fn panic(comptime format: []const u8, args: anytype) noreturn {
235232var panicking: u8 = 0;
236233
237234// Locked to avoid interleaving panic messages from multiple threads.
238var panic_mutex = std.Mutex.init();
235var panic_mutex = std.Mutex{};
239236
240237/// Counts how many times the panic handler is invoked by this thread.
241238/// This is used to catch and handle panics triggered by the panic handler.
lib/std/dwarf.zig+1-1
......@@ -322,7 +322,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: anytype, form_id: u64, e
322322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
323323 FORM_block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
324324 FORM_block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
325 FORM_block => x: {
325 FORM_block => {
326326 const block_len = try nosuspend leb.readULEB128(usize, in_stream);
327327 return parseFormValueBlockLen(allocator, in_stream, block_len);
328328 },
lib/std/dwarf_bits.zig+4-4
......@@ -69,7 +69,7 @@ pub const TAG_lo_user = 0x4080;
6969pub const TAG_hi_user = 0xffff;
7070
7171// SGI/MIPS Extensions.
72pub const DW_TAG_MIPS_loop = 0x4081;
72pub const TAG_MIPS_loop = 0x4081;
7373
7474// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .
7575pub const TAG_HP_array_descriptor = 0x4090;
......@@ -263,9 +263,9 @@ pub const AT_MIPS_has_inlines = 0x200b;
263263
264264// HP extensions.
265265pub const AT_HP_block_index = 0x2000;
266pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.
267pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.
268pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.
266pub const AT_HP_unmodifiable = 0x2001; // Same as AT_MIPS_fde.
267pub const AT_HP_prologue = 0x2005; // Same as AT_MIPS_loop_unroll.
268pub const AT_HP_epilogue = 0x2008; // Same as AT_MIPS_stride.
269269pub const AT_HP_actuals_stmt_list = 0x2010;
270270pub const AT_HP_proc_per_section = 0x2011;
271271pub const AT_HP_raw_data_ptr = 0x2012;
lib/std/fmt.zig+28-4
......@@ -88,8 +88,6 @@ pub fn format(
8888 if (args.len > ArgSetType.bit_count) {
8989 @compileError("32 arguments max are supported per format call");
9090 }
91 if (args.len == 0)
92 return writer.writeAll(fmt);
9391
9492 const State = enum {
9593 Start,
......@@ -562,13 +560,25 @@ fn formatFloatValue(
562560 options: FormatOptions,
563561 writer: anytype,
564562) !void {
563 // this buffer should be enough to display all decimal places of a decimal f64 number.
564 var buf: [512]u8 = undefined;
565 var buf_stream = std.io.fixedBufferStream(&buf);
566
565567 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
566 return formatFloatScientific(value, options, writer);
568 formatFloatScientific(value, options, buf_stream.writer()) catch |err| switch (err) {
569 error.NoSpaceLeft => unreachable,
570 else => |e| return e,
571 };
567572 } else if (comptime std.mem.eql(u8, fmt, "d")) {
568 return formatFloatDecimal(value, options, writer);
573 formatFloatDecimal(value, options, buf_stream.writer()) catch |err| switch (err) {
574 error.NoSpaceLeft => unreachable,
575 else => |e| return e,
576 };
569577 } else {
570578 @compileError("Unknown format string: '" ++ fmt ++ "'");
571579 }
580
581 return formatBuf(buf_stream.getWritten(), options, writer);
572582}
573583
574584pub fn formatText(
......@@ -1793,3 +1803,17 @@ test "padding" {
17931803 try testFmt("==================Filled", "{:=>24}", .{"Filled"});
17941804 try testFmt(" Centered ", "{:^24}", .{"Centered"});
17951805}
1806
1807test "decimal float padding" {
1808 var number: f32 = 3.1415;
1809 try testFmt("left-pad: **3.141\n", "left-pad: {d:*>7.3}\n", .{number});
1810 try testFmt("center-pad: *3.141*\n", "center-pad: {d:*^7.3}\n", .{number});
1811 try testFmt("right-pad: 3.141**\n", "right-pad: {d:*<7.3}\n", .{number});
1812}
1813
1814test "sci float padding" {
1815 var number: f32 = 3.1415;
1816 try testFmt("left-pad: **3.141e+00\n", "left-pad: {e:*>11.3}\n", .{number});
1817 try testFmt("center-pad: *3.141e+00*\n", "center-pad: {e:*^11.3}\n", .{number});
1818 try testFmt("right-pad: 3.141e+00**\n", "right-pad: {e:*<11.3}\n", .{number});
1819}
lib/std/fs.zig+118-1
......@@ -926,6 +926,123 @@ pub const Dir = struct {
926926 return self.openDir(sub_path, open_dir_options);
927927 }
928928
929 /// This function returns the canonicalized absolute pathname of
930 /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
931 /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`
932 /// argument.
933 /// This function is not universally supported by all platforms.
934 /// Currently supported hosts are: Linux, macOS, and Windows.
935 /// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
936 pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
937 if (builtin.os.tag == .wasi) {
938 @compileError("realpath is unsupported in WASI");
939 }
940 if (builtin.os.tag == .windows) {
941 const pathname_w = try os.windows.sliceToPrefixedFileW(pathname);
942 return self.realpathW(pathname_w.span(), out_buffer);
943 }
944 const pathname_c = try os.toPosixPath(pathname);
945 return self.realpathZ(&pathname_c, out_buffer);
946 }
947
948 /// Same as `Dir.realpath` except `pathname` is null-terminated.
949 /// See also `Dir.realpath`, `realpathZ`.
950 pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
951 if (builtin.os.tag == .windows) {
952 const pathname_w = try os.windows.cStrToPrefixedFileW(pathname);
953 return self.realpathW(pathname_w.span(), out_buffer);
954 }
955
956 const flags = if (builtin.os.tag == .linux) os.O_PATH | os.O_NONBLOCK | os.O_CLOEXEC else os.O_NONBLOCK | os.O_CLOEXEC;
957 const fd = os.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
958 error.FileLocksNotSupported => unreachable,
959 else => |e| return e,
960 };
961 defer os.close(fd);
962
963 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
964 // have a variant that takes an arbitrary-size buffer.
965 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
966 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
967 // paths. musl supports passing NULL but restricts the output to PATH_MAX
968 // anyway.
969 var buffer: [MAX_PATH_BYTES]u8 = undefined;
970 const out_path = try os.getFdPath(fd, &buffer);
971
972 if (out_path.len > out_buffer.len) {
973 return error.NameTooLong;
974 }
975
976 mem.copy(u8, out_buffer, out_path);
977
978 return out_buffer[0..out_path.len];
979 }
980
981 /// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.
982 /// See also `Dir.realpath`, `realpathW`.
983 pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {
984 const w = os.windows;
985
986 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
987 const share_access = w.FILE_SHARE_READ;
988 const creation = w.FILE_OPEN;
989 const h_file = blk: {
990 const res = w.OpenFile(pathname, .{
991 .dir = self.fd,
992 .access_mask = access_mask,
993 .share_access = share_access,
994 .creation = creation,
995 .io_mode = .blocking,
996 }) catch |err| switch (err) {
997 error.IsDir => break :blk w.OpenFile(pathname, .{
998 .dir = self.fd,
999 .access_mask = access_mask,
1000 .share_access = share_access,
1001 .creation = creation,
1002 .io_mode = .blocking,
1003 .open_dir = true,
1004 }) catch |er| switch (er) {
1005 error.WouldBlock => unreachable,
1006 else => |e2| return e2,
1007 },
1008 error.WouldBlock => unreachable,
1009 else => |e| return e,
1010 };
1011 break :blk res;
1012 };
1013 defer w.CloseHandle(h_file);
1014
1015 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1016 // have a variant that takes an arbitrary-size buffer.
1017 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1018 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1019 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1020 // anyway.
1021 var buffer: [MAX_PATH_BYTES]u8 = undefined;
1022 const out_path = try os.getFdPath(h_file, &buffer);
1023
1024 if (out_path.len > out_buffer.len) {
1025 return error.NameTooLong;
1026 }
1027
1028 mem.copy(u8, out_buffer, out_path);
1029
1030 return out_buffer[0..out_path.len];
1031 }
1032
1033 /// Same as `Dir.realpath` except caller must free the returned memory.
1034 /// See also `Dir.realpath`.
1035 pub fn realpathAlloc(self: Dir, allocator: *Allocator, pathname: []const u8) ![]u8 {
1036 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1037 // have a variant that takes an arbitrary-size buffer.
1038 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1039 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1040 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1041 // anyway.
1042 var buf: [MAX_PATH_BYTES]u8 = undefined;
1043 return allocator.dupe(u8, try self.realpath(pathname, buf[0..]));
1044 }
1045
9291046 /// Changes the current working directory to the open directory handle.
9301047 /// This modifies global state and can have surprising effects in multi-
9311048 /// threaded applications. Most applications and especially libraries should
......@@ -2060,7 +2177,7 @@ pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
20602177}
20612178
20622179/// `realpath`, except caller must free the returned memory.
2063/// TODO integrate with `Dir`
2180/// See also `Dir.realpath`.
20642181pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
20652182 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
20662183 // have a variant that takes an arbitrary-size buffer.
lib/std/fs/file.zig+2-7
......@@ -607,15 +607,10 @@ pub const File = struct {
607607 }
608608 }
609609
610 pub const CopyRangeError = PWriteError || PReadError;
610 pub const CopyRangeError = os.CopyFileRangeError;
611611
612612 pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {
613 // TODO take advantage of copy_file_range OS APIs
614 var buf: [8 * 4096]u8 = undefined;
615 const adjusted_count = math.min(buf.len, len);
616 const amt_read = try in.pread(buf[0..adjusted_count], in_offset);
617 if (amt_read == 0) return @as(usize, 0);
618 return out.pwrite(buf[0..amt_read], out_offset);
613 return os.copy_file_range(in.handle, in_offset, out.handle, out_offset, len, 0);
619614 }
620615
621616 /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it
lib/std/fs/test.zig+70-14
......@@ -109,17 +109,57 @@ test "Dir.Iterator" {
109109 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
110110}
111111
112fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
112fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
113113 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
114114}
115115
116116fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
117117 for (entries.items) |entry| {
118 if (entry_eql(entry, el)) return true;
118 if (entryEql(entry, el)) return true;
119119 }
120120 return false;
121121}
122122
123test "Dir.realpath smoke test" {
124 switch (builtin.os.tag) {
125 .linux, .windows, .macosx, .ios, .watchos, .tvos => {},
126 else => return error.SkipZigTest,
127 }
128
129 var tmp_dir = tmpDir(.{});
130 defer tmp_dir.cleanup();
131
132 var file = try tmp_dir.dir.createFile("test_file", .{ .lock = File.Lock.Shared });
133 // We need to close the file immediately as otherwise on Windows we'll end up
134 // with a sharing violation.
135 file.close();
136
137 var arena = ArenaAllocator.init(testing.allocator);
138 defer arena.deinit();
139
140 const base_path = blk: {
141 const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..] });
142 break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
143 };
144
145 // First, test non-alloc version
146 {
147 var buf1: [fs.MAX_PATH_BYTES]u8 = undefined;
148 const file_path = try tmp_dir.dir.realpath("test_file", buf1[0..]);
149 const expected_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "test_file" });
150
151 testing.expect(mem.eql(u8, file_path, expected_path));
152 }
153
154 // Next, test alloc version
155 {
156 const file_path = try tmp_dir.dir.realpathAlloc(&arena.allocator, "test_file");
157 const expected_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "test_file" });
158
159 testing.expect(mem.eql(u8, file_path, expected_path));
160 }
161}
162
123163test "readAllAlloc" {
124164 var tmp_dir = tmpDir(.{});
125165 defer tmp_dir.cleanup();
......@@ -167,12 +207,7 @@ test "directory operations on files" {
167207 testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));
168208
169209 if (builtin.os.tag != .wasi) {
170 // TODO: use Dir's realpath function once that exists
171 const absolute_path = blk: {
172 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_file_name });
173 defer testing.allocator.free(relative_path);
174 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
175 };
210 const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_file_name);
176211 defer testing.allocator.free(absolute_path);
177212
178213 testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));
......@@ -206,12 +241,7 @@ test "file operations on directories" {
206241 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));
207242
208243 if (builtin.os.tag != .wasi) {
209 // TODO: use Dir's realpath function once that exists
210 const absolute_path = blk: {
211 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_dir_name });
212 defer testing.allocator.free(relative_path);
213 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
214 };
244 const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_dir_name);
215245 defer testing.allocator.free(absolute_path);
216246
217247 testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));
......@@ -328,6 +358,32 @@ test "sendfile" {
328358 testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
329359}
330360
361test "copyRangeAll" {
362 var tmp = tmpDir(.{});
363 defer tmp.cleanup();
364
365 try tmp.dir.makePath("os_test_tmp");
366 defer tmp.dir.deleteTree("os_test_tmp") catch {};
367
368 var dir = try tmp.dir.openDir("os_test_tmp", .{});
369 defer dir.close();
370
371 var src_file = try dir.createFile("file1.txt", .{ .read = true });
372 defer src_file.close();
373
374 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
375 try src_file.writeAll(data);
376
377 var dest_file = try dir.createFile("file2.txt", .{ .read = true });
378 defer dest_file.close();
379
380 var written_buf: [100]u8 = undefined;
381 _ = try src_file.copyRangeAll(0, dest_file, 0, data.len);
382
383 const amt = try dest_file.preadAll(&written_buf, 0);
384 testing.expect(mem.eql(u8, written_buf[0..amt], data));
385}
386
331387test "fs.copyFile" {
332388 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
333389 const src_file = "tmp_test_copy_file.txt";
lib/std/hash/auto_hash.zig+1-1
......@@ -129,7 +129,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
129129 }
130130 },
131131
132 .Union => |info| blk: {
132 .Union => |info| {
133133 if (info.tag_type) |tag_type| {
134134 const tag = meta.activeTag(key);
135135 const s = hash(hasher, tag, strat);
lib/std/heap.zig+103-37
......@@ -12,6 +12,7 @@ const maxInt = std.math.maxInt;
1212pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
1313pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
1414pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
15pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
1516
1617const Allocator = mem.Allocator;
1718
......@@ -36,7 +37,7 @@ var c_allocator_state = Allocator{
3637 .resizeFn = cResize,
3738};
3839
39fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
40fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Allocator.Error![]u8 {
4041 assert(ptr_align <= @alignOf(c_longdouble));
4142 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);
4243 if (len_align == 0) {
......@@ -53,7 +54,14 @@ fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocato
5354 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
5455}
5556
56fn cResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
57fn cResize(
58 self: *Allocator,
59 buf: []u8,
60 old_align: u29,
61 new_len: usize,
62 len_align: u29,
63 ret_addr: usize,
64) Allocator.Error!usize {
5765 if (new_len == 0) {
5866 c.free(buf.ptr);
5967 return 0;
......@@ -88,8 +96,6 @@ var wasm_page_allocator_state = Allocator{
8896 .resizeFn = WasmPageAllocator.resize,
8997};
9098
91pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator");
92
9399/// Verifies that the adjusted length will still map to the full length
94100pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
95101 const aligned_len = mem.alignAllocLen(full_len, len, len_align);
......@@ -97,10 +103,13 @@ pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
97103 return aligned_len;
98104}
99105
106/// TODO Utilize this on Windows.
107pub var next_mmap_addr_hint: ?[*]align(mem.page_size) u8 = null;
108
100109const PageAllocator = struct {
101 fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
110 fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29, ra: usize) error{OutOfMemory}![]u8 {
102111 assert(n > 0);
103 const alignedLen = mem.alignForward(n, mem.page_size);
112 const aligned_len = mem.alignForward(n, mem.page_size);
104113
105114 if (builtin.os.tag == .windows) {
106115 const w = os.windows;
......@@ -112,14 +121,14 @@ const PageAllocator = struct {
112121 // see https://devblogs.microsoft.com/oldnewthing/?p=42223
113122 const addr = w.VirtualAlloc(
114123 null,
115 alignedLen,
124 aligned_len,
116125 w.MEM_COMMIT | w.MEM_RESERVE,
117126 w.PAGE_READWRITE,
118127 ) catch return error.OutOfMemory;
119128
120129 // If the allocation is sufficiently aligned, use it.
121130 if (@ptrToInt(addr) & (alignment - 1) == 0) {
122 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
131 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(aligned_len, n, len_align)];
123132 }
124133
125134 // If it wasn't, actually do an explicitely aligned allocation.
......@@ -146,20 +155,24 @@ const PageAllocator = struct {
146155 // until it succeeds.
147156 const ptr = w.VirtualAlloc(
148157 @intToPtr(*c_void, aligned_addr),
149 alignedLen,
158 aligned_len,
150159 w.MEM_COMMIT | w.MEM_RESERVE,
151160 w.PAGE_READWRITE,
152161 ) catch continue;
153162
154 return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(alignedLen, n, len_align)];
163 return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(aligned_len, n, len_align)];
155164 }
156165 }
157166
158 const maxDropLen = alignment - std.math.min(alignment, mem.page_size);
159 const allocLen = if (maxDropLen <= alignedLen - n) alignedLen else mem.alignForward(alignedLen + maxDropLen, mem.page_size);
167 const max_drop_len = alignment - std.math.min(alignment, mem.page_size);
168 const alloc_len = if (max_drop_len <= aligned_len - n)
169 aligned_len
170 else
171 mem.alignForward(aligned_len + max_drop_len, mem.page_size);
172 const hint = @atomicLoad(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, .Unordered);
160173 const slice = os.mmap(
161 null,
162 allocLen,
174 hint,
175 alloc_len,
163176 os.PROT_READ | os.PROT_WRITE,
164177 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
165178 -1,
......@@ -168,25 +181,36 @@ const PageAllocator = struct {
168181 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
169182
170183 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);
184 const result_ptr = @alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr));
171185
172186 // Unmap the extra bytes that were only requested in order to guarantee
173187 // that the range of memory we were provided had a proper alignment in
174188 // it somewhere. The extra bytes could be at the beginning, or end, or both.
175 const dropLen = aligned_addr - @ptrToInt(slice.ptr);
176 if (dropLen != 0) {
177 os.munmap(slice[0..dropLen]);
189 const drop_len = aligned_addr - @ptrToInt(slice.ptr);
190 if (drop_len != 0) {
191 os.munmap(slice[0..drop_len]);
178192 }
179193
180194 // Unmap extra pages
181 const alignedBufferLen = allocLen - dropLen;
182 if (alignedBufferLen > alignedLen) {
183 os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]);
195 const aligned_buffer_len = alloc_len - drop_len;
196 if (aligned_buffer_len > aligned_len) {
197 os.munmap(result_ptr[aligned_len..aligned_buffer_len]);
184198 }
185199
186 return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
200 const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len);
201 _ = @cmpxchgStrong(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, hint, new_hint, .Monotonic, .Monotonic);
202
203 return result_ptr[0..alignPageAllocLen(aligned_len, n, len_align)];
187204 }
188205
189 fn resize(allocator: *Allocator, buf_unaligned: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
206 fn resize(
207 allocator: *Allocator,
208 buf_unaligned: []u8,
209 buf_align: u29,
210 new_size: usize,
211 len_align: u29,
212 return_address: usize,
213 ) Allocator.Error!usize {
190214 const new_size_aligned = mem.alignForward(new_size, mem.page_size);
191215
192216 if (builtin.os.tag == .windows) {
......@@ -201,7 +225,7 @@ const PageAllocator = struct {
201225 w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE);
202226 return 0;
203227 }
204 if (new_size < buf_unaligned.len) {
228 if (new_size <= buf_unaligned.len) {
205229 const base_addr = @ptrToInt(buf_unaligned.ptr);
206230 const old_addr_end = base_addr + buf_unaligned.len;
207231 const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size);
......@@ -216,10 +240,10 @@ const PageAllocator = struct {
216240 }
217241 return alignPageAllocLen(new_size_aligned, new_size, len_align);
218242 }
219 if (new_size == buf_unaligned.len) {
243 const old_size_aligned = mem.alignForward(buf_unaligned.len, mem.page_size);
244 if (new_size_aligned <= old_size_aligned) {
220245 return alignPageAllocLen(new_size_aligned, new_size, len_align);
221246 }
222 // new_size > buf_unaligned.len not implemented
223247 return error.OutOfMemory;
224248 }
225249
......@@ -229,6 +253,7 @@ const PageAllocator = struct {
229253
230254 if (new_size_aligned < buf_aligned_len) {
231255 const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned);
256 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it
232257 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
233258 if (new_size_aligned == 0)
234259 return 0;
......@@ -236,6 +261,7 @@ const PageAllocator = struct {
236261 }
237262
238263 // TODO: call mremap
264 // TODO: if the next_mmap_addr_hint is within the remapped range, update it
239265 return error.OutOfMemory;
240266 }
241267};
......@@ -332,7 +358,7 @@ const WasmPageAllocator = struct {
332358 return mem.alignForward(memsize, mem.page_size) / mem.page_size;
333359 }
334360
335 fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
361 fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29, ra: usize) error{OutOfMemory}![]u8 {
336362 const page_count = nPages(len);
337363 const page_idx = try allocPages(page_count, alignment);
338364 return @intToPtr([*]u8, page_idx * mem.page_size)[0..alignPageAllocLen(page_count * mem.page_size, len, len_align)];
......@@ -385,7 +411,14 @@ const WasmPageAllocator = struct {
385411 }
386412 }
387413
388 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
414 fn resize(
415 allocator: *Allocator,
416 buf: []u8,
417 buf_align: u29,
418 new_len: usize,
419 len_align: u29,
420 return_address: usize,
421 ) error{OutOfMemory}!usize {
389422 const aligned_len = mem.alignForward(buf.len, mem.page_size);
390423 if (new_len > aligned_len) return error.OutOfMemory;
391424 const current_n = nPages(aligned_len);
......@@ -425,7 +458,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {
425458 return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len);
426459 }
427460
428 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
461 fn alloc(
462 allocator: *Allocator,
463 n: usize,
464 ptr_align: u29,
465 len_align: u29,
466 return_address: usize,
467 ) error{OutOfMemory}![]u8 {
429468 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
430469
431470 const amt = n + ptr_align - 1 + @sizeOf(usize);
......@@ -452,7 +491,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {
452491 return buf;
453492 }
454493
455 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
494 fn resize(
495 allocator: *Allocator,
496 buf: []u8,
497 buf_align: u29,
498 new_size: usize,
499 len_align: u29,
500 return_address: usize,
501 ) error{OutOfMemory}!usize {
456502 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
457503 if (new_size == 0) {
458504 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void, getRecordPtr(buf).*));
......@@ -524,7 +570,7 @@ pub const FixedBufferAllocator = struct {
524570 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
525571 }
526572
527 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
573 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {
528574 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
529575 const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align);
530576 const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr);
......@@ -538,7 +584,14 @@ pub const FixedBufferAllocator = struct {
538584 return result;
539585 }
540586
541 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
587 fn resize(
588 allocator: *Allocator,
589 buf: []u8,
590 buf_align: u29,
591 new_size: usize,
592 len_align: u29,
593 return_address: usize,
594 ) Allocator.Error!usize {
542595 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
543596 assert(self.ownsSlice(buf)); // sanity check
544597
......@@ -588,7 +641,7 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
588641 };
589642 }
590643
591 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
644 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {
592645 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
593646 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
594647 while (true) {
......@@ -636,18 +689,31 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
636689 return &self.allocator;
637690 }
638691
639 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![*]u8 {
692 fn alloc(
693 allocator: *Allocator,
694 len: usize,
695 ptr_align: u29,
696 len_align: u29,
697 return_address: usize,
698 ) error{OutOfMemory}![*]u8 {
640699 const self = @fieldParentPtr(Self, "allocator", allocator);
641700 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
642701 return fallback_allocator.alloc(len, ptr_align);
643702 }
644703
645 fn resize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!void {
704 fn resize(
705 self: *Allocator,
706 buf: []u8,
707 buf_align: u29,
708 new_len: usize,
709 len_align: u29,
710 return_address: usize,
711 ) error{OutOfMemory}!void {
646712 const self = @fieldParentPtr(Self, "allocator", allocator);
647713 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
648 try self.fixed_buffer_allocator.callResizeFn(buf, new_len);
714 try self.fixed_buffer_allocator.resize(buf, new_len);
649715 } else {
650 try self.fallback_allocator.callResizeFn(buf, new_len);
716 try self.fallback_allocator.resize(buf, new_len);
651717 }
652718 }
653719 };
......@@ -932,7 +998,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.
932998 slice[60] = 0x34;
933999
9341000 // realloc to a smaller size but with a larger alignment
935 slice = try allocator.alignedRealloc(slice, mem.page_size * 32, alloc_size / 2);
1001 slice = try allocator.reallocAdvanced(slice, mem.page_size * 32, alloc_size / 2, .exact);
9361002 testing.expect(slice[0] == 0x12);
9371003 testing.expect(slice[60] == 0x34);
9381004}
lib/std/heap/arena_allocator.zig+2-2
......@@ -49,7 +49,7 @@ pub const ArenaAllocator = struct {
4949 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);
5050 const big_enough_len = prev_len + actual_min_size;
5151 const len = big_enough_len + big_enough_len / 2;
52 const buf = try self.child_allocator.callAllocFn(len, @alignOf(BufNode), 1);
52 const buf = try self.child_allocator.allocFn(self.child_allocator, len, @alignOf(BufNode), 1, @returnAddress());
5353 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));
5454 buf_node.* = BufNode{
5555 .data = buf,
......@@ -60,7 +60,7 @@ pub const ArenaAllocator = struct {
6060 return buf_node;
6161 }
6262
63 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
63 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {
6464 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
6565
6666 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align);
lib/std/heap/general_purpose_allocator.zig created+946
......@@ -0,0 +1,946 @@
1//! # General Purpose Allocator
2//!
3//! ## Design Priorities
4//!
5//! ### `OptimizationMode.debug` and `OptimizationMode.release_safe`:
6//!
7//! * Detect double free, and emit stack trace of:
8//! - Where it was first allocated
9//! - Where it was freed the first time
10//! - Where it was freed the second time
11//!
12//! * Detect leaks and emit stack trace of:
13//! - Where it was allocated
14//!
15//! * When a page of memory is no longer needed, give it back to resident memory
16//! as soon as possible, so that it causes page faults when used.
17//!
18//! * Do not re-use memory slots, so that memory safety is upheld. For small
19//! allocations, this is handled here; for larger ones it is handled in the
20//! backing allocator (by default `std.heap.page_allocator`).
21//!
22//! * Make pointer math errors unlikely to harm memory from
23//! unrelated allocations.
24//!
25//! * It's OK for these mechanisms to cost some extra overhead bytes.
26//!
27//! * It's OK for performance cost for these mechanisms.
28//!
29//! * Rogue memory writes should not harm the allocator's state.
30//!
31//! * Cross platform. Operates based on a backing allocator which makes it work
32//! everywhere, even freestanding.
33//!
34//! * Compile-time configuration.
35//!
36//! ### `OptimizationMode.release_fast` (note: not much work has gone into this use case yet):
37//!
38//! * Low fragmentation is primary concern
39//! * Performance of worst-case latency is secondary concern
40//! * Performance of average-case latency is next
41//! * Finally, having freed memory unmapped, and pointer math errors unlikely to
42//! harm memory from unrelated allocations are nice-to-haves.
43//!
44//! ### `OptimizationMode.release_small` (note: not much work has gone into this use case yet):
45//!
46//! * Small binary code size of the executable is the primary concern.
47//! * Next, defer to the `.release_fast` priority list.
48//!
49//! ## Basic Design:
50//!
51//! Small allocations are divided into buckets:
52//!
53//! ```
54//! index obj_size
55//! 0 1
56//! 1 2
57//! 2 4
58//! 3 8
59//! 4 16
60//! 5 32
61//! 6 64
62//! 7 128
63//! 8 256
64//! 9 512
65//! 10 1024
66//! 11 2048
67//! ```
68//!
69//! The main allocator state has an array of all the "current" buckets for each
70//! size class. Each slot in the array can be null, meaning the bucket for that
71//! size class is not allocated. When the first object is allocated for a given
72//! size class, it allocates 1 page of memory from the OS. This page is
73//! divided into "slots" - one per allocated object. Along with the page of memory
74//! for object slots, as many pages as necessary are allocated to store the
75//! BucketHeader, followed by "used bits", and two stack traces for each slot
76//! (allocation trace and free trace).
77//!
78//! The "used bits" are 1 bit per slot representing whether the slot is used.
79//! Allocations use the data to iterate to find a free slot. Frees assert that the
80//! corresponding bit is 1 and set it to 0.
81//!
82//! Buckets have prev and next pointers. When there is only one bucket for a given
83//! size class, both prev and next point to itself. When all slots of a bucket are
84//! used, a new bucket is allocated, and enters the doubly linked list. The main
85//! allocator state tracks the "current" bucket for each size class. Leak detection
86//! currently only checks the current bucket.
87//!
88//! Resizing detects if the size class is unchanged or smaller, in which case the same
89//! pointer is returned unmodified. If a larger size class is required,
90//! `error.OutOfMemory` is returned.
91//!
92//! Large objects are allocated directly using the backing allocator and their metadata is stored
93//! in a `std.HashMap` using the backing allocator.
94
95const std = @import("std");
96const log = std.log.scoped(.std);
97const math = std.math;
98const assert = std.debug.assert;
99const mem = std.mem;
100const Allocator = std.mem.Allocator;
101const page_size = std.mem.page_size;
102const StackTrace = std.builtin.StackTrace;
103
104/// Integer type for pointing to slots in a small allocation
105const SlotIndex = std.meta.Int(false, math.log2(page_size) + 1);
106
107const sys_can_stack_trace = switch (std.Target.current.cpu.arch) {
108 // Observed to go into an infinite loop.
109 // TODO: Make this work.
110 .mips,
111 .mipsel,
112 => false,
113
114 // `@returnAddress()` in LLVM 10 gives
115 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".
116 .wasm32,
117 .wasm64,
118 => std.Target.current.os.tag == .emscripten,
119
120 else => true,
121};
122const default_test_stack_trace_frames: usize = if (std.builtin.is_test) 8 else 4;
123const default_sys_stack_trace_frames: usize = if (sys_can_stack_trace) default_test_stack_trace_frames else 0;
124const default_stack_trace_frames: usize = switch (std.builtin.mode) {
125 .Debug => default_sys_stack_trace_frames,
126 else => 0,
127};
128
129pub const Config = struct {
130 /// Number of stack frames to capture.
131 stack_trace_frames: usize = default_stack_trace_frames,
132
133 /// If true, the allocator will have two fields:
134 /// * `total_requested_bytes` which tracks the total allocated bytes of memory requested.
135 /// * `requested_memory_limit` which causes allocations to return `error.OutOfMemory`
136 /// when the `total_requested_bytes` exceeds this limit.
137 /// If false, these fields will be `void`.
138 enable_memory_limit: bool = false,
139
140 /// Whether to enable safety checks.
141 safety: bool = std.debug.runtime_safety,
142
143 /// Whether the allocator may be used simultaneously from multiple threads.
144 thread_safe: bool = !std.builtin.single_threaded,
145
146 /// This is a temporary debugging trick you can use to turn segfaults into more helpful
147 /// logged error messages with stack trace details. The downside is that every allocation
148 /// will be leaked!
149 never_unmap: bool = false,
150};
151
152pub fn GeneralPurposeAllocator(comptime config: Config) type {
153 return struct {
154 allocator: Allocator = Allocator{
155 .allocFn = alloc,
156 .resizeFn = resize,
157 },
158 backing_allocator: *Allocator = std.heap.page_allocator,
159 buckets: [small_bucket_count]?*BucketHeader = [1]?*BucketHeader{null} ** small_bucket_count,
160 large_allocations: LargeAllocTable = .{},
161
162 total_requested_bytes: @TypeOf(total_requested_bytes_init) = total_requested_bytes_init,
163 requested_memory_limit: @TypeOf(requested_memory_limit_init) = requested_memory_limit_init,
164
165 mutex: @TypeOf(mutex_init) = mutex_init,
166
167 const Self = @This();
168
169 const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {};
170 const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {};
171
172 const mutex_init = if (config.thread_safe) std.Mutex{} else std.mutex.Dummy{};
173
174 const stack_n = config.stack_trace_frames;
175 const one_trace_size = @sizeOf(usize) * stack_n;
176 const traces_per_slot = 2;
177
178 pub const Error = mem.Allocator.Error;
179
180 const small_bucket_count = math.log2(page_size);
181 const largest_bucket_object_size = 1 << (small_bucket_count - 1);
182
183 const LargeAlloc = struct {
184 bytes: []u8,
185 stack_addresses: [stack_n]usize,
186
187 fn dumpStackTrace(self: *LargeAlloc) void {
188 std.debug.dumpStackTrace(self.getStackTrace());
189 }
190
191 fn getStackTrace(self: *LargeAlloc) std.builtin.StackTrace {
192 var len: usize = 0;
193 while (len < stack_n and self.stack_addresses[len] != 0) {
194 len += 1;
195 }
196 return .{
197 .instruction_addresses = &self.stack_addresses,
198 .index = len,
199 };
200 }
201 };
202 const LargeAllocTable = std.AutoHashMapUnmanaged(usize, LargeAlloc);
203
204 // Bucket: In memory, in order:
205 // * BucketHeader
206 // * bucket_used_bits: [N]u8, // 1 bit for every slot; 1 byte for every 8 slots
207 // * stack_trace_addresses: [N]usize, // traces_per_slot for every allocation
208
209 const BucketHeader = struct {
210 prev: *BucketHeader,
211 next: *BucketHeader,
212 page: [*]align(page_size) u8,
213 alloc_cursor: SlotIndex,
214 used_count: SlotIndex,
215
216 fn usedBits(bucket: *BucketHeader, index: usize) *u8 {
217 return @intToPtr(*u8, @ptrToInt(bucket) + @sizeOf(BucketHeader) + index);
218 }
219
220 fn stackTracePtr(
221 bucket: *BucketHeader,
222 size_class: usize,
223 slot_index: SlotIndex,
224 trace_kind: TraceKind,
225 ) *[stack_n]usize {
226 const start_ptr = @ptrCast([*]u8, bucket) + bucketStackFramesStart(size_class);
227 const addr = start_ptr + one_trace_size * traces_per_slot * slot_index +
228 @enumToInt(trace_kind) * @as(usize, one_trace_size);
229 return @ptrCast(*[stack_n]usize, @alignCast(@alignOf(usize), addr));
230 }
231
232 fn captureStackTrace(
233 bucket: *BucketHeader,
234 ret_addr: usize,
235 size_class: usize,
236 slot_index: SlotIndex,
237 trace_kind: TraceKind,
238 ) void {
239 // Initialize them to 0. When determining the count we must look
240 // for non zero addresses.
241 const stack_addresses = bucket.stackTracePtr(size_class, slot_index, trace_kind);
242 collectStackTrace(ret_addr, stack_addresses);
243 }
244 };
245
246 fn bucketStackTrace(
247 bucket: *BucketHeader,
248 size_class: usize,
249 slot_index: SlotIndex,
250 trace_kind: TraceKind,
251 ) StackTrace {
252 const stack_addresses = bucket.stackTracePtr(size_class, slot_index, trace_kind);
253 var len: usize = 0;
254 while (len < stack_n and stack_addresses[len] != 0) {
255 len += 1;
256 }
257 return StackTrace{
258 .instruction_addresses = stack_addresses,
259 .index = len,
260 };
261 }
262
263 fn bucketStackFramesStart(size_class: usize) usize {
264 return mem.alignForward(
265 @sizeOf(BucketHeader) + usedBitsCount(size_class),
266 @alignOf(usize),
267 );
268 }
269
270 fn bucketSize(size_class: usize) usize {
271 const slot_count = @divExact(page_size, size_class);
272 return bucketStackFramesStart(size_class) + one_trace_size * traces_per_slot * slot_count;
273 }
274
275 fn usedBitsCount(size_class: usize) usize {
276 const slot_count = @divExact(page_size, size_class);
277 if (slot_count < 8) return 1;
278 return @divExact(slot_count, 8);
279 }
280
281 fn detectLeaksInBucket(
282 bucket: *BucketHeader,
283 size_class: usize,
284 used_bits_count: usize,
285 ) bool {
286 var leaks = false;
287 var used_bits_byte: usize = 0;
288 while (used_bits_byte < used_bits_count) : (used_bits_byte += 1) {
289 const used_byte = bucket.usedBits(used_bits_byte).*;
290 if (used_byte != 0) {
291 var bit_index: u3 = 0;
292 while (true) : (bit_index += 1) {
293 const is_used = @truncate(u1, used_byte >> bit_index) != 0;
294 if (is_used) {
295 const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index);
296 const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);
297 log.err("Memory leak detected: {}", .{stack_trace});
298 leaks = true;
299 }
300 if (bit_index == math.maxInt(u3))
301 break;
302 }
303 }
304 }
305 return leaks;
306 }
307
308 /// Emits log messages for leaks and then returns whether there were any leaks.
309 pub fn detectLeaks(self: *Self) bool {
310 var leaks = false;
311 for (self.buckets) |optional_bucket, bucket_i| {
312 const first_bucket = optional_bucket orelse continue;
313 const size_class = @as(usize, 1) << @intCast(math.Log2Int(usize), bucket_i);
314 const used_bits_count = usedBitsCount(size_class);
315 var bucket = first_bucket;
316 while (true) {
317 leaks = detectLeaksInBucket(bucket, size_class, used_bits_count) or leaks;
318 bucket = bucket.next;
319 if (bucket == first_bucket)
320 break;
321 }
322 }
323 for (self.large_allocations.items()) |*large_alloc| {
324 log.err("Memory leak detected: {}", .{large_alloc.value.getStackTrace()});
325 leaks = true;
326 }
327 return leaks;
328 }
329
330 pub fn deinit(self: *Self) bool {
331 const leaks = if (config.safety) self.detectLeaks() else false;
332 self.large_allocations.deinit(self.backing_allocator);
333 self.* = undefined;
334 return leaks;
335 }
336
337 fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {
338 if (stack_n == 0) return;
339 mem.set(usize, addresses, 0);
340 var stack_trace = StackTrace{
341 .instruction_addresses = addresses,
342 .index = 0,
343 };
344 std.debug.captureStackTrace(first_trace_addr, &stack_trace);
345 }
346
347 fn allocSlot(self: *Self, size_class: usize, trace_addr: usize) Error![*]u8 {
348 const bucket_index = math.log2(size_class);
349 const first_bucket = self.buckets[bucket_index] orelse try self.createBucket(
350 size_class,
351 bucket_index,
352 );
353 var bucket = first_bucket;
354 const slot_count = @divExact(page_size, size_class);
355 while (bucket.alloc_cursor == slot_count) {
356 const prev_bucket = bucket;
357 bucket = prev_bucket.next;
358 if (bucket == first_bucket) {
359 // make a new one
360 bucket = try self.createBucket(size_class, bucket_index);
361 bucket.prev = prev_bucket;
362 bucket.next = prev_bucket.next;
363 prev_bucket.next = bucket;
364 bucket.next.prev = bucket;
365 }
366 }
367 // change the allocator's current bucket to be this one
368 self.buckets[bucket_index] = bucket;
369
370 const slot_index = bucket.alloc_cursor;
371 bucket.alloc_cursor += 1;
372
373 var used_bits_byte = bucket.usedBits(slot_index / 8);
374 const used_bit_index: u3 = @intCast(u3, slot_index % 8); // TODO cast should be unnecessary
375 used_bits_byte.* |= (@as(u8, 1) << used_bit_index);
376 bucket.used_count += 1;
377 bucket.captureStackTrace(trace_addr, size_class, slot_index, .alloc);
378 return bucket.page + slot_index * size_class;
379 }
380
381 fn searchBucket(
382 self: *Self,
383 bucket_index: usize,
384 addr: usize,
385 ) ?*BucketHeader {
386 const first_bucket = self.buckets[bucket_index] orelse return null;
387 var bucket = first_bucket;
388 while (true) {
389 const in_bucket_range = (addr >= @ptrToInt(bucket.page) and
390 addr < @ptrToInt(bucket.page) + page_size);
391 if (in_bucket_range) return bucket;
392 bucket = bucket.prev;
393 if (bucket == first_bucket) {
394 return null;
395 }
396 self.buckets[bucket_index] = bucket;
397 }
398 }
399
400 fn freeSlot(
401 self: *Self,
402 bucket: *BucketHeader,
403 bucket_index: usize,
404 size_class: usize,
405 slot_index: SlotIndex,
406 used_byte: *u8,
407 used_bit_index: u3,
408 trace_addr: usize,
409 ) void {
410 // Capture stack trace to be the "first free", in case a double free happens.
411 bucket.captureStackTrace(trace_addr, size_class, slot_index, .free);
412
413 used_byte.* &= ~(@as(u8, 1) << used_bit_index);
414 bucket.used_count -= 1;
415 if (bucket.used_count == 0) {
416 if (bucket.next == bucket) {
417 // it's the only bucket and therefore the current one
418 self.buckets[bucket_index] = null;
419 } else {
420 bucket.next.prev = bucket.prev;
421 bucket.prev.next = bucket.next;
422 self.buckets[bucket_index] = bucket.prev;
423 }
424 if (!config.never_unmap) {
425 self.backing_allocator.free(bucket.page[0..page_size]);
426 }
427 const bucket_size = bucketSize(size_class);
428 const bucket_slice = @ptrCast([*]align(@alignOf(BucketHeader)) u8, bucket)[0..bucket_size];
429 self.backing_allocator.free(bucket_slice);
430 } else {
431 // TODO Set the slot data to undefined.
432 // Related: https://github.com/ziglang/zig/issues/4298
433 }
434 }
435
436 /// This function assumes the object is in the large object storage regardless
437 /// of the parameters.
438 fn resizeLarge(
439 self: *Self,
440 old_mem: []u8,
441 old_align: u29,
442 new_size: usize,
443 len_align: u29,
444 ret_addr: usize,
445 ) Error!usize {
446 const entry = self.large_allocations.getEntry(@ptrToInt(old_mem.ptr)) orelse {
447 if (config.safety) {
448 @panic("Invalid free");
449 } else {
450 unreachable;
451 }
452 };
453
454 if (config.safety and old_mem.len != entry.value.bytes.len) {
455 var addresses: [stack_n]usize = [1]usize{0} ** stack_n;
456 var free_stack_trace = StackTrace{
457 .instruction_addresses = &addresses,
458 .index = 0,
459 };
460 std.debug.captureStackTrace(ret_addr, &free_stack_trace);
461 log.err("Allocation size {} bytes does not match free size {}. Allocation: {} Free: {}", .{
462 entry.value.bytes.len,
463 old_mem.len,
464 entry.value.getStackTrace(),
465 free_stack_trace,
466 });
467 }
468
469 const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr);
470
471 if (result_len == 0) {
472 self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr));
473 return 0;
474 }
475
476 entry.value.bytes = old_mem.ptr[0..result_len];
477 collectStackTrace(ret_addr, &entry.value.stack_addresses);
478 return result_len;
479 }
480
481 pub fn setRequestedMemoryLimit(self: *Self, limit: usize) void {
482 self.requested_memory_limit = limit;
483 }
484
485 fn resize(
486 allocator: *Allocator,
487 old_mem: []u8,
488 old_align: u29,
489 new_size: usize,
490 len_align: u29,
491 ret_addr: usize,
492 ) Error!usize {
493 const self = @fieldParentPtr(Self, "allocator", allocator);
494
495 const held = self.mutex.acquire();
496 defer held.release();
497
498 const prev_req_bytes = self.total_requested_bytes;
499 if (config.enable_memory_limit) {
500 const new_req_bytes = prev_req_bytes + new_size - old_mem.len;
501 if (new_req_bytes > prev_req_bytes and new_req_bytes > self.requested_memory_limit) {
502 return error.OutOfMemory;
503 }
504 self.total_requested_bytes = new_req_bytes;
505 }
506 errdefer if (config.enable_memory_limit) {
507 self.total_requested_bytes = prev_req_bytes;
508 };
509
510 assert(old_mem.len != 0);
511
512 const aligned_size = math.max(old_mem.len, old_align);
513 if (aligned_size > largest_bucket_object_size) {
514 return self.resizeLarge(old_mem, old_align, new_size, len_align, ret_addr);
515 }
516 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);
517
518 var bucket_index = math.log2(size_class_hint);
519 var size_class: usize = size_class_hint;
520 const bucket = while (bucket_index < small_bucket_count) : (bucket_index += 1) {
521 if (self.searchBucket(bucket_index, @ptrToInt(old_mem.ptr))) |bucket| {
522 break bucket;
523 }
524 size_class *= 2;
525 } else {
526 return self.resizeLarge(old_mem, old_align, new_size, len_align, ret_addr);
527 };
528 const byte_offset = @ptrToInt(old_mem.ptr) - @ptrToInt(bucket.page);
529 const slot_index = @intCast(SlotIndex, byte_offset / size_class);
530 const used_byte_index = slot_index / 8;
531 const used_bit_index = @intCast(u3, slot_index % 8);
532 const used_byte = bucket.usedBits(used_byte_index);
533 const is_used = @truncate(u1, used_byte.* >> used_bit_index) != 0;
534 if (!is_used) {
535 if (config.safety) {
536 const alloc_stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);
537 const free_stack_trace = bucketStackTrace(bucket, size_class, slot_index, .free);
538 var addresses: [stack_n]usize = [1]usize{0} ** stack_n;
539 var second_free_stack_trace = StackTrace{
540 .instruction_addresses = &addresses,
541 .index = 0,
542 };
543 std.debug.captureStackTrace(ret_addr, &second_free_stack_trace);
544 log.err("Double free detected. Allocation: {} First free: {} Second free: {}", .{
545 alloc_stack_trace,
546 free_stack_trace,
547 second_free_stack_trace,
548 });
549 if (new_size == 0) {
550 // Recoverable.
551 return @as(usize, 0);
552 }
553 @panic("Unrecoverable double free");
554 } else {
555 unreachable;
556 }
557 }
558 if (new_size == 0) {
559 self.freeSlot(bucket, bucket_index, size_class, slot_index, used_byte, used_bit_index, ret_addr);
560 return @as(usize, 0);
561 }
562 const new_aligned_size = math.max(new_size, old_align);
563 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
564 if (new_size_class <= size_class) {
565 return new_size;
566 }
567 return error.OutOfMemory;
568 }
569
570 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8 {
571 const self = @fieldParentPtr(Self, "allocator", allocator);
572
573 const held = self.mutex.acquire();
574 defer held.release();
575
576 const prev_req_bytes = self.total_requested_bytes;
577 if (config.enable_memory_limit) {
578 const new_req_bytes = prev_req_bytes + len;
579 if (new_req_bytes > self.requested_memory_limit) {
580 return error.OutOfMemory;
581 }
582 self.total_requested_bytes = new_req_bytes;
583 }
584 errdefer if (config.enable_memory_limit) {
585 self.total_requested_bytes = prev_req_bytes;
586 };
587
588 const new_aligned_size = math.max(len, ptr_align);
589 if (new_aligned_size > largest_bucket_object_size) {
590 try self.large_allocations.ensureCapacity(
591 self.backing_allocator,
592 self.large_allocations.entries.items.len + 1,
593 );
594
595 const slice = try self.backing_allocator.allocFn(self.backing_allocator, len, ptr_align, len_align, ret_addr);
596
597 const gop = self.large_allocations.getOrPutAssumeCapacity(@ptrToInt(slice.ptr));
598 assert(!gop.found_existing); // This would mean the kernel double-mapped pages.
599 gop.entry.value.bytes = slice;
600 collectStackTrace(ret_addr, &gop.entry.value.stack_addresses);
601
602 return slice;
603 } else {
604 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
605 const ptr = try self.allocSlot(new_size_class, ret_addr);
606 return ptr[0..len];
607 }
608 }
609
610 fn createBucket(self: *Self, size_class: usize, bucket_index: usize) Error!*BucketHeader {
611 const page = try self.backing_allocator.allocAdvanced(u8, page_size, page_size, .exact);
612 errdefer self.backing_allocator.free(page);
613
614 const bucket_size = bucketSize(size_class);
615 const bucket_bytes = try self.backing_allocator.allocAdvanced(u8, @alignOf(BucketHeader), bucket_size, .exact);
616 const ptr = @ptrCast(*BucketHeader, bucket_bytes.ptr);
617 ptr.* = BucketHeader{
618 .prev = ptr,
619 .next = ptr,
620 .page = page.ptr,
621 .alloc_cursor = 0,
622 .used_count = 0,
623 };
624 self.buckets[bucket_index] = ptr;
625 // Set the used bits to all zeroes
626 @memset(@as(*[1]u8, ptr.usedBits(0)), 0, usedBitsCount(size_class));
627 return ptr;
628 }
629 };
630}
631
632const TraceKind = enum {
633 alloc,
634 free,
635};
636
637const test_config = Config{};
638
639test "small allocations - free in same order" {
640 var gpa = GeneralPurposeAllocator(test_config){};
641 defer std.testing.expect(!gpa.deinit());
642 const allocator = &gpa.allocator;
643
644 var list = std.ArrayList(*u64).init(std.testing.allocator);
645 defer list.deinit();
646
647 var i: usize = 0;
648 while (i < 513) : (i += 1) {
649 const ptr = try allocator.create(u64);
650 try list.append(ptr);
651 }
652
653 for (list.items) |ptr| {
654 allocator.destroy(ptr);
655 }
656}
657
658test "small allocations - free in reverse order" {
659 var gpa = GeneralPurposeAllocator(test_config){};
660 defer std.testing.expect(!gpa.deinit());
661 const allocator = &gpa.allocator;
662
663 var list = std.ArrayList(*u64).init(std.testing.allocator);
664 defer list.deinit();
665
666 var i: usize = 0;
667 while (i < 513) : (i += 1) {
668 const ptr = try allocator.create(u64);
669 try list.append(ptr);
670 }
671
672 while (list.popOrNull()) |ptr| {
673 allocator.destroy(ptr);
674 }
675}
676
677test "large allocations" {
678 var gpa = GeneralPurposeAllocator(test_config){};
679 defer std.testing.expect(!gpa.deinit());
680 const allocator = &gpa.allocator;
681
682 const ptr1 = try allocator.alloc(u64, 42768);
683 const ptr2 = try allocator.alloc(u64, 52768);
684 allocator.free(ptr1);
685 const ptr3 = try allocator.alloc(u64, 62768);
686 allocator.free(ptr3);
687 allocator.free(ptr2);
688}
689
690test "realloc" {
691 var gpa = GeneralPurposeAllocator(test_config){};
692 defer std.testing.expect(!gpa.deinit());
693 const allocator = &gpa.allocator;
694
695 var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1);
696 defer allocator.free(slice);
697 slice[0] = 0x12;
698
699 // This reallocation should keep its pointer address.
700 const old_slice = slice;
701 slice = try allocator.realloc(slice, 2);
702 std.testing.expect(old_slice.ptr == slice.ptr);
703 std.testing.expect(slice[0] == 0x12);
704 slice[1] = 0x34;
705
706 // This requires upgrading to a larger size class
707 slice = try allocator.realloc(slice, 17);
708 std.testing.expect(slice[0] == 0x12);
709 std.testing.expect(slice[1] == 0x34);
710}
711
712test "shrink" {
713 var gpa = GeneralPurposeAllocator(test_config){};
714 defer std.testing.expect(!gpa.deinit());
715 const allocator = &gpa.allocator;
716
717 var slice = try allocator.alloc(u8, 20);
718 defer allocator.free(slice);
719
720 mem.set(u8, slice, 0x11);
721
722 slice = allocator.shrink(slice, 17);
723
724 for (slice) |b| {
725 std.testing.expect(b == 0x11);
726 }
727
728 slice = allocator.shrink(slice, 16);
729
730 for (slice) |b| {
731 std.testing.expect(b == 0x11);
732 }
733}
734
735test "large object - grow" {
736 var gpa = GeneralPurposeAllocator(test_config){};
737 defer std.testing.expect(!gpa.deinit());
738 const allocator = &gpa.allocator;
739
740 var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
741 defer allocator.free(slice1);
742
743 const old = slice1;
744 slice1 = try allocator.realloc(slice1, page_size * 2 - 10);
745 std.testing.expect(slice1.ptr == old.ptr);
746
747 slice1 = try allocator.realloc(slice1, page_size * 2);
748 std.testing.expect(slice1.ptr == old.ptr);
749
750 slice1 = try allocator.realloc(slice1, page_size * 2 + 1);
751}
752
753test "realloc small object to large object" {
754 var gpa = GeneralPurposeAllocator(test_config){};
755 defer std.testing.expect(!gpa.deinit());
756 const allocator = &gpa.allocator;
757
758 var slice = try allocator.alloc(u8, 70);
759 defer allocator.free(slice);
760 slice[0] = 0x12;
761 slice[60] = 0x34;
762
763 // This requires upgrading to a large object
764 const large_object_size = page_size * 2 + 50;
765 slice = try allocator.realloc(slice, large_object_size);
766 std.testing.expect(slice[0] == 0x12);
767 std.testing.expect(slice[60] == 0x34);
768}
769
770test "shrink large object to large object" {
771 var gpa = GeneralPurposeAllocator(test_config){};
772 defer std.testing.expect(!gpa.deinit());
773 const allocator = &gpa.allocator;
774
775 var slice = try allocator.alloc(u8, page_size * 2 + 50);
776 defer allocator.free(slice);
777 slice[0] = 0x12;
778 slice[60] = 0x34;
779
780 slice = try allocator.resize(slice, page_size * 2 + 1);
781 std.testing.expect(slice[0] == 0x12);
782 std.testing.expect(slice[60] == 0x34);
783
784 slice = allocator.shrink(slice, page_size * 2 + 1);
785 std.testing.expect(slice[0] == 0x12);
786 std.testing.expect(slice[60] == 0x34);
787
788 slice = try allocator.realloc(slice, page_size * 2);
789 std.testing.expect(slice[0] == 0x12);
790 std.testing.expect(slice[60] == 0x34);
791}
792
793test "shrink large object to large object with larger alignment" {
794 var gpa = GeneralPurposeAllocator(test_config){};
795 defer std.testing.expect(!gpa.deinit());
796 const allocator = &gpa.allocator;
797
798 var debug_buffer: [1000]u8 = undefined;
799 const debug_allocator = &std.heap.FixedBufferAllocator.init(&debug_buffer).allocator;
800
801 const alloc_size = page_size * 2 + 50;
802 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
803 defer allocator.free(slice);
804
805 const big_alignment: usize = switch (std.Target.current.os.tag) {
806 .windows => page_size * 32, // Windows aligns to 64K.
807 else => page_size * 2,
808 };
809 // This loop allocates until we find a page that is not aligned to the big
810 // alignment. Then we shrink the allocation after the loop, but increase the
811 // alignment to the higher one, that we know will force it to realloc.
812 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
813 while (mem.isAligned(@ptrToInt(slice.ptr), big_alignment)) {
814 try stuff_to_free.append(slice);
815 slice = try allocator.alignedAlloc(u8, 16, alloc_size);
816 }
817 while (stuff_to_free.popOrNull()) |item| {
818 allocator.free(item);
819 }
820 slice[0] = 0x12;
821 slice[60] = 0x34;
822
823 slice = try allocator.reallocAdvanced(slice, big_alignment, alloc_size / 2, .exact);
824 std.testing.expect(slice[0] == 0x12);
825 std.testing.expect(slice[60] == 0x34);
826}
827
828test "realloc large object to small object" {
829 var gpa = GeneralPurposeAllocator(test_config){};
830 defer std.testing.expect(!gpa.deinit());
831 const allocator = &gpa.allocator;
832
833 var slice = try allocator.alloc(u8, page_size * 2 + 50);
834 defer allocator.free(slice);
835 slice[0] = 0x12;
836 slice[16] = 0x34;
837
838 slice = try allocator.realloc(slice, 19);
839 std.testing.expect(slice[0] == 0x12);
840 std.testing.expect(slice[16] == 0x34);
841}
842
843test "non-page-allocator backing allocator" {
844 var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator };
845 defer std.testing.expect(!gpa.deinit());
846 const allocator = &gpa.allocator;
847
848 const ptr = try allocator.create(i32);
849 defer allocator.destroy(ptr);
850}
851
852test "realloc large object to larger alignment" {
853 var gpa = GeneralPurposeAllocator(test_config){};
854 defer std.testing.expect(!gpa.deinit());
855 const allocator = &gpa.allocator;
856
857 var debug_buffer: [1000]u8 = undefined;
858 const debug_allocator = &std.heap.FixedBufferAllocator.init(&debug_buffer).allocator;
859
860 var slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
861 defer allocator.free(slice);
862
863 const big_alignment: usize = switch (std.Target.current.os.tag) {
864 .windows => page_size * 32, // Windows aligns to 64K.
865 else => page_size * 2,
866 };
867 // This loop allocates until we find a page that is not aligned to the big alignment.
868 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
869 while (mem.isAligned(@ptrToInt(slice.ptr), big_alignment)) {
870 try stuff_to_free.append(slice);
871 slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
872 }
873 while (stuff_to_free.popOrNull()) |item| {
874 allocator.free(item);
875 }
876 slice[0] = 0x12;
877 slice[16] = 0x34;
878
879 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100, .exact);
880 std.testing.expect(slice[0] == 0x12);
881 std.testing.expect(slice[16] == 0x34);
882
883 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25, .exact);
884 std.testing.expect(slice[0] == 0x12);
885 std.testing.expect(slice[16] == 0x34);
886
887 slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100, .exact);
888 std.testing.expect(slice[0] == 0x12);
889 std.testing.expect(slice[16] == 0x34);
890}
891
892test "large object shrinks to small but allocation fails during shrink" {
893 var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3);
894 var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = &failing_allocator.allocator };
895 defer std.testing.expect(!gpa.deinit());
896 const allocator = &gpa.allocator;
897
898 var slice = try allocator.alloc(u8, page_size * 2 + 50);
899 defer allocator.free(slice);
900 slice[0] = 0x12;
901 slice[3] = 0x34;
902
903 // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator
904
905 slice = allocator.shrink(slice, 4);
906 std.testing.expect(slice[0] == 0x12);
907 std.testing.expect(slice[3] == 0x34);
908}
909
910test "objects of size 1024 and 2048" {
911 var gpa = GeneralPurposeAllocator(test_config){};
912 defer std.testing.expect(!gpa.deinit());
913 const allocator = &gpa.allocator;
914
915 const slice = try allocator.alloc(u8, 1025);
916 const slice2 = try allocator.alloc(u8, 3000);
917
918 allocator.free(slice);
919 allocator.free(slice2);
920}
921
922test "setting a memory cap" {
923 var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
924 defer std.testing.expect(!gpa.deinit());
925 const allocator = &gpa.allocator;
926
927 gpa.setRequestedMemoryLimit(1010);
928
929 const small = try allocator.create(i32);
930 std.testing.expect(gpa.total_requested_bytes == 4);
931
932 const big = try allocator.alloc(u8, 1000);
933 std.testing.expect(gpa.total_requested_bytes == 1004);
934
935 std.testing.expectError(error.OutOfMemory, allocator.create(u64));
936
937 allocator.destroy(small);
938 std.testing.expect(gpa.total_requested_bytes == 1000);
939
940 allocator.free(big);
941 std.testing.expect(gpa.total_requested_bytes == 0);
942
943 const exact = try allocator.alloc(u8, 1010);
944 std.testing.expect(gpa.total_requested_bytes == 1010);
945 allocator.free(exact);
946}
lib/std/heap/logging_allocator.zig+19-6
......@@ -23,10 +23,16 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
2323 };
2424 }
2525
26 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
26 fn alloc(
27 allocator: *Allocator,
28 len: usize,
29 ptr_align: u29,
30 len_align: u29,
31 ra: usize,
32 ) error{OutOfMemory}![]u8 {
2733 const self = @fieldParentPtr(Self, "allocator", allocator);
2834 self.out_stream.print("alloc : {}", .{len}) catch {};
29 const result = self.parent_allocator.callAllocFn(len, ptr_align, len_align);
35 const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
3036 if (result) |buff| {
3137 self.out_stream.print(" success!\n", .{}) catch {};
3238 } else |err| {
......@@ -35,7 +41,14 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
3541 return result;
3642 }
3743
38 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
44 fn resize(
45 allocator: *Allocator,
46 buf: []u8,
47 buf_align: u29,
48 new_len: usize,
49 len_align: u29,
50 ra: usize,
51 ) error{OutOfMemory}!usize {
3952 const self = @fieldParentPtr(Self, "allocator", allocator);
4053 if (new_len == 0) {
4154 self.out_stream.print("free : {}\n", .{buf.len}) catch {};
......@@ -44,7 +57,7 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
4457 } else {
4558 self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
4659 }
47 if (self.parent_allocator.callResizeFn(buf, new_len, len_align)) |resized_len| {
60 if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
4861 if (new_len > buf.len) {
4962 self.out_stream.print(" success!\n", .{}) catch {};
5063 }
......@@ -74,9 +87,9 @@ test "LoggingAllocator" {
7487 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;
7588
7689 var a = try allocator.alloc(u8, 10);
77 a.len = allocator.shrinkBytes(a, 5, 0);
90 a = allocator.shrink(a, 5);
7891 std.debug.assert(a.len == 5);
79 std.testing.expectError(error.OutOfMemory, allocator.callResizeFn(a, 20, 0));
92 std.testing.expectError(error.OutOfMemory, allocator.resize(a, 20));
8093 allocator.free(a);
8194
8295 std.testing.expectEqualSlices(u8,
lib/std/json.zig+1-1
......@@ -1742,7 +1742,7 @@ test "parse into tagged union" {
17421742 A: struct { x: u32 },
17431743 B: struct { y: u32 },
17441744 };
1745 testing.expectEqual(T{ .B = .{.y = 42} }, try parse(T, &TokenStream.init("{\"y\":42}"), ParseOptions{}));
1745 testing.expectEqual(T{ .B = .{ .y = 42 } }, try parse(T, &TokenStream.init("{\"y\":42}"), ParseOptions{}));
17461746 }
17471747}
17481748
lib/std/log.zig+142-88
......@@ -2,12 +2,20 @@ const std = @import("std.zig");
22const builtin = std.builtin;
33const root = @import("root");
44
5//! std.log is standardized interface for logging which allows for the logging
5//! std.log is a standardized interface for logging which allows for the logging
66//! of programs and libraries using this interface to be formatted and filtered
77//! by the implementer of the root.log function.
88//!
9//! The scope parameter should be used to give context to the logging. For
10//! example, a library called 'libfoo' might use .libfoo as its scope.
9//! Each log message has an associated scope enum, which can be used to give
10//! context to the logging. The logging functions in std.log implicitly use a
11//! scope of .default.
12//!
13//! A logging namespace using a custom scope can be created using the
14//! std.log.scoped function, passing the scope as an argument; the logging
15//! functions in the resulting struct use the provided scope parameter.
16//! For example, a library called 'libfoo' might use
17//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
18//! log messages.
1119//!
1220//! An example root.log might look something like this:
1321//!
......@@ -25,9 +33,9 @@ const root = @import("root");
2533//! args: anytype,
2634//! ) void {
2735//! // Ignore all non-critical logging from sources other than
28//! // .my_project and .nice_library
36//! // .my_project, .nice_library and .default
2937//! const scope_prefix = "(" ++ switch (scope) {
30//! .my_project, .nice_library => @tagName(scope),
38//! .my_project, .nice_library, .default => @tagName(scope),
3139//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
3240//! @tagName(scope)
3341//! else
......@@ -44,16 +52,24 @@ const root = @import("root");
4452//! }
4553//!
4654//! pub fn main() void {
47//! // Won't be printed as log_level is .warn
48//! std.log.info(.my_project, "Starting up.", .{});
49//! std.log.err(.nice_library, "Something went very wrong, sorry.", .{});
50//! // Won't be printed as it gets filtered out by our log function
51//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1", .{});
55//! // Using the default scope:
56//! std.log.info("Just a simple informational log message", .{}); // Won't be printed as log_level is .warn
57//! std.log.warn("Flux capacitor is starting to overheat", .{});
58//!
59//! // Using scoped logging:
60//! const my_project_log = std.log.scoped(.my_project);
61//! const nice_library_log = std.log.scoped(.nice_library);
62//! const verbose_lib_log = std.log.scoped(.verbose_lib);
63//!
64//! my_project_log.info("Starting up", .{}); // Won't be printed as log_level is .warn
65//! nice_library_log.err("Something went very wrong, sorry", .{});
66//! verbose_lib_log.err("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function
5267//! }
5368//! ```
5469//! Which produces the following output:
5570//! ```
56//! [err] (nice_library): Something went very wrong, sorry.
71//! [warn] (default): Flux capacitor is starting to overheat
72//! [err] (nice_library): Something went very wrong, sorry
5773//! ```
5874
5975pub const Level = enum {
......@@ -115,88 +131,126 @@ fn log(
115131 }
116132}
117133
118/// Log an emergency message to stderr. This log level is intended to be used
119/// for conditions that cannot be handled and is usually followed by a panic.
120pub fn emerg(
121 comptime scope: @Type(.EnumLiteral),
122 comptime format: []const u8,
123 args: anytype,
124) void {
125 @setCold(true);
126 log(.emerg, scope, format, args);
127}
134/// Returns a scoped logging namespace that logs all messages using the scope
135/// provided here.
136pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
137 return struct {
138 /// Log an emergency message. This log level is intended to be used
139 /// for conditions that cannot be handled and is usually followed by a panic.
140 pub fn emerg(
141 comptime format: []const u8,
142 args: anytype,
143 ) void {
144 @setCold(true);
145 log(.emerg, scope, format, args);
146 }
128147
129/// Log an alert message to stderr. This log level is intended to be used for
130/// conditions that should be corrected immediately (e.g. database corruption).
131pub fn alert(
132 comptime scope: @Type(.EnumLiteral),
133 comptime format: []const u8,
134 args: anytype,
135) void {
136 @setCold(true);
137 log(.alert, scope, format, args);
138}
148 /// Log an alert message. This log level is intended to be used for
149 /// conditions that should be corrected immediately (e.g. database corruption).
150 pub fn alert(
151 comptime format: []const u8,
152 args: anytype,
153 ) void {
154 @setCold(true);
155 log(.alert, scope, format, args);
156 }
139157
140/// Log a critical message to stderr. This log level is intended to be used
141/// when a bug has been detected or something has gone wrong and it will have
142/// an effect on the operation of the program.
143pub fn crit(
144 comptime scope: @Type(.EnumLiteral),
145 comptime format: []const u8,
146 args: anytype,
147) void {
148 @setCold(true);
149 log(.crit, scope, format, args);
150}
158 /// Log a critical message. This log level is intended to be used
159 /// when a bug has been detected or something has gone wrong and it will have
160 /// an effect on the operation of the program.
161 pub fn crit(
162 comptime format: []const u8,
163 args: anytype,
164 ) void {
165 @setCold(true);
166 log(.crit, scope, format, args);
167 }
151168
152/// Log an error message to stderr. This log level is intended to be used when
153/// a bug has been detected or something has gone wrong but it is recoverable.
154pub fn err(
155 comptime scope: @Type(.EnumLiteral),
156 comptime format: []const u8,
157 args: anytype,
158) void {
159 @setCold(true);
160 log(.err, scope, format, args);
161}
169 /// Log an error message. This log level is intended to be used when
170 /// a bug has been detected or something has gone wrong but it is recoverable.
171 pub fn err(
172 comptime format: []const u8,
173 args: anytype,
174 ) void {
175 @setCold(true);
176 log(.err, scope, format, args);
177 }
162178
163/// Log a warning message to stderr. This log level is intended to be used if
164/// it is uncertain whether something has gone wrong or not, but the
165/// circumstances would be worth investigating.
166pub fn warn(
167 comptime scope: @Type(.EnumLiteral),
168 comptime format: []const u8,
169 args: anytype,
170) void {
171 log(.warn, scope, format, args);
172}
179 /// Log a warning message. This log level is intended to be used if
180 /// it is uncertain whether something has gone wrong or not, but the
181 /// circumstances would be worth investigating.
182 pub fn warn(
183 comptime format: []const u8,
184 args: anytype,
185 ) void {
186 log(.warn, scope, format, args);
187 }
173188
174/// Log a notice message to stderr. This log level is intended to be used for
175/// non-error but significant conditions.
176pub fn notice(
177 comptime scope: @Type(.EnumLiteral),
178 comptime format: []const u8,
179 args: anytype,
180) void {
181 log(.notice, scope, format, args);
182}
189 /// Log a notice message. This log level is intended to be used for
190 /// non-error but significant conditions.
191 pub fn notice(
192 comptime format: []const u8,
193 args: anytype,
194 ) void {
195 log(.notice, scope, format, args);
196 }
183197
184/// Log an info message to stderr. This log level is intended to be used for
185/// general messages about the state of the program.
186pub fn info(
187 comptime scope: @Type(.EnumLiteral),
188 comptime format: []const u8,
189 args: anytype,
190) void {
191 log(.info, scope, format, args);
192}
198 /// Log an info message. This log level is intended to be used for
199 /// general messages about the state of the program.
200 pub fn info(
201 comptime format: []const u8,
202 args: anytype,
203 ) void {
204 log(.info, scope, format, args);
205 }
193206
194/// Log a debug message to stderr. This log level is intended to be used for
195/// messages which are only useful for debugging.
196pub fn debug(
197 comptime scope: @Type(.EnumLiteral),
198 comptime format: []const u8,
199 args: anytype,
200) void {
201 log(.debug, scope, format, args);
207 /// Log a debug message. This log level is intended to be used for
208 /// messages which are only useful for debugging.
209 pub fn debug(
210 comptime format: []const u8,
211 args: anytype,
212 ) void {
213 log(.debug, scope, format, args);
214 }
215 };
202216}
217
218/// The default scoped logging namespace.
219pub const default = scoped(.default);
220
221/// Log an emergency message using the default scope. This log level is
222/// intended to be used for conditions that cannot be handled and is usually
223/// followed by a panic.
224pub const emerg = default.emerg;
225
226/// Log an alert message using the default scope. This log level is intended to
227/// be used for conditions that should be corrected immediately (e.g. database
228/// corruption).
229pub const alert = default.alert;
230
231/// Log a critical message using the default scope. This log level is intended
232/// to be used when a bug has been detected or something has gone wrong and it
233/// will have an effect on the operation of the program.
234pub const crit = default.crit;
235
236/// Log an error message using the default scope. This log level is intended to
237/// be used when a bug has been detected or something has gone wrong but it is
238/// recoverable.
239pub const err = default.err;
240
241/// Log a warning message using the default scope. This log level is intended
242/// to be used if it is uncertain whether something has gone wrong or not, but
243/// the circumstances would be worth investigating.
244pub const warn = default.warn;
245
246/// Log a notice message using the default scope. This log level is intended to
247/// be used for non-error but significant conditions.
248pub const notice = default.notice;
249
250/// Log an info message using the default scope. This log level is intended to
251/// be used for general messages about the state of the program.
252pub const info = default.info;
253
254/// Log a debug message using the default scope. This log level is intended to
255/// be used for messages which are only useful for debugging.
256pub const debug = default.debug;
lib/std/math.zig+5
......@@ -747,6 +747,7 @@ test "math.negateCast" {
747747
748748/// Cast an integer to a different integer type. If the value doesn't fit,
749749/// return an error.
750/// TODO make this an optional not an error.
750751pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) {
751752 comptime assert(@typeInfo(T) == .Int); // must pass an integer
752753 comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer
......@@ -837,6 +838,10 @@ pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
837838 return @intCast(T, x);
838839}
839840
841pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T {
842 return ceilPowerOfTwo(T, value) catch unreachable;
843}
844
840845test "math.ceilPowerOfTwoPromote" {
841846 testCeilPowerOfTwoPromote();
842847 comptime testCeilPowerOfTwoPromote();
lib/std/mem.zig+32-387
......@@ -8,391 +8,13 @@ const meta = std.meta;
88const trait = meta.trait;
99const testing = std.testing;
1010
11// https://github.com/ziglang/zig/issues/2564
11/// https://github.com/ziglang/zig/issues/2564
1212pub const page_size = switch (builtin.arch) {
1313 .wasm32, .wasm64 => 64 * 1024,
1414 else => 4 * 1024,
1515};
1616
17pub const Allocator = struct {
18 pub const Error = error{OutOfMemory};
19
20 /// Attempt to allocate at least `len` bytes aligned to `ptr_align`.
21 ///
22 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
23 /// otherwise, the length must be aligned to `len_align`.
24 ///
25 /// `len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
26 allocFn: fn (self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Error![]u8,
27
28 /// Attempt to expand or shrink memory in place. `buf.len` must equal the most recent
29 /// length returned by `allocFn` or `resizeFn`.
30 ///
31 /// Passing a `new_len` of 0 frees and invalidates the buffer such that it can no
32 /// longer be passed to `resizeFn`.
33 ///
34 /// error.OutOfMemory can only be returned if `new_len` is greater than `buf.len`.
35 /// If `buf` cannot be expanded to accomodate `new_len`, then the allocation MUST be
36 /// unmodified and error.OutOfMemory MUST be returned.
37 ///
38 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
39 /// otherwise, the length must be aligned to `len_align`.
40 ///
41 /// `new_len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
42 resizeFn: fn (self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize,
43
44 pub fn callAllocFn(self: *Allocator, new_len: usize, alignment: u29, len_align: u29) Error![]u8 {
45 return self.allocFn(self, new_len, alignment, len_align);
46 }
47
48 pub fn callResizeFn(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
49 return self.resizeFn(self, buf, new_len, len_align);
50 }
51
52 /// Set to resizeFn if in-place resize is not supported.
53 pub fn noResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
54 if (new_len > buf.len)
55 return error.OutOfMemory;
56 return new_len;
57 }
58
59 /// Call `resizeFn`, but caller guarantees that `new_len` <= `buf.len` meaning
60 /// error.OutOfMemory should be impossible.
61 pub fn shrinkBytes(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) usize {
62 assert(new_len <= buf.len);
63 return self.callResizeFn(buf, new_len, len_align) catch unreachable;
64 }
65
66 /// Realloc is used to modify the size or alignment of an existing allocation,
67 /// as well as to provide the allocator with an opportunity to move an allocation
68 /// to a better location.
69 /// When the size/alignment is greater than the previous allocation, this function
70 /// returns `error.OutOfMemory` when the requested new allocation could not be granted.
71 /// When the size/alignment is less than or equal to the previous allocation,
72 /// this function returns `error.OutOfMemory` when the allocator decides the client
73 /// would be better off keeping the extra alignment/size. Clients will call
74 /// `callResizeFn` when they require the allocator to track a new alignment/size,
75 /// and so this function should only return success when the allocator considers
76 /// the reallocation desirable from the allocator's perspective.
77 /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
78 /// reallocation failure, even when `new_n` <= `old_mem.len`. A `FixedBufferAllocator`
79 /// would always return `error.OutOfMemory` for `reallocFn` when the size/alignment
80 /// is less than or equal to the old allocation, because it cannot reclaim the memory,
81 /// and thus the `std.ArrayList` would be better off retaining its capacity.
82 /// When `reallocFn` returns,
83 /// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same
84 /// as `old_mem` was when `reallocFn` is called. The bytes of
85 /// `return_value[old_mem.len..]` have undefined values.
86 /// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
87 fn reallocBytes(
88 self: *Allocator,
89 /// Guaranteed to be the same as what was returned from most recent call to
90 /// `allocFn` or `resizeFn`.
91 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
92 /// is guaranteed to be >= 1.
93 old_mem: []u8,
94 /// If `old_mem.len == 0` then this is `undefined`, otherwise:
95 /// Guaranteed to be the same as what was passed to `allocFn`.
96 /// Guaranteed to be >= 1.
97 /// Guaranteed to be a power of 2.
98 old_alignment: u29,
99 /// If `new_byte_count` is 0 then this is a free and it is guaranteed that
100 /// `old_mem.len != 0`.
101 new_byte_count: usize,
102 /// Guaranteed to be >= 1.
103 /// Guaranteed to be a power of 2.
104 /// Returned slice's pointer must have this alignment.
105 new_alignment: u29,
106 /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
107 /// non-zero means the length of the returned slice must be aligned by `len_align`
108 /// `new_len` must be aligned by `len_align`
109 len_align: u29,
110 ) Error![]u8 {
111 if (old_mem.len == 0) {
112 const new_mem = try self.callAllocFn(new_byte_count, new_alignment, len_align);
113 @memset(new_mem.ptr, undefined, new_byte_count);
114 return new_mem;
115 }
116
117 if (isAligned(@ptrToInt(old_mem.ptr), new_alignment)) {
118 if (new_byte_count <= old_mem.len) {
119 const shrunk_len = self.shrinkBytes(old_mem, new_byte_count, len_align);
120 return old_mem.ptr[0..shrunk_len];
121 }
122 if (self.callResizeFn(old_mem, new_byte_count, len_align)) |resized_len| {
123 assert(resized_len >= new_byte_count);
124 @memset(old_mem.ptr + new_byte_count, undefined, resized_len - new_byte_count);
125 return old_mem.ptr[0..resized_len];
126 } else |_| {}
127 }
128 if (new_byte_count <= old_mem.len and new_alignment <= old_alignment) {
129 return error.OutOfMemory;
130 }
131 return self.moveBytes(old_mem, new_byte_count, new_alignment, len_align);
132 }
133
134 /// Move the given memory to a new location in the given allocator to accomodate a new
135 /// size and alignment.
136 fn moveBytes(self: *Allocator, old_mem: []u8, new_len: usize, new_alignment: u29, len_align: u29) Error![]u8 {
137 assert(old_mem.len > 0);
138 assert(new_len > 0);
139 const new_mem = try self.callAllocFn(new_len, new_alignment, len_align);
140 @memcpy(new_mem.ptr, old_mem.ptr, std.math.min(new_len, old_mem.len));
141 // DISABLED TO AVOID BUGS IN TRANSLATE C
142 // use './zig build test-translate-c' to reproduce, some of the symbols in the
143 // generated C code will be a sequence of 0xaa (the undefined value), meaning
144 // it is printing data that has been freed
145 //@memset(old_mem.ptr, undefined, old_mem.len);
146 _ = self.shrinkBytes(old_mem, 0, 0);
147 return new_mem;
148 }
149
150 /// Returns a pointer to undefined memory.
151 /// Call `destroy` with the result to free the memory.
152 pub fn create(self: *Allocator, comptime T: type) Error!*T {
153 if (@sizeOf(T) == 0) return &(T{});
154 const slice = try self.alloc(T, 1);
155 return &slice[0];
156 }
157
158 /// `ptr` should be the return value of `create`, or otherwise
159 /// have the same address and alignment property.
160 pub fn destroy(self: *Allocator, ptr: anytype) void {
161 const T = @TypeOf(ptr).Child;
162 if (@sizeOf(T) == 0) return;
163 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
164 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], 0, 0);
165 }
166
167 /// Allocates an array of `n` items of type `T` and sets all the
168 /// items to `undefined`. Depending on the Allocator
169 /// implementation, it may be required to call `free` once the
170 /// memory is no longer needed, to avoid a resource leak. If the
171 /// `Allocator` implementation is unknown, then correct code will
172 /// call `free` when done.
173 ///
174 /// For allocating a single item, see `create`.
175 pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T {
176 return self.alignedAlloc(T, null, n);
177 }
178
179 pub fn allocWithOptions(
180 self: *Allocator,
181 comptime Elem: type,
182 n: usize,
183 /// null means naturally aligned
184 comptime optional_alignment: ?u29,
185 comptime optional_sentinel: ?Elem,
186 ) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
187 if (optional_sentinel) |sentinel| {
188 const ptr = try self.alignedAlloc(Elem, optional_alignment, n + 1);
189 ptr[n] = sentinel;
190 return ptr[0..n :sentinel];
191 } else {
192 return self.alignedAlloc(Elem, optional_alignment, n);
193 }
194 }
195
196 fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, comptime sentinel: ?Elem) type {
197 if (sentinel) |s| {
198 return [:s]align(alignment orelse @alignOf(Elem)) Elem;
199 } else {
200 return []align(alignment orelse @alignOf(Elem)) Elem;
201 }
202 }
203
204 /// Allocates an array of `n + 1` items of type `T` and sets the first `n`
205 /// items to `undefined` and the last item to `sentinel`. Depending on the
206 /// Allocator implementation, it may be required to call `free` once the
207 /// memory is no longer needed, to avoid a resource leak. If the
208 /// `Allocator` implementation is unknown, then correct code will
209 /// call `free` when done.
210 ///
211 /// For allocating a single item, see `create`.
212 ///
213 /// Deprecated; use `allocWithOptions`.
214 pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem {
215 return self.allocWithOptions(Elem, n, null, sentinel);
216 }
217
218 /// Deprecated: use `allocAdvanced`
219 pub fn alignedAlloc(
220 self: *Allocator,
221 comptime T: type,
222 /// null means naturally aligned
223 comptime alignment: ?u29,
224 n: usize,
225 ) Error![]align(alignment orelse @alignOf(T)) T {
226 return self.allocAdvanced(T, alignment, n, .exact);
227 }
228
229 const Exact = enum { exact, at_least };
230 pub fn allocAdvanced(
231 self: *Allocator,
232 comptime T: type,
233 /// null means naturally aligned
234 comptime alignment: ?u29,
235 n: usize,
236 exact: Exact,
237 ) Error![]align(alignment orelse @alignOf(T)) T {
238 const a = if (alignment) |a| blk: {
239 if (a == @alignOf(T)) return allocAdvanced(self, T, null, n, exact);
240 break :blk a;
241 } else @alignOf(T);
242
243 if (n == 0) {
244 return @as([*]align(a) T, undefined)[0..0];
245 }
246
247 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
248 // TODO The `if (alignment == null)` blocks are workarounds for zig not being able to
249 // access certain type information about T without creating a circular dependency in async
250 // functions that heap-allocate their own frame with @Frame(func).
251 const sizeOfT = if (alignment == null) @intCast(u29, @divExact(byte_count, n)) else @sizeOf(T);
252 const byte_slice = try self.callAllocFn(byte_count, a, if (exact == .exact) @as(u29, 0) else sizeOfT);
253 switch (exact) {
254 .exact => assert(byte_slice.len == byte_count),
255 .at_least => assert(byte_slice.len >= byte_count),
256 }
257 @memset(byte_slice.ptr, undefined, byte_slice.len);
258 if (alignment == null) {
259 // This if block is a workaround (see comment above)
260 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..@divExact(byte_slice.len, @sizeOf(T))];
261 } else {
262 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
263 }
264 }
265
266 /// This function requests a new byte size for an existing allocation,
267 /// which can be larger, smaller, or the same size as the old memory
268 /// allocation.
269 /// This function is preferred over `shrink`, because it can fail, even
270 /// when shrinking. This gives the allocator a chance to perform a
271 /// cheap shrink operation if possible, or otherwise return OutOfMemory,
272 /// indicating that the caller should keep their capacity, for example
273 /// in `std.ArrayList.shrink`.
274 /// If you need guaranteed success, call `shrink`.
275 /// If `new_n` is 0, this is the same as `free` and it always succeeds.
276 pub fn realloc(self: *Allocator, old_mem: anytype, new_n: usize) t: {
277 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
278 break :t Error![]align(Slice.alignment) Slice.child;
279 } {
280 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
281 return self.reallocAdvanced(old_mem, old_alignment, new_n, .exact);
282 }
283
284 pub fn reallocAtLeast(self: *Allocator, old_mem: anytype, new_n: usize) t: {
285 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
286 break :t Error![]align(Slice.alignment) Slice.child;
287 } {
288 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
289 return self.reallocAdvanced(old_mem, old_alignment, new_n, .at_least);
290 }
291
292 // Deprecated: use `reallocAdvanced`
293 pub fn alignedRealloc(
294 self: *Allocator,
295 old_mem: anytype,
296 comptime new_alignment: u29,
297 new_n: usize,
298 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
299 return self.reallocAdvanced(old_mem, new_alignment, new_n, .exact);
300 }
301
302 /// This is the same as `realloc`, except caller may additionally request
303 /// a new alignment, which can be larger, smaller, or the same as the old
304 /// allocation.
305 pub fn reallocAdvanced(
306 self: *Allocator,
307 old_mem: anytype,
308 comptime new_alignment: u29,
309 new_n: usize,
310 exact: Exact,
311 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
312 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
313 const T = Slice.child;
314 if (old_mem.len == 0) {
315 return self.allocAdvanced(T, new_alignment, new_n, exact);
316 }
317 if (new_n == 0) {
318 self.free(old_mem);
319 return @as([*]align(new_alignment) T, undefined)[0..0];
320 }
321
322 const old_byte_slice = mem.sliceAsBytes(old_mem);
323 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
324 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
325 const new_byte_slice = try self.reallocBytes(old_byte_slice, Slice.alignment, byte_count, new_alignment, if (exact == .exact) @as(u29, 0) else @sizeOf(T));
326 return mem.bytesAsSlice(T, @alignCast(new_alignment, new_byte_slice));
327 }
328
329 /// Prefer calling realloc to shrink if you can tolerate failure, such as
330 /// in an ArrayList data structure with a storage capacity.
331 /// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.
332 /// Returned slice has same alignment as old_mem.
333 /// Shrinking to 0 is the same as calling `free`.
334 pub fn shrink(self: *Allocator, old_mem: anytype, new_n: usize) t: {
335 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
336 break :t []align(Slice.alignment) Slice.child;
337 } {
338 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
339 return self.alignedShrink(old_mem, old_alignment, new_n);
340 }
341
342 /// This is the same as `shrink`, except caller may additionally request
343 /// a new alignment, which must be smaller or the same as the old
344 /// allocation.
345 pub fn alignedShrink(
346 self: *Allocator,
347 old_mem: anytype,
348 comptime new_alignment: u29,
349 new_n: usize,
350 ) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
351 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
352 const T = Slice.child;
353
354 if (new_n == old_mem.len)
355 return old_mem;
356 assert(new_n < old_mem.len);
357 assert(new_alignment <= Slice.alignment);
358
359 // Here we skip the overflow checking on the multiplication because
360 // new_n <= old_mem.len and the multiplication didn't overflow for that operation.
361 const byte_count = @sizeOf(T) * new_n;
362
363 const old_byte_slice = mem.sliceAsBytes(old_mem);
364 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);
365 _ = self.shrinkBytes(old_byte_slice, byte_count, 0);
366 return old_mem[0..new_n];
367 }
368
369 /// Free an array allocated with `alloc`. To free a single item,
370 /// see `destroy`.
371 pub fn free(self: *Allocator, memory: anytype) void {
372 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
373 const bytes = mem.sliceAsBytes(memory);
374 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
375 if (bytes_len == 0) return;
376 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
377 @memset(non_const_ptr, undefined, bytes_len);
378 _ = self.shrinkBytes(non_const_ptr[0..bytes_len], 0, 0);
379 }
380
381 /// Copies `m` to newly allocated memory. Caller owns the memory.
382 pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T {
383 const new_buf = try allocator.alloc(T, m.len);
384 copy(T, new_buf, m);
385 return new_buf;
386 }
387
388 /// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
389 pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T {
390 const new_buf = try allocator.alloc(T, m.len + 1);
391 copy(T, new_buf, m);
392 new_buf[m.len] = 0;
393 return new_buf[0..m.len :0];
394 }
395};
17pub const Allocator = @import("mem/Allocator.zig");
39618
39719/// Detects and asserts if the std.mem.Allocator interface is violated by the caller
39820/// or the allocator.
......@@ -415,7 +37,13 @@ pub fn ValidationAllocator(comptime T: type) type {
41537 if (*T == *Allocator) return &self.underlying_allocator;
41638 return &self.underlying_allocator.allocator;
41739 }
418 pub fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
40 pub fn alloc(
41 allocator: *Allocator,
42 n: usize,
43 ptr_align: u29,
44 len_align: u29,
45 ret_addr: usize,
46 ) Allocator.Error![]u8 {
41947 assert(n > 0);
42048 assert(mem.isValidAlign(ptr_align));
42149 if (len_align != 0) {
......@@ -424,7 +52,8 @@ pub fn ValidationAllocator(comptime T: type) type {
42452 }
42553
42654 const self = @fieldParentPtr(@This(), "allocator", allocator);
427 const result = try self.getUnderlyingAllocatorPtr().callAllocFn(n, ptr_align, len_align);
55 const underlying = self.getUnderlyingAllocatorPtr();
56 const result = try underlying.allocFn(underlying, n, ptr_align, len_align, ret_addr);
42857 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));
42958 if (len_align == 0) {
43059 assert(result.len == n);
......@@ -434,14 +63,22 @@ pub fn ValidationAllocator(comptime T: type) type {
43463 }
43564 return result;
43665 }
437 pub fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
66 pub fn resize(
67 allocator: *Allocator,
68 buf: []u8,
69 buf_align: u29,
70 new_len: usize,
71 len_align: u29,
72 ret_addr: usize,
73 ) Allocator.Error!usize {
43874 assert(buf.len > 0);
43975 if (len_align != 0) {
44076 assert(mem.isAlignedAnyAlign(new_len, len_align));
44177 assert(new_len >= len_align);
44278 }
44379 const self = @fieldParentPtr(@This(), "allocator", allocator);
444 const result = try self.getUnderlyingAllocatorPtr().callResizeFn(buf, new_len, len_align);
80 const underlying = self.getUnderlyingAllocatorPtr();
81 const result = try underlying.resizeFn(underlying, buf, buf_align, new_len, len_align, ret_addr);
44582 if (len_align == 0) {
44683 assert(result == new_len);
44784 } else {
......@@ -481,7 +118,7 @@ var failAllocator = Allocator{
481118 .allocFn = failAllocatorAlloc,
482119 .resizeFn = Allocator.noResize,
483120};
484fn failAllocatorAlloc(self: *Allocator, n: usize, alignment: u29, len_align: u29) Allocator.Error![]u8 {
121fn failAllocatorAlloc(self: *Allocator, n: usize, alignment: u29, len_align: u29, ra: usize) Allocator.Error![]u8 {
485122 return error.OutOfMemory;
486123}
487124
......@@ -977,7 +614,7 @@ test "spanZ" {
977614}
978615
979616/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,
980/// or a slice, and returns the length.
617/// a slice or a tuple, and returns the length.
981618/// In the case of a sentinel-terminated array, it uses the array length.
982619/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
983620pub fn len(value: anytype) usize {
......@@ -996,6 +633,9 @@ pub fn len(value: anytype) usize {
996633 .C => indexOfSentinel(info.child, 0, value),
997634 .Slice => value.len,
998635 },
636 .Struct => |info| if (info.is_tuple) {
637 return info.fields.len;
638 } else @compileError("invalid type given to std.mem.len"),
999639 else => @compileError("invalid type given to std.mem.len"),
1000640 };
1001641}
......@@ -1021,6 +661,11 @@ test "len" {
1021661 const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 };
1022662 testing.expect(len(vector) == 2);
1023663 }
664 {
665 const tuple = .{ 1, 2 };
666 testing.expect(len(tuple) == 2);
667 testing.expect(tuple[0] == 1);
668 }
1024669}
1025670
1026671/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
......@@ -2038,7 +1683,7 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
20381683 var replacements: usize = 0;
20391684 while (slide < input.len) {
20401685 if (mem.indexOf(T, input[slide..], needle) == @as(usize, 0)) {
2041 mem.copy(T, output[i..i + replacement.len], replacement);
1686 mem.copy(T, output[i .. i + replacement.len], replacement);
20421687 i += replacement.len;
20431688 slide += needle.len;
20441689 replacements += 1;
lib/std/mem/Allocator.zig created+486
......@@ -0,0 +1,486 @@
1//! The standard memory allocation interface.
2
3const std = @import("../std.zig");
4const assert = std.debug.assert;
5const math = std.math;
6const mem = std.mem;
7const Allocator = @This();
8
9pub const Error = error{OutOfMemory};
10
11/// Attempt to allocate at least `len` bytes aligned to `ptr_align`.
12///
13/// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
14/// otherwise, the length must be aligned to `len_align`.
15///
16/// `len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
17///
18/// `ret_addr` is optionally provided as the first return address of the allocation call stack.
19/// If the value is `0` it means no return address has been provided.
20allocFn: fn (self: *Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8,
21
22/// Attempt to expand or shrink memory in place. `buf.len` must equal the most recent
23/// length returned by `allocFn` or `resizeFn`. `buf_align` must equal the same value
24/// that was passed as the `ptr_align` parameter to the original `allocFn` call.
25///
26/// Passing a `new_len` of 0 frees and invalidates the buffer such that it can no
27/// longer be passed to `resizeFn`.
28///
29/// error.OutOfMemory can only be returned if `new_len` is greater than `buf.len`.
30/// If `buf` cannot be expanded to accomodate `new_len`, then the allocation MUST be
31/// unmodified and error.OutOfMemory MUST be returned.
32///
33/// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
34/// otherwise, the length must be aligned to `len_align`. Note that `len_align` does *not*
35/// provide a way to modify the alignment of a pointer. Rather it provides an API for
36/// accepting more bytes of memory from the allocator than requested.
37///
38/// `new_len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
39///
40/// `ret_addr` is optionally provided as the first return address of the allocation call stack.
41/// If the value is `0` it means no return address has been provided.
42resizeFn: fn (self: *Allocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) Error!usize,
43
44/// Set to resizeFn if in-place resize is not supported.
45pub fn noResize(
46 self: *Allocator,
47 buf: []u8,
48 buf_align: u29,
49 new_len: usize,
50 len_align: u29,
51 ret_addr: usize,
52) Error!usize {
53 if (new_len > buf.len)
54 return error.OutOfMemory;
55 return new_len;
56}
57
58/// Realloc is used to modify the size or alignment of an existing allocation,
59/// as well as to provide the allocator with an opportunity to move an allocation
60/// to a better location.
61/// When the size/alignment is greater than the previous allocation, this function
62/// returns `error.OutOfMemory` when the requested new allocation could not be granted.
63/// When the size/alignment is less than or equal to the previous allocation,
64/// this function returns `error.OutOfMemory` when the allocator decides the client
65/// would be better off keeping the extra alignment/size. Clients will call
66/// `resizeFn` when they require the allocator to track a new alignment/size,
67/// and so this function should only return success when the allocator considers
68/// the reallocation desirable from the allocator's perspective.
69/// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
70/// reallocation failure, even when `new_n` <= `old_mem.len`. A `FixedBufferAllocator`
71/// would always return `error.OutOfMemory` for `reallocFn` when the size/alignment
72/// is less than or equal to the old allocation, because it cannot reclaim the memory,
73/// and thus the `std.ArrayList` would be better off retaining its capacity.
74/// When `reallocFn` returns,
75/// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same
76/// as `old_mem` was when `reallocFn` is called. The bytes of
77/// `return_value[old_mem.len..]` have undefined values.
78/// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
79fn reallocBytes(
80 self: *Allocator,
81 /// Guaranteed to be the same as what was returned from most recent call to
82 /// `allocFn` or `resizeFn`.
83 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
84 /// is guaranteed to be >= 1.
85 old_mem: []u8,
86 /// If `old_mem.len == 0` then this is `undefined`, otherwise:
87 /// Guaranteed to be the same as what was passed to `allocFn`.
88 /// Guaranteed to be >= 1.
89 /// Guaranteed to be a power of 2.
90 old_alignment: u29,
91 /// If `new_byte_count` is 0 then this is a free and it is guaranteed that
92 /// `old_mem.len != 0`.
93 new_byte_count: usize,
94 /// Guaranteed to be >= 1.
95 /// Guaranteed to be a power of 2.
96 /// Returned slice's pointer must have this alignment.
97 new_alignment: u29,
98 /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
99 /// non-zero means the length of the returned slice must be aligned by `len_align`
100 /// `new_len` must be aligned by `len_align`
101 len_align: u29,
102 return_address: usize,
103) Error![]u8 {
104 if (old_mem.len == 0) {
105 const new_mem = try self.allocFn(self, new_byte_count, new_alignment, len_align, return_address);
106 // TODO: https://github.com/ziglang/zig/issues/4298
107 @memset(new_mem.ptr, undefined, new_byte_count);
108 return new_mem;
109 }
110
111 if (mem.isAligned(@ptrToInt(old_mem.ptr), new_alignment)) {
112 if (new_byte_count <= old_mem.len) {
113 const shrunk_len = self.shrinkBytes(old_mem, old_alignment, new_byte_count, len_align, return_address);
114 return old_mem.ptr[0..shrunk_len];
115 }
116 if (self.resizeFn(self, old_mem, old_alignment, new_byte_count, len_align, return_address)) |resized_len| {
117 assert(resized_len >= new_byte_count);
118 // TODO: https://github.com/ziglang/zig/issues/4298
119 @memset(old_mem.ptr + new_byte_count, undefined, resized_len - new_byte_count);
120 return old_mem.ptr[0..resized_len];
121 } else |_| {}
122 }
123 if (new_byte_count <= old_mem.len and new_alignment <= old_alignment) {
124 return error.OutOfMemory;
125 }
126 return self.moveBytes(old_mem, old_alignment, new_byte_count, new_alignment, len_align, return_address);
127}
128
129/// Move the given memory to a new location in the given allocator to accomodate a new
130/// size and alignment.
131fn moveBytes(
132 self: *Allocator,
133 old_mem: []u8,
134 old_align: u29,
135 new_len: usize,
136 new_alignment: u29,
137 len_align: u29,
138 return_address: usize,
139) Error![]u8 {
140 assert(old_mem.len > 0);
141 assert(new_len > 0);
142 const new_mem = try self.allocFn(self, new_len, new_alignment, len_align, return_address);
143 @memcpy(new_mem.ptr, old_mem.ptr, math.min(new_len, old_mem.len));
144 // TODO DISABLED TO AVOID BUGS IN TRANSLATE C
145 // TODO see also https://github.com/ziglang/zig/issues/4298
146 // use './zig build test-translate-c' to reproduce, some of the symbols in the
147 // generated C code will be a sequence of 0xaa (the undefined value), meaning
148 // it is printing data that has been freed
149 //@memset(old_mem.ptr, undefined, old_mem.len);
150 _ = self.shrinkBytes(old_mem, old_align, 0, 0, return_address);
151 return new_mem;
152}
153
154/// Returns a pointer to undefined memory.
155/// Call `destroy` with the result to free the memory.
156pub fn create(self: *Allocator, comptime T: type) Error!*T {
157 if (@sizeOf(T) == 0) return &(T{});
158 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, .exact, @returnAddress());
159 return &slice[0];
160}
161
162/// `ptr` should be the return value of `create`, or otherwise
163/// have the same address and alignment property.
164pub fn destroy(self: *Allocator, ptr: anytype) void {
165 const T = @TypeOf(ptr).Child;
166 if (@sizeOf(T) == 0) return;
167 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
168 const ptr_align = @typeInfo(@TypeOf(ptr)).Pointer.alignment;
169 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], ptr_align, 0, 0, @returnAddress());
170}
171
172/// Allocates an array of `n` items of type `T` and sets all the
173/// items to `undefined`. Depending on the Allocator
174/// implementation, it may be required to call `free` once the
175/// memory is no longer needed, to avoid a resource leak. If the
176/// `Allocator` implementation is unknown, then correct code will
177/// call `free` when done.
178///
179/// For allocating a single item, see `create`.
180pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T {
181 return self.allocAdvancedWithRetAddr(T, null, n, .exact, @returnAddress());
182}
183
184pub fn allocWithOptions(
185 self: *Allocator,
186 comptime Elem: type,
187 n: usize,
188 /// null means naturally aligned
189 comptime optional_alignment: ?u29,
190 comptime optional_sentinel: ?Elem,
191) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
192 return self.allocWithOptionsRetAddr(Elem, n, optional_alignment, optional_sentinel, @returnAddress());
193}
194
195pub fn allocWithOptionsRetAddr(
196 self: *Allocator,
197 comptime Elem: type,
198 n: usize,
199 /// null means naturally aligned
200 comptime optional_alignment: ?u29,
201 comptime optional_sentinel: ?Elem,
202 return_address: usize,
203) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
204 if (optional_sentinel) |sentinel| {
205 const ptr = try self.allocAdvancedWithRetAddr(Elem, optional_alignment, n + 1, .exact, return_address);
206 ptr[n] = sentinel;
207 return ptr[0..n :sentinel];
208 } else {
209 return self.allocAdvancedWithRetAddr(Elem, optional_alignment, n, .exact, return_address);
210 }
211}
212
213fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, comptime sentinel: ?Elem) type {
214 if (sentinel) |s| {
215 return [:s]align(alignment orelse @alignOf(Elem)) Elem;
216 } else {
217 return []align(alignment orelse @alignOf(Elem)) Elem;
218 }
219}
220
221/// Allocates an array of `n + 1` items of type `T` and sets the first `n`
222/// items to `undefined` and the last item to `sentinel`. Depending on the
223/// Allocator implementation, it may be required to call `free` once the
224/// memory is no longer needed, to avoid a resource leak. If the
225/// `Allocator` implementation is unknown, then correct code will
226/// call `free` when done.
227///
228/// For allocating a single item, see `create`.
229///
230/// Deprecated; use `allocWithOptions`.
231pub fn allocSentinel(
232 self: *Allocator,
233 comptime Elem: type,
234 n: usize,
235 comptime sentinel: Elem,
236) Error![:sentinel]Elem {
237 return self.allocWithOptionsRetAddr(Elem, n, null, sentinel, @returnAddress());
238}
239
240/// Deprecated: use `allocAdvanced`
241pub fn alignedAlloc(
242 self: *Allocator,
243 comptime T: type,
244 /// null means naturally aligned
245 comptime alignment: ?u29,
246 n: usize,
247) Error![]align(alignment orelse @alignOf(T)) T {
248 return self.allocAdvancedWithRetAddr(T, alignment, n, .exact, @returnAddress());
249}
250
251pub fn allocAdvanced(
252 self: *Allocator,
253 comptime T: type,
254 /// null means naturally aligned
255 comptime alignment: ?u29,
256 n: usize,
257 exact: Exact,
258) Error![]align(alignment orelse @alignOf(T)) T {
259 return self.allocAdvancedWithRetAddr(T, alignment, n, exact, @returnAddress());
260}
261
262pub const Exact = enum { exact, at_least };
263
264pub fn allocAdvancedWithRetAddr(
265 self: *Allocator,
266 comptime T: type,
267 /// null means naturally aligned
268 comptime alignment: ?u29,
269 n: usize,
270 exact: Exact,
271 return_address: usize,
272) Error![]align(alignment orelse @alignOf(T)) T {
273 const a = if (alignment) |a| blk: {
274 if (a == @alignOf(T)) return allocAdvancedWithRetAddr(self, T, null, n, exact, return_address);
275 break :blk a;
276 } else @alignOf(T);
277
278 if (n == 0) {
279 return @as([*]align(a) T, undefined)[0..0];
280 }
281
282 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
283 // TODO The `if (alignment == null)` blocks are workarounds for zig not being able to
284 // access certain type information about T without creating a circular dependency in async
285 // functions that heap-allocate their own frame with @Frame(func).
286 const size_of_T = if (alignment == null) @intCast(u29, @divExact(byte_count, n)) else @sizeOf(T);
287 const len_align: u29 = switch (exact) {
288 .exact => 0,
289 .at_least => size_of_T,
290 };
291 const byte_slice = try self.allocFn(self, byte_count, a, len_align, return_address);
292 switch (exact) {
293 .exact => assert(byte_slice.len == byte_count),
294 .at_least => assert(byte_slice.len >= byte_count),
295 }
296 // TODO: https://github.com/ziglang/zig/issues/4298
297 @memset(byte_slice.ptr, undefined, byte_slice.len);
298 if (alignment == null) {
299 // This if block is a workaround (see comment above)
300 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..@divExact(byte_slice.len, @sizeOf(T))];
301 } else {
302 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
303 }
304}
305
306/// Increases or decreases the size of an allocation. It is guaranteed to not move the pointer.
307pub fn resize(self: *Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(old_mem) {
308 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
309 const T = Slice.child;
310 if (new_n == 0) {
311 self.free(old_mem);
312 return &[0]T{};
313 }
314 const old_byte_slice = mem.sliceAsBytes(old_mem);
315 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
316 const rc = try self.resizeFn(self, old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress());
317 assert(rc == new_byte_count);
318 const new_byte_slice = old_mem.ptr[0..new_byte_count];
319 return mem.bytesAsSlice(T, new_byte_slice);
320}
321
322/// This function requests a new byte size for an existing allocation,
323/// which can be larger, smaller, or the same size as the old memory
324/// allocation.
325/// This function is preferred over `shrink`, because it can fail, even
326/// when shrinking. This gives the allocator a chance to perform a
327/// cheap shrink operation if possible, or otherwise return OutOfMemory,
328/// indicating that the caller should keep their capacity, for example
329/// in `std.ArrayList.shrink`.
330/// If you need guaranteed success, call `shrink`.
331/// If `new_n` is 0, this is the same as `free` and it always succeeds.
332pub fn realloc(self: *Allocator, old_mem: anytype, new_n: usize) t: {
333 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
334 break :t Error![]align(Slice.alignment) Slice.child;
335} {
336 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
337 return self.reallocAdvancedWithRetAddr(old_mem, old_alignment, new_n, .exact, @returnAddress());
338}
339
340pub fn reallocAtLeast(self: *Allocator, old_mem: anytype, new_n: usize) t: {
341 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
342 break :t Error![]align(Slice.alignment) Slice.child;
343} {
344 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
345 return self.reallocAdvancedWithRetAddr(old_mem, old_alignment, new_n, .at_least, @returnAddress());
346}
347
348/// This is the same as `realloc`, except caller may additionally request
349/// a new alignment, which can be larger, smaller, or the same as the old
350/// allocation.
351pub fn reallocAdvanced(
352 self: *Allocator,
353 old_mem: anytype,
354 comptime new_alignment: u29,
355 new_n: usize,
356 exact: Exact,
357) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
358 return self.reallocAdvancedWithRetAddr(old_mem, new_alignment, new_n, exact, @returnAddress());
359}
360
361pub fn reallocAdvancedWithRetAddr(
362 self: *Allocator,
363 old_mem: anytype,
364 comptime new_alignment: u29,
365 new_n: usize,
366 exact: Exact,
367 return_address: usize,
368) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
369 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
370 const T = Slice.child;
371 if (old_mem.len == 0) {
372 return self.allocAdvancedWithRetAddr(T, new_alignment, new_n, exact, return_address);
373 }
374 if (new_n == 0) {
375 self.free(old_mem);
376 return @as([*]align(new_alignment) T, undefined)[0..0];
377 }
378
379 const old_byte_slice = mem.sliceAsBytes(old_mem);
380 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
381 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
382 const len_align: u29 = switch (exact) {
383 .exact => 0,
384 .at_least => @sizeOf(T),
385 };
386 const new_byte_slice = try self.reallocBytes(old_byte_slice, Slice.alignment, byte_count, new_alignment, len_align, return_address);
387 return mem.bytesAsSlice(T, @alignCast(new_alignment, new_byte_slice));
388}
389
390/// Prefer calling realloc to shrink if you can tolerate failure, such as
391/// in an ArrayList data structure with a storage capacity.
392/// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.
393/// Returned slice has same alignment as old_mem.
394/// Shrinking to 0 is the same as calling `free`.
395pub fn shrink(self: *Allocator, old_mem: anytype, new_n: usize) t: {
396 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
397 break :t []align(Slice.alignment) Slice.child;
398} {
399 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
400 return self.alignedShrinkWithRetAddr(old_mem, old_alignment, new_n, @returnAddress());
401}
402
403/// This is the same as `shrink`, except caller may additionally request
404/// a new alignment, which must be smaller or the same as the old
405/// allocation.
406pub fn alignedShrink(
407 self: *Allocator,
408 old_mem: anytype,
409 comptime new_alignment: u29,
410 new_n: usize,
411) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
412 return self.alignedShrinkWithRetAddr(old_mem, new_alignment, new_n, @returnAddress());
413}
414
415/// This is the same as `alignedShrink`, except caller may additionally pass
416/// the return address of the first stack frame, which may be relevant for
417/// allocators which collect stack traces.
418pub fn alignedShrinkWithRetAddr(
419 self: *Allocator,
420 old_mem: anytype,
421 comptime new_alignment: u29,
422 new_n: usize,
423 return_address: usize,
424) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
425 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
426 const T = Slice.child;
427
428 if (new_n == old_mem.len)
429 return old_mem;
430 assert(new_n < old_mem.len);
431 assert(new_alignment <= Slice.alignment);
432
433 // Here we skip the overflow checking on the multiplication because
434 // new_n <= old_mem.len and the multiplication didn't overflow for that operation.
435 const byte_count = @sizeOf(T) * new_n;
436
437 const old_byte_slice = mem.sliceAsBytes(old_mem);
438 // TODO: https://github.com/ziglang/zig/issues/4298
439 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);
440 _ = self.shrinkBytes(old_byte_slice, Slice.alignment, byte_count, 0, return_address);
441 return old_mem[0..new_n];
442}
443
444/// Free an array allocated with `alloc`. To free a single item,
445/// see `destroy`.
446pub fn free(self: *Allocator, memory: anytype) void {
447 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
448 const bytes = mem.sliceAsBytes(memory);
449 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
450 if (bytes_len == 0) return;
451 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
452 // TODO: https://github.com/ziglang/zig/issues/4298
453 @memset(non_const_ptr, undefined, bytes_len);
454 _ = self.shrinkBytes(non_const_ptr[0..bytes_len], Slice.alignment, 0, 0, @returnAddress());
455}
456
457/// Copies `m` to newly allocated memory. Caller owns the memory.
458pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T {
459 const new_buf = try allocator.alloc(T, m.len);
460 mem.copy(T, new_buf, m);
461 return new_buf;
462}
463
464/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
465pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T {
466 const new_buf = try allocator.alloc(T, m.len + 1);
467 mem.copy(T, new_buf, m);
468 new_buf[m.len] = 0;
469 return new_buf[0..m.len :0];
470}
471
472/// Call `resizeFn`, but caller guarantees that `new_len` <= `buf.len` meaning
473/// error.OutOfMemory should be impossible.
474/// This function allows a runtime `buf_align` value. Callers should generally prefer
475/// to call `shrink` directly.
476pub fn shrinkBytes(
477 self: *Allocator,
478 buf: []u8,
479 buf_align: u29,
480 new_len: usize,
481 len_align: u29,
482 return_address: usize,
483) usize {
484 assert(new_len <= buf.len);
485 return self.resizeFn(self, buf, buf_align, new_len, len_align, return_address) catch unreachable;
486}
lib/std/meta/trait.zig+3-1
......@@ -269,19 +269,21 @@ pub fn isIndexable(comptime T: type) bool {
269269 }
270270 return true;
271271 }
272 return comptime is(.Array)(T) or is(.Vector)(T);
272 return comptime is(.Array)(T) or is(.Vector)(T) or isTuple(T);
273273}
274274
275275test "std.meta.trait.isIndexable" {
276276 const array = [_]u8{0} ** 10;
277277 const slice = @as([]const u8, &array);
278278 const vector: meta.Vector(2, u32) = [_]u32{0} ** 2;
279 const tuple = .{ 1, 2, 3 };
279280
280281 testing.expect(isIndexable(@TypeOf(array)));
281282 testing.expect(isIndexable(@TypeOf(&array)));
282283 testing.expect(isIndexable(@TypeOf(slice)));
283284 testing.expect(!isIndexable(meta.Child(@TypeOf(slice))));
284285 testing.expect(isIndexable(@TypeOf(vector)));
286 testing.expect(isIndexable(@TypeOf(tuple)));
285287}
286288
287289pub fn isNumber(comptime T: type) bool {
lib/std/mutex.zig+127-143
......@@ -15,8 +15,7 @@ const ResetEvent = std.ResetEvent;
1515/// deadlock detection.
1616///
1717/// Example usage:
18/// var m = Mutex.init();
19/// defer m.deinit();
18/// var m = Mutex{};
2019///
2120/// const lock = m.acquire();
2221/// defer lock.release();
......@@ -30,141 +29,13 @@ const ResetEvent = std.ResetEvent;
3029/// // ... lock not acquired
3130/// }
3231pub const Mutex = if (builtin.single_threaded)
33 struct {
34 lock: @TypeOf(lock_init),
35
36 const lock_init = if (std.debug.runtime_safety) false else {};
37
38 pub const Held = struct {
39 mutex: *Mutex,
40
41 pub fn release(self: Held) void {
42 if (std.debug.runtime_safety) {
43 self.mutex.lock = false;
44 }
45 }
46 };
47
48 /// Create a new mutex in unlocked state.
49 pub fn init() Mutex {
50 return Mutex{ .lock = lock_init };
51 }
52
53 /// Free a mutex created with init. Calling this while the
54 /// mutex is held is illegal behavior.
55 pub fn deinit(self: *Mutex) void {
56 self.* = undefined;
57 }
58
59 /// Try to acquire the mutex without blocking. Returns null if
60 /// the mutex is unavailable. Otherwise returns Held. Call
61 /// release on Held.
62 pub fn tryAcquire(self: *Mutex) ?Held {
63 if (std.debug.runtime_safety) {
64 if (self.lock) return null;
65 self.lock = true;
66 }
67 return Held{ .mutex = self };
68 }
69
70 /// Acquire the mutex. Will deadlock if the mutex is already
71 /// held by the calling thread.
72 pub fn acquire(self: *Mutex) Held {
73 return self.tryAcquire() orelse @panic("deadlock detected");
74 }
75 }
32 Dummy
7633else if (builtin.os.tag == .windows)
77// https://locklessinc.com/articles/keyed_events/
78 extern union {
79 locked: u8,
80 waiters: u32,
81
82 const WAKE = 1 << 8;
83 const WAIT = 1 << 9;
84
85 pub fn init() Mutex {
86 return Mutex{ .waiters = 0 };
87 }
88
89 pub fn deinit(self: *Mutex) void {
90 self.* = undefined;
91 }
92
93 pub fn tryAcquire(self: *Mutex) ?Held {
94 if (@atomicRmw(u8, &self.locked, .Xchg, 1, .Acquire) != 0)
95 return null;
96 return Held{ .mutex = self };
97 }
98
99 pub fn acquire(self: *Mutex) Held {
100 return self.tryAcquire() orelse self.acquireSlow();
101 }
102
103 fn acquireSpinning(self: *Mutex) Held {
104 @setCold(true);
105 while (true) : (SpinLock.yield()) {
106 return self.tryAcquire() orelse continue;
107 }
108 }
109
110 fn acquireSlow(self: *Mutex) Held {
111 // try to use NT keyed events for blocking, falling back to spinlock if unavailable
112 @setCold(true);
113 const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return self.acquireSpinning();
114 const key = @ptrCast(*const c_void, &self.waiters);
115
116 while (true) : (SpinLock.loopHint(1)) {
117 const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);
118
119 // try and take lock if unlocked
120 if ((waiters & 1) == 0) {
121 if (@atomicRmw(u8, &self.locked, .Xchg, 1, .Acquire) == 0) {
122 return Held{ .mutex = self };
123 }
124
125 // otherwise, try and update the waiting count.
126 // then unset the WAKE bit so that another unlocker can wake up a thread.
127 } else if (@cmpxchgWeak(u32, &self.waiters, waiters, (waiters + WAIT) | 1, .Monotonic, .Monotonic) == null) {
128 const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
129 assert(rc == .SUCCESS);
130 _ = @atomicRmw(u32, &self.waiters, .Sub, WAKE, .Monotonic);
131 }
132 }
133 }
134
135 pub const Held = struct {
136 mutex: *Mutex,
137
138 pub fn release(self: Held) void {
139 // unlock without a rmw/cmpxchg instruction
140 @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);
141 const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return;
142 const key = @ptrCast(*const c_void, &self.mutex.waiters);
143
144 while (true) : (SpinLock.loopHint(1)) {
145 const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
146
147 // no one is waiting
148 if (waiters < WAIT) return;
149 // someone grabbed the lock and will do the wake instead
150 if (waiters & 1 != 0) return;
151 // someone else is currently waking up
152 if (waiters & WAKE != 0) return;
153
154 // try to decrease the waiter count & set the WAKE bit meaning a thread is waking up
155 if (@cmpxchgWeak(u32, &self.mutex.waiters, waiters, waiters - WAIT + WAKE, .Release, .Monotonic) == null) {
156 const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
157 assert(rc == .SUCCESS);
158 return;
159 }
160 }
161 }
162 };
163 }
34 WindowsMutex
16435else if (builtin.link_libc or builtin.os.tag == .linux)
16536// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
16637 struct {
167 state: usize,
38 state: usize = 0,
16839
16940 /// number of times to spin trying to acquire the lock.
17041 /// https://webkit.org/blog/6161/locking-in-webkit/
......@@ -179,14 +50,6 @@ else if (builtin.link_libc or builtin.os.tag == .linux)
17950 event: ResetEvent,
18051 };
18152
182 pub fn init() Mutex {
183 return Mutex{ .state = 0 };
184 }
185
186 pub fn deinit(self: *Mutex) void {
187 self.* = undefined;
188 }
189
19053 pub fn tryAcquire(self: *Mutex) ?Held {
19154 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
19255 return null;
......@@ -298,6 +161,128 @@ else if (builtin.link_libc or builtin.os.tag == .linux)
298161else
299162 SpinLock;
300163
164/// This has the sematics as `Mutex`, however it does not actually do any
165/// synchronization. Operations are safety-checked no-ops.
166pub const Dummy = struct {
167 lock: @TypeOf(lock_init) = lock_init,
168
169 const lock_init = if (std.debug.runtime_safety) false else {};
170
171 pub const Held = struct {
172 mutex: *Dummy,
173
174 pub fn release(self: Held) void {
175 if (std.debug.runtime_safety) {
176 self.mutex.lock = false;
177 }
178 }
179 };
180
181 /// Create a new mutex in unlocked state.
182 pub const init = Dummy{};
183
184 /// Try to acquire the mutex without blocking. Returns null if
185 /// the mutex is unavailable. Otherwise returns Held. Call
186 /// release on Held.
187 pub fn tryAcquire(self: *Dummy) ?Held {
188 if (std.debug.runtime_safety) {
189 if (self.lock) return null;
190 self.lock = true;
191 }
192 return Held{ .mutex = self };
193 }
194
195 /// Acquire the mutex. Will deadlock if the mutex is already
196 /// held by the calling thread.
197 pub fn acquire(self: *Dummy) Held {
198 return self.tryAcquire() orelse @panic("deadlock detected");
199 }
200};
201
202// https://locklessinc.com/articles/keyed_events/
203const WindowsMutex = struct {
204 state: State = State{ .waiters = 0 },
205
206 const State = extern union {
207 locked: u8,
208 waiters: u32,
209 };
210
211 const WAKE = 1 << 8;
212 const WAIT = 1 << 9;
213
214 pub fn tryAcquire(self: *WindowsMutex) ?Held {
215 if (@atomicRmw(u8, &self.state.locked, .Xchg, 1, .Acquire) != 0)
216 return null;
217 return Held{ .mutex = self };
218 }
219
220 pub fn acquire(self: *WindowsMutex) Held {
221 return self.tryAcquire() orelse self.acquireSlow();
222 }
223
224 fn acquireSpinning(self: *WindowsMutex) Held {
225 @setCold(true);
226 while (true) : (SpinLock.yield()) {
227 return self.tryAcquire() orelse continue;
228 }
229 }
230
231 fn acquireSlow(self: *WindowsMutex) Held {
232 // try to use NT keyed events for blocking, falling back to spinlock if unavailable
233 @setCold(true);
234 const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return self.acquireSpinning();
235 const key = @ptrCast(*const c_void, &self.state.waiters);
236
237 while (true) : (SpinLock.loopHint(1)) {
238 const waiters = @atomicLoad(u32, &self.state.waiters, .Monotonic);
239
240 // try and take lock if unlocked
241 if ((waiters & 1) == 0) {
242 if (@atomicRmw(u8, &self.state.locked, .Xchg, 1, .Acquire) == 0) {
243 return Held{ .mutex = self };
244 }
245
246 // otherwise, try and update the waiting count.
247 // then unset the WAKE bit so that another unlocker can wake up a thread.
248 } else if (@cmpxchgWeak(u32, &self.state.waiters, waiters, (waiters + WAIT) | 1, .Monotonic, .Monotonic) == null) {
249 const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
250 assert(rc == .SUCCESS);
251 _ = @atomicRmw(u32, &self.state.waiters, .Sub, WAKE, .Monotonic);
252 }
253 }
254 }
255
256 pub const Held = struct {
257 mutex: *WindowsMutex,
258
259 pub fn release(self: Held) void {
260 // unlock without a rmw/cmpxchg instruction
261 @atomicStore(u8, @ptrCast(*u8, &self.mutex.state.locked), 0, .Release);
262 const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return;
263 const key = @ptrCast(*const c_void, &self.mutex.state.waiters);
264
265 while (true) : (SpinLock.loopHint(1)) {
266 const waiters = @atomicLoad(u32, &self.mutex.state.waiters, .Monotonic);
267
268 // no one is waiting
269 if (waiters < WAIT) return;
270 // someone grabbed the lock and will do the wake instead
271 if (waiters & 1 != 0) return;
272 // someone else is currently waking up
273 if (waiters & WAKE != 0) return;
274
275 // try to decrease the waiter count & set the WAKE bit meaning a thread is waking up
276 if (@cmpxchgWeak(u32, &self.mutex.state.waiters, waiters, waiters - WAIT + WAKE, .Release, .Monotonic) == null) {
277 const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
278 assert(rc == .SUCCESS);
279 return;
280 }
281 }
282 }
283 };
284};
285
301286const TestContext = struct {
302287 mutex: *Mutex,
303288 data: i128,
......@@ -306,8 +291,7 @@ const TestContext = struct {
306291};
307292
308293test "std.Mutex" {
309 var mutex = Mutex.init();
310 defer mutex.deinit();
294 var mutex = Mutex{};
311295
312296 var context = TestContext{
313297 .mutex = &mutex,
lib/std/net.zig+9-10
......@@ -77,23 +77,23 @@ pub const Address = extern union {
7777 }
7878
7979 pub fn parseIp6(buf: []const u8, port: u16) !Address {
80 return Address{.in6 = try Ip6Address.parse(buf, port) };
80 return Address{ .in6 = try Ip6Address.parse(buf, port) };
8181 }
8282
8383 pub fn resolveIp6(buf: []const u8, port: u16) !Address {
84 return Address{.in6 = try Ip6Address.resolve(buf, port) };
84 return Address{ .in6 = try Ip6Address.resolve(buf, port) };
8585 }
8686
8787 pub fn parseIp4(buf: []const u8, port: u16) !Address {
88 return Address {.in = try Ip4Address.parse(buf, port) };
88 return Address{ .in = try Ip4Address.parse(buf, port) };
8989 }
9090
9191 pub fn initIp4(addr: [4]u8, port: u16) Address {
92 return Address{.in = Ip4Address.init(addr, port) };
92 return Address{ .in = Ip4Address.init(addr, port) };
9393 }
9494
9595 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
96 return Address{.in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
96 return Address{ .in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
9797 }
9898
9999 pub fn initUnix(path: []const u8) !Address {
......@@ -136,8 +136,8 @@ pub const Address = extern union {
136136 /// on the address family.
137137 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
138138 switch (addr.family) {
139 os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).*} },
140 os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).*} },
139 os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).* } },
140 os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).* } },
141141 else => unreachable,
142142 }
143143 }
......@@ -193,7 +193,7 @@ pub const Ip4Address = extern struct {
193193 .sa = .{
194194 .port = mem.nativeToBig(u16, port),
195195 .addr = undefined,
196 }
196 },
197197 };
198198 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);
199199
......@@ -240,7 +240,7 @@ pub const Ip4Address = extern struct {
240240 }
241241
242242 pub fn init(addr: [4]u8, port: u16) Ip4Address {
243 return Ip4Address {
243 return Ip4Address{
244244 .sa = os.sockaddr_in{
245245 .port = mem.nativeToBig(u16, port),
246246 .addr = @ptrCast(*align(1) const u32, &addr).*,
......@@ -598,7 +598,6 @@ pub const Ip6Address = extern struct {
598598 }
599599};
600600
601
602601pub fn connectUnixSocket(path: []const u8) !fs.File {
603602 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
604603 const sockfd = try os.socket(
lib/std/once.zig+1-1
......@@ -10,7 +10,7 @@ pub fn once(comptime f: fn () void) Once(f) {
1010pub fn Once(comptime f: fn () void) type {
1111 return struct {
1212 done: bool = false,
13 mutex: std.Mutex = std.Mutex.init(),
13 mutex: std.Mutex = std.Mutex{},
1414
1515 /// Call the function `f`.
1616 /// If `call` is invoked multiple times `f` will be executed only the
lib/std/os.zig+128-24
......@@ -4025,23 +4025,15 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
40254025 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
40264026 return realpathW(pathname_w.span(), out_buffer);
40274027 }
4028 if (builtin.os.tag == .linux and !builtin.link_libc) {
4029 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
4028 if (!builtin.link_libc) {
4029 const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;
4030 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
40304031 error.FileLocksNotSupported => unreachable,
40314032 else => |e| return e,
40324033 };
40334034 defer close(fd);
40344035
4035 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
4036 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
4037
4038 const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
4039 switch (err) {
4040 error.UnsupportedReparsePointType => unreachable, // Windows only,
4041 else => |e| return e,
4042 }
4043 };
4044 return target;
4036 return getFdPath(fd, out_buffer);
40454037 }
40464038 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
40474039 EINVAL => unreachable,
......@@ -4060,7 +4052,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
40604052}
40614053
40624054/// Same as `realpath` except `pathname` is UTF16LE-encoded.
4063/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
40644055pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
40654056 const w = windows;
40664057
......@@ -4094,17 +4085,51 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
40944085 };
40954086 defer w.CloseHandle(h_file);
40964087
4097 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
4098 const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
4088 return getFdPath(h_file, out_buffer);
4089}
4090
4091/// Return canonical path of handle `fd`.
4092/// This function is very host-specific and is not universally supported by all hosts.
4093/// For example, while it generally works on Linux, macOS or Windows, it is unsupported
4094/// on FreeBSD, or WASI.
4095pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4096 switch (builtin.os.tag) {
4097 .windows => {
4098 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
4099 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
40994100
4100 // Windows returns \\?\ prepended to the path.
4101 // We strip it to make this function consistent across platforms.
4102 const prefix = [_]u16{ '\\', '\\', '?', '\\' };
4103 const start_index = if (mem.startsWith(u16, wide_slice, &prefix)) prefix.len else 0;
4101 // Trust that Windows gives us valid UTF-16LE.
4102 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
4103 return out_buffer[0..end_index];
4104 },
4105 .macosx, .ios, .watchos, .tvos => {
4106 // On macOS, we can use F_GETPATH fcntl command to query the OS for
4107 // the path to the file descriptor.
4108 @memset(out_buffer, 0, MAX_PATH_BYTES);
4109 switch (errno(system.fcntl(fd, F_GETPATH, out_buffer))) {
4110 0 => {},
4111 EBADF => return error.FileNotFound,
4112 // TODO man pages for fcntl on macOS don't really tell you what
4113 // errno values to expect when command is F_GETPATH...
4114 else => |err| return unexpectedErrno(err),
4115 }
4116 const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
4117 return out_buffer[0..len];
4118 },
4119 .linux => {
4120 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
4121 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
41044122
4105 // Trust that Windows gives us valid UTF-16LE.
4106 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable;
4107 return out_buffer[0..end_index];
4123 const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
4124 switch (err) {
4125 error.UnsupportedReparsePointType => unreachable, // Windows only,
4126 else => |e| return e,
4127 }
4128 };
4129 return target;
4130 },
4131 else => @compileError("querying for canonical path of a handle is unsupported on this host"),
4132 }
41084133}
41094134
41104135/// Spurious wakeups are possible and no precision of timing is guaranteed.
......@@ -4932,6 +4957,85 @@ pub fn sendfile(
49324957 return total_written;
49334958}
49344959
4960pub const CopyFileRangeError = error{
4961 FileTooBig,
4962 InputOutput,
4963 IsDir,
4964 OutOfMemory,
4965 NoSpaceLeft,
4966 Unseekable,
4967 PermissionDenied,
4968 FileBusy,
4969} || PReadError || PWriteError || UnexpectedError;
4970
4971/// Transfer data between file descriptors at specified offsets.
4972/// Returns the number of bytes written, which can less than requested.
4973///
4974/// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
4975/// this is done within the operating system kernel, which can provide better performance
4976/// characteristics than transferring data from kernel to user space and back, such as with
4977/// `pread` and `pwrite` calls.
4978///
4979/// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
4980/// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
4981/// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
4982/// atomicity guarantees no longer apply.
4983///
4984/// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
4985/// The file descriptor seek positions are ignored and not updated.
4986/// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
4987///
4988/// `flags` has different meanings per operating system; refer to the respective man pages.
4989///
4990/// These systems support in-kernel data copying:
4991/// * Linux 4.5 (cross-filesystem 5.3)
4992///
4993/// Other systems fall back to calling `pread` / `pwrite`.
4994///
4995/// Maximum offsets on Linux are `math.maxInt(i64)`.
4996pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
4997 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
4998
4999 // TODO support for other systems than linux
5000 const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false;
5001
5002 if (use_c or try_syscall) {
5003 const sys = if (use_c) std.c else linux;
5004
5005 var off_in_copy = @bitCast(i64, off_in);
5006 var off_out_copy = @bitCast(i64, off_out);
5007
5008 const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
5009
5010 // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018
5011
5012 switch (sys.getErrno(rc)) {
5013 0 => return @intCast(usize, rc),
5014 EBADF => unreachable,
5015 EFBIG => return error.FileTooBig,
5016 EIO => return error.InputOutput,
5017 EISDIR => return error.IsDir,
5018 ENOMEM => return error.OutOfMemory,
5019 ENOSPC => return error.NoSpaceLeft,
5020 EOVERFLOW => return error.Unseekable,
5021 EPERM => return error.PermissionDenied,
5022 ETXTBSY => return error.FileBusy,
5023 EINVAL => {}, // these may not be regular files, try fallback
5024 EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback
5025 ENOSYS => {}, // syscall added in Linux 4.5, use fallback
5026 else => |err| return unexpectedErrno(err),
5027 }
5028 }
5029
5030 var buf: [8 * 4096]u8 = undefined;
5031 const adjusted_count = math.min(buf.len, len);
5032 const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
5033 // TODO without @as the line below fails to compile for wasm32-wasi:
5034 // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
5035 if (amt_read == 0) return @as(usize, 0);
5036 return pwrite(fd_out, buf[0..amt_read], off_out);
5037}
5038
49355039pub const PollError = error{
49365040 /// The kernel had no space to allocate file descriptor tables.
49375041 SystemResources,
......@@ -5204,8 +5308,8 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
52045308 }
52055309}
52065310
5207pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: i32) !fd_t {
5208 const rc = system.signalfd4(fd, mask, flags);
5311pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
5312 const rc = system.signalfd(fd, mask, flags);
52095313 switch (errno(rc)) {
52105314 0 => return @intCast(fd_t, rc),
52115315 EBADF, EINVAL => unreachable,
lib/std/os/bits/linux.zig+1
......@@ -19,6 +19,7 @@ pub usingnamespace switch (builtin.arch) {
1919};
2020
2121pub usingnamespace @import("linux/netlink.zig");
22pub const bpf = @import("linux/bpf.zig");
2223
2324const is_mips = builtin.arch.isMIPS();
2425
lib/std/os/bits/linux/bpf.zig created+606
......@@ -0,0 +1,606 @@
1usingnamespace std.os;
2const std = @import("../../../std.zig");
3
4// instruction classes
5/// jmp mode in word width
6pub const JMP32 = 0x06;
7/// alu mode in double word width
8pub const ALU64 = 0x07;
9
10// ld/ldx fields
11/// double word (64-bit)
12pub const DW = 0x18;
13/// exclusive add
14pub const XADD = 0xc0;
15
16// alu/jmp fields
17/// mov reg to reg
18pub const MOV = 0xb0;
19/// sign extending arithmetic shift right */
20pub const ARSH = 0xc0;
21
22// change endianness of a register
23/// flags for endianness conversion:
24pub const END = 0xd0;
25/// convert to little-endian */
26pub const TO_LE = 0x00;
27/// convert to big-endian
28pub const TO_BE = 0x08;
29pub const FROM_LE = TO_LE;
30pub const FROM_BE = TO_BE;
31
32// jmp encodings
33/// jump != *
34pub const JNE = 0x50;
35/// LT is unsigned, '<'
36pub const JLT = 0xa0;
37/// LE is unsigned, '<=' *
38pub const JLE = 0xb0;
39/// SGT is signed '>', GT in x86
40pub const JSGT = 0x60;
41/// SGE is signed '>=', GE in x86
42pub const JSGE = 0x70;
43/// SLT is signed, '<'
44pub const JSLT = 0xc0;
45/// SLE is signed, '<='
46pub const JSLE = 0xd0;
47/// function call
48pub const CALL = 0x80;
49/// function return
50pub const EXIT = 0x90;
51
52/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
53/// program in this cgroup yields to sub-cgroup program.
54pub const F_ALLOW_OVERRIDE = 0x1;
55/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
56/// that cgroup program gets run in addition to the program in this cgroup.
57pub const F_ALLOW_MULTI = 0x2;
58/// Flag for prog_attach command.
59pub const F_REPLACE = 0x4;
60
61/// If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the verifier
62/// will perform strict alignment checking as if the kernel has been built with
63/// CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, and NET_IP_ALIGN defined to 2.
64pub const F_STRICT_ALIGNMENT = 0x1;
65
66/// If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the verifier will
67/// allow any alignment whatsoever. On platforms with strict alignment
68/// requirements for loads ands stores (such as sparc and mips) the verifier
69/// validates that all loads and stores provably follow this requirement. This
70/// flag turns that checking and enforcement off.
71///
72/// It is mostly used for testing when we want to validate the context and
73/// memory access aspects of the verifier, but because of an unaligned access
74/// the alignment check would trigger before the one we are interested in.
75pub const F_ANY_ALIGNMENT = 0x2;
76
77/// BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose.
78/// Verifier does sub-register def/use analysis and identifies instructions
79/// whose def only matters for low 32-bit, high 32-bit is never referenced later
80/// through implicit zero extension. Therefore verifier notifies JIT back-ends
81/// that it is safe to ignore clearing high 32-bit for these instructions. This
82/// saves some back-ends a lot of code-gen. However such optimization is not
83/// necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends
84/// hence hasn't used verifier's analysis result. But, we really want to have a
85/// way to be able to verify the correctness of the described optimization on
86/// x86_64 on which testsuites are frequently exercised.
87///
88/// So, this flag is introduced. Once it is set, verifier will randomize high
89/// 32-bit for those instructions who has been identified as safe to ignore
90/// them. Then, if verifier is not doing correct analysis, such randomization
91/// will regress tests to expose bugs.
92pub const F_TEST_RND_HI32 = 0x4;
93
94/// When BPF ldimm64's insn[0].src_reg != 0 then this can have two extensions:
95/// insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE
96/// insn[0].imm: map fd map fd
97/// insn[1].imm: 0 offset into value
98/// insn[0].off: 0 0
99/// insn[1].off: 0 0
100/// ldimm64 rewrite: address of map address of map[0]+offset
101/// verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE
102pub const PSEUDO_MAP_FD = 1;
103pub const PSEUDO_MAP_VALUE = 2;
104
105/// when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
106/// offset to another bpf function
107pub const PSEUDO_CALL = 1;
108
109/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
110pub const ANY = 0;
111/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
112pub const NOEXIST = 1;
113/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
114pub const EXIST = 2;
115/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
116pub const F_LOCK = 4;
117
118/// flag for BPF_MAP_CREATE command */
119pub const BPF_F_NO_PREALLOC = 0x1;
120/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
121/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
122/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
123/// be moved across different LRU lists.
124pub const BPF_F_NO_COMMON_LRU = 0x2;
125/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
126pub const BPF_F_NUMA_NODE = 0x4;
127/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
128/// syscall side
129pub const BPF_F_RDONLY = 0x8;
130/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
131/// syscall side
132pub const BPF_F_WRONLY = 0x10;
133/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
134/// instead of pointer
135pub const BPF_F_STACK_BUILD_ID = 0x20;
136/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
137/// should only be used for testing.
138pub const BPF_F_ZERO_SEED = 0x40;
139/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
140/// side.
141pub const BPF_F_RDONLY_PROG = 0x80;
142/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
143/// side.
144pub const BPF_F_WRONLY_PROG = 0x100;
145/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
146/// socket
147pub const BPF_F_CLONE = 0x200;
148/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
149pub const BPF_F_MMAPABLE = 0x400;
150
151/// a single BPF instruction
152pub const Insn = packed struct {
153 code: u8,
154 dst: u4,
155 src: u4,
156 off: i16,
157 imm: i32,
158
159 /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack
160 /// frame
161 pub const Reg = enum(u4) {
162 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10
163 };
164
165 const alu = 0x04;
166 const jmp = 0x05;
167 const mov = 0xb0;
168 const k = 0;
169 const exit_code = 0x90;
170
171 // TODO: implement more factory functions for the other instructions
172 /// load immediate value into a register
173 pub fn load_imm(dst: Reg, imm: i32) Insn {
174 return Insn{
175 .code = alu | mov | k,
176 .dst = @enumToInt(dst),
177 .src = 0,
178 .off = 0,
179 .imm = imm,
180 };
181 }
182
183 /// exit BPF program
184 pub fn exit() Insn {
185 return Insn{
186 .code = jmp | exit_code,
187 .dst = 0,
188 .src = 0,
189 .off = 0,
190 .imm = 0,
191 };
192 }
193};
194
195pub const Cmd = extern enum(usize) {
196 map_create,
197 map_lookup_elem,
198 map_update_elem,
199 map_delete_elem,
200 map_get_next_key,
201 prog_load,
202 obj_pin,
203 obj_get,
204 prog_attach,
205 prog_detach,
206 prog_test_run,
207 prog_get_next_id,
208 map_get_next_id,
209 prog_get_fd_by_id,
210 map_get_fd_by_id,
211 obj_get_info_by_fd,
212 prog_query,
213 raw_tracepoint_open,
214 btf_load,
215 btf_get_fd_by_id,
216 task_fd_query,
217 map_lookup_and_delete_elem,
218 map_freeze,
219 btf_get_next_id,
220 map_lookup_batch,
221 map_lookup_and_delete_batch,
222 map_update_batch,
223 map_delete_batch,
224 link_create,
225 link_update,
226 link_get_fd_by_id,
227 link_get_next_id,
228 enable_stats,
229 iter_create,
230 link_detach,
231 _,
232};
233
234pub const MapType = extern enum(u32) {
235 unspec,
236 hash,
237 array,
238 prog_array,
239 perf_event_array,
240 percpu_hash,
241 percpu_array,
242 stack_trace,
243 cgroup_array,
244 lru_hash,
245 lru_percpu_hash,
246 lpm_trie,
247 array_of_maps,
248 hash_of_maps,
249 devmap,
250 sockmap,
251 cpumap,
252 xskmap,
253 sockhash,
254 cgroup_storage,
255 reuseport_sockarray,
256 percpu_cgroup_storage,
257 queue,
258 stack,
259 sk_storage,
260 devmap_hash,
261 struct_ops,
262 ringbuf,
263 _,
264};
265
266pub const ProgType = extern enum(u32) {
267 unspec,
268 socket_filter,
269 kprobe,
270 sched_cls,
271 sched_act,
272 tracepoint,
273 xdp,
274 perf_event,
275 cgroup_skb,
276 cgroup_sock,
277 lwt_in,
278 lwt_out,
279 lwt_xmit,
280 sock_ops,
281 sk_skb,
282 cgroup_device,
283 sk_msg,
284 raw_tracepoint,
285 cgroup_sock_addr,
286 lwt_seg6local,
287 lirc_mode2,
288 sk_reuseport,
289 flow_dissector,
290 cgroup_sysctl,
291 raw_tracepoint_writable,
292 cgroup_sockopt,
293 tracing,
294 struct_ops,
295 ext,
296 lsm,
297 sk_lookup,
298};
299
300pub const AttachType = extern enum(u32) {
301 cgroup_inet_ingress,
302 cgroup_inet_egress,
303 cgroup_inet_sock_create,
304 cgroup_sock_ops,
305 sk_skb_stream_parser,
306 sk_skb_stream_verdict,
307 cgroup_device,
308 sk_msg_verdict,
309 cgroup_inet4_bind,
310 cgroup_inet6_bind,
311 cgroup_inet4_connect,
312 cgroup_inet6_connect,
313 cgroup_inet4_post_bind,
314 cgroup_inet6_post_bind,
315 cgroup_udp4_sendmsg,
316 cgroup_udp6_sendmsg,
317 lirc_mode2,
318 flow_dissector,
319 cgroup_sysctl,
320 cgroup_udp4_recvmsg,
321 cgroup_udp6_recvmsg,
322 cgroup_getsockopt,
323 cgroup_setsockopt,
324 trace_raw_tp,
325 trace_fentry,
326 trace_fexit,
327 modify_return,
328 lsm_mac,
329 trace_iter,
330 cgroup_inet4_getpeername,
331 cgroup_inet6_getpeername,
332 cgroup_inet4_getsockname,
333 cgroup_inet6_getsockname,
334 xdp_devmap,
335 cgroup_inet_sock_release,
336 xdp_cpumap,
337 sk_lookup,
338 xdp,
339 _,
340};
341
342const obj_name_len = 16;
343/// struct used by Cmd.map_create command
344pub const MapCreateAttr = extern struct {
345 /// one of MapType
346 map_type: u32,
347 /// size of key in bytes
348 key_size: u32,
349 /// size of value in bytes
350 value_size: u32,
351 /// max number of entries in a map
352 max_entries: u32,
353 /// .map_create related flags
354 map_flags: u32,
355 /// fd pointing to the inner map
356 inner_map_fd: fd_t,
357 /// numa node (effective only if MapCreateFlags.numa_node is set)
358 numa_node: u32,
359 map_name: [obj_name_len]u8,
360 /// ifindex of netdev to create on
361 map_ifindex: u32,
362 /// fd pointing to a BTF type data
363 btf_fd: fd_t,
364 /// BTF type_id of the key
365 btf_key_type_id: u32,
366 /// BTF type_id of the value
367 bpf_value_type_id: u32,
368 /// BTF type_id of a kernel struct stored as the map value
369 btf_vmlinux_value_type_id: u32,
370};
371
372/// struct used by Cmd.map_*_elem commands
373pub const MapElemAttr = extern struct {
374 map_fd: fd_t,
375 key: u64,
376 result: extern union {
377 value: u64,
378 next_key: u64,
379 },
380 flags: u64,
381};
382
383/// struct used by Cmd.map_*_batch commands
384pub const MapBatchAttr = extern struct {
385 /// start batch, NULL to start from beginning
386 in_batch: u64,
387 /// output: next start batch
388 out_batch: u64,
389 keys: u64,
390 values: u64,
391 /// input/output:
392 /// input: # of key/value elements
393 /// output: # of filled elements
394 count: u32,
395 map_fd: fd_t,
396 elem_flags: u64,
397 flags: u64,
398};
399
400/// struct used by Cmd.prog_load command
401pub const ProgLoadAttr = extern struct {
402 /// one of ProgType
403 prog_type: u32,
404 insn_cnt: u32,
405 insns: u64,
406 license: u64,
407 /// verbosity level of verifier
408 log_level: u32,
409 /// size of user buffer
410 log_size: u32,
411 /// user supplied buffer
412 log_buf: u64,
413 /// not used
414 kern_version: u32,
415 prog_flags: u32,
416 prog_name: [obj_name_len]u8,
417 /// ifindex of netdev to prep for. For some prog types expected attach
418 /// type must be known at load time to verify attach type specific parts
419 /// of prog (context accesses, allowed helpers, etc).
420 prog_ifindex: u32,
421 expected_attach_type: u32,
422 /// fd pointing to BTF type data
423 prog_btf_fd: fd_t,
424 /// userspace bpf_func_info size
425 func_info_rec_size: u32,
426 func_info: u64,
427 /// number of bpf_func_info records
428 func_info_cnt: u32,
429 /// userspace bpf_line_info size
430 line_info_rec_size: u32,
431 line_info: u64,
432 /// number of bpf_line_info records
433 line_info_cnt: u32,
434 /// in-kernel BTF type id to attach to
435 attact_btf_id: u32,
436 /// 0 to attach to vmlinux
437 attach_prog_id: u32,
438};
439
440/// struct used by Cmd.obj_* commands
441pub const ObjAttr = extern struct {
442 pathname: u64,
443 bpf_fd: fd_t,
444 file_flags: u32,
445};
446
447/// struct used by Cmd.prog_attach/detach commands
448pub const ProgAttachAttr = extern struct {
449 /// container object to attach to
450 target_fd: fd_t,
451 /// eBPF program to attach
452 attach_bpf_fd: fd_t,
453 attach_type: u32,
454 attach_flags: u32,
455 // TODO: BPF_F_REPLACE flags
456 /// previously attached eBPF program to replace if .replace is used
457 replace_bpf_fd: fd_t,
458};
459
460/// struct used by Cmd.prog_test_run command
461pub const TestAttr = extern struct {
462 prog_fd: fd_t,
463 retval: u32,
464 /// input: len of data_in
465 data_size_in: u32,
466 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
467 data_size_out: u32,
468 data_in: u64,
469 data_out: u64,
470 repeat: u32,
471 duration: u32,
472 /// input: len of ctx_in
473 ctx_size_in: u32,
474 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
475 ctx_size_out: u32,
476 ctx_in: u64,
477 ctx_out: u64,
478};
479
480/// struct used by Cmd.*_get_*_id commands
481pub const GetIdAttr = extern struct {
482 id: extern union {
483 start_id: u32,
484 prog_id: u32,
485 map_id: u32,
486 btf_id: u32,
487 link_id: u32,
488 },
489 next_id: u32,
490 open_flags: u32,
491};
492
493/// struct used by Cmd.obj_get_info_by_fd command
494pub const InfoAttr = extern struct {
495 bpf_fd: fd_t,
496 info_len: u32,
497 info: u64,
498};
499
500/// struct used by Cmd.prog_query command
501pub const QueryAttr = extern struct {
502 /// container object to query
503 target_fd: fd_t,
504 attach_type: u32,
505 query_flags: u32,
506 attach_flags: u32,
507 prog_ids: u64,
508 prog_cnt: u32,
509};
510
511/// struct used by Cmd.raw_tracepoint_open command
512pub const RawTracepointAttr = extern struct {
513 name: u64,
514 prog_fd: fd_t,
515};
516
517/// struct used by Cmd.btf_load command
518pub const BtfLoadAttr = extern struct {
519 btf: u64,
520 btf_log_buf: u64,
521 btf_size: u32,
522 btf_log_size: u32,
523 btf_log_level: u32,
524};
525
526pub const TaskFdQueryAttr = extern struct {
527 /// input: pid
528 pid: pid_t,
529 /// input: fd
530 fd: fd_t,
531 /// input: flags
532 flags: u32,
533 /// input/output: buf len
534 buf_len: u32,
535 /// input/output:
536 /// tp_name for tracepoint
537 /// symbol for kprobe
538 /// filename for uprobe
539 buf: u64,
540 /// output: prod_id
541 prog_id: u32,
542 /// output: BPF_FD_TYPE
543 fd_type: u32,
544 /// output: probe_offset
545 probe_offset: u64,
546 /// output: probe_addr
547 probe_addr: u64,
548};
549
550/// struct used by Cmd.link_create command
551pub const LinkCreateAttr = extern struct {
552 /// eBPF program to attach
553 prog_fd: fd_t,
554 /// object to attach to
555 target_fd: fd_t,
556 attach_type: u32,
557 /// extra flags
558 flags: u32,
559};
560
561/// struct used by Cmd.link_update command
562pub const LinkUpdateAttr = extern struct {
563 link_fd: fd_t,
564 /// new program to update link with
565 new_prog_fd: fd_t,
566 /// extra flags
567 flags: u32,
568 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
569 /// set in flags
570 old_prog_fd: fd_t,
571};
572
573/// struct used by Cmd.enable_stats command
574pub const EnableStatsAttr = extern struct {
575 type: u32,
576};
577
578/// struct used by Cmd.iter_create command
579pub const IterCreateAttr = extern struct {
580 link_fd: fd_t,
581 flags: u32,
582};
583
584pub const Attr = extern union {
585 map_create: MapCreateAttr,
586 map_elem: MapElemAttr,
587 map_batch: MapBatchAttr,
588 prog_load: ProgLoadAttr,
589 obj: ObjAttr,
590 prog_attach: ProgAttachAttr,
591 test_run: TestRunAttr,
592 get_id: GetIdAttr,
593 info: InfoAttr,
594 query: QueryAttr,
595 raw_tracepoint: RawTracepointAttr,
596 btf_load: BtfLoadAttr,
597 task_fd_query: TaskFdQueryAttr,
598 link_create: LinkCreateAttr,
599 link_update: LinkUpdateAttr,
600 enable_stats: EnableStatsAttr,
601 iter_create: IterCreateAttr,
602};
603
604pub fn bpf(cmd: Cmd, attr: *Attr, size: u32) usize {
605 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);
606}
lib/std/os/bits/windows.zig+1-1
......@@ -261,4 +261,4 @@ pub const O_LARGEFILE = 0;
261261pub const O_NOATIME = 0o1000000;
262262pub const O_PATH = 0o10000000;
263263pub const O_TMPFILE = 0o20200000;
264pub const O_NDELAY = O_NONBLOCK;
\ No newline at end of file
264pub const O_NDELAY = O_NONBLOCK;
lib/std/os/linux.zig+13-7
......@@ -1200,13 +1200,19 @@ pub fn ioctl(fd: fd_t, request: u32, arg: usize) usize {
12001200 return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg);
12011201}
12021202
1203pub fn signalfd4(fd: fd_t, mask: *const sigset_t, flags: i32) usize {
1204 return syscall4(
1205 .signalfd4,
1206 @bitCast(usize, @as(isize, fd)),
1207 @ptrToInt(mask),
1208 @bitCast(usize, @as(usize, NSIG / 8)),
1209 @intCast(usize, flags),
1203pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) usize {
1204 return syscall4(.signalfd4, @bitCast(usize, @as(isize, fd)), @ptrToInt(mask), NSIG / 8, flags);
1205}
1206
1207pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: u32) usize {
1208 return syscall6(
1209 .copy_file_range,
1210 @bitCast(usize, @as(isize, fd_in)),
1211 @ptrToInt(off_in),
1212 @bitCast(usize, @as(isize, fd_out)),
1213 @ptrToInt(off_out),
1214 len,
1215 flags,
12101216 );
12111217}
12121218
lib/std/os/test.zig+10-1
......@@ -112,8 +112,11 @@ test "openat smoke test" {
112112test "symlink with relative paths" {
113113 if (builtin.os.tag == .wasi) return error.SkipZigTest;
114114
115 const cwd = fs.cwd();
116 cwd.deleteFile("file.txt") catch {};
117 cwd.deleteFile("symlinked") catch {};
118
115119 // First, try relative paths in cwd
116 var cwd = fs.cwd();
117120 try cwd.writeFile("file.txt", "nonsense");
118121
119122 if (builtin.os.tag == .windows) {
......@@ -519,3 +522,9 @@ test "fcntl" {
519522 expect((flags & os.FD_CLOEXEC) != 0);
520523 }
521524}
525
526test "signalfd" {
527 if (builtin.os.tag != .linux)
528 return error.SkipZigTest;
529 _ = std.os.signalfd;
530}
lib/std/os/windows.zig+144-18
......@@ -51,7 +51,7 @@ pub const OpenFileOptions = struct {
5151 open_dir: bool = false,
5252 /// If false, tries to open path as a reparse point without dereferencing it.
5353 /// Defaults to true.
54 follow_symlinks: bool = true,
54 follow_symlinks: bool = true,
5555};
5656
5757/// TODO when share_access_nonblocking is false, this implementation uses
......@@ -897,30 +897,156 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
897897}
898898
899899pub const GetFinalPathNameByHandleError = error{
900 BadPathName,
900901 FileNotFound,
901 SystemResources,
902902 NameTooLong,
903903 Unexpected,
904904};
905905
906pub fn GetFinalPathNameByHandleW(
906/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
907/// Defaults to DOS volume names.
908pub const GetFinalPathNameByHandleFormat = struct {
909 volume_name: enum {
910 /// Format as DOS volume name
911 Dos,
912 /// Format as NT volume name
913 Nt,
914 } = .Dos,
915};
916
917/// Returns canonical (normalized) path of handle.
918/// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
919/// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
920/// If DOS volume name format is selected, note that this function does *not* prepend
921/// `\\?\` prefix to the resultant path.
922pub fn GetFinalPathNameByHandle(
907923 hFile: HANDLE,
908 buf_ptr: [*]u16,
909 buf_len: DWORD,
910 flags: DWORD,
911) GetFinalPathNameByHandleError![:0]u16 {
912 const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags);
913 if (rc == 0) {
914 switch (kernel32.GetLastError()) {
915 .FILE_NOT_FOUND => return error.FileNotFound,
916 .PATH_NOT_FOUND => return error.FileNotFound,
917 .NOT_ENOUGH_MEMORY => return error.SystemResources,
918 .FILENAME_EXCED_RANGE => return error.NameTooLong,
919 .INVALID_PARAMETER => unreachable,
920 else => |err| return unexpectedError(err),
921 }
924 fmt: GetFinalPathNameByHandleFormat,
925 out_buffer: []u16,
926) GetFinalPathNameByHandleError![]u16 {
927 // Get normalized path; doesn't include volume name though.
928 var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
929 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
930
931 // Get NT volume name.
932 var volume_buffer: [@sizeOf(FILE_NAME_INFORMATION) + MAX_PATH]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; // MAX_PATH bytes should be enough since it's Windows-defined name
933 try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
934
935 const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
936 const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0 .. file_name.FileNameLength / 2];
937
938 const volume_name = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
939
940 switch (fmt.volume_name) {
941 .Nt => {
942 // Nothing to do, we simply copy the bytes to the user-provided buffer.
943 const volume_name_u16 = @ptrCast([*]const u16, &volume_name.FileName[0])[0 .. volume_name.FileNameLength / 2];
944
945 if (out_buffer.len < volume_name_u16.len + file_name_u16.len) return error.NameTooLong;
946
947 std.mem.copy(u16, out_buffer[0..], volume_name_u16);
948 std.mem.copy(u16, out_buffer[volume_name_u16.len..], file_name_u16);
949
950 return out_buffer[0 .. volume_name_u16.len + file_name_u16.len];
951 },
952 .Dos => {
953 // Get DOS volume name. DOS volume names are actually symbolic link objects to the
954 // actual NT volume. For example:
955 // (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
956 const MIN_SIZE = @sizeOf(MOUNTMGR_MOUNT_POINT) + MAX_PATH;
957 // We initialize the input buffer to all zeros for convenience since
958 // `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
959 var input_buf: [MIN_SIZE]u8 align(@alignOf(MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
960 var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(MOUNTMGR_MOUNT_POINTS)) = undefined;
961
962 // This surprising path is a filesystem path to the mount manager on Windows.
963 // Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
964 const mgmt_path = "\\MountPointManager";
965 const mgmt_path_u16 = sliceToPrefixedFileW(mgmt_path) catch unreachable;
966 const mgmt_handle = OpenFile(mgmt_path_u16.span(), .{
967 .access_mask = SYNCHRONIZE,
968 .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
969 .creation = FILE_OPEN,
970 .io_mode = .blocking,
971 }) catch |err| switch (err) {
972 error.IsDir => unreachable,
973 error.NotDir => unreachable,
974 error.NoDevice => unreachable,
975 error.AccessDenied => unreachable,
976 error.PipeBusy => unreachable,
977 error.PathAlreadyExists => unreachable,
978 error.WouldBlock => unreachable,
979 else => |e| return e,
980 };
981 defer CloseHandle(mgmt_handle);
982
983 var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
984 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
985 input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength);
986 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength);
987
988 try DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]);
989 const mount_points_struct = @ptrCast(*const MOUNTMGR_MOUNT_POINTS, &output_buf[0]);
990
991 const mount_points = @ptrCast(
992 [*]const MOUNTMGR_MOUNT_POINT,
993 &mount_points_struct.MountPoints[0],
994 )[0..mount_points_struct.NumberOfMountPoints];
995
996 var found: bool = false;
997 for (mount_points) |mount_point| {
998 const symlink = @ptrCast(
999 [*]const u16,
1000 @alignCast(@alignOf(u16), &output_buf[mount_point.SymbolicLinkNameOffset]),
1001 )[0 .. mount_point.SymbolicLinkNameLength / 2];
1002
1003 // Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
1004 // with traditional DOS drive letters, so pick the first one available.
1005 const prefix_u8 = "\\DosDevices\\";
1006 var prefix_buf_u16: [prefix_u8.len]u16 = undefined;
1007 const prefix_len_u16 = std.unicode.utf8ToUtf16Le(prefix_buf_u16[0..], prefix_u8[0..]) catch unreachable;
1008 const prefix = prefix_buf_u16[0..prefix_len_u16];
1009
1010 if (std.mem.startsWith(u16, symlink, prefix)) {
1011 const drive_letter = symlink[prefix.len..];
1012
1013 if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
1014
1015 std.mem.copy(u16, out_buffer[0..], drive_letter);
1016 std.mem.copy(u16, out_buffer[drive_letter.len..], file_name_u16);
1017 const total_len = drive_letter.len + file_name_u16.len;
1018
1019 // Validate that DOS does not contain any spurious nul bytes.
1020 if (std.mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
1021 return error.BadPathName;
1022 }
1023
1024 return out_buffer[0..total_len];
1025 }
1026 }
1027
1028 // If we've ended up here, then something went wrong/is corrupted in the OS,
1029 // so error out!
1030 return error.FileNotFound;
1031 },
1032 }
1033}
1034
1035pub const QueryInformationFileError = error{Unexpected};
1036
1037pub fn QueryInformationFile(
1038 handle: HANDLE,
1039 info_class: FILE_INFORMATION_CLASS,
1040 out_buffer: []u8,
1041) QueryInformationFileError!void {
1042 var io: IO_STATUS_BLOCK = undefined;
1043 const len_bytes = std.math.cast(u32, out_buffer.len) catch unreachable;
1044 const rc = ntdll.NtQueryInformationFile(handle, &io, out_buffer.ptr, len_bytes, info_class);
1045 switch (rc) {
1046 .SUCCESS => {},
1047 .INVALID_PARAMETER => unreachable,
1048 else => return unexpectedStatus(rc),
9221049 }
923 return buf_ptr[0..rc :0];
9241050}
9251051
9261052pub const GetFileSizeError = error{Unexpected};
lib/std/os/windows/bits.zig+18
......@@ -1573,3 +1573,21 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
15731573
15741574pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
15751575pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
1576
1577pub const MOUNTMGR_MOUNT_POINT = extern struct {
1578 SymbolicLinkNameOffset: ULONG,
1579 SymbolicLinkNameLength: USHORT,
1580 Reserved1: USHORT,
1581 UniqueIdOffset: ULONG,
1582 UniqueIdLength: USHORT,
1583 Reserved2: USHORT,
1584 DeviceNameOffset: ULONG,
1585 DeviceNameLength: USHORT,
1586 Reserved3: USHORT,
1587};
1588pub const MOUNTMGR_MOUNT_POINTS = extern struct {
1589 Size: ULONG,
1590 NumberOfMountPoints: ULONG,
1591 MountPoints: [1]MOUNTMGR_MOUNT_POINT,
1592};
1593pub const IOCTL_MOUNTMGR_QUERY_POINTS: ULONG = 0x6d0008;
lib/std/special/c.zig+3-3
......@@ -536,7 +536,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
536536 // normalize x and y
537537 if (ex == 0) {
538538 i = ux << exp_bits;
539 while (i >> bits_minus_1 == 0) : (b: {
539 while (i >> bits_minus_1 == 0) : ({
540540 ex -= 1;
541541 i <<= 1;
542542 }) {}
......@@ -547,7 +547,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
547547 }
548548 if (ey == 0) {
549549 i = uy << exp_bits;
550 while (i >> bits_minus_1 == 0) : (b: {
550 while (i >> bits_minus_1 == 0) : ({
551551 ey -= 1;
552552 i <<= 1;
553553 }) {}
......@@ -573,7 +573,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
573573 return 0 * x;
574574 ux = i;
575575 }
576 while (ux >> digits == 0) : (b: {
576 while (ux >> digits == 0) : ({
577577 ux <<= 1;
578578 ex -= 1;
579579 }) {}
lib/std/special/compiler_rt/floatditf.zig+1-1
......@@ -18,7 +18,7 @@ pub fn __floatditf(arg: i64) callconv(.C) f128 {
1818 var aAbs = @bitCast(u64, arg);
1919 if (arg < 0) {
2020 sign = 1 << 127;
21 aAbs = ~@bitCast(u64, arg)+ 1;
21 aAbs = ~@bitCast(u64, arg) + 1;
2222 }
2323
2424 // Exponent of (fp_t)a is the width of abs(a).
lib/std/special/test_runner.zig+27-11
......@@ -4,6 +4,8 @@ const builtin = @import("builtin");
44
55pub const io_mode: io.Mode = builtin.test_io_mode;
66
7var log_err_count: usize = 0;
8
79pub fn main() anyerror!void {
810 const test_fn_list = builtin.test_functions;
911 var ok_count: usize = 0;
......@@ -19,15 +21,21 @@ pub fn main() anyerror!void {
1921 // ignores the alignment of the slice.
2022 async_frame_buffer = &[_]u8{};
2123
24 var leaks: usize = 0;
2225 for (test_fn_list) |test_fn, i| {
23 std.testing.base_allocator_instance.reset();
26 std.testing.allocator_instance = .{};
27 defer {
28 if (std.testing.allocator_instance.deinit()) {
29 leaks += 1;
30 }
31 }
2432 std.testing.log_level = .warn;
2533
2634 var test_node = root_node.start(test_fn.name, null);
2735 test_node.activate();
2836 progress.refresh();
2937 if (progress.terminal == null) {
30 std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
38 std.debug.print("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
3139 }
3240 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
3341 .evented => blk: {
......@@ -42,24 +50,20 @@ pub fn main() anyerror!void {
4250 skip_count += 1;
4351 test_node.end();
4452 progress.log("{}...SKIP (async test)\n", .{test_fn.name});
45 if (progress.terminal == null) std.debug.warn("SKIP (async test)\n", .{});
53 if (progress.terminal == null) std.debug.print("SKIP (async test)\n", .{});
4654 continue;
4755 },
4856 } else test_fn.func();
4957 if (result) |_| {
5058 ok_count += 1;
5159 test_node.end();
52 std.testing.allocator_instance.validate() catch |err| switch (err) {
53 error.Leak => std.debug.panic("", .{}),
54 else => std.debug.panic("error.{}", .{@errorName(err)}),
55 };
56 if (progress.terminal == null) std.debug.warn("OK\n", .{});
60 if (progress.terminal == null) std.debug.print("OK\n", .{});
5761 } else |err| switch (err) {
5862 error.SkipZigTest => {
5963 skip_count += 1;
6064 test_node.end();
6165 progress.log("{}...SKIP\n", .{test_fn.name});
62 if (progress.terminal == null) std.debug.warn("SKIP\n", .{});
66 if (progress.terminal == null) std.debug.print("SKIP\n", .{});
6367 },
6468 else => {
6569 progress.log("", .{});
......@@ -69,9 +73,18 @@ pub fn main() anyerror!void {
6973 }
7074 root_node.end();
7175 if (ok_count == test_fn_list.len) {
72 std.debug.warn("All {} tests passed.\n", .{ok_count});
76 std.debug.print("All {} tests passed.\n", .{ok_count});
7377 } else {
74 std.debug.warn("{} passed; {} skipped.\n", .{ ok_count, skip_count });
78 std.debug.print("{} passed; {} skipped.\n", .{ ok_count, skip_count });
79 }
80 if (log_err_count != 0) {
81 std.debug.print("{} errors were logged.\n", .{log_err_count});
82 }
83 if (leaks != 0) {
84 std.debug.print("{} tests leaked memory.\n", .{ok_count});
85 }
86 if (leaks != 0 or log_err_count != 0) {
87 std.process.exit(1);
7588 }
7689}
7790
......@@ -81,6 +94,9 @@ pub fn log(
8194 comptime format: []const u8,
8295 args: anytype,
8396) void {
97 if (@enumToInt(message_level) <= @enumToInt(std.log.Level.err)) {
98 log_err_count += 1;
99 }
84100 if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
85101 std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);
86102 }
lib/std/std.zig+2-1
......@@ -13,7 +13,8 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
1313pub const DynLib = @import("dynamic_library.zig").DynLib;
1414pub const HashMap = hash_map.HashMap;
1515pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
16pub const Mutex = @import("mutex.zig").Mutex;
16pub const mutex = @import("mutex.zig");
17pub const Mutex = mutex.Mutex;
1718pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
1819pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
1920pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
lib/std/target.zig+28
......@@ -100,6 +100,14 @@ pub const Target = struct {
100100 pub fn includesVersion(self: Range, ver: WindowsVersion) bool {
101101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
102102 }
103
104 /// Checks if system is guaranteed to be at least `version` or older than `version`.
105 /// Returns `null` if a runtime check is required.
106 pub fn isAtLeast(self: Range, ver: WindowsVersion) ?bool {
107 if (@enumToInt(self.min) >= @enumToInt(ver)) return true;
108 if (@enumToInt(self.max) < @enumToInt(ver)) return false;
109 return null;
110 }
103111 };
104112
105113 /// This function is defined to serialize a Zig source code representation of this
......@@ -135,6 +143,12 @@ pub const Target = struct {
135143 pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
136144 return self.range.includesVersion(ver);
137145 }
146
147 /// Checks if system is guaranteed to be at least `version` or older than `version`.
148 /// Returns `null` if a runtime check is required.
149 pub fn isAtLeast(self: LinuxVersionRange, ver: Version) ?bool {
150 return self.range.isAtLeast(ver);
151 }
138152 };
139153
140154 /// The version ranges here represent the minimum OS version to be supported
......@@ -158,6 +172,8 @@ pub const Target = struct {
158172 ///
159173 /// Binaries built with a given maximum version will continue to function on newer operating system
160174 /// versions. However, such a binary may not take full advantage of the newer operating system APIs.
175 ///
176 /// See `Os.isAtLeast`.
161177 pub const VersionRange = union {
162178 none: void,
163179 semver: Version.Range,
......@@ -273,6 +289,18 @@ pub const Target = struct {
273289 };
274290 }
275291
292 /// Checks if system is guaranteed to be at least `version` or older than `version`.
293 /// Returns `null` if a runtime check is required.
294 pub fn isAtLeast(self: Os, comptime tag: Tag, version: anytype) ?bool {
295 if (self.tag != tag) return false;
296
297 return switch (tag) {
298 .linux => self.version_range.linux.isAtLeast(version),
299 .windows => self.version_range.windows.isAtLeast(version),
300 else => self.version_range.semver.isAtLeast(version),
301 };
302 }
303
276304 pub fn requiresLibC(os: Os) bool {
277305 return switch (os.tag) {
278306 .freebsd,
lib/std/target/powerpc.zig+28-28
......@@ -447,8 +447,8 @@ pub const all_features = blk: {
447447};
448448
449449pub const cpu = struct {
450 pub const @"440" = CpuModel{
451 .name = "440",
450 pub const @"ppc440" = CpuModel{
451 .name = "ppc440",
452452 .llvm_name = "440",
453453 .features = featureSet(&[_]Feature{
454454 .booke,
......@@ -459,8 +459,8 @@ pub const cpu = struct {
459459 .msync,
460460 }),
461461 };
462 pub const @"450" = CpuModel{
463 .name = "450",
462 pub const @"ppc450" = CpuModel{
463 .name = "ppc450",
464464 .llvm_name = "450",
465465 .features = featureSet(&[_]Feature{
466466 .booke,
......@@ -471,70 +471,70 @@ pub const cpu = struct {
471471 .msync,
472472 }),
473473 };
474 pub const @"601" = CpuModel{
475 .name = "601",
474 pub const @"ppc601" = CpuModel{
475 .name = "ppc601",
476476 .llvm_name = "601",
477477 .features = featureSet(&[_]Feature{
478478 .fpu,
479479 }),
480480 };
481 pub const @"602" = CpuModel{
482 .name = "602",
481 pub const @"ppc602" = CpuModel{
482 .name = "ppc602",
483483 .llvm_name = "602",
484484 .features = featureSet(&[_]Feature{
485485 .fpu,
486486 }),
487487 };
488 pub const @"603" = CpuModel{
489 .name = "603",
488 pub const @"ppc603" = CpuModel{
489 .name = "ppc603",
490490 .llvm_name = "603",
491491 .features = featureSet(&[_]Feature{
492492 .fres,
493493 .frsqrte,
494494 }),
495495 };
496 pub const @"603e" = CpuModel{
497 .name = "603e",
496 pub const @"ppc603e" = CpuModel{
497 .name = "ppc603e",
498498 .llvm_name = "603e",
499499 .features = featureSet(&[_]Feature{
500500 .fres,
501501 .frsqrte,
502502 }),
503503 };
504 pub const @"603ev" = CpuModel{
505 .name = "603ev",
504 pub const @"ppc603ev" = CpuModel{
505 .name = "ppc603ev",
506506 .llvm_name = "603ev",
507507 .features = featureSet(&[_]Feature{
508508 .fres,
509509 .frsqrte,
510510 }),
511511 };
512 pub const @"604" = CpuModel{
513 .name = "604",
512 pub const @"ppc604" = CpuModel{
513 .name = "ppc604",
514514 .llvm_name = "604",
515515 .features = featureSet(&[_]Feature{
516516 .fres,
517517 .frsqrte,
518518 }),
519519 };
520 pub const @"604e" = CpuModel{
521 .name = "604e",
520 pub const @"ppc604e" = CpuModel{
521 .name = "ppc604e",
522522 .llvm_name = "604e",
523523 .features = featureSet(&[_]Feature{
524524 .fres,
525525 .frsqrte,
526526 }),
527527 };
528 pub const @"620" = CpuModel{
529 .name = "620",
528 pub const @"ppc620" = CpuModel{
529 .name = "ppc620",
530530 .llvm_name = "620",
531531 .features = featureSet(&[_]Feature{
532532 .fres,
533533 .frsqrte,
534534 }),
535535 };
536 pub const @"7400" = CpuModel{
537 .name = "7400",
536 pub const @"ppc7400" = CpuModel{
537 .name = "ppc7400",
538538 .llvm_name = "7400",
539539 .features = featureSet(&[_]Feature{
540540 .altivec,
......@@ -542,8 +542,8 @@ pub const cpu = struct {
542542 .frsqrte,
543543 }),
544544 };
545 pub const @"7450" = CpuModel{
546 .name = "7450",
545 pub const @"ppc7450" = CpuModel{
546 .name = "ppc7450",
547547 .llvm_name = "7450",
548548 .features = featureSet(&[_]Feature{
549549 .altivec,
......@@ -551,16 +551,16 @@ pub const cpu = struct {
551551 .frsqrte,
552552 }),
553553 };
554 pub const @"750" = CpuModel{
555 .name = "750",
554 pub const @"ppc750" = CpuModel{
555 .name = "ppc750",
556556 .llvm_name = "750",
557557 .features = featureSet(&[_]Feature{
558558 .fres,
559559 .frsqrte,
560560 }),
561561 };
562 pub const @"970" = CpuModel{
563 .name = "970",
562 pub const @"ppc970" = CpuModel{
563 .name = "ppc970",
564564 .llvm_name = "970",
565565 .features = featureSet(&[_]Feature{
566566 .@"64bit",
lib/std/testing.zig+14-16
......@@ -1,18 +1,16 @@
11const std = @import("std.zig");
2const warn = std.debug.warn;
2const print = std.debug.print;
33
4pub const LeakCountAllocator = @import("testing/leak_count_allocator.zig").LeakCountAllocator;
54pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator;
65
76/// This should only be used in temporary test programs.
87pub const allocator = &allocator_instance.allocator;
9pub var allocator_instance = LeakCountAllocator.init(&base_allocator_instance.allocator);
8pub var allocator_instance = std.heap.GeneralPurposeAllocator(.{}){};
109
1110pub const failing_allocator = &failing_allocator_instance.allocator;
1211pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1312
14pub var base_allocator_instance = std.mem.validationWrap(std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]));
15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
13pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
1614
1715/// TODO https://github.com/ziglang/zig/issues/5738
1816pub var log_level = std.log.Level.warn;
......@@ -326,22 +324,22 @@ test "expectEqual vector" {
326324
327325pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {
328326 if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
329 warn("\n====== expected this output: =========\n", .{});
327 print("\n====== expected this output: =========\n", .{});
330328 printWithVisibleNewlines(expected);
331 warn("\n======== instead found this: =========\n", .{});
329 print("\n======== instead found this: =========\n", .{});
332330 printWithVisibleNewlines(actual);
333 warn("\n======================================\n", .{});
331 print("\n======================================\n", .{});
334332
335333 var diff_line_number: usize = 1;
336334 for (expected[0..diff_index]) |value| {
337335 if (value == '\n') diff_line_number += 1;
338336 }
339 warn("First difference occurs on line {}:\n", .{diff_line_number});
337 print("First difference occurs on line {}:\n", .{diff_line_number});
340338
341 warn("expected:\n", .{});
339 print("expected:\n", .{});
342340 printIndicatorLine(expected, diff_index);
343341
344 warn("found:\n", .{});
342 print("found:\n", .{});
345343 printIndicatorLine(actual, diff_index);
346344
347345 @panic("test failure");
......@@ -362,9 +360,9 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
362360 {
363361 var i: usize = line_begin_index;
364362 while (i < indicator_index) : (i += 1)
365 warn(" ", .{});
363 print(" ", .{});
366364 }
367 warn("^\n", .{});
365 print("^\n", .{});
368366}
369367
370368fn printWithVisibleNewlines(source: []const u8) void {
......@@ -372,15 +370,15 @@ fn printWithVisibleNewlines(source: []const u8) void {
372370 while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) {
373371 printLine(source[i .. i + nl]);
374372 }
375 warn("{}␃\n", .{source[i..]}); // End of Text symbol (ETX)
373 print("{}␃\n", .{source[i..]}); // End of Text symbol (ETX)
376374}
377375
378376fn printLine(line: []const u8) void {
379377 if (line.len != 0) switch (line[line.len - 1]) {
380 ' ', '\t' => warn("{}⏎\n", .{line}), // Carriage return symbol,
378 ' ', '\t' => print("{}⏎\n", .{line}), // Carriage return symbol,
381379 else => {},
382380 };
383 warn("{}\n", .{line});
381 print("{}\n", .{line});
384382}
385383
386384test "" {
lib/std/testing/failing_allocator.zig+17-4
......@@ -45,21 +45,34 @@ pub const FailingAllocator = struct {
4545 };
4646 }
4747
48 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
48 fn alloc(
49 allocator: *std.mem.Allocator,
50 len: usize,
51 ptr_align: u29,
52 len_align: u29,
53 return_address: usize,
54 ) error{OutOfMemory}![]u8 {
4955 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
5056 if (self.index == self.fail_index) {
5157 return error.OutOfMemory;
5258 }
53 const result = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
59 const result = try self.internal_allocator.allocFn(self.internal_allocator, len, ptr_align, len_align, return_address);
5460 self.allocated_bytes += result.len;
5561 self.allocations += 1;
5662 self.index += 1;
5763 return result;
5864 }
5965
60 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
66 fn resize(
67 allocator: *std.mem.Allocator,
68 old_mem: []u8,
69 old_align: u29,
70 new_len: usize,
71 len_align: u29,
72 ra: usize,
73 ) error{OutOfMemory}!usize {
6174 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
62 const r = self.internal_allocator.callResizeFn(old_mem, new_len, len_align) catch |e| {
75 const r = self.internal_allocator.resizeFn(self.internal_allocator, old_mem, old_align, new_len, len_align, ra) catch |e| {
6376 std.debug.assert(new_len > old_mem.len);
6477 return e;
6578 };
lib/std/testing/leak_count_allocator.zig deleted-51
......@@ -1,51 +0,0 @@
1const std = @import("../std.zig");
2
3/// This allocator is used in front of another allocator and counts the numbers of allocs and frees.
4/// The test runner asserts every alloc has a corresponding free at the end of each test.
5///
6/// The detection algorithm is incredibly primitive and only accounts for number of calls.
7/// This should be replaced by the general purpose debug allocator.
8pub const LeakCountAllocator = struct {
9 count: usize,
10 allocator: std.mem.Allocator,
11 internal_allocator: *std.mem.Allocator,
12
13 pub fn init(allocator: *std.mem.Allocator) LeakCountAllocator {
14 return .{
15 .count = 0,
16 .allocator = .{
17 .allocFn = alloc,
18 .resizeFn = resize,
19 },
20 .internal_allocator = allocator,
21 };
22 }
23
24 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
25 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
26 const ptr = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
27 self.count += 1;
28 return ptr;
29 }
30
31 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
32 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
33 if (new_size == 0) {
34 if (self.count == 0) {
35 std.debug.panic("error - too many calls to free, most likely double free", .{});
36 }
37 self.count -= 1;
38 }
39 return self.internal_allocator.callResizeFn(old_mem, new_size, len_align) catch |e| {
40 std.debug.assert(new_size > old_mem.len);
41 return e;
42 };
43 }
44
45 pub fn validate(self: LeakCountAllocator) !void {
46 if (self.count > 0) {
47 std.debug.warn("error - detected leaked allocations without matching free: {}\n", .{self.count});
48 return error.Leak;
49 }
50 }
51};
lib/std/zig/ast.zig+88-20
......@@ -526,18 +526,19 @@ pub const Node = struct {
526526 Comptime,
527527 Nosuspend,
528528 Block,
529 LabeledBlock,
529530
530531 // Misc
531532 DocComment,
532 SwitchCase,
533 SwitchElse,
534 Else,
535 Payload,
536 PointerPayload,
537 PointerIndexPayload,
533 SwitchCase, // TODO make this not a child of AST Node
534 SwitchElse, // TODO make this not a child of AST Node
535 Else, // TODO make this not a child of AST Node
536 Payload, // TODO make this not a child of AST Node
537 PointerPayload, // TODO make this not a child of AST Node
538 PointerIndexPayload, // TODO make this not a child of AST Node
538539 ContainerField,
539 ErrorTag,
540 FieldInitializer,
540 ErrorTag, // TODO make this not a child of AST Node
541 FieldInitializer, // TODO make this not a child of AST Node
541542
542543 pub fn Type(tag: Tag) type {
543544 return switch (tag) {
......@@ -654,6 +655,7 @@ pub const Node = struct {
654655 .Comptime => Comptime,
655656 .Nosuspend => Nosuspend,
656657 .Block => Block,
658 .LabeledBlock => LabeledBlock,
657659 .DocComment => DocComment,
658660 .SwitchCase => SwitchCase,
659661 .SwitchElse => SwitchElse,
......@@ -666,6 +668,13 @@ pub const Node = struct {
666668 .FieldInitializer => FieldInitializer,
667669 };
668670 }
671
672 pub fn isBlock(tag: Tag) bool {
673 return switch (tag) {
674 .Block, .LabeledBlock => true,
675 else => false,
676 };
677 }
669678 };
670679
671680 /// Prefer `castTag` to this.
......@@ -729,6 +738,7 @@ pub const Node = struct {
729738 .Root,
730739 .ContainerField,
731740 .Block,
741 .LabeledBlock,
732742 .Payload,
733743 .PointerPayload,
734744 .PointerIndexPayload,
......@@ -739,6 +749,7 @@ pub const Node = struct {
739749 .DocComment,
740750 .TestDecl,
741751 => return false,
752
742753 .While => {
743754 const while_node = @fieldParentPtr(While, "base", n);
744755 if (while_node.@"else") |@"else"| {
......@@ -746,7 +757,7 @@ pub const Node = struct {
746757 continue;
747758 }
748759
749 return while_node.body.tag != .Block;
760 return !while_node.body.tag.isBlock();
750761 },
751762 .For => {
752763 const for_node = @fieldParentPtr(For, "base", n);
......@@ -755,7 +766,7 @@ pub const Node = struct {
755766 continue;
756767 }
757768
758 return for_node.body.tag != .Block;
769 return !for_node.body.tag.isBlock();
759770 },
760771 .If => {
761772 const if_node = @fieldParentPtr(If, "base", n);
......@@ -764,7 +775,7 @@ pub const Node = struct {
764775 continue;
765776 }
766777
767 return if_node.body.tag != .Block;
778 return !if_node.body.tag.isBlock();
768779 },
769780 .Else => {
770781 const else_node = @fieldParentPtr(Else, "base", n);
......@@ -773,29 +784,40 @@ pub const Node = struct {
773784 },
774785 .Defer => {
775786 const defer_node = @fieldParentPtr(Defer, "base", n);
776 return defer_node.expr.tag != .Block;
787 return !defer_node.expr.tag.isBlock();
777788 },
778789 .Comptime => {
779790 const comptime_node = @fieldParentPtr(Comptime, "base", n);
780 return comptime_node.expr.tag != .Block;
791 return !comptime_node.expr.tag.isBlock();
781792 },
782793 .Suspend => {
783794 const suspend_node = @fieldParentPtr(Suspend, "base", n);
784795 if (suspend_node.body) |body| {
785 return body.tag != .Block;
796 return !body.tag.isBlock();
786797 }
787798
788799 return true;
789800 },
790801 .Nosuspend => {
791802 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
792 return nosuspend_node.expr.tag != .Block;
803 return !nosuspend_node.expr.tag.isBlock();
793804 },
794805 else => return true,
795806 }
796807 }
797808 }
798809
810 /// Asserts the node is a Block or LabeledBlock and returns the statements slice.
811 pub fn blockStatements(base: *Node) []*Node {
812 if (base.castTag(.Block)) |block| {
813 return block.statements();
814 } else if (base.castTag(.LabeledBlock)) |labeled_block| {
815 return labeled_block.statements();
816 } else {
817 unreachable;
818 }
819 }
820
799821 pub fn dump(self: *Node, indent: usize) void {
800822 {
801823 var i: usize = 0;
......@@ -1460,7 +1482,6 @@ pub const Node = struct {
14601482 statements_len: NodeIndex,
14611483 lbrace: TokenIndex,
14621484 rbrace: TokenIndex,
1463 label: ?TokenIndex,
14641485
14651486 /// After this the caller must initialize the statements list.
14661487 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {
......@@ -1483,10 +1504,6 @@ pub const Node = struct {
14831504 }
14841505
14851506 pub fn firstToken(self: *const Block) TokenIndex {
1486 if (self.label) |label| {
1487 return label;
1488 }
1489
14901507 return self.lbrace;
14911508 }
14921509
......@@ -1509,6 +1526,57 @@ pub const Node = struct {
15091526 }
15101527 };
15111528
1529 /// The statements of the block follow LabeledBlock directly in memory.
1530 pub const LabeledBlock = struct {
1531 base: Node = Node{ .tag = .LabeledBlock },
1532 statements_len: NodeIndex,
1533 lbrace: TokenIndex,
1534 rbrace: TokenIndex,
1535 label: TokenIndex,
1536
1537 /// After this the caller must initialize the statements list.
1538 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock {
1539 const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len));
1540 return @ptrCast(*LabeledBlock, bytes.ptr);
1541 }
1542
1543 pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void {
1544 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
1545 allocator.free(bytes);
1546 }
1547
1548 pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node {
1549 var i = index;
1550
1551 if (i < self.statements_len) return self.statementsConst()[i];
1552 i -= self.statements_len;
1553
1554 return null;
1555 }
1556
1557 pub fn firstToken(self: *const LabeledBlock) TokenIndex {
1558 return self.label;
1559 }
1560
1561 pub fn lastToken(self: *const LabeledBlock) TokenIndex {
1562 return self.rbrace;
1563 }
1564
1565 pub fn statements(self: *LabeledBlock) []*Node {
1566 const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock);
1567 return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
1568 }
1569
1570 pub fn statementsConst(self: *const LabeledBlock) []const *Node {
1571 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock);
1572 return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
1573 }
1574
1575 fn sizeInBytes(statements_len: NodeIndex) usize {
1576 return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len);
1577 }
1578 };
1579
15121580 pub const Defer = struct {
15131581 base: Node = Node{ .tag = .Defer },
15141582 defer_token: TokenIndex,
lib/std/zig/parse.zig+37-29
......@@ -364,9 +364,10 @@ const Parser = struct {
364364 const name_node = try p.expectNode(parseStringLiteralSingle, .{
365365 .ExpectedStringLiteral = .{ .token = p.tok_i },
366366 });
367 const block_node = try p.expectNode(parseBlock, .{
368 .ExpectedLBrace = .{ .token = p.tok_i },
369 });
367 const block_node = (try p.parseBlock(null)) orelse {
368 try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } });
369 return error.ParseError;
370 };
370371
371372 const test_node = try p.arena.allocator.create(Node.TestDecl);
372373 test_node.* = .{
......@@ -540,12 +541,14 @@ const Parser = struct {
540541 if (p.eatToken(.Semicolon)) |_| {
541542 break :blk null;
542543 }
543 break :blk try p.expectNodeRecoverable(parseBlock, .{
544 const body_block = (try p.parseBlock(null)) orelse {
544545 // Since parseBlock only return error.ParseError on
545546 // a missing '}' we can assume this function was
546547 // supposed to end here.
547 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },
548 });
548 try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });
549 break :blk null;
550 };
551 break :blk body_block;
549552 },
550553 .as_type => null,
551554 };
......@@ -823,10 +826,7 @@ const Parser = struct {
823826 var colon: TokenIndex = undefined;
824827 const label_token = p.parseBlockLabel(&colon);
825828
826 if (try p.parseBlock()) |node| {
827 node.cast(Node.Block).?.label = label_token;
828 return node;
829 }
829 if (try p.parseBlock(label_token)) |node| return node;
830830
831831 if (try p.parseLoopStatement()) |node| {
832832 if (node.cast(Node.For)) |for_node| {
......@@ -1003,14 +1003,13 @@ const Parser = struct {
10031003 fn parseBlockExpr(p: *Parser) Error!?*Node {
10041004 var colon: TokenIndex = undefined;
10051005 const label_token = p.parseBlockLabel(&colon);
1006 const block_node = (try p.parseBlock()) orelse {
1006 const block_node = (try p.parseBlock(label_token)) orelse {
10071007 if (label_token) |label| {
10081008 p.putBackToken(label + 1); // ":"
10091009 p.putBackToken(label); // IDENTIFIER
10101010 }
10111011 return null;
10121012 };
1013 block_node.cast(Node.Block).?.label = label_token;
10141013 return block_node;
10151014 }
10161015
......@@ -1177,7 +1176,7 @@ const Parser = struct {
11771176 p.putBackToken(token); // IDENTIFIER
11781177 }
11791178
1180 if (try p.parseBlock()) |node| return node;
1179 if (try p.parseBlock(null)) |node| return node;
11811180 if (try p.parseCurlySuffixExpr()) |node| return node;
11821181
11831182 return null;
......@@ -1189,7 +1188,7 @@ const Parser = struct {
11891188 }
11901189
11911190 /// Block <- LBRACE Statement* RBRACE
1192 fn parseBlock(p: *Parser) !?*Node {
1191 fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node {
11931192 const lbrace = p.eatToken(.LBrace) orelse return null;
11941193
11951194 var statements = std.ArrayList(*Node).init(p.gpa);
......@@ -1211,16 +1210,26 @@ const Parser = struct {
12111210
12121211 const statements_len = @intCast(NodeIndex, statements.items.len);
12131212
1214 const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len);
1215 block_node.* = .{
1216 .label = null,
1217 .lbrace = lbrace,
1218 .statements_len = statements_len,
1219 .rbrace = rbrace,
1220 };
1221 std.mem.copy(*Node, block_node.statements(), statements.items);
1222
1223 return &block_node.base;
1213 if (label_token) |label| {
1214 const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len);
1215 block_node.* = .{
1216 .label = label,
1217 .lbrace = lbrace,
1218 .statements_len = statements_len,
1219 .rbrace = rbrace,
1220 };
1221 std.mem.copy(*Node, block_node.statements(), statements.items);
1222 return &block_node.base;
1223 } else {
1224 const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len);
1225 block_node.* = .{
1226 .lbrace = lbrace,
1227 .statements_len = statements_len,
1228 .rbrace = rbrace,
1229 };
1230 std.mem.copy(*Node, block_node.statements(), statements.items);
1231 return &block_node.base;
1232 }
12241233 }
12251234
12261235 /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
......@@ -1658,11 +1667,8 @@ const Parser = struct {
16581667 var colon: TokenIndex = undefined;
16591668 const label = p.parseBlockLabel(&colon);
16601669
1661 if (label) |token| {
1662 if (try p.parseBlock()) |node| {
1663 node.cast(Node.Block).?.label = token;
1664 return node;
1665 }
1670 if (label) |label_token| {
1671 if (try p.parseBlock(label_token)) |node| return node;
16661672 }
16671673
16681674 if (try p.parseLoopTypeExpr()) |node| {
......@@ -3440,6 +3446,7 @@ const Parser = struct {
34403446 }
34413447 }
34423448
3449 /// TODO Delete this function. I don't like the inversion of control.
34433450 fn expectNode(
34443451 p: *Parser,
34453452 parseFn: NodeParseFn,
......@@ -3449,6 +3456,7 @@ const Parser = struct {
34493456 return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError;
34503457 }
34513458
3459 /// TODO Delete this function. I don't like the inversion of control.
34523460 fn expectNodeRecoverable(
34533461 p: *Parser,
34543462 parseFn: NodeParseFn,
lib/std/zig/render.zig+32-9
......@@ -392,28 +392,50 @@ fn renderExpression(
392392 return renderToken(tree, stream, any_type.token, indent, start_col, space);
393393 },
394394
395 .Block => {
396 const block = @fieldParentPtr(ast.Node.Block, "base", base);
395 .Block, .LabeledBlock => {
396 const block: struct {
397 label: ?ast.TokenIndex,
398 statements: []*ast.Node,
399 lbrace: ast.TokenIndex,
400 rbrace: ast.TokenIndex,
401 } = b: {
402 if (base.castTag(.Block)) |block| {
403 break :b .{
404 .label = null,
405 .statements = block.statements(),
406 .lbrace = block.lbrace,
407 .rbrace = block.rbrace,
408 };
409 } else if (base.castTag(.LabeledBlock)) |block| {
410 break :b .{
411 .label = block.label,
412 .statements = block.statements(),
413 .lbrace = block.lbrace,
414 .rbrace = block.rbrace,
415 };
416 } else {
417 unreachable;
418 }
419 };
397420
398421 if (block.label) |label| {
399422 try renderToken(tree, stream, label, indent, start_col, Space.None);
400423 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);
401424 }
402425
403 if (block.statements_len == 0) {
426 if (block.statements.len == 0) {
404427 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);
405428 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
406429 } else {
407430 const block_indent = indent + indent_delta;
408431 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);
409432
410 const block_statements = block.statements();
411 for (block_statements) |statement, i| {
433 for (block.statements) |statement, i| {
412434 try stream.writeByteNTimes(' ', block_indent);
413435 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);
414436
415 if (i + 1 < block_statements.len) {
416 try renderExtraNewline(tree, stream, start_col, block_statements[i + 1]);
437 if (i + 1 < block.statements.len) {
438 try renderExtraNewline(tree, stream, start_col, block.statements[i + 1]);
417439 }
418440 }
419441
......@@ -1841,7 +1863,7 @@ fn renderExpression(
18411863
18421864 const rparen = tree.nextToken(for_node.array_expr.lastToken());
18431865
1844 const body_is_block = for_node.body.tag == .Block;
1866 const body_is_block = for_node.body.tag.isBlock();
18451867 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
18461868 const body_on_same_line = body_is_block or src_one_line_to_body;
18471869
......@@ -2385,7 +2407,7 @@ fn renderTokenOffset(
23852407 }
23862408 }
23872409
2388 if (next_token_id != .LineComment) blk: {
2410 if (next_token_id != .LineComment) {
23892411 switch (space) {
23902412 Space.None, Space.NoNewline => return,
23912413 Space.Newline => {
......@@ -2578,6 +2600,7 @@ fn renderDocCommentsToken(
25782600fn nodeIsBlock(base: *const ast.Node) bool {
25792601 return switch (base.tag) {
25802602 .Block,
2603 .LabeledBlock,
25812604 .If,
25822605 .For,
25832606 .While,
src-self-hosted/Module.zig+226-77
......@@ -6,7 +6,7 @@ const Value = @import("value.zig").Value;
66const Type = @import("type.zig").Type;
77const TypedValue = @import("TypedValue.zig");
88const assert = std.debug.assert;
9const log = std.log;
9const log = std.log.scoped(.module);
1010const BigIntConst = std.math.big.int.Const;
1111const BigIntMutable = std.math.big.int.Mutable;
1212const Target = std.Target;
......@@ -177,14 +177,14 @@ pub const Decl = struct {
177177
178178 /// Represents the position of the code in the output file.
179179 /// This is populated regardless of semantic analysis and code generation.
180 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
180 link: link.File.LinkBlock,
181181
182182 /// Represents the function in the linked output file, if the `Decl` is a function.
183183 /// This is stored here and not in `Fn` because `Decl` survives across updates but
184184 /// `Fn` does not.
185185 /// TODO Look into making `Fn` a longer lived structure and moving this field there
186186 /// to save on memory usage.
187 fn_link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,
187 fn_link: link.File.LinkFn,
188188
189189 contents_hash: std.zig.SrcHash,
190190
......@@ -301,6 +301,23 @@ pub const Fn = struct {
301301 body: zir.Module.Body,
302302 arena: std.heap.ArenaAllocator.State,
303303 };
304
305 /// For debugging purposes.
306 pub fn dump(self: *Fn, mod: Module) void {
307 std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
308 switch (self.analysis) {
309 .queued => {
310 std.debug.print("queued\n", .{});
311 },
312 .in_progress => {
313 std.debug.print("in_progress\n", .{});
314 },
315 else => {
316 std.debug.print("\n", .{});
317 zir.dumpFn(mod, self);
318 },
319 }
320 }
304321};
305322
306323pub const Scope = struct {
......@@ -720,6 +737,13 @@ pub const Scope = struct {
720737 arena: *Allocator,
721738 /// The first N instructions in a function body ZIR are arg instructions.
722739 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
740 label: ?Label = null,
741
742 pub const Label = struct {
743 token: ast.TokenIndex,
744 block_inst: *zir.Inst.Block,
745 result_loc: astgen.ResultLoc,
746 };
723747 };
724748
725749 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
......@@ -949,10 +973,8 @@ pub fn update(self: *Module) !void {
949973 try self.deleteDecl(decl);
950974 }
951975
952 if (self.totalErrorCount() == 0) {
953 // This is needed before reading the error flags.
954 try self.bin_file.flush();
955 }
976 // This is needed before reading the error flags.
977 try self.bin_file.flush(self);
956978
957979 self.link_error_flags = self.bin_file.errorFlags();
958980
......@@ -1057,7 +1079,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
10571079 // lifetime annotations in the ZIR.
10581080 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
10591081 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1060 std.log.debug(.module, "analyze liveness of {}\n", .{decl.name});
1082 log.debug("analyze liveness of {}\n", .{decl.name});
10611083 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
10621084 }
10631085
......@@ -1119,7 +1141,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
11191141 .complete => return,
11201142
11211143 .outdated => blk: {
1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});
1144 log.debug("re-analyzing {}\n", .{decl.name});
11231145
11241146 // The exports this Decl performs will be re-discovered, so we remove them here
11251147 // prior to re-analysis.
......@@ -1303,14 +1325,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13031325 for (fn_proto.params()) |param, i| {
13041326 const name_token = param.name_token.?;
13051327 const src = tree.token_locs[name_token].start;
1306 const param_name = tree.tokenSlice(name_token);
1307 const arg = try gen_scope_arena.allocator.create(zir.Inst.NoOp);
1328 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
1329 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
13081330 arg.* = .{
13091331 .base = .{
13101332 .tag = .arg,
13111333 .src = src,
13121334 },
1313 .positionals = .{},
1335 .positionals = .{
1336 .name = param_name,
1337 },
13141338 .kw_args = .{},
13151339 };
13161340 gen_scope.instructions.items[i] = &arg.base;
......@@ -1328,8 +1352,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13281352
13291353 try astgen.blockExpr(self, params_scope, body_block);
13301354
1331 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
1332 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
1355 if (gen_scope.instructions.items.len == 0 or
1356 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
13331357 {
13341358 const src = tree.token_locs[body_block.rbrace].start;
13351359 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
......@@ -1538,10 +1562,16 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15381562 if (!srcHashEql(decl.contents_hash, contents_hash)) {
15391563 try self.markOutdatedDecl(decl);
15401564 decl.contents_hash = contents_hash;
1541 } else if (decl.fn_link.len != 0) {
1542 // TODO Look into detecting when this would be unnecessary by storing enough state
1543 // in `Decl` to notice that the line number did not change.
1544 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1565 } else switch (self.bin_file.tag) {
1566 .elf => if (decl.fn_link.elf.len != 0) {
1567 // TODO Look into detecting when this would be unnecessary by storing enough state
1568 // in `Decl` to notice that the line number did not change.
1569 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1570 },
1571 .macho => {
1572 // TODO Implement for MachO
1573 },
1574 .c, .wasm => {},
15451575 }
15461576 }
15471577 } else {
......@@ -1553,6 +1583,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15531583 }
15541584 }
15551585 }
1586 } else {
1587 std.debug.panic("TODO: analyzeRootSrcFile {}", .{src_decl.tag});
15561588 }
15571589 // TODO also look for global variable declarations
15581590 // TODO also look for comptime blocks and exported globals
......@@ -1560,7 +1592,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15601592 // Handle explicitly deleted decls from the source code. Not to be confused
15611593 // with when we delete decls because they are no longer referenced.
15621594 for (deleted_decls.items()) |entry| {
1563 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1595 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
15641596 try self.deleteDecl(entry.key);
15651597 }
15661598}
......@@ -1613,7 +1645,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
16131645 // Handle explicitly deleted decls from the source code. Not to be confused
16141646 // with when we delete decls because they are no longer referenced.
16151647 for (deleted_decls.items()) |entry| {
1616 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1648 log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
16171649 try self.deleteDecl(entry.key);
16181650 }
16191651}
......@@ -1625,7 +1657,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
16251657 // not be present in the set, and this does nothing.
16261658 decl.scope.removeDecl(decl);
16271659
1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});
1660 log.debug("deleting decl '{}'\n", .{decl.name});
16291661 const name_hash = decl.fullyQualifiedNameHash();
16301662 self.decl_table.removeAssertDiscard(name_hash);
16311663 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
......@@ -1712,17 +1744,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
17121744 const fn_zir = func.analysis.queued;
17131745 defer fn_zir.arena.promote(self.gpa).deinit();
17141746 func.analysis = .{ .in_progress = {} };
1715 log.debug(.module, "set {} to in_progress\n", .{decl.name});
1747 log.debug("set {} to in_progress\n", .{decl.name});
17161748
17171749 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
17181750
17191751 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
17201752 func.analysis = .{ .success = .{ .instructions = instructions } };
1721 log.debug(.module, "set {} to success\n", .{decl.name});
1753 log.debug("set {} to success\n", .{decl.name});
17221754}
17231755
17241756fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1725 log.debug(.module, "mark {} outdated\n", .{decl.name});
1757 log.debug("mark {} outdated\n", .{decl.name});
17261758 try self.work_queue.writeItem(.{ .analyze_decl = decl });
17271759 if (self.failed_decls.remove(decl)) |entry| {
17281760 entry.value.destroy(self.gpa);
......@@ -1745,7 +1777,18 @@ fn allocateNewDecl(
17451777 .analysis = .unreferenced,
17461778 .deletion_flag = false,
17471779 .contents_hash = contents_hash,
1748 .link = link.File.Elf.TextBlock.empty,
1780 .link = switch (self.bin_file.tag) {
1781 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
1782 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1783 .c => .{ .c = {} },
1784 .wasm => .{ .wasm = {} },
1785 },
1786 .fn_link = switch (self.bin_file.tag) {
1787 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
1788 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1789 .c => .{ .c = {} },
1790 .wasm => .{ .wasm = null },
1791 },
17491792 .generation = 0,
17501793 };
17511794 return new_decl;
......@@ -1926,6 +1969,20 @@ pub fn addBinOp(
19261969 return &inst.base;
19271970}
19281971
1972pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
1973 const inst = try block.arena.create(Inst.Arg);
1974 inst.* = .{
1975 .base = .{
1976 .tag = .arg,
1977 .ty = ty,
1978 .src = src,
1979 },
1980 .name = name,
1981 };
1982 try block.instructions.append(self.gpa, &inst.base);
1983 return &inst.base;
1984}
1985
19291986pub fn addBr(
19301987 self: *Module,
19311988 scope_block: *Scope.Block,
......@@ -2152,8 +2209,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
21522209 };
21532210
21542211 const decl_tv = try decl.typedValue();
2155 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2156 ty_payload.* = .{ .pointee_type = decl_tv.ty };
2212 const ty_payload = try scope.arena().create(Type.Payload.Pointer);
2213 ty_payload.* = .{
2214 .base = .{ .tag = .single_const_pointer },
2215 .pointee_type = decl_tv.ty,
2216 };
21572217 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
21582218 val_payload.* = .{ .decl = decl };
21592219
......@@ -2195,11 +2255,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {
21952255 };
21962256}
21972257
2198pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
2199 const b = try self.requireRuntimeBlock(scope, src);
2200 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
2201}
2202
22032258pub fn analyzeIsNull(
22042259 self: *Module,
22052260 scope: *Scope,
......@@ -2382,6 +2437,15 @@ pub fn cmpNumeric(
23822437 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
23832438}
23842439
2440fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2441 if (inst.value()) |val| {
2442 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2443 }
2444
2445 const b = try self.requireRuntimeBlock(scope, inst.src);
2446 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
2447}
2448
23852449fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
23862450 if (signed) {
23872451 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
......@@ -2452,6 +2516,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
24522516 }
24532517 assert(inst.ty.zigTypeTag() != .Undefined);
24542518
2519 // null to ?T
2520 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
2521 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
2522 }
2523
2524 // T to ?T
2525 if (dest_type.zigTypeTag() == .Optional) {
2526 var buf: Type.Payload.Pointer = undefined;
2527 const child_type = dest_type.optionalChild(&buf);
2528 if (child_type.eql(inst.ty)) {
2529 return self.wrapOptional(scope, dest_type, inst);
2530 } else if (try self.coerceNum(scope, child_type, inst)) |some| {
2531 return self.wrapOptional(scope, dest_type, some);
2532 }
2533 }
2534
24552535 // *[N]T to []T
24562536 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
24572537 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
......@@ -2466,39 +2546,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
24662546 }
24672547
24682548 // comptime known number to other number
2469 if (inst.value()) |val| {
2470 const src_zig_tag = inst.ty.zigTypeTag();
2471 const dst_zig_tag = dest_type.zigTypeTag();
2472
2473 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
2474 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2475 if (val.floatHasFraction()) {
2476 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
2477 }
2478 return self.fail(scope, inst.src, "TODO float to int", .{});
2479 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2480 if (!val.intFitsInType(dest_type, self.target())) {
2481 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2482 }
2483 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2484 }
2485 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
2486 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2487 const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
2488 error.Overflow => return self.fail(
2489 scope,
2490 inst.src,
2491 "cast of value {} to type '{}' loses information",
2492 .{ val, dest_type },
2493 ),
2494 error.OutOfMemory => return error.OutOfMemory,
2495 };
2496 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
2497 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2498 return self.fail(scope, inst.src, "TODO int to float", .{});
2499 }
2500 }
2501 }
2549 if (try self.coerceNum(scope, dest_type, inst)) |some|
2550 return some;
25022551
25032552 // integer widening
25042553 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
......@@ -2527,7 +2576,43 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
25272576 }
25282577 }
25292578
2530 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
2579 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
2580}
2581
2582pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
2583 const val = inst.value() orelse return null;
2584 const src_zig_tag = inst.ty.zigTypeTag();
2585 const dst_zig_tag = dest_type.zigTypeTag();
2586
2587 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
2588 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2589 if (val.floatHasFraction()) {
2590 return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
2591 }
2592 return self.fail(scope, inst.src, "TODO float to int", .{});
2593 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2594 if (!val.intFitsInType(dest_type, self.target())) {
2595 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2596 }
2597 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2598 }
2599 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
2600 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
2601 const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
2602 error.Overflow => return self.fail(
2603 scope,
2604 inst.src,
2605 "cast of value {} to type '{}' loses information",
2606 .{ val, dest_type },
2607 ),
2608 error.OutOfMemory => return error.OutOfMemory,
2609 };
2610 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
2611 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
2612 return self.fail(scope, inst.src, "TODO int to float", .{});
2613 }
2614 }
2615 return null;
25312616}
25322617
25332618pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
......@@ -2774,7 +2859,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
27742859 val_payload.* = .{ .val = lhs_val + rhs_val };
27752860 break :blk &val_payload.base;
27762861 },
2777 128 => blk: {
2862 128 => {
27782863 return self.fail(scope, src, "TODO Implement addition for big floats", .{});
27792864 },
27802865 else => unreachable,
......@@ -2808,7 +2893,7 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
28082893 val_payload.* = .{ .val = lhs_val - rhs_val };
28092894 break :blk &val_payload.base;
28102895 },
2811 128 => blk: {
2896 128 => {
28122897 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
28132898 },
28142899 else => unreachable,
......@@ -2817,15 +2902,12 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
28172902 return Value.initPayload(val_payload);
28182903}
28192904
2820pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2821 const type_payload = try scope.arena().create(Type.Payload.SingleMutPointer);
2822 type_payload.* = .{ .pointee_type = elem_ty };
2823 return Type.initPayload(&type_payload.base);
2824}
2825
2826pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2827 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2828 type_payload.* = .{ .pointee_type = elem_ty };
2905pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) error{OutOfMemory}!Type {
2906 const type_payload = try scope.arena().create(Type.Payload.Pointer);
2907 type_payload.* = .{
2908 .base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer },
2909 .pointee_type = elem_ty,
2910 };
28292911 return Type.initPayload(&type_payload.base);
28302912}
28312913
......@@ -2860,3 +2942,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
28602942 });
28612943 }
28622944}
2945
2946pub const PanicId = enum {
2947 unreach,
2948 unwrap_null,
2949};
2950
2951pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
2952 const block_inst = try parent_block.arena.create(Inst.Block);
2953 block_inst.* = .{
2954 .base = .{
2955 .tag = Inst.Block.base_tag,
2956 .ty = Type.initTag(.void),
2957 .src = ok.src,
2958 },
2959 .body = .{
2960 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
2961 },
2962 };
2963
2964 const ok_body: ir.Body = .{
2965 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
2966 };
2967 const brvoid = try parent_block.arena.create(Inst.BrVoid);
2968 brvoid.* = .{
2969 .base = .{
2970 .tag = .brvoid,
2971 .ty = Type.initTag(.noreturn),
2972 .src = ok.src,
2973 },
2974 .block = block_inst,
2975 };
2976 ok_body.instructions[0] = &brvoid.base;
2977
2978 var fail_block: Scope.Block = .{
2979 .parent = parent_block,
2980 .func = parent_block.func,
2981 .decl = parent_block.decl,
2982 .instructions = .{},
2983 .arena = parent_block.arena,
2984 };
2985 defer fail_block.instructions.deinit(mod.gpa);
2986
2987 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
2988
2989 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
2990
2991 const condbr = try parent_block.arena.create(Inst.CondBr);
2992 condbr.* = .{
2993 .base = .{
2994 .tag = .condbr,
2995 .ty = Type.initTag(.noreturn),
2996 .src = ok.src,
2997 },
2998 .condition = ok,
2999 .then_body = ok_body,
3000 .else_body = fail_body,
3001 };
3002 block_inst.body.instructions[0] = &condbr.base;
3003
3004 try parent_block.instructions.append(mod.gpa, &block_inst.base);
3005}
3006
3007pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
3008 // TODO Once we have a panic function to call, call it here instead of breakpoint.
3009 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
3010 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
3011}
src-self-hosted/astgen.zig+525-103
......@@ -47,21 +47,34 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
4747/// Turn Zig AST into untyped ZIR istructions.
4848pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
4949 switch (node.tag) {
50 .Root => unreachable, // Top-level declaration.
51 .Use => unreachable, // Top-level declaration.
52 .TestDecl => unreachable, // Top-level declaration.
53 .DocComment => unreachable, // Top-level declaration.
5054 .VarDecl => unreachable, // Handled in `blockExpr`.
51 .Assign => unreachable, // Handled in `blockExpr`.
52 .AssignBitAnd => unreachable, // Handled in `blockExpr`.
53 .AssignBitOr => unreachable, // Handled in `blockExpr`.
54 .AssignBitShiftLeft => unreachable, // Handled in `blockExpr`.
55 .AssignBitShiftRight => unreachable, // Handled in `blockExpr`.
56 .AssignBitXor => unreachable, // Handled in `blockExpr`.
57 .AssignDiv => unreachable, // Handled in `blockExpr`.
58 .AssignSub => unreachable, // Handled in `blockExpr`.
59 .AssignSubWrap => unreachable, // Handled in `blockExpr`.
60 .AssignMod => unreachable, // Handled in `blockExpr`.
61 .AssignAdd => unreachable, // Handled in `blockExpr`.
62 .AssignAddWrap => unreachable, // Handled in `blockExpr`.
63 .AssignMul => unreachable, // Handled in `blockExpr`.
64 .AssignMulWrap => unreachable, // Handled in `blockExpr`.
55 .SwitchCase => unreachable, // Handled in `switchExpr`.
56 .SwitchElse => unreachable, // Handled in `switchExpr`.
57 .Else => unreachable, // Handled explicitly the control flow expression functions.
58 .Payload => unreachable, // Handled explicitly.
59 .PointerPayload => unreachable, // Handled explicitly.
60 .PointerIndexPayload => unreachable, // Handled explicitly.
61 .ErrorTag => unreachable, // Handled explicitly.
62 .FieldInitializer => unreachable, // Handled explicitly.
63
64 .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
65 .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
66 .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
67 .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
68 .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
69 .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
70 .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
71 .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
72 .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
73 .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
74 .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
75 .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
76 .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
77 .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
6578
6679 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
6780 .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
......@@ -96,41 +109,186 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
96109 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
97110 .Return => return ret(mod, scope, node.castTag(.Return).?),
98111 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
112 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
99113 .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)),
100114 .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
101115 .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
116 .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
102117 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
103118 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
104119 .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
105120 .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
106 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
121 .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
122 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
123 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
124 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?),
125 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
126 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
127
128 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
129 .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
130 .BoolAnd => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolAnd", .{}),
131 .BoolOr => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolOr", .{}),
132 .ErrorUnion => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorUnion", .{}),
133 .MergeErrorSets => return mod.failNode(scope, node, "TODO implement astgen.expr for .MergeErrorSets", .{}),
134 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
135 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
136 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
137 .BitNot => return mod.failNode(scope, node, "TODO implement astgen.expr for .BitNot", .{}),
138 .Negation => return mod.failNode(scope, node, "TODO implement astgen.expr for .Negation", .{}),
139 .NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}),
140 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
141 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
142 .ArrayType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayType", .{}),
143 .ArrayTypeSentinel => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayTypeSentinel", .{}),
144 .SliceType => return mod.failNode(scope, node, "TODO implement astgen.expr for .SliceType", .{}),
145 .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
146 .ArrayAccess => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayAccess", .{}),
147 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
148 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
149 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
150 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
151 .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
152 .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}),
153 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
154 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
155 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
156 .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}),
157 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
158 .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}),
159 .EnumLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .EnumLiteral", .{}),
160 .MultilineStringLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .MultilineStringLiteral", .{}),
161 .CharLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .CharLiteral", .{}),
162 .GroupedExpression => return mod.failNode(scope, node, "TODO implement astgen.expr for .GroupedExpression", .{}),
163 .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}),
164 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
165 .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),
166 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
167 .ContainerField => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerField", .{}),
107168 }
108169}
109170
110pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) !void {
171fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
172 const tree = parent_scope.tree();
173 const src = tree.token_locs[node.ltoken].start;
174
175 if (node.getLabel()) |break_label| {
176 // Look for the label in the scope.
177 var scope = parent_scope;
178 while (true) {
179 switch (scope.tag) {
180 .gen_zir => {
181 const gen_zir = scope.cast(Scope.GenZIR).?;
182 if (gen_zir.label) |label| {
183 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
184 if (node.getRHS()) |rhs| {
185 // Most result location types can be forwarded directly; however
186 // if we need to write to a pointer which has an inferred type,
187 // proper type inference requires peer type resolution on the block's
188 // break operand expressions.
189 const branch_rl: ResultLoc = switch (label.result_loc) {
190 .discard, .none, .ty, .ptr, .lvalue => label.result_loc,
191 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
192 };
193 const operand = try expr(mod, parent_scope, branch_rl, rhs);
194 return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
195 .block = label.block_inst,
196 .operand = operand,
197 }, .{});
198 } else {
199 return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
200 .block = label.block_inst,
201 }, .{});
202 }
203 }
204 }
205 scope = gen_zir.parent;
206 },
207 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
208 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
209 else => {
210 const label_name = try identifierTokenString(mod, parent_scope, break_label);
211 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
212 },
213 }
214 }
215 } else {
216 return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
217 }
218}
219
220pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
111221 const tracy = trace(@src());
112222 defer tracy.end();
113223
114 if (block_node.label) |label| {
115 return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{});
116 }
224 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
225}
226
227fn labeledBlockExpr(
228 mod: *Module,
229 parent_scope: *Scope,
230 rl: ResultLoc,
231 block_node: *ast.Node.LabeledBlock,
232) InnerError!*zir.Inst {
233 const tracy = trace(@src());
234 defer tracy.end();
235
236 const tree = parent_scope.tree();
237 const src = tree.token_locs[block_node.lbrace].start;
238
239 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
240 // so that break statements can reference it.
241 const gen_zir = parent_scope.getGenZIR();
242 const block_inst = try gen_zir.arena.create(zir.Inst.Block);
243 block_inst.* = .{
244 .base = .{
245 .tag = .block,
246 .src = src,
247 },
248 .positionals = .{
249 .body = .{ .instructions = undefined },
250 },
251 .kw_args = .{},
252 };
253
254 var block_scope: Scope.GenZIR = .{
255 .parent = parent_scope,
256 .decl = parent_scope.decl().?,
257 .arena = gen_zir.arena,
258 .instructions = .{},
259 // TODO @as here is working around a stage1 miscompilation bug :(
260 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
261 .token = block_node.label,
262 .block_inst = block_inst,
263 .result_loc = rl,
264 }),
265 };
266 defer block_scope.instructions.deinit(mod.gpa);
267
268 try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
269
270 block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
271 try gen_zir.instructions.append(mod.gpa, &block_inst.base);
272
273 return &block_inst.base;
274}
275
276fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
277 const tree = parent_scope.tree();
117278
118279 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
119280 defer block_arena.deinit();
120281
121282 var scope = parent_scope;
122 for (block_node.statements()) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;
283 for (statements) |statement| {
284 const src = tree.token_locs[statement.firstToken()].start;
124285 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
125286 switch (statement.tag) {
126287 .VarDecl => {
127288 const var_decl_node = statement.castTag(.VarDecl).?;
128289 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
129290 },
130 .Assign => {
131 const ass = statement.castTag(.Assign).?;
132 try assign(mod, scope, ass);
133 },
291 .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
134292 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
135293 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
136294 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
......@@ -177,76 +335,49 @@ fn varDecl(
177335 // Depending on the type of AST the initialization expression is, we may need an lvalue
178336 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
179337 // the variable, no memory location needed.
180 if (nodeMayNeedMemoryLocation(init_node)) {
338 const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
181339 if (node.getTrailer("type_node")) |type_node| {
182340 const type_inst = try typeExpr(mod, scope, type_node);
183341 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
184 const result_loc: ResultLoc = .{ .ptr = alloc };
185 const init_inst = try expr(mod, scope, result_loc, init_node);
186 const sub_scope = try block_arena.create(Scope.LocalVal);
187 sub_scope.* = .{
188 .parent = scope,
189 .gen_zir = scope.getGenZIR(),
190 .name = ident_name,
191 .inst = init_inst,
192 };
193 return &sub_scope.base;
342 break :r ResultLoc{ .ptr = alloc };
194343 } else {
195344 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
196 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
197 const init_inst = try expr(mod, scope, result_loc, init_node);
198 const sub_scope = try block_arena.create(Scope.LocalVal);
199 sub_scope.* = .{
200 .parent = scope,
201 .gen_zir = scope.getGenZIR(),
202 .name = ident_name,
203 .inst = init_inst,
204 };
205 return &sub_scope.base;
345 break :r ResultLoc{ .inferred_ptr = alloc };
206346 }
207 } else {
208 const result_loc: ResultLoc = if (node.getTrailer("type_node")) |type_node|
209 .{ .ty = try typeExpr(mod, scope, type_node) }
347 } else r: {
348 if (node.getTrailer("type_node")) |type_node|
349 break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
210350 else
211 .none;
212 const init_inst = try expr(mod, scope, result_loc, init_node);
213 const sub_scope = try block_arena.create(Scope.LocalVal);
214 sub_scope.* = .{
215 .parent = scope,
216 .gen_zir = scope.getGenZIR(),
217 .name = ident_name,
218 .inst = init_inst,
219 };
220 return &sub_scope.base;
221 }
351 break :r .none;
352 };
353 const init_inst = try expr(mod, scope, result_loc, init_node);
354 const sub_scope = try block_arena.create(Scope.LocalVal);
355 sub_scope.* = .{
356 .parent = scope,
357 .gen_zir = scope.getGenZIR(),
358 .name = ident_name,
359 .inst = init_inst,
360 };
361 return &sub_scope.base;
222362 },
223363 .Keyword_var => {
224 if (node.getTrailer("type_node")) |type_node| {
364 const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: {
225365 const type_inst = try typeExpr(mod, scope, type_node);
226366 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
227 const result_loc: ResultLoc = .{ .ptr = alloc };
228 const init_inst = try expr(mod, scope, result_loc, init_node);
229 const sub_scope = try block_arena.create(Scope.LocalPtr);
230 sub_scope.* = .{
231 .parent = scope,
232 .gen_zir = scope.getGenZIR(),
233 .name = ident_name,
234 .ptr = alloc,
235 };
236 return &sub_scope.base;
237 } else {
367 break :a .{ .alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst), .result_loc = .{ .ptr = alloc } };
368 } else a: {
238369 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
239 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
240 const init_inst = try expr(mod, scope, result_loc, init_node);
241 const sub_scope = try block_arena.create(Scope.LocalPtr);
242 sub_scope.* = .{
243 .parent = scope,
244 .gen_zir = scope.getGenZIR(),
245 .name = ident_name,
246 .ptr = alloc,
247 };
248 return &sub_scope.base;
249 }
370 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
371 };
372 const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
373 const sub_scope = try block_arena.create(Scope.LocalPtr);
374 sub_scope.* = .{
375 .parent = scope,
376 .gen_zir = scope.getGenZIR(),
377 .name = ident_name,
378 .ptr = var_data.alloc,
379 };
380 return &sub_scope.base;
250381 },
251382 else => unreachable,
252383 }
......@@ -256,7 +387,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne
256387 if (infix_node.lhs.castTag(.Identifier)) |ident| {
257388 // This intentionally does not support @"_" syntax.
258389 const ident_name = scope.tree().tokenSlice(ident.token);
259 if (std.mem.eql(u8, ident_name, "_")) {
390 if (mem.eql(u8, ident_name, "_")) {
260391 _ = try expr(mod, scope, .discard, infix_node.rhs);
261392 return;
262393 }
......@@ -294,12 +425,90 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr
294425 return addZIRUnOp(mod, scope, src, .boolnot, operand);
295426}
296427
428fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
429 return expr(mod, scope, .lvalue, node.rhs);
430}
431
432fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
433 const tree = scope.tree();
434 const src = tree.token_locs[node.op_token].start;
435 const meta_type = try addZIRInstConst(mod, scope, src, .{
436 .ty = Type.initTag(.type),
437 .val = Value.initTag(.type_type),
438 });
439 const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
440 return addZIRUnOp(mod, scope, src, .optional_type, operand);
441}
442
443fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
444 const tree = scope.tree();
445 const src = tree.token_locs[node.op_token].start;
446 const meta_type = try addZIRInstConst(mod, scope, src, .{
447 .ty = Type.initTag(.type),
448 .val = Value.initTag(.type_type),
449 });
450
451 const simple = node.ptr_info.allowzero_token == null and
452 node.ptr_info.align_info == null and
453 node.ptr_info.volatile_token == null and
454 node.ptr_info.sentinel == null;
455
456 if (simple) {
457 const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
458 return addZIRUnOp(mod, scope, src, if (node.ptr_info.const_token == null)
459 .single_mut_ptr_type
460 else
461 .single_const_ptr_type, child_type);
462 }
463
464 var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
465 kw_args.@"allowzero" = node.ptr_info.allowzero_token != null;
466 if (node.ptr_info.align_info) |some| {
467 kw_args.@"align" = try expr(mod, scope, .none, some.node);
468 if (some.bit_range) |bit_range| {
469 kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
470 kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
471 }
472 }
473 kw_args.@"const" = node.ptr_info.const_token != null;
474 kw_args.@"volatile" = node.ptr_info.volatile_token != null;
475 if (node.ptr_info.sentinel) |some| {
476 kw_args.sentinel = try expr(mod, scope, .none, some);
477 }
478
479 const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
480 if (kw_args.sentinel) |some| {
481 kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
482 }
483
484 return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
485}
486
487fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
488 const tree = scope.tree();
489 const src = tree.token_locs[node.rtoken].start;
490
491 const operand = try expr(mod, scope, .lvalue, node.lhs);
492 const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand);
493 if (rl == .lvalue) return unwrapped_ptr;
494
495 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr));
496}
497
498/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
499/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
500fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
501 const ident_name_1 = try identifierTokenString(mod, scope, token1);
502 const ident_name_2 = try identifierTokenString(mod, scope, token2);
503 return mem.eql(u8, ident_name_1, ident_name_2);
504}
505
297506/// Identifier token -> String (allocated in scope.arena())
298pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
507fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
299508 const tree = scope.tree();
300509
301510 const ident_name = tree.tokenSlice(token);
302 if (std.mem.startsWith(u8, ident_name, "@")) {
511 if (mem.startsWith(u8, ident_name, "@")) {
303512 const raw_string = ident_name[1..];
304513 var bad_index: usize = undefined;
305514 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
......@@ -359,13 +568,77 @@ fn simpleBinOp(
359568 return rlWrap(mod, scope, rl, result);
360569}
361570
362fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
363 if (if_node.payload) |payload| {
364 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
571const CondKind = union(enum) {
572 bool,
573 optional: ?*zir.Inst,
574 err_union: ?*zir.Inst,
575
576 fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
577 switch (self.*) {
578 .bool => {
579 const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
580 .ty = Type.initTag(.type),
581 .val = Value.initTag(.bool_type),
582 });
583 return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
584 },
585 .optional => {
586 const cond_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node);
587 self.* = .{ .optional = cond_ptr };
588 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
589 return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result);
590 },
591 .err_union => {
592 const err_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node);
593 self.* = .{ .err_union = err_ptr };
594 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
595 return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result);
596 },
597 }
598 }
599
600 fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
601 if (self == .bool) return &then_scope.base;
602
603 const payload = payload_node.?.castTag(.PointerPayload).?;
604 const is_ptr = payload.ptr_token != null;
605 const ident_node = payload.value_symbol.castTag(.Identifier).?;
606
607 // This intentionally does not support @"_" syntax.
608 const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
609 if (mem.eql(u8, ident_name, "_")) {
610 if (is_ptr)
611 return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
612 return &then_scope.base;
613 }
614
615 return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
616 }
617
618 fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
619 if (self != .err_union) return &else_scope.base;
620
621 const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?);
622
623 const payload = payload_node.?.castTag(.Payload).?;
624 const ident_node = payload.error_symbol.castTag(.Identifier).?;
625
626 // This intentionally does not support @"_" syntax.
627 const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
628 if (mem.eql(u8, ident_name, "_")) {
629 return &else_scope.base;
630 }
631
632 return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
365633 }
634};
635
636fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
637 var cond_kind: CondKind = .bool;
638 if (if_node.payload) |_| cond_kind = .{ .optional = null };
366639 if (if_node.@"else") |else_node| {
367640 if (else_node.payload) |payload| {
368 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});
641 cond_kind = .{ .err_union = null };
369642 }
370643 }
371644 var block_scope: Scope.GenZIR = .{
......@@ -378,11 +651,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
378651
379652 const tree = scope.tree();
380653 const if_src = tree.token_locs[if_node.if_token].start;
381 const bool_type = try addZIRInstConst(mod, scope, if_src, .{
382 .ty = Type.initTag(.type),
383 .val = Value.initTag(.bool_type),
384 });
385 const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
654 const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
386655
387656 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
388657 .condition = cond,
......@@ -393,6 +662,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
393662 const block = try addZIRInstBlock(mod, scope, if_src, .{
394663 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
395664 });
665
666 const then_src = tree.token_locs[if_node.body.lastToken()].start;
396667 var then_scope: Scope.GenZIR = .{
397668 .parent = scope,
398669 .decl = block_scope.decl,
......@@ -401,6 +672,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
401672 };
402673 defer then_scope.instructions.deinit(mod.gpa);
403674
675 // declare payload to the then_scope
676 const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
677
404678 // Most result location types can be forwarded directly; however
405679 // if we need to write to a pointer which has an inferred type,
406680 // proper type inference requires peer type resolution on the if's
......@@ -410,10 +684,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
410684 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
411685 };
412686
413 const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
687 const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
414688 if (!then_result.tag.isNoReturn()) {
415 const then_src = tree.token_locs[if_node.body.lastToken()].start;
416 _ = try addZIRInst(mod, &then_scope.base, then_src, zir.Inst.Break, .{
689 _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
417690 .block = block,
418691 .operand = then_result,
419692 }, .{});
......@@ -431,10 +704,13 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
431704 defer else_scope.instructions.deinit(mod.gpa);
432705
433706 if (if_node.@"else") |else_node| {
434 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
707 const else_src = tree.token_locs[else_node.body.lastToken()].start;
708 // declare payload to the then_scope
709 const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
710
711 const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
435712 if (!else_result.tag.isNoReturn()) {
436 const else_src = tree.token_locs[else_node.body.lastToken()].start;
437 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
713 _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
438714 .block = block,
439715 .operand = else_result,
440716 }, .{});
......@@ -454,6 +730,133 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
454730 return &block.base;
455731}
456732
733fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
734 var cond_kind: CondKind = .bool;
735 if (while_node.payload) |_| cond_kind = .{ .optional = null };
736 if (while_node.@"else") |else_node| {
737 if (else_node.payload) |payload| {
738 cond_kind = .{ .err_union = null };
739 }
740 }
741
742 var expr_scope: Scope.GenZIR = .{
743 .parent = scope,
744 .decl = scope.decl().?,
745 .arena = scope.arena(),
746 .instructions = .{},
747 };
748 defer expr_scope.instructions.deinit(mod.gpa);
749
750 var loop_scope: Scope.GenZIR = .{
751 .parent = &expr_scope.base,
752 .decl = expr_scope.decl,
753 .arena = expr_scope.arena,
754 .instructions = .{},
755 };
756 defer loop_scope.instructions.deinit(mod.gpa);
757
758 var continue_scope: Scope.GenZIR = .{
759 .parent = &loop_scope.base,
760 .decl = loop_scope.decl,
761 .arena = loop_scope.arena,
762 .instructions = .{},
763 };
764 defer continue_scope.instructions.deinit(mod.gpa);
765
766 const tree = scope.tree();
767 const while_src = tree.token_locs[while_node.while_token].start;
768 const void_type = try addZIRInstConst(mod, scope, while_src, .{
769 .ty = Type.initTag(.type),
770 .val = Value.initTag(.void_type),
771 });
772 const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
773
774 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
775 .condition = cond,
776 .then_body = undefined, // populated below
777 .else_body = undefined, // populated below
778 }, .{});
779 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .{
780 .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
781 });
782 // TODO avoid emitting the continue expr when there
783 // are no jumps to it. This happens when the last statement of a while body is noreturn
784 // and there are no `continue` statements.
785 // The "repeat" at the end of a loop body is implied.
786 if (while_node.continue_expr) |cont_expr| {
787 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
788 }
789 const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
790 .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
791 });
792 const while_block = try addZIRInstBlock(mod, scope, while_src, .{
793 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
794 });
795
796 const then_src = tree.token_locs[while_node.body.lastToken()].start;
797 var then_scope: Scope.GenZIR = .{
798 .parent = &continue_scope.base,
799 .decl = continue_scope.decl,
800 .arena = continue_scope.arena,
801 .instructions = .{},
802 };
803 defer then_scope.instructions.deinit(mod.gpa);
804
805 // declare payload to the then_scope
806 const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
807
808 // Most result location types can be forwarded directly; however
809 // if we need to write to a pointer which has an inferred type,
810 // proper type inference requires peer type resolution on the while's
811 // branches.
812 const branch_rl: ResultLoc = switch (rl) {
813 .discard, .none, .ty, .ptr, .lvalue => rl,
814 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
815 };
816
817 const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
818 if (!then_result.tag.isNoReturn()) {
819 _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
820 .block = cond_block,
821 .operand = then_result,
822 }, .{});
823 }
824 condbr.positionals.then_body = .{
825 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
826 };
827
828 var else_scope: Scope.GenZIR = .{
829 .parent = &continue_scope.base,
830 .decl = continue_scope.decl,
831 .arena = continue_scope.arena,
832 .instructions = .{},
833 };
834 defer else_scope.instructions.deinit(mod.gpa);
835
836 if (while_node.@"else") |else_node| {
837 const else_src = tree.token_locs[else_node.body.lastToken()].start;
838 // declare payload to the then_scope
839 const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
840
841 const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
842 if (!else_result.tag.isNoReturn()) {
843 _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
844 .block = while_block,
845 .operand = else_result,
846 }, .{});
847 }
848 } else {
849 const else_src = tree.token_locs[while_node.lastToken()].start;
850 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
851 .block = while_block,
852 }, .{});
853 }
854 condbr.positionals.else_body = .{
855 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
856 };
857 return &while_block.base;
858}
859
457860fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
458861 const tree = scope.tree();
459862 const src = tree.token_locs[cfe.ltoken].start;
......@@ -510,7 +913,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
510913 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
511914 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
512915 const result = try addZIRInstConst(mod, scope, src, .{
513 .ty = Type.initTag(.comptime_int),
916 .ty = Type.initTag(.type),
514917 .val = Value.initPayload(&int_type_payload.base),
515918 });
516919 return rlWrap(mod, scope, rl, result);
......@@ -852,6 +1255,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
8521255 return simpleCast(mod, scope, rl, call, .intcast);
8531256 } else if (mem.eql(u8, builtin_name, "@bitCast")) {
8541257 return bitCast(mod, scope, rl, call);
1258 } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
1259 const src = tree.token_locs[call.builtin_token].start;
1260 return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
8551261 } else {
8561262 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
8571263 }
......@@ -1022,6 +1428,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
10221428 .Slice,
10231429 .Deref,
10241430 .ArrayAccess,
1431 .Block,
10251432 => return false,
10261433
10271434 // Forward the question to a sub-expression.
......@@ -1048,11 +1455,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
10481455 .Switch,
10491456 .Call,
10501457 .BuiltinCall, // TODO some of these can return false
1458 .LabeledBlock,
10511459 => return true,
10521460
10531461 // Depending on AST properties, they may need memory locations.
10541462 .If => return node.castTag(.If).?.@"else" != null,
1055 .Block => return node.castTag(.Block).?.label != null,
10561463 }
10571464 }
10581465}
......@@ -1094,6 +1501,15 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
10941501 }
10951502}
10961503
1504fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
1505 const src = scope.tree().token_locs[node.firstToken()].start;
1506 const void_inst = try addZIRInstConst(mod, scope, src, .{
1507 .ty = Type.initTag(.void),
1508 .val = Value.initTag(.void_value),
1509 });
1510 return rlWrap(mod, scope, rl, void_inst);
1511}
1512
10971513pub fn addZIRInstSpecial(
10981514 mod: *Module,
10991515 scope: *Scope,
......@@ -1211,3 +1627,9 @@ pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module
12111627 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
12121628 return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
12131629}
1630
1631/// TODO The existence of this function is a workaround for a bug in stage1.
1632pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
1633 const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
1634 return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
1635}
src-self-hosted/cbe.h+11-4
......@@ -1,8 +1,15 @@
11#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn
3#elif __GNUC__ && !__STRICT_ANSI__
4#define noreturn __attribute__ ((noreturn))
2#define zig_noreturn _Noreturn
3#elif __GNUC__
4#define zig_noreturn __attribute__ ((noreturn))
5#elif _MSC_VER
6#define zig_noreturn __declspec(noreturn)
57#else
6#define noreturn
8#define zig_noreturn
79#endif
810
11#if __GNUC__
12#define zig_unreachable() __builtin_unreachable()
13#else
14#define zig_unreachable()
15#endif
src-self-hosted/clang.zig+1
......@@ -1141,6 +1141,7 @@ pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigC
11411141
11421142pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool;
11431143pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation;
1144pub extern fn ZigClangIntegerLiteral_isZero(*const ZigClangIntegerLiteral, *bool, *const ZigClangASTContext) bool;
11441145
11451146pub extern fn ZigClangReturnStmt_getRetValue(*const ZigClangReturnStmt) ?*const ZigClangExpr;
11461147
src-self-hosted/codegen.zig+246-142
......@@ -20,7 +20,21 @@ const leb128 = std.debug.leb;
2020
2121/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
2222pub const BlockData = struct {
23 relocs: std.ArrayListUnmanaged(Reloc) = .{},
23 relocs: std.ArrayListUnmanaged(Reloc) = undefined,
24 /// The first break instruction encounters `null` here and chooses a
25 /// machine code value for the block result, populating this field.
26 /// Following break instructions encounter that value and use it for
27 /// the location to store their block results.
28 mcv: AnyMCValue = undefined,
29};
30
31/// Architecture-independent MCValue. Here, we have a type that is the same size as
32/// the architecture-specific MCValue. Next to the declaration of MCValue is a
33/// comptime assert that makes sure we guessed correctly about the size. This only
34/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
35pub const AnyMCValue = extern struct {
36 a: u64,
37 b: u64,
2438};
2539
2640pub const Reloc = union(enum) {
......@@ -50,6 +64,8 @@ pub fn generateSymbol(
5064 typed_value: TypedValue,
5165 code: *std.ArrayList(u8),
5266 dbg_line: *std.ArrayList(u8),
67 dbg_info: *std.ArrayList(u8),
68 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
5369) GenerateSymbolError!Result {
5470 const tracy = trace(@src());
5571 defer tracy.end();
......@@ -57,61 +73,62 @@ pub fn generateSymbol(
5773 switch (typed_value.ty.zigTypeTag()) {
5874 .Fn => {
5975 switch (bin_file.base.options.target.cpu.arch) {
60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 .wasm32 => unreachable, // has its own code path
77 .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),
80 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
81 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
82 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
83 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
84 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
85 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
86 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
87 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
88 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
89 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
90 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
91 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
92 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
93 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
94 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
95 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
96 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
97 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
98 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
99 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
100 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
101 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
102 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
104 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
107 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
108 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
109 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
110 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
111 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
112 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
113 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
114 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
115 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
116 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
117 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
118 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
119 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
120 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
121 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
122 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
123 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
124 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
125 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
126 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
111127 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
112128 }
113129 },
114130 .Array => {
131 // TODO populate .debug_info for the array
115132 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
116133 if (typed_value.ty.arraySentinel()) |sentinel| {
117134 try code.ensureCapacity(code.items.len + payload.data.len + 1);
......@@ -120,7 +137,7 @@ pub fn generateSymbol(
120137 switch (try generateSymbol(bin_file, src, .{
121138 .ty = typed_value.ty.elemType(),
122139 .val = sentinel,
123 }, code, dbg_line)) {
140 }, code, dbg_line, dbg_info, dbg_info_type_relocs)) {
124141 .appended => return Result{ .appended = {} },
125142 .externally_managed => |slice| {
126143 code.appendSliceAssumeCapacity(slice);
......@@ -134,7 +151,7 @@ pub fn generateSymbol(
134151 }
135152 return Result{
136153 .fail = try ErrorMsg.create(
137 bin_file.allocator,
154 bin_file.base.allocator,
138155 src,
139156 "TODO implement generateSymbol for more kinds of arrays",
140157 .{},
......@@ -142,29 +159,36 @@ pub fn generateSymbol(
142159 };
143160 },
144161 .Pointer => {
162 // TODO populate .debug_info for the pointer
163
145164 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
146165 const decl = payload.decl;
147166 if (decl.analysis != .complete) return error.AnalysisFail;
148 assert(decl.link.local_sym_index != 0);
167 assert(decl.link.elf.local_sym_index != 0);
149168 // TODO handle the dependency of this symbol on the decl's vaddr.
150169 // If the decl changes vaddr, then this symbol needs to get regenerated.
151 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
170 const vaddr = bin_file.local_symbols.items[decl.link.elf.local_sym_index].st_value;
152171 const endian = bin_file.base.options.target.cpu.arch.endian();
153 switch (bin_file.ptr_width) {
154 .p32 => {
172 switch (bin_file.base.options.target.cpu.arch.ptrBitWidth()) {
173 16 => {
174 try code.resize(2);
175 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
176 },
177 32 => {
155178 try code.resize(4);
156179 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
157180 },
158 .p64 => {
181 64 => {
159182 try code.resize(8);
160183 mem.writeInt(u64, code.items[0..8], vaddr, endian);
161184 },
185 else => unreachable,
162186 }
163187 return Result{ .appended = {} };
164188 }
165189 return Result{
166190 .fail = try ErrorMsg.create(
167 bin_file.allocator,
191 bin_file.base.allocator,
168192 src,
169193 "TODO implement generateSymbol for pointer {}",
170194 .{typed_value.val},
......@@ -172,6 +196,8 @@ pub fn generateSymbol(
172196 };
173197 },
174198 .Int => {
199 // TODO populate .debug_info for the integer
200
175201 const info = typed_value.ty.intInfo(bin_file.base.options.target);
176202 if (info.bits == 8 and !info.signed) {
177203 const x = typed_value.val.toUnsignedInt();
......@@ -180,7 +206,7 @@ pub fn generateSymbol(
180206 }
181207 return Result{
182208 .fail = try ErrorMsg.create(
183 bin_file.allocator,
209 bin_file.base.allocator,
184210 src,
185211 "TODO implement generateSymbol for int type '{}'",
186212 .{typed_value.ty},
......@@ -190,7 +216,7 @@ pub fn generateSymbol(
190216 else => |t| {
191217 return Result{
192218 .fail = try ErrorMsg.create(
193 bin_file.allocator,
219 bin_file.base.allocator,
194220 src,
195221 "TODO implement generateSymbol for type '{}'",
196222 .{@tagName(t)},
......@@ -213,6 +239,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
213239 mod_fn: *const Module.Fn,
214240 code: *std.ArrayList(u8),
215241 dbg_line: *std.ArrayList(u8),
242 dbg_info: *std.ArrayList(u8),
243 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
216244 err_msg: ?*ErrorMsg,
217245 args: []MCValue,
218246 ret_mcv: MCValue,
......@@ -382,15 +410,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
382410 typed_value: TypedValue,
383411 code: *std.ArrayList(u8),
384412 dbg_line: *std.ArrayList(u8),
413 dbg_info: *std.ArrayList(u8),
414 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
385415 ) GenerateSymbolError!Result {
386416 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
387417
388418 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
389419
390 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
420 var branch_stack = std.ArrayList(Branch).init(bin_file.base.allocator);
391421 defer {
392422 assert(branch_stack.items.len == 1);
393 branch_stack.items[0].deinit(bin_file.allocator);
423 branch_stack.items[0].deinit(bin_file.base.allocator);
394424 branch_stack.deinit();
395425 }
396426 const branch = try branch_stack.addOne();
......@@ -413,12 +443,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
413443 };
414444
415445 var function = Self{
416 .gpa = bin_file.allocator,
446 .gpa = bin_file.base.allocator,
417447 .target = &bin_file.base.options.target,
418448 .bin_file = bin_file,
419449 .mod_fn = module_fn,
420450 .code = code,
421451 .dbg_line = dbg_line,
452 .dbg_info = dbg_info,
453 .dbg_info_type_relocs = dbg_info_type_relocs,
422454 .err_msg = null,
423455 .args = undefined, // populated after `resolveCallingConventionValues`
424456 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
......@@ -432,7 +464,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
432464 .rbrace_src = src_data.rbrace_src,
433465 .source = src_data.source,
434466 };
435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
467 defer function.exitlude_jump_relocs.deinit(bin_file.base.allocator);
436468
437469 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
438470 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -536,7 +568,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
536568 }
537569
538570 fn genBody(self: *Self, body: ir.Body) InnerError!void {
539 const inst_table = &self.branch_stack.items[0].inst_table;
571 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
572 const inst_table = &branch.inst_table;
540573 for (body.instructions) |inst| {
541574 const new_inst = try self.genFuncInst(inst);
542575 try inst_table.putNoClobber(self.gpa, inst, new_inst);
......@@ -596,6 +629,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
596629 }
597630 }
598631
632 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
633 /// after codegen for this symbol is done.
634 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
635 assert(ty.hasCodeGenBits());
636 const index = self.dbg_info.items.len;
637 try self.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
638
639 const gop = try self.dbg_info_type_relocs.getOrPut(self.gpa, ty);
640 if (!gop.found_existing) {
641 gop.entry.value = .{
642 .off = undefined,
643 .relocs = .{},
644 };
645 }
646 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
647 }
648
599649 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
600650 switch (inst.tag) {
601651 .add => return self.genAdd(inst.castTag(.add).?),
......@@ -621,7 +671,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
621671 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
622672 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
623673 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
674 .iserr => return self.genIsErr(inst.castTag(.iserr).?),
624675 .load => return self.genLoad(inst.castTag(.load).?),
676 .loop => return self.genLoop(inst.castTag(.loop).?),
625677 .not => return self.genNot(inst.castTag(.not).?),
626678 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
627679 .ref => return self.genRef(inst.castTag(.ref).?),
......@@ -630,6 +682,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
630682 .store => return self.genStore(inst.castTag(.store).?),
631683 .sub => return self.genSub(inst.castTag(.sub).?),
632684 .unreach => return MCValue{ .unreach = {} },
685 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
686 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
633687 }
634688 }
635689
......@@ -779,6 +833,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779833 }
780834 }
781835
836 fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
837 // No side effects, so if it's unreferenced, do nothing.
838 if (inst.base.isUnused())
839 return MCValue.dead;
840 switch (arch) {
841 else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}),
842 }
843 }
844
845 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
846 const optional_ty = inst.base.ty;
847
848 // No side effects, so if it's unreferenced, do nothing.
849 if (inst.base.isUnused())
850 return MCValue.dead;
851
852 // Optional type is just a boolean true
853 if (optional_ty.abiSize(self.target.*) == 1)
854 return MCValue{ .immediate = 1 };
855
856 switch (arch) {
857 else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),
858 }
859 }
860
782861 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
783862 const elem_ty = inst.base.ty;
784863 if (!elem_ty.hasCodeGenBits())
......@@ -995,7 +1074,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9951074 }
9961075 }
9971076
998 fn genArg(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1077 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
9991078 if (FreeRegInt == u0) {
10001079 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
10011080 }
......@@ -1008,10 +1087,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10081087 const result = self.args[self.arg_index];
10091088 self.arg_index += 1;
10101089
1090 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
10111091 switch (result) {
10121092 .register => |reg| {
10131093 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
10141094 branch.markRegUsed(reg);
1095
1096 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
1097 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1098 self.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
1099 1, // ULEB128 dwarf expression length
1100 reg.dwarfLocOp(),
1101 });
1102 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
1103 self.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
10151104 },
10161105 else => {},
10171106 }
......@@ -1024,11 +1113,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10241113 try self.code.append(0xcc); // int3
10251114 },
10261115 .riscv64 => {
1027 const full = @bitCast(u32, instructions.CallBreak{
1028 .mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
1029 });
1030
1031 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1116 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
10321117 },
10331118 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
10341119 }
......@@ -1080,7 +1165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10801165 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
10811166 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
10821167 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1083 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
1168 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
10841169 // ff 14 25 xx xx xx xx call [addr]
10851170 try self.code.ensureCapacity(self.code.items.len + 7);
10861171 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
......@@ -1101,15 +1186,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11011186 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
11021187 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
11031188 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1104 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
1189 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
11051190
11061191 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1107 const jalr = instructions.Jalr{
1108 .rd = Register.ra.id(),
1109 .rs1 = Register.ra.id(),
1110 .offset = 0,
1111 };
1112 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1192 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
11131193 } else {
11141194 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
11151195 }
......@@ -1166,12 +1246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11661246 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
11671247 },
11681248 .riscv64 => {
1169 const jalr = instructions.Jalr{
1170 .rd = Register.zero.id(),
1171 .rs1 = Register.ra.id(),
1172 .offset = 0,
1173 };
1174 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
11751250 },
11761251 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
11771252 }
......@@ -1226,6 +1301,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12261301 }
12271302
12281303 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
1304 // TODO Rework this so that the arch-independent logic isn't buried and duplicated.
12291305 switch (arch) {
12301306 .x86_64 => {
12311307 try self.code.ensureCapacity(self.code.items.len + 6);
......@@ -1278,6 +1354,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12781354 }
12791355
12801356 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {
1357 // TODO deal with liveness / deaths condbr's then_entry_deaths and else_entry_deaths
12811358 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
12821359 const reloc = Reloc{ .rel32 = self.code.items.len };
12831360 self.code.items.len += 4;
......@@ -1301,17 +1378,56 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13011378 }
13021379 }
13031380
1304 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
1305 if (inst.base.ty.hasCodeGenBits()) {
1306 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
1381 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1382 switch (arch) {
1383 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),
1384 }
1385 }
1386
1387 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
1388 // A loop is a setup to be able to jump back to the beginning.
1389 const start_index = self.code.items.len;
1390 try self.genBody(inst.body);
1391 try self.jump(inst.base.src, start_index);
1392 return MCValue.unreach;
1393 }
1394
1395 /// Send control flow to the `index` of `self.code`.
1396 fn jump(self: *Self, src: usize, index: usize) !void {
1397 switch (arch) {
1398 .i386, .x86_64 => {
1399 try self.code.ensureCapacity(self.code.items.len + 5);
1400 if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {
1401 self.code.appendAssumeCapacity(0xeb); // jmp rel8
1402 self.code.appendAssumeCapacity(@bitCast(u8, delta));
1403 } else |_| {
1404 const delta = @intCast(i32, index) - (@intCast(i32, self.code.items.len + 5));
1405 self.code.appendAssumeCapacity(0xe9); // jmp rel32
1406 mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);
1407 }
1408 },
1409 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
13071410 }
1308 // A block is nothing but a setup to be able to jump to the end.
1411 }
1412
1413 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
1414 inst.codegen = .{
1415 // A block is a setup to be able to jump to the end.
1416 .relocs = .{},
1417 // It also acts as a receptical for break operands.
1418 // Here we use `MCValue.none` to represent a null value so that the first
1419 // break instruction will choose a MCValue for the block result and overwrite
1420 // this field. Following break instructions will use that MCValue to put their
1421 // block results.
1422 .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
1423 };
13091424 defer inst.codegen.relocs.deinit(self.gpa);
1425
13101426 try self.genBody(inst.body);
13111427
13121428 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
13131429
1314 return MCValue.none;
1430 return @bitCast(MCValue, inst.codegen.mcv);
13151431 }
13161432
13171433 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
......@@ -1331,13 +1447,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13311447 }
13321448
13331449 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
1334 if (!inst.operand.ty.hasCodeGenBits())
1335 return self.brVoid(inst.base.src, inst.block);
1336
1337 const operand = try self.resolveInst(inst.operand);
1338 switch (arch) {
1339 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
1450 if (inst.operand.ty.hasCodeGenBits()) {
1451 const operand = try self.resolveInst(inst.operand);
1452 const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
1453 if (block_mcv == .none) {
1454 inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
1455 } else {
1456 try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
1457 }
13401458 }
1459 return self.brVoid(inst.base.src, inst.block);
13411460 }
13421461
13431462 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
......@@ -1379,11 +1498,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13791498 }
13801499
13811500 if (mem.eql(u8, inst.asm_source, "ecall")) {
1382 const full = @bitCast(u32, instructions.CallBreak{
1383 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
1384 });
1385
1386 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1501 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
13871502 } else {
13881503 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
13891504 }
......@@ -1590,36 +1705,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15901705 .immediate => |unsigned_x| {
15911706 const x = @bitCast(i64, unsigned_x);
15921707 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1593 const instruction = @bitCast(u32, instructions.Addi{
1594 .mode = @enumToInt(instructions.Addi.Mode.addi),
1595 .imm = @truncate(i12, x),
1596 .rs1 = Register.zero.id(),
1597 .rd = reg.id(),
1598 });
1599
1600 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction);
1708 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
16011709 return;
16021710 }
16031711 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1604 const split = @bitCast(packed struct {
1605 low12: i12,
1606 up20: i20,
1607 }, @truncate(i32, x));
1608 if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{});
1609
1610 const lui = @bitCast(u32, instructions.Lui{
1611 .imm = split.up20,
1612 .rd = reg.id(),
1613 });
1614 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui);
1712 const lo12 = @truncate(i12, x);
1713 const carry: i32 = if (lo12 < 0) 1 else 0;
1714 const hi20 = @truncate(i20, (x >> 12) +% carry);
16151715
1616 const addi = @bitCast(u32, instructions.Addi{
1617 .mode = @enumToInt(instructions.Addi.Mode.addi),
1618 .imm = @truncate(i12, split.low12),
1619 .rs1 = reg.id(),
1620 .rd = reg.id(),
1621 });
1622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
1716 // TODO: add test case for 32-bit immediate
1717 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
1718 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
16231719 return;
16241720 }
16251721 // li rd, immediate
......@@ -1631,14 +1727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16311727 // If the type is a pointer, it means the pointer address is at this memory location.
16321728 try self.genSetReg(src, reg, .{ .immediate = addr });
16331729
1634 const ld = @bitCast(u32, instructions.Load{
1635 .mode = @enumToInt(instructions.Load.Mode.ld),
1636 .rs1 = reg.id(),
1637 .rd = reg.id(),
1638 .offset = 0,
1639 });
1640
1641 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld);
1730 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
16421731 // LOAD imm=[i12 offset = 0], rs1 =
16431732
16441733 // return self.fail("TODO implement genSetReg memory for riscv64");
......@@ -1919,9 +2008,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19192008 return mcv;
19202009 }
19212010
1922 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
2011 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
19232012 if (typed_value.val.isUndef())
1924 return MCValue.undef;
2013 return MCValue{ .undef = {} };
19252014 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
19262015 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
19272016 switch (typed_value.ty.zigTypeTag()) {
......@@ -1929,7 +2018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19292018 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
19302019 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
19312020 const decl = payload.decl;
1932 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
2021 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
19332022 return MCValue{ .memory = got_addr };
19342023 }
19352024 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
......@@ -1946,6 +2035,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19462035 },
19472036 .ComptimeInt => unreachable, // semantic analysis prevents this
19482037 .ComptimeFloat => unreachable, // semantic analysis prevents this
2038 .Optional => {
2039 if (typed_value.ty.isPtrLikeOptional()) {
2040 if (typed_value.val.isNull())
2041 return MCValue{ .immediate = 0 };
2042
2043 var buf: Type.Payload.Pointer = undefined;
2044 return self.genTypedValue(src, .{
2045 .ty = typed_value.ty.optionalChild(&buf),
2046 .val = typed_value.val,
2047 });
2048 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
2049 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
2050 }
2051 return self.fail(src, "TODO non pointer optionals", .{});
2052 },
19492053 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
19502054 }
19512055 }
......@@ -2051,10 +2155,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20512155 };
20522156 }
20532157
2054 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
2158 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
20552159 @setCold(true);
20562160 assert(self.err_msg == null);
2057 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
2161 self.err_msg = try ErrorMsg.create(self.bin_file.base.allocator, src, format, args);
20582162 return error.CodegenFail;
20592163 }
20602164
src-self-hosted/codegen/c.zig+174-73
......@@ -11,46 +11,64 @@ const C = link.File.C;
1111const Decl = Module.Decl;
1212const mem = std.mem;
1313
14/// Maps a name from Zig source to C. This will always give the same output for
15/// any given input.
14/// Maps a name from Zig source to C. Currently, this will always give the same
15/// output for any given input, sometimes resulting in broken identifiers.
1616fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
1717 return allocator.dupe(u8, name);
1818}
1919
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
21 if (T.tag() == .usize) {
22 file.need_stddef = true;
23 try writer.writeAll("size_t");
24 } else {
25 switch (T.zigTypeTag()) {
26 .NoReturn => {
27 file.need_noreturn = true;
28 try writer.writeAll("noreturn void");
29 },
30 .Void => try writer.writeAll("void"),
31 .Int => {
32 if (T.tag() == .u8) {
33 file.need_stdint = true;
34 try writer.writeAll("uint8_t");
35 } else {
36 return file.fail(src, "TODO implement int types", .{});
37 }
38 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
20fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {
21 switch (T.zigTypeTag()) {
22 .NoReturn => {
23 try writer.writeAll("zig_noreturn void");
24 },
25 .Void => try writer.writeAll("void"),
26 .Int => {
27 if (T.tag() == .u8) {
28 ctx.file.need_stdint = true;
29 try writer.writeAll("uint8_t");
30 } else if (T.tag() == .usize) {
31 ctx.file.need_stddef = true;
32 try writer.writeAll("size_t");
33 } else {
34 return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{});
35 }
36 },
37 else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
4138 }
4239}
4340
44fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
41fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {
42 switch (T.zigTypeTag()) {
43 .Int => {
44 if (T.isSignedInt())
45 return writer.print("{}", .{val.toSignedInt()});
46 return writer.print("{}", .{val.toUnsignedInt()});
47 },
48 else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}),
49 }
50}
51
52fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
4553 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
47 const name = try map(file.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);
54 try renderType(ctx, writer, tv.ty.fnReturnType());
55 const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));
56 defer ctx.file.base.allocator.free(name);
4957 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
53 return file.fail(decl.src(), "TODO implement parameters", .{});
58 var param_len = tv.ty.fnParamLen();
59 if (param_len == 0)
60 try writer.writeAll("void")
61 else {
62 var index: usize = 0;
63 while (index < param_len) : (index += 1) {
64 if (index > 0) {
65 try writer.writeAll(", ");
66 }
67 try renderType(ctx, writer, tv.ty.fnParamType(index));
68 try writer.print(" arg{}", .{index});
69 }
70 }
71 try writer.writeByte(')');
5472}
5573
5674pub fn generate(file: *C, decl: *Decl) !void {
......@@ -64,8 +82,8 @@ pub fn generate(file: *C, decl: *Decl) !void {
6482fn genArray(file: *C, decl: *Decl) !void {
6583 const tv = decl.typed_value.most_recent.typed_value;
6684 // TODO: prevent inline asm constants from being emitted
67 const name = try map(file.allocator, mem.span(decl.name));
68 defer file.allocator.free(name);
85 const name = try map(file.base.allocator, mem.span(decl.name));
86 defer file.base.allocator.free(name);
6987 if (tv.val.cast(Value.Payload.Bytes)) |payload|
7088 if (tv.ty.arraySentinel()) |sentinel|
7189 if (sentinel.toUnsignedInt() == 0)
......@@ -78,11 +96,40 @@ fn genArray(file: *C, decl: *Decl) !void {
7896 return file.fail(decl.src(), "TODO non-byte arrays", .{});
7997}
8098
99const Context = struct {
100 file: *C,
101 decl: *Decl,
102 inst_map: std.AutoHashMap(*Inst, []u8),
103 argdex: usize = 0,
104 unnamed_index: usize = 0,
105
106 fn name(self: *Context) ![]u8 {
107 const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index});
108 self.unnamed_index += 1;
109 return val;
110 }
111
112 fn deinit(self: *Context) void {
113 for (self.inst_map.items()) |kv| {
114 self.file.base.allocator.free(kv.value);
115 }
116 self.inst_map.deinit();
117 self.* = undefined;
118 }
119};
120
81121fn genFn(file: *C, decl: *Decl) !void {
82122 const writer = file.main.writer();
83123 const tv = decl.typed_value.most_recent.typed_value;
84124
85 try renderFunctionSignature(file, writer, decl);
125 var ctx = Context{
126 .file = file,
127 .decl = decl,
128 .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),
129 };
130 defer ctx.deinit();
131
132 try renderFunctionSignature(&ctx, writer, decl);
86133
87134 try writer.writeAll(" {");
88135
......@@ -91,13 +138,19 @@ fn genFn(file: *C, decl: *Decl) !void {
91138 if (instructions.len > 0) {
92139 try writer.writeAll("\n");
93140 for (instructions) |inst| {
94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print(" return;\n", .{}),
99 .dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),
141 if (switch (inst.tag) {
142 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
143 .call => try genCall(&ctx, inst.castTag(.call).?),
144 .ret => try genRet(&ctx, inst.castTag(.ret).?),
145 .retvoid => try genRetVoid(&ctx),
146 .arg => try genArg(&ctx),
147 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
148 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
149 .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?),
150 .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?),
100151 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
152 }) |name| {
153 try ctx.inst_map.putNoClobber(inst, name);
101154 }
102155 }
103156 }
......@@ -105,13 +158,40 @@ fn genFn(file: *C, decl: *Decl) !void {
105158 try writer.writeAll("}\n\n");
106159}
107160
108fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {
109 return file.fail(decl.src(), "TODO return {}", .{expected_return_type});
161fn genArg(ctx: *Context) !?[]u8 {
162 const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
163 ctx.argdex += 1;
164 return name;
110165}
111166
112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
113 const writer = file.main.writer();
114 const header = file.header.writer();
167fn genRetVoid(ctx: *Context) !?[]u8 {
168 try ctx.file.main.writer().print(" return;\n", .{});
169 return null;
170}
171
172fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
173 return ctx.file.fail(ctx.decl.src(), "TODO return", .{});
174}
175
176fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
177 if (inst.base.isUnused())
178 return null;
179 const op = inst.operand;
180 const writer = ctx.file.main.writer();
181 const name = try ctx.name();
182 const from = ctx.inst_map.get(op) orelse
183 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});
184 try writer.writeAll(" const ");
185 try renderType(ctx, writer, inst.base.ty);
186 try writer.print(" {} = (", .{name});
187 try renderType(ctx, writer, inst.base.ty);
188 try writer.print("){};\n", .{from});
189 return name;
190}
191
192fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
193 const writer = ctx.file.main.writer();
194 const header = ctx.file.header.writer();
115195 try writer.writeAll(" ");
116196 if (inst.func.castTag(.constant)) |func_inst| {
117197 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
......@@ -122,52 +202,77 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
122202 try writer.print("(void)", .{});
123203 }
124204 const tname = mem.spanZ(target.name);
125 if (file.called.get(tname) == null) {
126 try file.called.put(tname, void{});
127 try renderFunctionSignature(file, header, target);
205 if (ctx.file.called.get(tname) == null) {
206 try ctx.file.called.put(tname, void{});
207 try renderFunctionSignature(ctx, header, target);
128208 try header.writeAll(";\n");
129209 }
130 try writer.print("{}();\n", .{tname});
210 try writer.print("{}(", .{tname});
211 if (inst.args.len != 0) {
212 for (inst.args) |arg, i| {
213 if (i > 0) {
214 try writer.writeAll(", ");
215 }
216 if (arg.cast(Inst.Constant)) |con| {
217 try renderValue(ctx, writer, arg.ty, con.val);
218 } else {
219 return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg});
220 }
221 }
222 }
223 try writer.writeAll(");\n");
131224 } else {
132 return file.fail(decl.src(), "TODO non-function call target?", .{});
133 }
134 if (inst.args.len != 0) {
135 return file.fail(decl.src(), "TODO function arguments", .{});
225 return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});
136226 }
137227 } else {
138 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
228 return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
139229 }
230 return null;
140231}
141232
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
233fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
143234 // TODO emit #line directive here with line number and filename
235 return null;
144236}
145237
146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
147 const writer = file.main.writer();
238fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
239 // TODO ??
240 return null;
241}
242
243fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
244 try ctx.file.main.writer().writeAll(" zig_unreachable();\n");
245 return null;
246}
247
248fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
249 const writer = ctx.file.main.writer();
148250 try writer.writeAll(" ");
149251 for (as.inputs) |i, index| {
150252 if (i[0] == '{' and i[i.len - 1] == '}') {
151253 const reg = i[1 .. i.len - 1];
152254 const arg = as.args[index];
255 try writer.writeAll("register ");
256 try renderType(ctx, writer, arg.ty);
257 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
258 // TODO merge constant handling into inst_map as well
153259 if (arg.castTag(.constant)) |c| {
154 if (c.val.tag() == .int_u64) {
155 try writer.writeAll("register ");
156 try renderType(file, writer, arg.ty, decl.src());
157 try writer.print(" {}_constant __asm__(\"{}\") = {};\n ", .{ reg, reg, c.val.toUnsignedInt() });
158 } else {
159 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
160 }
260 try renderValue(ctx, writer, arg.ty, c.val);
261 try writer.writeAll(";\n ");
161262 } else {
162 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
263 const gop = try ctx.inst_map.getOrPut(arg);
264 if (!gop.found_existing) {
265 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
266 }
267 try writer.print("{};\n ", .{gop.entry.value});
163268 }
164269 } else {
165 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
270 return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
166271 }
167272 }
168273 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
169274 if (as.output) |o| {
170 return file.fail(decl.src(), "TODO inline asm output", .{});
275 return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{});
171276 }
172277 if (as.inputs.len > 0) {
173278 if (as.output == null) {
......@@ -181,12 +286,7 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
181286 if (index > 0) {
182287 try writer.writeAll(", ");
183288 }
184 if (arg.castTag(.constant)) |c| {
185 try writer.print("\"\"({}_constant)", .{reg});
186 } else {
187 // This is blocked by the earlier test
188 unreachable;
189 }
289 try writer.print("\"\"({}_constant)", .{reg});
190290 } else {
191291 // This is blocked by the earlier test
192292 unreachable;
......@@ -194,4 +294,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
194294 }
195295 }
196296 try writer.writeAll(");\n");
297 return null;
197298}
src-self-hosted/codegen/riscv64.zig+383-42
......@@ -1,56 +1,398 @@
11const std = @import("std");
2const DW = std.dwarf;
23
3pub const instructions = struct {
4 pub const CallBreak = packed struct {
5 pub const Mode = packed enum(u12) { ecall, ebreak };
6 opcode: u7 = 0b1110011,
7 unused1: u5 = 0,
8 unused2: u3 = 0,
9 unused3: u5 = 0,
10 mode: u12, //: Mode
11 };
12 // I-type
13 pub const Addi = packed struct {
14 pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
15 opcode: u7 = 0b0010011,
4// TODO: this is only tagged to facilitate the monstrosity.
5// Once packed structs work make it packed.
6pub const Instruction = union(enum) {
7 R: packed struct {
8 opcode: u7,
169 rd: u5,
17 mode: u3, //: Mode
10 funct3: u3,
1811 rs1: u5,
19 imm: i12,
20 };
21 pub const Lui = packed struct {
22 opcode: u7 = 0b0110111,
12 rs2: u5,
13 funct7: u7,
14 },
15 I: packed struct {
16 opcode: u7,
2317 rd: u5,
24 imm: i20,
25 };
26 // I_type
27 pub const Load = packed struct {
28 pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
29 opcode: u7 = 0b0000011,
30 rd: u5,
31 mode: u3, //: Mode
18 funct3: u3,
3219 rs1: u5,
33 offset: i12,
34 };
35 // I-type
36 pub const Jalr = packed struct {
37 opcode: u7 = 0b1100111,
38 rd: u5,
39 mode: u3 = 0,
20 imm0_11: u12,
21 },
22 S: packed struct {
23 opcode: u7,
24 imm0_4: u5,
25 funct3: u3,
26 rs1: u5,
27 rs2: u5,
28 imm5_11: u7,
29 },
30 B: packed struct {
31 opcode: u7,
32 imm11: u1,
33 imm1_4: u4,
34 funct3: u3,
4035 rs1: u5,
41 offset: i12,
42 };
36 rs2: u5,
37 imm5_10: u6,
38 imm12: u1,
39 },
40 U: packed struct {
41 opcode: u7,
42 rd: u5,
43 imm12_31: u20,
44 },
45 J: packed struct {
46 opcode: u7,
47 rd: u5,
48 imm12_19: u8,
49 imm11: u1,
50 imm1_10: u10,
51 imm20: u1,
52 },
53
54 // TODO: once packed structs work we can remove this monstrosity.
55 pub fn toU32(self: Instruction) u32 {
56 return switch (self) {
57 .R => |v| @bitCast(u32, v),
58 .I => |v| @bitCast(u32, v),
59 .S => |v| @bitCast(u32, v),
60 .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
61 .U => |v| @bitCast(u32, v),
62 .J => |v| @bitCast(u32, v),
63 };
64 }
65
66 fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
67 return Instruction{
68 .R = .{
69 .opcode = op,
70 .funct3 = fn3,
71 .funct7 = fn7,
72 .rd = @enumToInt(rd),
73 .rs1 = @enumToInt(r1),
74 .rs2 = @enumToInt(r2),
75 },
76 };
77 }
78
79 // RISC-V is all signed all the time -- convert immediates to unsigned for processing
80 fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
81 const umm = @bitCast(u12, imm);
82
83 return Instruction{
84 .I = .{
85 .opcode = op,
86 .funct3 = fn3,
87 .rd = @enumToInt(rd),
88 .rs1 = @enumToInt(r1),
89 .imm0_11 = umm,
90 },
91 };
92 }
93
94 fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
95 const umm = @bitCast(u12, imm);
96
97 return Instruction{
98 .S = .{
99 .opcode = op,
100 .funct3 = fn3,
101 .rs1 = @enumToInt(r1),
102 .rs2 = @enumToInt(r2),
103 .imm0_4 = @truncate(u5, umm),
104 .imm5_11 = @truncate(u7, umm >> 5),
105 },
106 };
107 }
108
109 // Use significance value rather than bit value, same for J-type
110 // -- less burden on callsite, bonus semantic checking
111 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
112 const umm = @bitCast(u13, imm);
113 if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
114
115 return Instruction{
116 .B = .{
117 .opcode = op,
118 .funct3 = fn3,
119 .rs1 = @enumToInt(r1),
120 .rs2 = @enumToInt(r2),
121 .imm1_4 = @truncate(u4, umm >> 1),
122 .imm5_10 = @truncate(u6, umm >> 5),
123 .imm11 = @truncate(u1, umm >> 11),
124 .imm12 = @truncate(u1, umm >> 12),
125 },
126 };
127 }
128
129 // We have to extract the 20 bits anyway -- let's not make it more painful
130 fn uType(op: u7, rd: Register, imm: i20) Instruction {
131 const umm = @bitCast(u20, imm);
132
133 return Instruction{
134 .U = .{
135 .opcode = op,
136 .rd = @enumToInt(rd),
137 .imm12_31 = umm,
138 },
139 };
140 }
141
142 fn jType(op: u7, rd: Register, imm: i21) Instruction {
143 const umm = @bitcast(u21, imm);
144 if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
145
146 return Instruction{
147 .J = .{
148 .opcode = op,
149 .rd = @enumToInt(rd),
150 .imm1_10 = @truncate(u10, umm >> 1),
151 .imm11 = @truncate(u1, umm >> 1),
152 .imm12_19 = @truncate(u8, umm >> 12),
153 .imm20 = @truncate(u1, umm >> 20),
154 },
155 };
156 }
157
158 // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
159
160 // Arithmetic/Logical, Register-Register
161
162 pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
163 return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
164 }
165
166 pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
167 return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
168 }
169
170 pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
171 return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
172 }
173
174 pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
175 return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
176 }
177
178 pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
179 return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
180 }
181
182 pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
183 return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
184 }
185
186 pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
187 return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
188 }
189
190 pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
191 return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
192 }
193
194 pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
195 return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
196 }
197
198 pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
199 return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
200 }
201
202 // Arithmetic/Logical, Register-Register (32-bit)
203
204 pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
205 return rType(0b0111011, 0b000, rd, r1, r2);
206 }
207
208 pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
209 return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
210 }
211
212 pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
213 return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
214 }
215
216 pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
217 return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
218 }
219
220 pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
221 return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
222 }
223
224 // Arithmetic/Logical, Register-Immediate
225
226 pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
227 return iType(0b0010011, 0b000, rd, r1, imm);
228 }
229
230 pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
231 return iType(0b0010011, 0b111, rd, r1, imm);
232 }
233
234 pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
235 return iType(0b0010011, 0b110, rd, r1, imm);
236 }
237
238 pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
239 return iType(0b0010011, 0b100, rd, r1, imm);
240 }
241
242 pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
243 return iType(0b0010011, 0b001, rd, r1, shamt);
244 }
245
246 pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
247 return iType(0b0010011, 0b101, rd, r1, shamt);
248 }
249
250 pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
251 return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
252 }
253
254 pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
255 return iType(0b0010011, 0b010, rd, r1, imm);
256 }
257
258 pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
259 return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
260 }
261
262 // Arithmetic/Logical, Register-Immediate (32-bit)
263
264 pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
265 return iType(0b0011011, 0b000, rd, r1, imm);
266 }
267
268 pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
269 return iType(0b0011011, 0b001, rd, r1, shamt);
270 }
271
272 pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
273 return iType(0b0011011, 0b101, rd, r1, shamt);
274 }
275
276 pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
277 return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
278 }
279
280 // Upper Immediate
281
282 pub fn lui(rd: Register, imm: i20) Instruction {
283 return uType(0b0110111, rd, imm);
284 }
285
286 pub fn auipc(rd: Register, imm: i20) Instruction {
287 return uType(0b0010111, rd, imm);
288 }
289
290 // Load
291
292 pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
293 return iType(0b0000011, 0b011, rd, base, offset);
294 }
295
296 pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
297 return iType(0b0000011, 0b010, rd, base, offset);
298 }
299
300 pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
301 return iType(0b0000011, 0b110, rd, base, offset);
302 }
303
304 pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
305 return iType(0b0000011, 0b001, rd, base, offset);
306 }
307
308 pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
309 return iType(0b0000011, 0b101, rd, base, offset);
310 }
311
312 pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
313 return iType(0b0000011, 0b000, rd, base, offset);
314 }
315
316 pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
317 return iType(0b0000011, 0b100, rd, base, offset);
318 }
319
320 // Store
321
322 pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
323 return sType(0b0100011, 0b011, base, rs, offset);
324 }
325
326 pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
327 return sType(0b0100011, 0b010, base, rs, offset);
328 }
329
330 pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
331 return sType(0b0100011, 0b001, base, rs, offset);
332 }
333
334 pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
335 return sType(0b0100011, 0b000, base, rs, offset);
336 }
337
338 // Fence
339 // TODO: implement fence
340
341 // Branch
342
343 pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
344 return bType(0b1100011, 0b000, r1, r2, offset);
345 }
346
347 pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
348 return bType(0b1100011, 0b001, r1, r2, offset);
349 }
350
351 pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
352 return bType(0b1100011, 0b100, r1, r2, offset);
353 }
354
355 pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
356 return bType(0b1100011, 0b101, r1, r2, offset);
357 }
358
359 pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
360 return bType(0b1100011, 0b110, r1, r2, offset);
361 }
362
363 pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
364 return bType(0b1100011, 0b111, r1, r2, offset);
365 }
366
367 // Jump
368
369 pub fn jal(link: Register, offset: i21) Instruction {
370 return jType(0b1101111, link, offset);
371 }
372
373 pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
374 return iType(0b1100111, 0b000, link, base, offset);
375 }
376
377 // System
378
379 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
380 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
43381};
44382
45383// zig fmt: off
46pub const RawRegister = enum(u8) {
384pub const RawRegister = enum(u5) {
47385 x0, x1, x2, x3, x4, x5, x6, x7,
48386 x8, x9, x10, x11, x12, x13, x14, x15,
49387 x16, x17, x18, x19, x20, x21, x22, x23,
50388 x24, x25, x26, x27, x28, x29, x30, x31,
389
390 pub fn dwarfLocOp(reg: RawRegister) u8 {
391 return @enumToInt(reg) + DW.OP_reg0;
392 }
51393};
52394
53pub const Register = enum(u8) {
395pub const Register = enum(u5) {
54396 // 64 bit registers
55397 zero, // zero
56398 ra, // return address. caller saved
......@@ -71,11 +413,6 @@ pub const Register = enum(u8) {
71413 return null;
72414 }
73415
74 /// Returns the register's id.
75 pub fn id(self: @This()) u5 {
76 return @truncate(u5, @enumToInt(self));
77 }
78
79416 /// Returns the index into `callee_preserved_regs`.
80417 pub fn allocIndex(self: Register) ?u4 {
81418 inline for(callee_preserved_regs) |cpreg, i| {
......@@ -83,6 +420,10 @@ pub const Register = enum(u8) {
83420 }
84421 return null;
85422 }
423
424 pub fn dwarfLocOp(reg: Register) u8 {
425 return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
426 }
86427};
87428
88429// zig fmt: on
src-self-hosted/codegen/wasm.zig created+119
......@@ -0,0 +1,119 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const ArrayList = std.ArrayList;
4const assert = std.debug.assert;
5const leb = std.debug.leb;
6const mem = std.mem;
7
8const Decl = @import("../Module.zig").Decl;
9const Inst = @import("../ir.zig").Inst;
10const Type = @import("../type.zig").Type;
11const Value = @import("../value.zig").Value;
12
13fn genValtype(ty: Type) u8 {
14 return switch (ty.tag()) {
15 .u32, .i32 => 0x7F,
16 .u64, .i64 => 0x7E,
17 .f32 => 0x7D,
18 .f64 => 0x7C,
19 else => @panic("TODO: Implement more types for wasm."),
20 };
21}
22
23pub fn genFunctype(buf: *ArrayList(u8), decl: *Decl) !void {
24 const ty = decl.typed_value.most_recent.typed_value.ty;
25 const writer = buf.writer();
26
27 // functype magic
28 try writer.writeByte(0x60);
29
30 // param types
31 try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
32 if (ty.fnParamLen() != 0) {
33 const params = try buf.allocator.alloc(Type, ty.fnParamLen());
34 defer buf.allocator.free(params);
35 ty.fnParamTypes(params);
36 for (params) |param_type| try writer.writeByte(genValtype(param_type));
37 }
38
39 // return type
40 const return_type = ty.fnReturnType();
41 switch (return_type.tag()) {
42 .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),
43 else => {
44 try leb.writeULEB128(writer, @as(u32, 1));
45 try writer.writeByte(genValtype(return_type));
46 },
47 }
48}
49
50pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
51 assert(buf.items.len == 0);
52 const writer = buf.writer();
53
54 // Reserve space to write the size after generating the code
55 try buf.resize(5);
56
57 // Write the size of the locals vec
58 // TODO: implement locals
59 try leb.writeULEB128(writer, @as(u32, 0));
60
61 // Write instructions
62 // TODO: check for and handle death of instructions
63 const tv = decl.typed_value.most_recent.typed_value;
64 const mod_fn = tv.val.cast(Value.Payload.Function).?.func;
65 for (mod_fn.analysis.success.instructions) |inst| try genInst(writer, inst);
66
67 // Write 'end' opcode
68 try writer.writeByte(0x0B);
69
70 // Fill in the size of the generated code to the reserved space at the
71 // beginning of the buffer.
72 leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, buf.items.len - 5));
73}
74
75fn genInst(writer: ArrayList(u8).Writer, inst: *Inst) !void {
76 return switch (inst.tag) {
77 .dbg_stmt => {},
78 .ret => genRet(writer, inst.castTag(.ret).?),
79 else => error.TODOImplementMoreWasmCodegen,
80 };
81}
82
83fn genRet(writer: ArrayList(u8).Writer, inst: *Inst.UnOp) !void {
84 switch (inst.operand.tag) {
85 .constant => {
86 const constant = inst.operand.castTag(.constant).?;
87 switch (inst.operand.ty.tag()) {
88 .u32 => {
89 try writer.writeByte(0x41); // i32.const
90 try leb.writeILEB128(writer, constant.val.toUnsignedInt());
91 },
92 .i32 => {
93 try writer.writeByte(0x41); // i32.const
94 try leb.writeILEB128(writer, constant.val.toSignedInt());
95 },
96 .u64 => {
97 try writer.writeByte(0x42); // i64.const
98 try leb.writeILEB128(writer, constant.val.toUnsignedInt());
99 },
100 .i64 => {
101 try writer.writeByte(0x42); // i64.const
102 try leb.writeILEB128(writer, constant.val.toSignedInt());
103 },
104 .f32 => {
105 try writer.writeByte(0x43); // f32.const
106 // TODO: enforce LE byte order
107 try writer.writeAll(mem.asBytes(&constant.val.toFloat(f32)));
108 },
109 .f64 => {
110 try writer.writeByte(0x44); // f64.const
111 // TODO: enforce LE byte order
112 try writer.writeAll(mem.asBytes(&constant.val.toFloat(f64)));
113 },
114 else => return error.TODOImplementMoreWasmCodegen,
115 }
116 },
117 else => return error.TODOImplementMoreWasmCodegen,
118 }
119}
src-self-hosted/codegen/x86.zig+79
......@@ -1,3 +1,6 @@
1const std = @import("std");
2const DW = std.dwarf;
3
14// zig fmt: off
25pub const Register = enum(u8) {
36 // 0 through 7, 32-bit registers. id is int value
......@@ -37,8 +40,84 @@ pub const Register = enum(u8) {
3740 else => null,
3841 };
3942 }
43
44 /// Convert from any register to its 32 bit alias.
45 pub fn to32(self: Register) Register {
46 return @intToEnum(Register, @as(u8, self.id()));
47 }
48
49 /// Convert from any register to its 16 bit alias.
50 pub fn to16(self: Register) Register {
51 return @intToEnum(Register, @as(u8, self.id()) + 8);
52 }
53
54 /// Convert from any register to its 8 bit alias.
55 pub fn to8(self: Register) Register {
56 return @intToEnum(Register, @as(u8, self.id()) + 16);
57 }
58
59
60 pub fn dwarfLocOp(reg: Register) u8 {
61 return switch (reg.to32()) {
62 .eax => DW.OP_reg0,
63 .ecx => DW.OP_reg1,
64 .edx => DW.OP_reg2,
65 .ebx => DW.OP_reg3,
66 .esp => DW.OP_reg4,
67 .ebp => DW.OP_reg5,
68 .esi => DW.OP_reg6,
69 .edi => DW.OP_reg7,
70 else => unreachable,
71 };
72 }
4073};
4174
4275// zig fmt: on
4376
4477pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
78
79// TODO add these to Register enum and corresponding dwarfLocOp
80// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
81// RA = (8, "RA"),
82//
83// ST0 = (11, "st0"),
84// ST1 = (12, "st1"),
85// ST2 = (13, "st2"),
86// ST3 = (14, "st3"),
87// ST4 = (15, "st4"),
88// ST5 = (16, "st5"),
89// ST6 = (17, "st6"),
90// ST7 = (18, "st7"),
91//
92// XMM0 = (21, "xmm0"),
93// XMM1 = (22, "xmm1"),
94// XMM2 = (23, "xmm2"),
95// XMM3 = (24, "xmm3"),
96// XMM4 = (25, "xmm4"),
97// XMM5 = (26, "xmm5"),
98// XMM6 = (27, "xmm6"),
99// XMM7 = (28, "xmm7"),
100//
101// MM0 = (29, "mm0"),
102// MM1 = (30, "mm1"),
103// MM2 = (31, "mm2"),
104// MM3 = (32, "mm3"),
105// MM4 = (33, "mm4"),
106// MM5 = (34, "mm5"),
107// MM6 = (35, "mm6"),
108// MM7 = (36, "mm7"),
109//
110// MXCSR = (39, "mxcsr"),
111//
112// ES = (40, "es"),
113// CS = (41, "cs"),
114// SS = (42, "ss"),
115// DS = (43, "ds"),
116// FS = (44, "fs"),
117// GS = (45, "gs"),
118//
119// TR = (48, "tr"),
120// LDTR = (49, "ldtr"),
121//
122// FS_BASE = (93, "fs.base"),
123// GS_BASE = (94, "gs.base"),
src-self-hosted/codegen/x86_64.zig+109
......@@ -1,4 +1,6 @@
1const std = @import("std");
12const Type = @import("../Type.zig");
3const DW = std.dwarf;
24
35// zig fmt: off
46
......@@ -101,6 +103,30 @@ pub const Register = enum(u8) {
101103 pub fn to8(self: Register) Register {
102104 return @intToEnum(Register, @as(u8, self.id()) + 48);
103105 }
106
107 pub fn dwarfLocOp(self: Register) u8 {
108 return switch (self.to64()) {
109 .rax => DW.OP_reg0,
110 .rdx => DW.OP_reg1,
111 .rcx => DW.OP_reg2,
112 .rbx => DW.OP_reg3,
113 .rsi => DW.OP_reg4,
114 .rdi => DW.OP_reg5,
115 .rbp => DW.OP_reg6,
116 .rsp => DW.OP_reg7,
117
118 .r8 => DW.OP_reg8,
119 .r9 => DW.OP_reg9,
120 .r10 => DW.OP_reg10,
121 .r11 => DW.OP_reg11,
122 .r12 => DW.OP_reg12,
123 .r13 => DW.OP_reg13,
124 .r14 => DW.OP_reg14,
125 .r15 => DW.OP_reg15,
126
127 else => unreachable,
128 };
129 }
104130};
105131
106132// zig fmt: on
......@@ -109,3 +135,86 @@ pub const Register = enum(u8) {
109135pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
110136pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
111137pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
138
139// TODO add these registers to the enum and populate dwarfLocOp
140// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
141// RA = (16, "RA"),
142//
143// XMM0 = (17, "xmm0"),
144// XMM1 = (18, "xmm1"),
145// XMM2 = (19, "xmm2"),
146// XMM3 = (20, "xmm3"),
147// XMM4 = (21, "xmm4"),
148// XMM5 = (22, "xmm5"),
149// XMM6 = (23, "xmm6"),
150// XMM7 = (24, "xmm7"),
151//
152// XMM8 = (25, "xmm8"),
153// XMM9 = (26, "xmm9"),
154// XMM10 = (27, "xmm10"),
155// XMM11 = (28, "xmm11"),
156// XMM12 = (29, "xmm12"),
157// XMM13 = (30, "xmm13"),
158// XMM14 = (31, "xmm14"),
159// XMM15 = (32, "xmm15"),
160//
161// ST0 = (33, "st0"),
162// ST1 = (34, "st1"),
163// ST2 = (35, "st2"),
164// ST3 = (36, "st3"),
165// ST4 = (37, "st4"),
166// ST5 = (38, "st5"),
167// ST6 = (39, "st6"),
168// ST7 = (40, "st7"),
169//
170// MM0 = (41, "mm0"),
171// MM1 = (42, "mm1"),
172// MM2 = (43, "mm2"),
173// MM3 = (44, "mm3"),
174// MM4 = (45, "mm4"),
175// MM5 = (46, "mm5"),
176// MM6 = (47, "mm6"),
177// MM7 = (48, "mm7"),
178//
179// RFLAGS = (49, "rFLAGS"),
180// ES = (50, "es"),
181// CS = (51, "cs"),
182// SS = (52, "ss"),
183// DS = (53, "ds"),
184// FS = (54, "fs"),
185// GS = (55, "gs"),
186//
187// FS_BASE = (58, "fs.base"),
188// GS_BASE = (59, "gs.base"),
189//
190// TR = (62, "tr"),
191// LDTR = (63, "ldtr"),
192// MXCSR = (64, "mxcsr"),
193// FCW = (65, "fcw"),
194// FSW = (66, "fsw"),
195//
196// XMM16 = (67, "xmm16"),
197// XMM17 = (68, "xmm17"),
198// XMM18 = (69, "xmm18"),
199// XMM19 = (70, "xmm19"),
200// XMM20 = (71, "xmm20"),
201// XMM21 = (72, "xmm21"),
202// XMM22 = (73, "xmm22"),
203// XMM23 = (74, "xmm23"),
204// XMM24 = (75, "xmm24"),
205// XMM25 = (76, "xmm25"),
206// XMM26 = (77, "xmm26"),
207// XMM27 = (78, "xmm27"),
208// XMM28 = (79, "xmm28"),
209// XMM29 = (80, "xmm29"),
210// XMM30 = (81, "xmm30"),
211// XMM31 = (82, "xmm31"),
212//
213// K0 = (118, "k0"),
214// K1 = (119, "k1"),
215// K2 = (120, "k2"),
216// K3 = (121, "k3"),
217// K4 = (122, "k4"),
218// K5 = (123, "k5"),
219// K6 = (124, "k6"),
220// K7 = (125, "k7"),
src-self-hosted/introspect.zig+62-6
......@@ -3,8 +3,7 @@
33const std = @import("std");
44const mem = std.mem;
55const fs = std.fs;
6
7const warn = std.debug.warn;
6const CacheHash = std.cache_hash.CacheHash;
87
98/// Caller must free result
109pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
......@@ -63,7 +62,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
6362
6463pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
6564 return findZigLibDir(allocator) catch |err| {
66 warn(
65 std.debug.print(
6766 \\Unable to find zig lib directory: {}.
6867 \\Reinstall Zig or use --zig-install-prefix.
6968 \\
......@@ -73,7 +72,64 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
7372 };
7473}
7574
76/// Caller must free result
77pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 {
78 return std.mem.dupe(allocator, u8, "zig-cache");
75/// Caller owns returned memory.
76pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
77 const appname = "zig";
78
79 if (std.Target.current.os.tag != .windows) {
80 if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| {
81 return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
82 } else if (std.os.getenv("HOME")) |home| {
83 return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
84 }
85 }
86
87 return fs.getAppDataDir(allocator, appname);
88}
89
90var compiler_id_mutex = std.Mutex{};
91var compiler_id: [16]u8 = undefined;
92var compiler_id_computed = false;
93
94pub fn resolveCompilerId(gpa: *mem.Allocator) ![16]u8 {
95 const held = compiler_id_mutex.acquire();
96 defer held.release();
97
98 if (compiler_id_computed)
99 return compiler_id;
100 compiler_id_computed = true;
101
102 const global_cache_dir = try resolveGlobalCacheDir(gpa);
103 defer gpa.free(global_cache_dir);
104
105 // TODO Introduce openGlobalCacheDir which returns a dir handle rather than a string.
106 var cache_dir = try fs.cwd().openDir(global_cache_dir, .{});
107 defer cache_dir.close();
108
109 var ch = try CacheHash.init(gpa, cache_dir, "exe");
110 defer ch.release();
111
112 const self_exe_path = try fs.selfExePathAlloc(gpa);
113 defer gpa.free(self_exe_path);
114
115 _ = try ch.addFile(self_exe_path, null);
116
117 if (try ch.hit()) |digest| {
118 compiler_id = digest[0..16].*;
119 return compiler_id;
120 }
121
122 const libs = try std.process.getSelfExeSharedLibPaths(gpa);
123 defer {
124 for (libs) |lib| gpa.free(lib);
125 gpa.free(libs);
126 }
127
128 for (libs) |lib| {
129 try ch.addFilePost(lib);
130 }
131
132 const digest = ch.final();
133 compiler_id = digest[0..16].*;
134 return compiler_id;
79135}
src-self-hosted/ir.zig+47-5
......@@ -68,8 +68,10 @@ pub const Inst = struct {
6868 dbg_stmt,
6969 isnonnull,
7070 isnull,
71 iserr,
7172 /// Read a value from a pointer.
7273 load,
74 loop,
7375 ptrtoint,
7476 ref,
7577 ret,
......@@ -81,13 +83,14 @@ pub const Inst = struct {
8183 not,
8284 floatcast,
8385 intcast,
86 unwrap_optional,
87 wrap_optional,
8488
8589 pub fn Type(tag: Tag) type {
8690 return switch (tag) {
8791 .alloc,
8892 .retvoid,
8993 .unreach,
90 .arg,
9194 .breakpoint,
9295 .dbg_stmt,
9396 => NoOp,
......@@ -98,10 +101,13 @@ pub const Inst = struct {
98101 .not,
99102 .isnonnull,
100103 .isnull,
104 .iserr,
101105 .ptrtoint,
102106 .floatcast,
103107 .intcast,
104108 .load,
109 .unwrap_optional,
110 .wrap_optional,
105111 => UnOp,
106112
107113 .add,
......@@ -115,6 +121,7 @@ pub const Inst = struct {
115121 .store,
116122 => BinOp,
117123
124 .arg => Arg,
118125 .assembly => Assembly,
119126 .block => Block,
120127 .br => Br,
......@@ -122,6 +129,7 @@ pub const Inst = struct {
122129 .call => Call,
123130 .condbr => CondBr,
124131 .constant => Constant,
132 .loop => Loop,
125133 };
126134 }
127135
......@@ -253,6 +261,20 @@ pub const Inst = struct {
253261 }
254262 };
255263
264 pub const Arg = struct {
265 pub const base_tag = Tag.arg;
266
267 base: Inst,
268 name: [*:0]const u8,
269
270 pub fn operandCount(self: *const Arg) usize {
271 return 0;
272 }
273 pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
274 return null;
275 }
276 };
277
256278 pub const Assembly = struct {
257279 pub const base_tag = Tag.assembly;
258280
......@@ -354,11 +376,11 @@ pub const Inst = struct {
354376 then_body: Body,
355377 else_body: Body,
356378 /// Set of instructions whose lifetimes end at the start of one of the branches.
357 /// The `true` branch is first: `deaths[0..true_death_count]`.
358 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
379 /// The `then` branch is first: `deaths[0..then_death_count]`.
380 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
359381 deaths: [*]*Inst = undefined,
360 true_death_count: u32 = 0,
361 false_death_count: u32 = 0,
382 then_death_count: u32 = 0,
383 else_death_count: u32 = 0,
362384
363385 pub fn operandCount(self: *const CondBr) usize {
364386 return 1;
......@@ -372,6 +394,12 @@ pub const Inst = struct {
372394
373395 return null;
374396 }
397 pub fn thenDeaths(self: *const CondBr) []*Inst {
398 return self.deaths[0..self.then_death_count];
399 }
400 pub fn elseDeaths(self: *const CondBr) []*Inst {
401 return (self.deaths + self.then_death_count)[0..self.else_death_count];
402 }
375403 };
376404
377405 pub const Constant = struct {
......@@ -387,6 +415,20 @@ pub const Inst = struct {
387415 return null;
388416 }
389417 };
418
419 pub const Loop = struct {
420 pub const base_tag = Tag.loop;
421
422 base: Inst,
423 body: Body,
424
425 pub fn operandCount(self: *const Loop) usize {
426 return 0;
427 }
428 pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
429 return null;
430 }
431 };
390432};
391433
392434pub const Body = struct {
src-self-hosted/link.zig+674-267
......@@ -8,12 +8,16 @@ const fs = std.fs;
88const elf = std.elf;
99const codegen = @import("codegen.zig");
1010const c_codegen = @import("codegen/c.zig");
11const log = std.log;
11const log = std.log.scoped(.link);
1212const DW = std.dwarf;
1313const trace = @import("tracy.zig").trace;
1414const leb128 = std.debug.leb;
1515const Package = @import("Package.zig");
1616const Value = @import("value.zig").Value;
17const Type = @import("type.zig").Type;
18const build_options = @import("build_options");
19
20const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
1721
1822// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
1923// zig fmt: off
......@@ -36,9 +40,26 @@ pub const Options = struct {
3640 program_code_size_hint: u64 = 256 * 1024,
3741};
3842
43
3944pub const File = struct {
45 pub const LinkBlock = union {
46 elf: Elf.TextBlock,
47 macho: MachO.TextBlock,
48 c: void,
49 wasm: void,
50 };
51
52 pub const LinkFn = union {
53 elf: Elf.SrcFn,
54 macho: MachO.SrcFn,
55 c: void,
56 wasm: ?Wasm.FnData,
57 };
58
4059 tag: Tag,
4160 options: Options,
61 file: ?fs.File,
62 allocator: *Allocator,
4263
4364 /// Attempts incremental linking, if the file already exists. If
4465 /// incremental linking fails, falls back to truncating the file and
......@@ -49,8 +70,8 @@ pub const File = struct {
4970 .unknown => unreachable,
5071 .coff => return error.TODOImplementCoff,
5172 .elf => return Elf.openPath(allocator, dir, sub_path, options),
52 .macho => return error.TODOImplementMacho,
53 .wasm => return error.TODOImplementWasm,
73 .macho => return MachO.openPath(allocator, dir, sub_path, options),
74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
5475 .c => return C.openPath(allocator, dir, sub_path, options),
5576 .hex => return error.TODOImplementHex,
5677 .raw => return error.TODOImplementRaw,
......@@ -66,43 +87,61 @@ pub const File = struct {
6687
6788 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
6889 switch (base.tag) {
69 .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
70 .c => {},
90 .elf, .macho => {
91 if (base.file != null) return;
92 base.file = try dir.createFile(sub_path, .{
93 .truncate = false,
94 .read = true,
95 .mode = determineMode(base.options),
96 });
97 },
98 .c, .wasm => {},
7199 }
72100 }
73101
74102 pub fn makeExecutable(base: *File) !void {
75103 switch (base.tag) {
76 .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
77104 .c => unreachable,
105 .wasm => {},
106 else => if (base.file) |f| {
107 f.close();
108 base.file = null;
109 },
78110 }
79111 }
80112
81113 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
82114 switch (base.tag) {
83115 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
84117 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
118 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
85119 }
86120 }
87121
88122 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
89123 switch (base.tag) {
90124 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
91 .c => {},
125 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126 .c, .wasm => {},
92127 }
93128 }
94129
95130 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
96131 switch (base.tag) {
97132 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
98 .c => {},
133 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
134 .c, .wasm => {},
99135 }
100136 }
101137
102138 pub fn deinit(base: *File) void {
139 if (base.file) |f| f.close();
103140 switch (base.tag) {
104141 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
142 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
105143 .c => @fieldParentPtr(C, "base", base).deinit(),
144 .wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
106145 }
107146 }
108147
......@@ -111,37 +150,53 @@ pub const File = struct {
111150 .elf => {
112151 const parent = @fieldParentPtr(Elf, "base", base);
113152 parent.deinit();
114 parent.allocator.destroy(parent);
153 base.allocator.destroy(parent);
154 },
155 .macho => {
156 const parent = @fieldParentPtr(MachO, "base", base);
157 parent.deinit();
158 base.allocator.destroy(parent);
115159 },
116160 .c => {
117161 const parent = @fieldParentPtr(C, "base", base);
118162 parent.deinit();
119 parent.allocator.destroy(parent);
163 base.allocator.destroy(parent);
164 },
165 .wasm => {
166 const parent = @fieldParentPtr(Wasm, "base", base);
167 parent.deinit();
168 base.allocator.destroy(parent);
120169 },
121170 }
122171 }
123172
124 pub fn flush(base: *File) !void {
173 pub fn flush(base: *File, module: *Module) !void {
125174 const tracy = trace(@src());
126175 defer tracy.end();
127176
128177 try switch (base.tag) {
129 .elf => @fieldParentPtr(Elf, "base", base).flush(),
130 .c => @fieldParentPtr(C, "base", base).flush(),
178 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
179 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
180 .c => @fieldParentPtr(C, "base", base).flush(module),
181 .wasm => @fieldParentPtr(Wasm, "base", base).flush(module),
131182 };
132183 }
133184
134185 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
135186 switch (base.tag) {
136187 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
188 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
137189 .c => unreachable,
190 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
138191 }
139192 }
140193
141194 pub fn errorFlags(base: *File) ErrorFlags {
142195 return switch (base.tag) {
143196 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
144198 .c => return .{ .no_entry_point_found = false },
199 .wasm => return ErrorFlags{},
145200 };
146201 }
147202
......@@ -154,13 +209,17 @@ pub const File = struct {
154209 ) !void {
155210 switch (base.tag) {
156211 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
212 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
157213 .c => return {},
214 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
158215 }
159216 }
160217
161218 pub const Tag = enum {
162219 elf,
220 macho,
163221 c,
222 wasm,
164223 };
165224
166225 pub const ErrorFlags = struct {
......@@ -172,15 +231,13 @@ pub const File = struct {
172231
173232 base: File,
174233
175 allocator: *Allocator,
176234 header: std.ArrayList(u8),
177235 constants: std.ArrayList(u8),
178236 main: std.ArrayList(u8),
179 file: ?fs.File,
237
180238 called: std.StringHashMap(void),
181239 need_stddef: bool = false,
182240 need_stdint: bool = false,
183 need_noreturn: bool = false,
184241 error_msg: *Module.ErrorMsg = undefined,
185242
186243 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
......@@ -196,9 +253,9 @@ pub const File = struct {
196253 .base = .{
197254 .tag = .c,
198255 .options = options,
256 .file = file,
257 .allocator = allocator,
199258 },
200 .allocator = allocator,
201 .file = file,
202259 .main = std.ArrayList(u8).init(allocator),
203260 .header = std.ArrayList(u8).init(allocator),
204261 .constants = std.ArrayList(u8).init(allocator),
......@@ -208,8 +265,8 @@ pub const File = struct {
208265 return &c_file.base;
209266 }
210267
211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
268 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{AnalysisFail, OutOfMemory} {
269 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
213270 return error.AnalysisFail;
214271 }
215272
......@@ -218,8 +275,6 @@ pub const File = struct {
218275 self.header.deinit();
219276 self.constants.deinit();
220277 self.called.deinit();
221 if (self.file) |f|
222 f.close();
223278 }
224279
225280 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
......@@ -231,8 +286,8 @@ pub const File = struct {
231286 };
232287 }
233288
234 pub fn flush(self: *File.C) !void {
235 const writer = self.file.?.writer();
289 pub fn flush(self: *File.C, module: *Module) !void {
290 const writer = self.base.file.?.writer();
236291 try writer.writeAll(@embedFile("cbe.h"));
237292 var includes = false;
238293 if (self.need_stddef) {
......@@ -259,8 +314,8 @@ pub const File = struct {
259314 }
260315 }
261316 try writer.writeAll(self.main.items);
262 self.file.?.close();
263 self.file = null;
317 self.base.file.?.close();
318 self.base.file = null;
264319 }
265320 };
266321
......@@ -269,9 +324,6 @@ pub const File = struct {
269324
270325 base: File,
271326
272 allocator: *Allocator,
273 file: ?fs.File,
274 owns_file_handle: bool,
275327 ptr_width: enum { p32, p64 },
276328
277329 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
......@@ -309,26 +361,27 @@ pub const File = struct {
309361 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
310362 /// write them at the end. These are only the local symbols. The length of this array
311363 /// is the value used for sh_info in the .symtab section.
312 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
313 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
364 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
365 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
314366
315 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
316 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
317 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
367 local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
368 global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
369 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
318370
319371 /// Same order as in the file. The value is the absolute vaddr value.
320372 /// If the vaddr of the executable program header changes, the entire
321373 /// offset table needs to be rewritten.
322 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
374 offset_table: std.ArrayListUnmanaged(u64) = .{},
323375
324376 phdr_table_dirty: bool = false,
325377 shdr_table_dirty: bool = false,
326378 shstrtab_dirty: bool = false,
327379 debug_strtab_dirty: bool = false,
328380 offset_table_count_dirty: bool = false,
329 debug_info_section_dirty: bool = false,
330381 debug_abbrev_section_dirty: bool = false,
331382 debug_aranges_section_dirty: bool = false,
383
384 debug_info_header_dirty: bool = false,
332385 debug_line_header_dirty: bool = false,
333386
334387 error_flags: ErrorFlags = ErrorFlags{},
......@@ -348,7 +401,7 @@ pub const File = struct {
348401 /// overcapacity can be negative. A simple way to have negative overcapacity is to
349402 /// allocate a fresh text block, which will have ideal capacity, and then grow it
350403 /// by 1 byte. It will then have -1 overcapacity.
351 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
404 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
352405 last_text_block: ?*TextBlock = null,
353406
354407 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
......@@ -357,6 +410,12 @@ pub const File = struct {
357410 dbg_line_fn_first: ?*SrcFn = null,
358411 dbg_line_fn_last: ?*SrcFn = null,
359412
413 /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
414 /// This is the same concept as `text_block_free_list`; see those doc comments.
415 dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
416 dbg_info_decl_first: ?*TextBlock = null,
417 dbg_info_decl_last: ?*TextBlock = null,
418
360419 /// `alloc_num / alloc_den` is the factor of padding when allocating.
361420 const alloc_num = 4;
362421 const alloc_den = 3;
......@@ -367,6 +426,17 @@ pub const File = struct {
367426 const minimum_text_block_size = 64;
368427 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
369428
429 pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, true);
430
431 const DbgInfoTypeReloc = struct {
432 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
433 /// This is where the .debug_info tag for the type is.
434 off: u32,
435 /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
436 /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
437 relocs: std.ArrayListUnmanaged(u32),
438 };
439
370440 pub const TextBlock = struct {
371441 /// Each decl always gets a local symbol with the fully qualified name.
372442 /// The vaddr and size are found here directly.
......@@ -382,11 +452,24 @@ pub const File = struct {
382452 prev: ?*TextBlock,
383453 next: ?*TextBlock,
384454
455 /// Previous/next linked list pointers. This value is `next ^ prev`.
456 /// This is the linked list node for this Decl's corresponding .debug_info tag.
457 dbg_info_prev: ?*TextBlock,
458 dbg_info_next: ?*TextBlock,
459 /// Offset into .debug_info pointing to the tag for this Decl.
460 dbg_info_off: u32,
461 /// Size of the .debug_info tag for this Decl, not including padding.
462 dbg_info_len: u32,
463
385464 pub const empty = TextBlock{
386465 .local_sym_index = 0,
387466 .offset_table_index = undefined,
388467 .prev = null,
389468 .next = null,
469 .dbg_info_prev = null,
470 .dbg_info_next = null,
471 .dbg_info_off = undefined,
472 .dbg_info_len = undefined,
390473 };
391474
392475 /// Returns how much room there is to grow in virtual address space.
......@@ -454,7 +537,6 @@ pub const File = struct {
454537 else => |e| return e,
455538 };
456539
457 elf_file.owns_file_handle = true;
458540 return &elf_file.base;
459541 }
460542
......@@ -467,12 +549,11 @@ pub const File = struct {
467549 }
468550 var self: Elf = .{
469551 .base = .{
552 .file = file,
470553 .tag = .elf,
471554 .options = options,
555 .allocator = allocator,
472556 },
473 .allocator = allocator,
474 .file = file,
475 .owns_file_handle = false,
476557 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
477558 32 => .p32,
478559 64 => .p64,
......@@ -499,16 +580,15 @@ pub const File = struct {
499580 .base = .{
500581 .tag = .elf,
501582 .options = options,
583 .allocator = allocator,
584 .file = file,
502585 },
503 .allocator = allocator,
504 .file = file,
505586 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
506587 32 => .p32,
507588 64 => .p64,
508589 else => return error.UnsupportedELFArchitecture,
509590 },
510591 .shdr_table_dirty = true,
511 .owns_file_handle = false,
512592 };
513593 errdefer self.deinit();
514594
......@@ -542,39 +622,19 @@ pub const File = struct {
542622 }
543623
544624 pub fn deinit(self: *Elf) void {
545 self.sections.deinit(self.allocator);
546 self.program_headers.deinit(self.allocator);
547 self.shstrtab.deinit(self.allocator);
548 self.debug_strtab.deinit(self.allocator);
549 self.local_symbols.deinit(self.allocator);
550 self.global_symbols.deinit(self.allocator);
551 self.global_symbol_free_list.deinit(self.allocator);
552 self.local_symbol_free_list.deinit(self.allocator);
553 self.offset_table_free_list.deinit(self.allocator);
554 self.text_block_free_list.deinit(self.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);
556 self.offset_table.deinit(self.allocator);
557 if (self.owns_file_handle) {
558 if (self.file) |f| f.close();
559 }
560 }
561
562 pub fn makeExecutable(self: *Elf) !void {
563 assert(self.owns_file_handle);
564 if (self.file) |f| {
565 f.close();
566 self.file = null;
567 }
568 }
569
570 pub fn makeWritable(self: *Elf, dir: fs.Dir, sub_path: []const u8) !void {
571 assert(self.owns_file_handle);
572 if (self.file != null) return;
573 self.file = try dir.createFile(sub_path, .{
574 .truncate = false,
575 .read = true,
576 .mode = determineMode(self.base.options),
577 });
625 self.sections.deinit(self.base.allocator);
626 self.program_headers.deinit(self.base.allocator);
627 self.shstrtab.deinit(self.base.allocator);
628 self.debug_strtab.deinit(self.base.allocator);
629 self.local_symbols.deinit(self.base.allocator);
630 self.global_symbols.deinit(self.base.allocator);
631 self.global_symbol_free_list.deinit(self.base.allocator);
632 self.local_symbol_free_list.deinit(self.base.allocator);
633 self.offset_table_free_list.deinit(self.base.allocator);
634 self.text_block_free_list.deinit(self.base.allocator);
635 self.dbg_line_fn_free_list.deinit(self.base.allocator);
636 self.dbg_info_decl_free_list.deinit(self.base.allocator);
637 self.offset_table.deinit(self.base.allocator);
578638 }
579639
580640 fn getDebugLineProgramOff(self: Elf) u32 {
......@@ -662,7 +722,7 @@ pub const File = struct {
662722
663723 /// TODO Improve this to use a table.
664724 fn makeString(self: *Elf, bytes: []const u8) !u32 {
665 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
725 try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1);
666726 const result = self.shstrtab.items.len;
667727 self.shstrtab.appendSliceAssumeCapacity(bytes);
668728 self.shstrtab.appendAssumeCapacity(0);
......@@ -671,7 +731,7 @@ pub const File = struct {
671731
672732 /// TODO Improve this to use a table.
673733 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
674 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
734 try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1);
675735 const result = self.debug_strtab.items.len;
676736 self.debug_strtab.appendSliceAssumeCapacity(bytes);
677737 self.debug_strtab.appendAssumeCapacity(0);
......@@ -702,8 +762,8 @@ pub const File = struct {
702762 const file_size = self.base.options.program_code_size_hint;
703763 const p_align = 0x1000;
704764 const off = self.findFreeSpace(file_size, p_align);
705 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
706 try self.program_headers.append(self.allocator, .{
765 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
766 try self.program_headers.append(self.base.allocator, .{
707767 .p_type = elf.PT_LOAD,
708768 .p_offset = off,
709769 .p_filesz = file_size,
......@@ -721,14 +781,14 @@ pub const File = struct {
721781 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
722782 // We really only need ptr alignment but since we are using PROGBITS, linux requires
723783 // page align.
724 const p_align = 0x1000;
784 const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
725785 const off = self.findFreeSpace(file_size, p_align);
726 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
786 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
727787 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
728788 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
729789 // else in virtual memory.
730 const default_got_addr = 0x4000000;
731 try self.program_headers.append(self.allocator, .{
790 const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000;
791 try self.program_headers.append(self.base.allocator, .{
732792 .p_type = elf.PT_LOAD,
733793 .p_offset = off,
734794 .p_filesz = file_size,
......@@ -743,10 +803,10 @@ pub const File = struct {
743803 if (self.shstrtab_index == null) {
744804 self.shstrtab_index = @intCast(u16, self.sections.items.len);
745805 assert(self.shstrtab.items.len == 0);
746 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
806 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
747807 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
748 log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
749 try self.sections.append(self.allocator, .{
808 log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
809 try self.sections.append(self.base.allocator, .{
750810 .sh_name = try self.makeString(".shstrtab"),
751811 .sh_type = elf.SHT_STRTAB,
752812 .sh_flags = 0,
......@@ -765,7 +825,7 @@ pub const File = struct {
765825 self.text_section_index = @intCast(u16, self.sections.items.len);
766826 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
767827
768 try self.sections.append(self.allocator, .{
828 try self.sections.append(self.base.allocator, .{
769829 .sh_name = try self.makeString(".text"),
770830 .sh_type = elf.SHT_PROGBITS,
771831 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
......@@ -783,7 +843,7 @@ pub const File = struct {
783843 self.got_section_index = @intCast(u16, self.sections.items.len);
784844 const phdr = &self.program_headers.items[self.phdr_got_index.?];
785845
786 try self.sections.append(self.allocator, .{
846 try self.sections.append(self.base.allocator, .{
787847 .sh_name = try self.makeString(".got"),
788848 .sh_type = elf.SHT_PROGBITS,
789849 .sh_flags = elf.SHF_ALLOC,
......@@ -803,9 +863,9 @@ pub const File = struct {
803863 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
804864 const file_size = self.base.options.symbol_count_hint * each_size;
805865 const off = self.findFreeSpace(file_size, min_align);
806 log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
866 log.debug("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
807867
808 try self.sections.append(self.allocator, .{
868 try self.sections.append(self.base.allocator, .{
809869 .sh_name = try self.makeString(".symtab"),
810870 .sh_type = elf.SHT_SYMTAB,
811871 .sh_flags = 0,
......@@ -824,7 +884,7 @@ pub const File = struct {
824884 if (self.debug_str_section_index == null) {
825885 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
826886 assert(self.debug_strtab.items.len == 0);
827 try self.sections.append(self.allocator, .{
887 try self.sections.append(self.base.allocator, .{
828888 .sh_name = try self.makeString(".debug_str"),
829889 .sh_type = elf.SHT_PROGBITS,
830890 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
......@@ -845,11 +905,11 @@ pub const File = struct {
845905 const file_size_hint = 200;
846906 const p_align = 1;
847907 const off = self.findFreeSpace(file_size_hint, p_align);
848 log.debug(.link, "found .debug_info free space 0x{x} to 0x{x}\n", .{
908 log.debug("found .debug_info free space 0x{x} to 0x{x}\n", .{
849909 off,
850910 off + file_size_hint,
851911 });
852 try self.sections.append(self.allocator, .{
912 try self.sections.append(self.base.allocator, .{
853913 .sh_name = try self.makeString(".debug_info"),
854914 .sh_type = elf.SHT_PROGBITS,
855915 .sh_flags = 0,
......@@ -862,7 +922,7 @@ pub const File = struct {
862922 .sh_entsize = 0,
863923 });
864924 self.shdr_table_dirty = true;
865 self.debug_info_section_dirty = true;
925 self.debug_info_header_dirty = true;
866926 }
867927 if (self.debug_abbrev_section_index == null) {
868928 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
......@@ -870,11 +930,11 @@ pub const File = struct {
870930 const file_size_hint = 128;
871931 const p_align = 1;
872932 const off = self.findFreeSpace(file_size_hint, p_align);
873 log.debug(.link, "found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
933 log.debug("found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
874934 off,
875935 off + file_size_hint,
876936 });
877 try self.sections.append(self.allocator, .{
937 try self.sections.append(self.base.allocator, .{
878938 .sh_name = try self.makeString(".debug_abbrev"),
879939 .sh_type = elf.SHT_PROGBITS,
880940 .sh_flags = 0,
......@@ -895,11 +955,11 @@ pub const File = struct {
895955 const file_size_hint = 160;
896956 const p_align = 16;
897957 const off = self.findFreeSpace(file_size_hint, p_align);
898 log.debug(.link, "found .debug_aranges free space 0x{x} to 0x{x}\n", .{
958 log.debug("found .debug_aranges free space 0x{x} to 0x{x}\n", .{
899959 off,
900960 off + file_size_hint,
901961 });
902 try self.sections.append(self.allocator, .{
962 try self.sections.append(self.base.allocator, .{
903963 .sh_name = try self.makeString(".debug_aranges"),
904964 .sh_type = elf.SHT_PROGBITS,
905965 .sh_flags = 0,
......@@ -920,11 +980,11 @@ pub const File = struct {
920980 const file_size_hint = 250;
921981 const p_align = 1;
922982 const off = self.findFreeSpace(file_size_hint, p_align);
923 log.debug(.link, "found .debug_line free space 0x{x} to 0x{x}\n", .{
983 log.debug("found .debug_line free space 0x{x} to 0x{x}\n", .{
924984 off,
925985 off + file_size_hint,
926986 });
927 try self.sections.append(self.allocator, .{
987 try self.sections.append(self.base.allocator, .{
928988 .sh_name = try self.makeString(".debug_line"),
929989 .sh_type = elf.SHT_PROGBITS,
930990 .sh_flags = 0,
......@@ -972,8 +1032,15 @@ pub const File = struct {
9721032 }
9731033 }
9741034
1035 pub const abbrev_compile_unit = 1;
1036 pub const abbrev_subprogram = 2;
1037 pub const abbrev_subprogram_retvoid = 3;
1038 pub const abbrev_base_type = 4;
1039 pub const abbrev_pad1 = 5;
1040 pub const abbrev_parameter = 6;
1041
9751042 /// Commit pending changes and write headers.
976 pub fn flush(self: *Elf) !void {
1043 pub fn flush(self: *Elf, module: *Module) !void {
9771044 const target_endian = self.base.options.target.cpu.arch.endian();
9781045 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
9791046 const ptr_width_bytes: u8 = self.ptrWidthBytes();
......@@ -992,7 +1059,7 @@ pub const File = struct {
9921059 // These are LEB encoded but since the values are all less than 127
9931060 // we can simply append these bytes.
9941061 const abbrev_buf = [_]u8{
995 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
1062 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
9961063 DW.AT_stmt_list, DW.FORM_sec_offset,
9971064 DW.AT_low_pc , DW.FORM_addr,
9981065 DW.AT_high_pc , DW.FORM_addr,
......@@ -1002,6 +1069,34 @@ pub const File = struct {
10021069 DW.AT_language , DW.FORM_data2,
10031070 0, 0, // table sentinel
10041071
1072 abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header
1073 DW.AT_low_pc , DW.FORM_addr,
1074 DW.AT_high_pc , DW.FORM_data4,
1075 DW.AT_type , DW.FORM_ref4,
1076 DW.AT_name , DW.FORM_string,
1077 0, 0, // table sentinel
1078
1079 abbrev_subprogram_retvoid, DW.TAG_subprogram, DW.CHILDREN_yes, // header
1080 DW.AT_low_pc , DW.FORM_addr,
1081 DW.AT_high_pc , DW.FORM_data4,
1082 DW.AT_name , DW.FORM_string,
1083 0, 0, // table sentinel
1084
1085 abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header
1086 DW.AT_encoding , DW.FORM_data1,
1087 DW.AT_byte_size, DW.FORM_data1,
1088 DW.AT_name , DW.FORM_string,
1089 0, 0, // table sentinel
1090
1091 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
1092 0, 0, // table sentinel
1093
1094 abbrev_parameter, DW.TAG_formal_parameter, DW.CHILDREN_no, // header
1095 DW.AT_location , DW.FORM_exprloc,
1096 DW.AT_type , DW.FORM_ref4,
1097 DW.AT_name , DW.FORM_string,
1098 0, 0, // table sentinel
1099
10051100 0, 0, 0, // section sentinel
10061101 };
10071102
......@@ -1012,14 +1107,14 @@ pub const File = struct {
10121107 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
10131108 }
10141109 debug_abbrev_sect.sh_size = needed_size;
1015 log.debug(.link, ".debug_abbrev start=0x{x} end=0x{x}\n", .{
1110 log.debug(".debug_abbrev start=0x{x} end=0x{x}\n", .{
10161111 debug_abbrev_sect.sh_offset,
10171112 debug_abbrev_sect.sh_offset + needed_size,
10181113 });
10191114
10201115 const abbrev_offset = 0;
10211116 self.debug_abbrev_table_offset = abbrev_offset;
1022 try self.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
1117 try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
10231118 if (!self.shdr_table_dirty) {
10241119 // Then it won't get written with the others and we need to do it.
10251120 try self.writeSectHeader(self.debug_abbrev_section_index.?);
......@@ -1027,21 +1122,37 @@ pub const File = struct {
10271122
10281123 self.debug_abbrev_section_dirty = false;
10291124 }
1030 if (self.debug_info_section_dirty) {
1125
1126 if (self.debug_info_header_dirty) debug_info: {
1127 // If this value is null it means there is an error in the module;
1128 // leave debug_info_header_dirty=true.
1129 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
1130 const last_dbg_info_decl = self.dbg_info_decl_last.?;
10311131 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
10321132
1033 var di_buf = std.ArrayList(u8).init(self.allocator);
1133 var di_buf = std.ArrayList(u8).init(self.base.allocator);
10341134 defer di_buf.deinit();
10351135
1036 // Enough for a 64-bit header and main compilation unit without resizing.
1037 try di_buf.ensureCapacity(100);
1136 // We have a function to compute the upper bound size, because it's needed
1137 // for determining where to put the offset of the first `LinkBlock`.
1138 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
10381139
10391140 // initial length - length of the .debug_info contribution for this compilation unit,
10401141 // not including the initial length itself.
10411142 // We have to come back and write it later after we know the size.
1042 const init_len_index = di_buf.items.len;
1043 di_buf.items.len += init_len_size;
1044 const after_init_len = di_buf.items.len;
1143 const after_init_len = di_buf.items.len + init_len_size;
1144 // +1 for the final 0 that ends the compilation unit children.
1145 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
1146 const init_len = dbg_info_end - after_init_len;
1147 switch (self.ptr_width) {
1148 .p32 => {
1149 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1150 },
1151 .p64 => {
1152 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1153 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1154 },
1155 }
10451156 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
10461157 const abbrev_offset = self.debug_abbrev_table_offset.?;
10471158 switch (self.ptr_width) {
......@@ -1057,14 +1168,14 @@ pub const File = struct {
10571168 // Write the form for the compile unit, which must match the abbrev table above.
10581169 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
10591170 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
1060 const producer_strp = try self.makeDebugString("zig (TODO version here)");
1171 const producer_strp = try self.makeDebugString(producer_string);
10611172 // Currently only one compilation unit is supported, so the address range is simply
10621173 // identical to the main program header virtual address and memory size.
10631174 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
10641175 const low_pc = text_phdr.p_vaddr;
10651176 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
10661177
1067 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
1178 di_buf.appendAssumeCapacity(abbrev_compile_unit);
10681179 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
10691180 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
10701181 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
......@@ -1076,43 +1187,19 @@ pub const File = struct {
10761187 // Until then we say it is C99.
10771188 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
10781189
1079 const init_len = di_buf.items.len - after_init_len;
1080 switch (self.ptr_width) {
1081 .p32 => {
1082 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1083 },
1084 .p64 => {
1085 // initial length - length of the .debug_info contribution for this compilation unit,
1086 // not including the initial length itself.
1087 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1088 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
1089 },
1090 }
1091
1092 const needed_size = di_buf.items.len;
1093 const allocated_size = self.allocatedSize(debug_info_sect.sh_offset);
1094 if (needed_size > allocated_size) {
1095 debug_info_sect.sh_size = 0; // free the space
1096 debug_info_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1097 }
1098 debug_info_sect.sh_size = needed_size;
1099 log.debug(.link, ".debug_info start=0x{x} end=0x{x}\n", .{
1100 debug_info_sect.sh_offset,
1101 debug_info_sect.sh_offset + needed_size,
1102 });
1103
1104 try self.file.?.pwriteAll(di_buf.items, debug_info_sect.sh_offset);
1105 if (!self.shdr_table_dirty) {
1106 // Then it won't get written with the others and we need to do it.
1107 try self.writeSectHeader(self.debug_info_section_index.?);
1190 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
1191 // Move the first N decls to the end to make more padding for the header.
1192 @panic("TODO: handle .debug_info header exceeding its padding");
11081193 }
1109
1110 self.debug_info_section_dirty = false;
1194 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
1195 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
1196 self.debug_info_header_dirty = false;
11111197 }
1198
11121199 if (self.debug_aranges_section_dirty) {
11131200 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
11141201
1115 var di_buf = std.ArrayList(u8).init(self.allocator);
1202 var di_buf = std.ArrayList(u8).init(self.base.allocator);
11161203 defer di_buf.deinit();
11171204
11181205 // Enough for all the data without resizing. When support for more compilation units
......@@ -1167,12 +1254,12 @@ pub const File = struct {
11671254 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
11681255 }
11691256 debug_aranges_sect.sh_size = needed_size;
1170 log.debug(.link, ".debug_aranges start=0x{x} end=0x{x}\n", .{
1257 log.debug(".debug_aranges start=0x{x} end=0x{x}\n", .{
11711258 debug_aranges_sect.sh_offset,
11721259 debug_aranges_sect.sh_offset + needed_size,
11731260 });
11741261
1175 try self.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
1262 try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
11761263 if (!self.shdr_table_dirty) {
11771264 // Then it won't get written with the others and we need to do it.
11781265 try self.writeSectHeader(self.debug_aranges_section_index.?);
......@@ -1180,14 +1267,17 @@ pub const File = struct {
11801267
11811268 self.debug_aranges_section_dirty = false;
11821269 }
1183 if (self.debug_line_header_dirty) {
1270 if (self.debug_line_header_dirty) debug_line: {
1271 if (self.dbg_line_fn_first == null) {
1272 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
1273 }
11841274 const dbg_line_prg_off = self.getDebugLineProgramOff();
11851275 const dbg_line_prg_end = self.getDebugLineProgramEnd();
11861276 assert(dbg_line_prg_end != 0);
11871277
11881278 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
11891279
1190 var di_buf = std.ArrayList(u8).init(self.allocator);
1280 var di_buf = std.ArrayList(u8).init(self.base.allocator);
11911281 defer di_buf.deinit();
11921282
11931283 // The size of this header is variable, depending on the number of directories,
......@@ -1271,7 +1361,7 @@ pub const File = struct {
12711361 @panic("TODO: handle .debug_line header exceeding its padding");
12721362 }
12731363 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1274 try self.pwriteWithNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
1364 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
12751365 self.debug_line_header_dirty = false;
12761366 }
12771367
......@@ -1294,8 +1384,8 @@ pub const File = struct {
12941384
12951385 switch (self.ptr_width) {
12961386 .p32 => {
1297 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1298 defer self.allocator.free(buf);
1387 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1388 defer self.base.allocator.free(buf);
12991389
13001390 for (buf) |*phdr, i| {
13011391 phdr.* = progHeaderTo32(self.program_headers.items[i]);
......@@ -1303,11 +1393,11 @@ pub const File = struct {
13031393 bswapAllFields(elf.Elf32_Phdr, phdr);
13041394 }
13051395 }
1306 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1396 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
13071397 },
13081398 .p64 => {
1309 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1310 defer self.allocator.free(buf);
1399 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1400 defer self.base.allocator.free(buf);
13111401
13121402 for (buf) |*phdr, i| {
13131403 phdr.* = self.program_headers.items[i];
......@@ -1315,7 +1405,7 @@ pub const File = struct {
13151405 bswapAllFields(elf.Elf64_Phdr, phdr);
13161406 }
13171407 }
1318 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1408 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
13191409 },
13201410 }
13211411 self.phdr_table_dirty = false;
......@@ -1332,9 +1422,9 @@ pub const File = struct {
13321422 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
13331423 }
13341424 shstrtab_sect.sh_size = needed_size;
1335 log.debug(.link, "writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
1425 log.debug("writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
13361426
1337 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
1427 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
13381428 if (!self.shdr_table_dirty) {
13391429 // Then it won't get written with the others and we need to do it.
13401430 try self.writeSectHeader(self.shstrtab_index.?);
......@@ -1353,9 +1443,9 @@ pub const File = struct {
13531443 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
13541444 }
13551445 debug_strtab_sect.sh_size = needed_size;
1356 log.debug(.link, "debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
1446 log.debug("debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
13571447
1358 try self.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
1448 try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
13591449 if (!self.shdr_table_dirty) {
13601450 // Then it won't get written with the others and we need to do it.
13611451 try self.writeSectHeader(self.debug_str_section_index.?);
......@@ -1382,53 +1472,53 @@ pub const File = struct {
13821472
13831473 switch (self.ptr_width) {
13841474 .p32 => {
1385 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1386 defer self.allocator.free(buf);
1475 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1476 defer self.base.allocator.free(buf);
13871477
13881478 for (buf) |*shdr, i| {
13891479 shdr.* = sectHeaderTo32(self.sections.items[i]);
1480 log.debug("writing section {}\n", .{shdr.*});
13901481 if (foreign_endian) {
13911482 bswapAllFields(elf.Elf32_Shdr, shdr);
13921483 }
13931484 }
1394 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1485 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
13951486 },
13961487 .p64 => {
1397 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1398 defer self.allocator.free(buf);
1488 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1489 defer self.base.allocator.free(buf);
13991490
14001491 for (buf) |*shdr, i| {
14011492 shdr.* = self.sections.items[i];
1402 log.debug(.link, "writing section {}\n", .{shdr.*});
1493 log.debug("writing section {}\n", .{shdr.*});
14031494 if (foreign_endian) {
14041495 bswapAllFields(elf.Elf64_Shdr, shdr);
14051496 }
14061497 }
1407 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1498 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
14081499 },
14091500 }
14101501 self.shdr_table_dirty = false;
14111502 }
14121503 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1413 log.debug(.link, "no_entry_point_found = true\n", .{});
1504 log.debug("flushing. no_entry_point_found = true\n", .{});
14141505 self.error_flags.no_entry_point_found = true;
14151506 } else {
1507 log.debug("flushing. no_entry_point_found = false\n", .{});
14161508 self.error_flags.no_entry_point_found = false;
14171509 try self.writeElfHeader();
14181510 }
14191511
1420 // The point of flush() is to commit changes, so nothing should be dirty after this.
1421 assert(!self.debug_info_section_dirty);
1512 // The point of flush() is to commit changes, so in theory, nothing should
1513 // be dirty after this. However, it is possible for some things to remain
1514 // dirty because they fail to be written in the event of compile errors,
1515 // such as debug_line_header_dirty and debug_info_header_dirty.
14221516 assert(!self.debug_abbrev_section_dirty);
14231517 assert(!self.debug_aranges_section_dirty);
1424 assert(!self.debug_line_header_dirty);
14251518 assert(!self.phdr_table_dirty);
14261519 assert(!self.shdr_table_dirty);
14271520 assert(!self.shstrtab_dirty);
14281521 assert(!self.debug_strtab_dirty);
1429 assert(!self.offset_table_count_dirty);
1430 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1431 assert(syms_sect.sh_info == self.local_symbols.items.len);
14321522 }
14331523
14341524 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
......@@ -1557,7 +1647,7 @@ pub const File = struct {
15571647
15581648 assert(index == e_ehsize);
15591649
1560 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
1650 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
15611651 }
15621652
15631653 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
......@@ -1587,7 +1677,7 @@ pub const File = struct {
15871677 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
15881678 // The free list is heuristics, it doesn't have to be perfect, so we can
15891679 // ignore the OOM here.
1590 self.text_block_free_list.append(self.allocator, prev) catch {};
1680 self.text_block_free_list.append(self.base.allocator, prev) catch {};
15911681 }
15921682 } else {
15931683 text_block.prev = null;
......@@ -1688,7 +1778,7 @@ pub const File = struct {
16881778 const sym = self.local_symbols.items[last.local_sym_index];
16891779 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
16901780 } else 0;
1691 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
1781 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
16921782 if (amt != text_size) return error.InputOutput;
16931783 shdr.sh_offset = new_offset;
16941784 phdr.p_offset = new_offset;
......@@ -1701,8 +1791,8 @@ pub const File = struct {
17011791
17021792 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
17031793 // range of the compilation unit. When we expand the text section, this range changes,
1704 // so the .debug_info section becomes dirty.
1705 self.debug_info_section_dirty = true;
1794 // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
1795 self.debug_info_header_dirty = true;
17061796 // This becomes dirty for the same reason. We could potentially make this more
17071797 // fine-grained with the addition of support for more compilation units. It is planned to
17081798 // model each package as a different compilation unit.
......@@ -1737,31 +1827,31 @@ pub const File = struct {
17371827 }
17381828
17391829 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1740 if (decl.link.local_sym_index != 0) return;
1830 if (decl.link.elf.local_sym_index != 0) return;
17411831
1742 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1743 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1832 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
1833 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
17441834
17451835 if (self.local_symbol_free_list.popOrNull()) |i| {
1746 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
1747 decl.link.local_sym_index = i;
1836 log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
1837 decl.link.elf.local_sym_index = i;
17481838 } else {
1749 log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1750 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1839 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1840 decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
17511841 _ = self.local_symbols.addOneAssumeCapacity();
17521842 }
17531843
17541844 if (self.offset_table_free_list.popOrNull()) |i| {
1755 decl.link.offset_table_index = i;
1845 decl.link.elf.offset_table_index = i;
17561846 } else {
1757 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1847 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
17581848 _ = self.offset_table.addOneAssumeCapacity();
17591849 self.offset_table_count_dirty = true;
17601850 }
17611851
17621852 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
17631853
1764 self.local_symbols.items[decl.link.local_sym_index] = .{
1854 self.local_symbols.items[decl.link.elf.local_sym_index] = .{
17651855 .st_name = 0,
17661856 .st_info = 0,
17671857 .st_other = 0,
......@@ -1769,39 +1859,39 @@ pub const File = struct {
17691859 .st_value = phdr.p_vaddr,
17701860 .st_size = 0,
17711861 };
1772 self.offset_table.items[decl.link.offset_table_index] = 0;
1862 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
17731863 }
17741864
17751865 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
17761866 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1777 self.freeTextBlock(&decl.link);
1778 if (decl.link.local_sym_index != 0) {
1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};
1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_index) catch {};
1867 self.freeTextBlock(&decl.link.elf);
1868 if (decl.link.elf.local_sym_index != 0) {
1869 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
1870 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
17811871
1782 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1872 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
17831873
1784 decl.link.local_sym_index = 0;
1874 decl.link.elf.local_sym_index = 0;
17851875 }
17861876 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
17871877 // is desired for both.
1788 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link);
1789 if (decl.fn_link.prev) |prev| {
1790 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
1791 prev.next = decl.fn_link.next;
1792 if (decl.fn_link.next) |next| {
1878 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
1879 if (decl.fn_link.elf.prev) |prev| {
1880 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1881 prev.next = decl.fn_link.elf.next;
1882 if (decl.fn_link.elf.next) |next| {
17931883 next.prev = prev;
17941884 } else {
17951885 self.dbg_line_fn_last = prev;
17961886 }
1797 } else if (decl.fn_link.next) |next| {
1887 } else if (decl.fn_link.elf.next) |next| {
17981888 self.dbg_line_fn_first = next;
17991889 next.prev = null;
18001890 }
1801 if (self.dbg_line_fn_first == &decl.fn_link) {
1891 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
18021892 self.dbg_line_fn_first = null;
18031893 }
1804 if (self.dbg_line_fn_last == &decl.fn_link) {
1894 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
18051895 self.dbg_line_fn_last = null;
18061896 }
18071897 }
......@@ -1810,18 +1900,33 @@ pub const File = struct {
18101900 const tracy = trace(@src());
18111901 defer tracy.end();
18121902
1813 var code_buffer = std.ArrayList(u8).init(self.allocator);
1903 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
18141904 defer code_buffer.deinit();
18151905
1816 var dbg_line_buffer = std.ArrayList(u8).init(self.allocator);
1906 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
18171907 defer dbg_line_buffer.deinit();
18181908
1909 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
1910 defer dbg_info_buffer.deinit();
1911
1912 var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{};
1913 defer {
1914 for (dbg_info_type_relocs.items()) |*entry| {
1915 entry.value.relocs.deinit(self.base.allocator);
1916 }
1917 dbg_info_type_relocs.deinit(self.base.allocator);
1918 }
1919
18191920 const typed_value = decl.typed_value.most_recent.typed_value;
18201921 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
18211922 .Fn => true,
18221923 else => false,
18231924 };
18241925 if (is_fn) {
1926 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1927 // typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1928 //}
1929
18251930 // For functions we need to add a prologue to the debug line program.
18261931 try dbg_line_buffer.ensureCapacity(26);
18271932
......@@ -1871,8 +1976,41 @@ pub const File = struct {
18711976 // Emit a line for the begin curly with prologue_end=false. The codegen will
18721977 // do the work of setting prologue_end=true and epilogue_begin=true.
18731978 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1979
1980 // .debug_info subprogram
1981 const decl_name_with_null = decl.name[0..mem.lenZ(decl.name) + 1];
1982 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
1983
1984 const fn_ret_type = typed_value.ty.fnReturnType();
1985 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1986 if (fn_ret_has_bits) {
1987 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1988 } else {
1989 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1990 }
1991 // These get overwritten after generating the machine code. These values are
1992 // "relocations" and have to be in this fixed place so that functions can be
1993 // moved in virtual address space.
1994 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1995 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
1996 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1997 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1998 if (fn_ret_has_bits) {
1999 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
2000 if (!gop.found_existing) {
2001 gop.entry.value = .{
2002 .off = undefined,
2003 .relocs = .{},
2004 };
2005 }
2006 try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
2007 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
2008 }
2009 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
2010 } else {
2011 // TODO implement .debug_info for global variables
18742012 }
1875 const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer);
2013 const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer, &dbg_info_type_relocs);
18762014 const code = switch (res) {
18772015 .externally_managed => |x| x,
18782016 .appended => code_buffer.items,
......@@ -1887,24 +2025,24 @@ pub const File = struct {
18872025
18882026 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
18892027
1890 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
2028 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
2029 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
18922030 if (local_sym.st_size != 0) {
1893 const capacity = decl.link.capacity(self.*);
2031 const capacity = decl.link.elf.capacity(self.*);
18942032 const need_realloc = code.len > capacity or
18952033 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
18962034 if (need_realloc) {
1897 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1898 log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
2035 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
2036 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
18992037 if (vaddr != local_sym.st_value) {
19002038 local_sym.st_value = vaddr;
19012039
1902 log.debug(.link, " (writing new offset table entry)\n", .{});
1903 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1904 try self.writeOffsetTableEntry(decl.link.offset_table_index);
2040 log.debug(" (writing new offset table entry)\n", .{});
2041 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
2042 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
19052043 }
19062044 } else if (code.len < local_sym.st_size) {
1907 self.shrinkTextBlock(&decl.link, code.len);
2045 self.shrinkTextBlock(&decl.link.elf, code.len);
19082046 }
19092047 local_sym.st_size = code.len;
19102048 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
......@@ -1912,13 +2050,13 @@ pub const File = struct {
19122050 local_sym.st_other = 0;
19132051 local_sym.st_shndx = self.text_section_index.?;
19142052 // TODO this write could be avoided if no fields of the symbol were changed.
1915 try self.writeSymbol(decl.link.local_sym_index);
2053 try self.writeSymbol(decl.link.elf.local_sym_index);
19162054 } else {
19172055 const decl_name = mem.spanZ(decl.name);
19182056 const name_str_index = try self.makeString(decl_name);
1919 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1920 log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1921 errdefer self.freeTextBlock(&decl.link);
2057 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
2058 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
2059 errdefer self.freeTextBlock(&decl.link.elf);
19222060
19232061 local_sym.* = .{
19242062 .st_name = name_str_index,
......@@ -1928,37 +2066,60 @@ pub const File = struct {
19282066 .st_value = vaddr,
19292067 .st_size = code.len,
19302068 };
1931 self.offset_table.items[decl.link.offset_table_index] = vaddr;
2069 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
19322070
1933 try self.writeSymbol(decl.link.local_sym_index);
1934 try self.writeOffsetTableEntry(decl.link.offset_table_index);
2071 try self.writeSymbol(decl.link.elf.local_sym_index);
2072 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
19352073 }
19362074
19372075 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
19382076 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1939 try self.file.?.pwriteAll(code, file_offset);
2077 try self.base.file.?.pwriteAll(code, file_offset);
2078
2079 const target_endian = self.base.options.target.cpu.arch.endian();
2080
2081 const text_block = &decl.link.elf;
19402082
19412083 // If the Decl is a function, we need to update the .debug_line program.
19422084 if (is_fn) {
1943 // Perform the relocation based on vaddr.
1944 const target_endian = self.base.options.target.cpu.arch.endian();
2085 // Perform the relocations based on vaddr.
19452086 switch (self.ptr_width) {
19462087 .p32 => {
1947 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
1948 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2088 {
2089 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
2090 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2091 }
2092 {
2093 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
2094 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2095 }
19492096 },
19502097 .p64 => {
1951 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1952 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2098 {
2099 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
2100 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2101 }
2102 {
2103 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
2104 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2105 }
19532106 },
19542107 }
2108 {
2109 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
2110 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
2111 }
19552112
19562113 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
19572114
19582115 // Now we have the full contents and may allocate a region to store it.
19592116
2117 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
2118 // `TextBlock` and the .debug_info. If you are editing this logic, you
2119 // probably need to edit that logic too.
2120
19602121 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1961 const src_fn = &decl.fn_link;
2122 const src_fn = &decl.fn_link.elf;
19622123 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
19632124 if (self.dbg_line_fn_last) |last| {
19642125 if (src_fn.next) |next| {
......@@ -1966,14 +2127,14 @@ pub const File = struct {
19662127 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
19672128 // It grew too big, so we move it to a new location.
19682129 if (src_fn.prev) |prev| {
1969 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
2130 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
19702131 prev.next = src_fn.next;
19712132 }
19722133 next.prev = src_fn.prev;
19732134 src_fn.next = null;
19742135 // Populate where it used to be with NOPs.
19752136 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1976 try self.pwriteWithNops(0, &[0]u8{}, src_fn.len, file_pos);
2137 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
19772138 // TODO Look at the free list before appending at the end.
19782139 src_fn.prev = last;
19792140 last.next = src_fn;
......@@ -2004,12 +2165,12 @@ pub const File = struct {
20042165 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
20052166 const new_offset = self.findFreeSpace(needed_size, 1);
20062167 const existing_size = last_src_fn.off;
2007 log.debug(.link, "moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
2168 log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
20082169 existing_size,
20092170 debug_line_sect.sh_offset,
20102171 new_offset,
20112172 });
2012 const amt = try self.file.?.copyRangeAll(debug_line_sect.sh_offset, self.file.?, new_offset, existing_size);
2173 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
20132174 if (amt != existing_size) return error.InputOutput;
20142175 debug_line_sect.sh_offset = new_offset;
20152176 }
......@@ -2023,15 +2184,169 @@ pub const File = struct {
20232184 // We only have support for one compilation unit so far, so the offsets are directly
20242185 // from the .debug_line section.
20252186 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2026 try self.pwriteWithNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2187 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2188
2189 // .debug_info - End the TAG_subprogram children.
2190 try dbg_info_buffer.append(0);
2191 }
2192
2193 // Now we emit the .debug_info types of the Decl. These will count towards the size of
2194 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
2195 // relocations yet.
2196 for (dbg_info_type_relocs.items()) |*entry| {
2197 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
2198 try self.addDbgInfoType(entry.key, &dbg_info_buffer);
20272199 }
20282200
2201 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
2202
2203 // Now that we have the offset assigned we can finally perform type relocations.
2204 for (dbg_info_type_relocs.items()) |entry| {
2205 for (entry.value.relocs.items) |off| {
2206 mem.writeInt(
2207 u32,
2208 dbg_info_buffer.items[off..][0..4],
2209 text_block.dbg_info_off + entry.value.off,
2210 target_endian,
2211 );
2212 }
2213 }
2214
2215 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
2216
20292217 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
20302218 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
20312219 return self.updateDeclExports(module, decl, decl_exports);
20322220 }
20332221
2034 /// Must be called only after a successful call to `updateDecl`.
2222 /// Asserts the type has codegen bits.
2223 fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
2224 switch (ty.zigTypeTag()) {
2225 .Void => unreachable,
2226 .NoReturn => unreachable,
2227 .Bool => {
2228 try dbg_info_buffer.appendSlice(&[_]u8{
2229 abbrev_base_type,
2230 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
2231 1, // DW.AT_byte_size, DW.FORM_data1
2232 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
2233 });
2234 },
2235 .Int => {
2236 const info = ty.intInfo(self.base.options.target);
2237 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
2238 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
2239 // DW.AT_encoding, DW.FORM_data1
2240 dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned);
2241 // DW.AT_byte_size, DW.FORM_data1
2242 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
2243 // DW.AT_name, DW.FORM_string
2244 try dbg_info_buffer.writer().print("{}\x00", .{ty});
2245 },
2246 else => {
2247 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
2248 try dbg_info_buffer.append(abbrev_pad1);
2249 },
2250 }
2251 }
2252
2253 fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
2254 const tracy = trace(@src());
2255 defer tracy.end();
2256
2257 // This logic is nearly identical to the logic above in `updateDecl` for
2258 // `SrcFn` and the line number programs. If you are editing this logic, you
2259 // probably need to edit that logic too.
2260
2261 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
2262 text_block.dbg_info_len = len;
2263 if (self.dbg_info_decl_last) |last| {
2264 if (text_block.dbg_info_next) |next| {
2265 // Update existing Decl - non-last item.
2266 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
2267 // It grew too big, so we move it to a new location.
2268 if (text_block.dbg_info_prev) |prev| {
2269 _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
2270 prev.dbg_info_next = text_block.dbg_info_next;
2271 }
2272 next.dbg_info_prev = text_block.dbg_info_prev;
2273 text_block.dbg_info_next = null;
2274 // Populate where it used to be with NOPs.
2275 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
2276 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
2277 // TODO Look at the free list before appending at the end.
2278 text_block.dbg_info_prev = last;
2279 last.dbg_info_next = text_block;
2280 self.dbg_info_decl_last = text_block;
2281
2282 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
2283 }
2284 } else if (text_block.dbg_info_prev == null) {
2285 // Append new Decl.
2286 // TODO Look at the free list before appending at the end.
2287 text_block.dbg_info_prev = last;
2288 last.dbg_info_next = text_block;
2289 self.dbg_info_decl_last = text_block;
2290
2291 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
2292 }
2293 } else {
2294 // This is the first Decl of the .debug_info
2295 self.dbg_info_decl_first = text_block;
2296 self.dbg_info_decl_last = text_block;
2297
2298 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
2299 }
2300 }
2301
2302 fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
2303 const tracy = trace(@src());
2304 defer tracy.end();
2305
2306 // This logic is nearly identical to the logic above in `updateDecl` for
2307 // `SrcFn` and the line number programs. If you are editing this logic, you
2308 // probably need to edit that logic too.
2309
2310 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
2311
2312 const last_decl = self.dbg_info_decl_last.?;
2313 // +1 for a trailing zero to end the children of the decl tag.
2314 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
2315 if (needed_size != debug_info_sect.sh_size) {
2316 if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
2317 const new_offset = self.findFreeSpace(needed_size, 1);
2318 const existing_size = last_decl.dbg_info_off;
2319 log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{
2320 existing_size,
2321 debug_info_sect.sh_offset,
2322 new_offset,
2323 });
2324 const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2325 if (amt != existing_size) return error.InputOutput;
2326 debug_info_sect.sh_offset = new_offset;
2327 }
2328 debug_info_sect.sh_size = needed_size;
2329 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2330 self.debug_info_header_dirty = true;
2331 }
2332 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
2333 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
2334 else
2335 0;
2336 const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
2337 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
2338 else
2339 0;
2340
2341 // To end the children of the decl tag.
2342 const trailing_zero = text_block.dbg_info_next == null;
2343
2344 // We only have support for one compilation unit so far, so the offsets are directly
2345 // from the .debug_info section.
2346 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
2347 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
2348 }
2349
20352350 pub fn updateDeclExports(
20362351 self: *Elf,
20372352 module: *Module,
......@@ -2041,10 +2356,10 @@ pub const File = struct {
20412356 const tracy = trace(@src());
20422357 defer tracy.end();
20432358
2044 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
2359 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
20452360 const typed_value = decl.typed_value.most_recent.typed_value;
2046 if (decl.link.local_sym_index == 0) return;
2047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
2361 if (decl.link.elf.local_sym_index == 0) return;
2362 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
20482363
20492364 for (exports) |exp| {
20502365 if (exp.options.section) |section_name| {
......@@ -2052,7 +2367,7 @@ pub const File = struct {
20522367 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
20532368 module.failed_exports.putAssumeCapacityNoClobber(
20542369 exp,
2055 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
2370 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
20562371 );
20572372 continue;
20582373 }
......@@ -2070,7 +2385,7 @@ pub const File = struct {
20702385 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
20712386 module.failed_exports.putAssumeCapacityNoClobber(
20722387 exp,
2073 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2388 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
20742389 );
20752390 continue;
20762391 },
......@@ -2122,15 +2437,15 @@ pub const File = struct {
21222437 const casted_line_off = @intCast(u28, line_delta);
21232438
21242439 const shdr = &self.sections.items[self.debug_line_section_index.?];
2125 const file_pos = shdr.sh_offset + decl.fn_link.off + self.getRelocDbgLineOff();
2440 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
21262441 var data: [4]u8 = undefined;
21272442 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2128 try self.file.?.pwriteAll(&data, file_pos);
2443 try self.base.file.?.pwriteAll(&data, file_pos);
21292444 }
21302445
21312446 pub fn deleteExport(self: *Elf, exp: Export) void {
21322447 const sym_index = exp.sym_index orelse return;
2133 self.global_symbol_free_list.append(self.allocator, sym_index) catch {};
2448 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
21342449 self.global_symbols.items[sym_index].st_info = 0;
21352450 }
21362451
......@@ -2143,14 +2458,14 @@ pub const File = struct {
21432458 if (foreign_endian) {
21442459 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
21452460 }
2146 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2461 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
21472462 },
21482463 64 => {
21492464 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
21502465 if (foreign_endian) {
21512466 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
21522467 }
2153 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2468 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
21542469 },
21552470 else => return error.UnsupportedArchitecture,
21562471 }
......@@ -2166,7 +2481,7 @@ pub const File = struct {
21662481 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
21672482 }
21682483 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
2169 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2484 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
21702485 },
21712486 64 => {
21722487 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
......@@ -2174,7 +2489,7 @@ pub const File = struct {
21742489 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
21752490 }
21762491 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
2177 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2492 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
21782493 },
21792494 else => return error.UnsupportedArchitecture,
21802495 }
......@@ -2191,7 +2506,7 @@ pub const File = struct {
21912506 if (needed_size > allocated_size) {
21922507 // Must move the entire got section.
21932508 const new_offset = self.findFreeSpace(needed_size, entry_size);
2194 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
2509 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
21952510 if (amt != shdr.sh_size) return error.InputOutput;
21962511 shdr.sh_offset = new_offset;
21972512 phdr.p_offset = new_offset;
......@@ -2211,17 +2526,20 @@ pub const File = struct {
22112526 .p32 => {
22122527 var buf: [4]u8 = undefined;
22132528 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
2214 try self.file.?.pwriteAll(&buf, off);
2529 try self.base.file.?.pwriteAll(&buf, off);
22152530 },
22162531 .p64 => {
22172532 var buf: [8]u8 = undefined;
22182533 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2219 try self.file.?.pwriteAll(&buf, off);
2534 try self.base.file.?.pwriteAll(&buf, off);
22202535 },
22212536 }
22222537 }
22232538
22242539 fn writeSymbol(self: *Elf, index: usize) !void {
2540 const tracy = trace(@src());
2541 defer tracy.end();
2542
22252543 const syms_sect = &self.sections.items[self.symtab_section_index.?];
22262544 // Make sure we are not pointlessly writing symbol data that will have to get relocated
22272545 // due to running out of space.
......@@ -2239,7 +2557,7 @@ pub const File = struct {
22392557 // Move all the symbols to a new file location.
22402558 const new_offset = self.findFreeSpace(needed_size, sym_align);
22412559 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
2242 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
2560 const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
22432561 if (amt != existing_size) return error.InputOutput;
22442562 syms_sect.sh_offset = new_offset;
22452563 }
......@@ -2264,7 +2582,7 @@ pub const File = struct {
22642582 bswapAllFields(elf.Elf32_Sym, &sym[0]);
22652583 }
22662584 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
2267 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2585 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
22682586 },
22692587 .p64 => {
22702588 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
......@@ -2272,7 +2590,7 @@ pub const File = struct {
22722590 bswapAllFields(elf.Elf64_Sym, &sym[0]);
22732591 }
22742592 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
2275 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
2593 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
22762594 },
22772595 }
22782596 }
......@@ -2287,8 +2605,8 @@ pub const File = struct {
22872605 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
22882606 switch (self.ptr_width) {
22892607 .p32 => {
2290 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
2291 defer self.allocator.free(buf);
2608 const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
2609 defer self.base.allocator.free(buf);
22922610
22932611 for (buf) |*sym, i| {
22942612 sym.* = .{
......@@ -2303,11 +2621,11 @@ pub const File = struct {
23032621 bswapAllFields(elf.Elf32_Sym, sym);
23042622 }
23052623 }
2306 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2624 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
23072625 },
23082626 .p64 => {
2309 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
2310 defer self.allocator.free(buf);
2627 const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
2628 defer self.base.allocator.free(buf);
23112629
23122630 for (buf) |*sym, i| {
23132631 sym.* = .{
......@@ -2322,7 +2640,7 @@ pub const File = struct {
23222640 bswapAllFields(elf.Elf64_Sym, sym);
23232641 }
23242642 }
2325 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2643 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
23262644 },
23272645 }
23282646 }
......@@ -2337,6 +2655,9 @@ pub const File = struct {
23372655 /// The reloc offset for the virtual address of a function in its Line Number Program.
23382656 /// Size is a virtual address integer.
23392657 const dbg_line_vaddr_reloc_index = 3;
2658 /// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
2659 /// Size is a virtual address integer.
2660 const dbg_info_low_pc_reloc_index = 1;
23402661
23412662 /// The reloc offset for the line offset of a function from the previous function's line.
23422663 /// It's a fixed-size 4-byte ULEB128.
......@@ -2348,6 +2669,10 @@ pub const File = struct {
23482669 return self.getRelocDbgLineOff() + 5;
23492670 }
23502671
2672 fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
2673 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
2674 }
2675
23512676 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
23522677 const directory_entry_format_count = 1;
23532678 const file_name_entry_format_count = 1;
......@@ -2362,18 +2687,27 @@ pub const File = struct {
23622687
23632688 }
23642689
2690 fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
2691 return 120;
2692 }
2693
2694 const min_nop_size = 2;
2695
23652696 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
23662697 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
23672698 /// are less than 126,976 bytes (if this limit is ever reached, this function can be
23682699 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
23692700 /// amount by increasing the length of `vecs`).
2370 fn pwriteWithNops(
2701 fn pwriteDbgLineNops(
23712702 self: *Elf,
23722703 prev_padding_size: usize,
23732704 buf: []const u8,
23742705 next_padding_size: usize,
23752706 offset: usize,
23762707 ) !void {
2708 const tracy = trace(@src());
2709 defer tracy.end();
2710
23772711 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
23782712 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};
23792713 var vecs: [32]std.os.iovec_const = undefined;
......@@ -2437,12 +2771,85 @@ pub const File = struct {
24372771 vec_index += 1;
24382772 }
24392773 }
2440 try self.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2774 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
24412775 }
24422776
2443 const min_nop_size = 2;
2777 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
2778 /// bytes of padding.
2779 fn pwriteDbgInfoNops(
2780 self: *Elf,
2781 prev_padding_size: usize,
2782 buf: []const u8,
2783 next_padding_size: usize,
2784 trailing_zero: bool,
2785 offset: usize,
2786 ) !void {
2787 const tracy = trace(@src());
2788 defer tracy.end();
2789
2790 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
2791 var vecs: [32]std.os.iovec_const = undefined;
2792 var vec_index: usize = 0;
2793 {
2794 var padding_left = prev_padding_size;
2795 while (padding_left > page_of_nops.len) {
2796 vecs[vec_index] = .{
2797 .iov_base = &page_of_nops,
2798 .iov_len = page_of_nops.len,
2799 };
2800 vec_index += 1;
2801 padding_left -= page_of_nops.len;
2802 }
2803 if (padding_left > 0) {
2804 vecs[vec_index] = .{
2805 .iov_base = &page_of_nops,
2806 .iov_len = padding_left,
2807 };
2808 vec_index += 1;
2809 }
2810 }
2811
2812 vecs[vec_index] = .{
2813 .iov_base = buf.ptr,
2814 .iov_len = buf.len,
2815 };
2816 vec_index += 1;
2817
2818 {
2819 var padding_left = next_padding_size;
2820 while (padding_left > page_of_nops.len) {
2821 vecs[vec_index] = .{
2822 .iov_base = &page_of_nops,
2823 .iov_len = page_of_nops.len,
2824 };
2825 vec_index += 1;
2826 padding_left -= page_of_nops.len;
2827 }
2828 if (padding_left > 0) {
2829 vecs[vec_index] = .{
2830 .iov_base = &page_of_nops,
2831 .iov_len = padding_left,
2832 };
2833 vec_index += 1;
2834 }
2835 }
2836
2837 if (trailing_zero) {
2838 var zbuf = [1]u8{0};
2839 vecs[vec_index] = .{
2840 .iov_base = &zbuf,
2841 .iov_len = zbuf.len,
2842 };
2843 vec_index += 1;
2844 }
2845
2846 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2847 }
24442848
24452849 };
2850
2851 pub const MachO = @import("link/MachO.zig");
2852 const Wasm = @import("link/Wasm.zig");
24462853};
24472854
24482855/// Saturating multiplication
......@@ -2483,7 +2890,7 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
24832890 };
24842891}
24852892
2486fn determineMode(options: Options) fs.File.Mode {
2893pub fn determineMode(options: Options) fs.File.Mode {
24872894 // On common systems with a 0o022 umask, 0o777 will still result in a file created
24882895 // with 0o755 permissions, but it works appropriately if the system is configured
24892896 // more leniently. As another data point, C's fopen seems to open files with the
src-self-hosted/link/MachO.zig created+93
......@@ -0,0 +1,93 @@
1const MachO = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;
6const fs = std.fs;
7
8const Module = @import("../Module.zig");
9const link = @import("../link.zig");
10const File = link.File;
11
12pub const base_tag: Tag = File.Tag.macho;
13
14base: File,
15
16error_flags: File.ErrorFlags = File.ErrorFlags{},
17
18pub const TextBlock = struct {
19 pub const empty = TextBlock{};
20};
21
22pub const SrcFn = struct {
23 pub const empty = SrcFn{};
24};
25
26pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
27 assert(options.object_format == .macho);
28
29 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
30 errdefer file.close();
31
32 var macho_file = try allocator.create(MachO);
33 errdefer allocator.destroy(macho_file);
34
35 macho_file.* = openFile(allocator, file, options) catch |err| switch (err) {
36 error.IncrFailed => try createFile(allocator, file, options),
37 else => |e| return e,
38 };
39
40 return &macho_file.base;
41}
42
43/// Returns error.IncrFailed if incremental update could not be performed.
44fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
45 switch (options.output_mode) {
46 .Exe => {},
47 .Obj => {},
48 .Lib => return error.IncrFailed,
49 }
50 var self: MachO = .{
51 .base = .{
52 .file = file,
53 .tag = .macho,
54 .options = options,
55 .allocator = allocator,
56 },
57 };
58 errdefer self.deinit();
59
60 // TODO implement reading the macho file
61 return error.IncrFailed;
62 //try self.populateMissingMetadata();
63 //return self;
64}
65
66/// Truncates the existing file contents and overwrites the contents.
67/// Returns an error if `file` is not already open with +read +write +seek abilities.
68fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
69 switch (options.output_mode) {
70 .Exe => return error.TODOImplementWritingMachOExeFiles,
71 .Obj => return error.TODOImplementWritingMachOObjFiles,
72 .Lib => return error.TODOImplementWritingLibFiles,
73 }
74}
75
76pub fn flush(self: *MachO, module: *Module) !void {}
77
78pub fn deinit(self: *MachO) void {}
79
80pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}
81
82pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {}
83
84pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
85
86pub fn updateDeclExports(
87 self: *MachO,
88 module: *Module,
89 decl: *const Module.Decl,
90 exports: []const *Module.Export,
91) !void {}
92
93pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
src-self-hosted/link/Wasm.zig created+453
......@@ -0,0 +1,453 @@
1const Wasm = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;
6const fs = std.fs;
7const leb = std.debug.leb;
8
9const Module = @import("../Module.zig");
10const codegen = @import("../codegen/wasm.zig");
11const link = @import("../link.zig");
12
13/// Various magic numbers defined by the wasm spec
14const spec = struct {
15 const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
16 const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
17
18 const custom_id = 0;
19 const types_id = 1;
20 const imports_id = 2;
21 const funcs_id = 3;
22 const tables_id = 4;
23 const memories_id = 5;
24 const globals_id = 6;
25 const exports_id = 7;
26 const start_id = 8;
27 const elements_id = 9;
28 const code_id = 10;
29 const data_id = 11;
30};
31
32pub const base_tag = link.File.Tag.wasm;
33
34pub const FnData = struct {
35 funcidx: u32,
36};
37
38base: link.File,
39
40types: Types,
41funcs: Funcs,
42exports: Exports,
43
44/// Array over the section structs used in the various sections above to
45/// allow iteration when shifting sections to make space.
46/// TODO: this should eventually be size 11 when we use all the sections.
47sections: [4]*Section,
48
49pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
50 assert(options.object_format == .wasm);
51
52 // TODO: read the file and keep vaild parts instead of truncating
53 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true });
54 errdefer file.close();
55
56 const wasm = try allocator.create(Wasm);
57 errdefer allocator.destroy(wasm);
58
59 try file.writeAll(&(spec.magic ++ spec.version));
60
61 // TODO: this should vary depending on the section and be less arbitrary
62 const size = 1024;
63 const offset = @sizeOf(@TypeOf(spec.magic ++ spec.version));
64
65 wasm.* = .{
66 .base = .{
67 .tag = .wasm,
68 .options = options,
69 .file = file,
70 .allocator = allocator,
71 },
72
73 .types = try Types.init(file, offset, size),
74 .funcs = try Funcs.init(file, offset + size, size, offset + 3 * size, size),
75 .exports = try Exports.init(file, offset + 2 * size, size),
76
77 // These must be ordered as they will appear in the output file
78 .sections = [_]*Section{
79 &wasm.types.typesec.section,
80 &wasm.funcs.funcsec,
81 &wasm.exports.exportsec,
82 &wasm.funcs.codesec.section,
83 },
84 };
85
86 try file.setEndPos(offset + 4 * size);
87
88 return &wasm.base;
89}
90
91pub fn deinit(self: *Wasm) void {
92 self.types.deinit();
93 self.funcs.deinit();
94}
95
96pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
97 if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn)
98 return error.TODOImplementNonFnDeclsForWasm;
99
100 if (decl.fn_link.wasm) |fn_data| {
101 self.funcs.free(fn_data.funcidx);
102 }
103
104 var buf = std.ArrayList(u8).init(self.base.allocator);
105 defer buf.deinit();
106
107 try codegen.genFunctype(&buf, decl);
108 const typeidx = try self.types.new(buf.items);
109 buf.items.len = 0;
110
111 try codegen.genCode(&buf, decl);
112 const funcidx = try self.funcs.new(typeidx, buf.items);
113
114 decl.fn_link.wasm = .{ .funcidx = funcidx };
115
116 // TODO: we should be more smart and set this only when needed
117 self.exports.dirty = true;
118}
119
120pub fn updateDeclExports(
121 self: *Wasm,
122 module: *Module,
123 decl: *const Module.Decl,
124 exports: []const *Module.Export,
125) !void {
126 self.exports.dirty = true;
127}
128
129pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
130 // TODO: remove this assert when non-function Decls are implemented
131 assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
132 if (decl.fn_link.wasm) |fn_data| {
133 self.funcs.free(fn_data.funcidx);
134 decl.fn_link.wasm = null;
135 }
136}
137
138pub fn flush(self: *Wasm, module: *Module) !void {
139 if (self.exports.dirty) try self.exports.writeAll(module);
140}
141
142/// This struct describes the location of a named section + custom section
143/// padding in the output file. This is all the data we need to allow for
144/// shifting sections around when padding runs out.
145const Section = struct {
146 /// The size of a section header: 1 byte section id + 5 bytes
147 /// for the fixed-width ULEB128 encoded contents size.
148 const header_size = 1 + 5;
149 /// Offset of the section id byte from the start of the file.
150 offset: u64,
151 /// Size of the section, including the header and directly
152 /// following custom section used for padding if any.
153 size: u64,
154
155 /// Resize the usable part of the section, handling the following custom
156 /// section used for padding. If there is not enough padding left, shift
157 /// all following sections to make space. Takes the current and target
158 /// contents sizes of the section as arguments.
159 fn resize(self: *Section, file: fs.File, current: u32, target: u32) !void {
160 // Section header + target contents size + custom section header
161 // + custom section name + empty custom section > owned chunk of the file
162 if (header_size + target + header_size + 1 + 0 > self.size)
163 return error.TODOImplementSectionShifting;
164
165 const new_custom_start = self.offset + header_size + target;
166 const new_custom_contents_size = self.size - target - 2 * header_size;
167 assert(new_custom_contents_size >= 1);
168 // +1 for the name of the custom section, which we set to an empty string
169 var custom_header: [header_size + 1]u8 = undefined;
170 custom_header[0] = spec.custom_id;
171 leb.writeUnsignedFixed(5, custom_header[1..header_size], @intCast(u32, new_custom_contents_size));
172 custom_header[header_size] = 0;
173 try file.pwriteAll(&custom_header, new_custom_start);
174 }
175};
176
177/// This can be used to manage the contents of any section which uses a vector
178/// of contents. This interface maintains index stability while allowing for
179/// reuse of "dead" indexes.
180const VecSection = struct {
181 /// Represents a single entry in the vector (e.g. a type in the type section)
182 const Entry = struct {
183 /// Offset from the start of the section contents in bytes
184 offset: u32,
185 /// Size in bytes of the entry
186 size: u32,
187 };
188 section: Section,
189 /// Size in bytes of the contents of the section. Does not include
190 /// the "header" containing the section id and this value.
191 contents_size: u32,
192 /// List of all entries in the contents of the section.
193 entries: std.ArrayListUnmanaged(Entry) = std.ArrayListUnmanaged(Entry){},
194 /// List of indexes of unreferenced entries which may be
195 /// overwritten and reused.
196 dead_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
197
198 /// Write the headers of the section and custom padding section
199 fn init(comptime section_id: u8, file: fs.File, offset: u64, initial_size: u64) !VecSection {
200 // section id, section size, empty vector, custom section id,
201 // custom section size, empty custom section name
202 var initial_data: [1 + 5 + 5 + 1 + 5 + 1]u8 = undefined;
203
204 assert(initial_size >= initial_data.len);
205
206 comptime var i = 0;
207 initial_data[i] = section_id;
208 i += 1;
209 leb.writeUnsignedFixed(5, initial_data[i..(i + 5)], 5);
210 i += 5;
211 leb.writeUnsignedFixed(5, initial_data[i..(i + 5)], 0);
212 i += 5;
213 initial_data[i] = spec.custom_id;
214 i += 1;
215 leb.writeUnsignedFixed(5, initial_data[i..(i + 5)], @intCast(u32, initial_size - @sizeOf(@TypeOf(initial_data))));
216 i += 5;
217 initial_data[i] = 0;
218
219 try file.pwriteAll(&initial_data, offset);
220
221 return VecSection{
222 .section = .{
223 .offset = offset,
224 .size = initial_size,
225 },
226 .contents_size = 5,
227 };
228 }
229
230 fn deinit(self: *VecSection, allocator: *Allocator) void {
231 self.entries.deinit(allocator);
232 self.dead_list.deinit(allocator);
233 }
234
235 /// Write a new entry into the file, returning the index used.
236 fn addEntry(self: *VecSection, file: fs.File, allocator: *Allocator, data: []const u8) !u32 {
237 // First look for a dead entry we can reuse
238 for (self.dead_list.items) |dead_idx, i| {
239 const dead_entry = &self.entries.items[dead_idx];
240 if (dead_entry.size == data.len) {
241 // Found a dead entry of the right length, overwrite it
242 try file.pwriteAll(data, self.section.offset + Section.header_size + dead_entry.offset);
243 _ = self.dead_list.swapRemove(i);
244 return dead_idx;
245 }
246 }
247
248 // TODO: We can be more efficient if we special-case one or
249 // more consecutive dead entries at the end of the vector.
250
251 // We failed to find a dead entry to reuse, so write the new
252 // entry to the end of the section.
253 try self.section.resize(file, self.contents_size, self.contents_size + @intCast(u32, data.len));
254 try file.pwriteAll(data, self.section.offset + Section.header_size + self.contents_size);
255 try self.entries.append(allocator, .{
256 .offset = self.contents_size,
257 .size = @intCast(u32, data.len),
258 });
259 self.contents_size += @intCast(u32, data.len);
260 // Make sure the dead list always has enough space to store all free'd
261 // entries. This makes it so that delEntry() cannot fail.
262 // TODO: figure out a better way that doesn't waste as much memory
263 try self.dead_list.ensureCapacity(allocator, self.entries.items.len);
264
265 // Update the size in the section header and the item count of
266 // the contents vector.
267 var size_and_count: [10]u8 = undefined;
268 leb.writeUnsignedFixed(5, size_and_count[0..5], self.contents_size);
269 leb.writeUnsignedFixed(5, size_and_count[5..], @intCast(u32, self.entries.items.len));
270 try file.pwriteAll(&size_and_count, self.section.offset + 1);
271
272 return @intCast(u32, self.entries.items.len - 1);
273 }
274
275 /// Mark the type referenced by the given index as dead.
276 fn delEntry(self: *VecSection, index: u32) void {
277 self.dead_list.appendAssumeCapacity(index);
278 }
279};
280
281const Types = struct {
282 typesec: VecSection,
283
284 fn init(file: fs.File, offset: u64, initial_size: u64) !Types {
285 return Types{ .typesec = try VecSection.init(spec.types_id, file, offset, initial_size) };
286 }
287
288 fn deinit(self: *Types) void {
289 const wasm = @fieldParentPtr(Wasm, "types", self);
290 self.typesec.deinit(wasm.base.allocator);
291 }
292
293 fn new(self: *Types, data: []const u8) !u32 {
294 const wasm = @fieldParentPtr(Wasm, "types", self);
295 return self.typesec.addEntry(wasm.base.file.?, wasm.base.allocator, data);
296 }
297
298 fn free(self: *Types, typeidx: u32) void {
299 self.typesec.delEntry(typeidx);
300 }
301};
302
303const Funcs = struct {
304 /// This section needs special handling to keep the indexes matching with
305 /// the codesec, so we cant just use a VecSection.
306 funcsec: Section,
307 /// The typeidx stored for each function, indexed by funcidx.
308 func_types: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
309 codesec: VecSection,
310
311 fn init(file: fs.File, funcs_offset: u64, funcs_size: u64, code_offset: u64, code_size: u64) !Funcs {
312 return Funcs{
313 .funcsec = (try VecSection.init(spec.funcs_id, file, funcs_offset, funcs_size)).section,
314 .codesec = try VecSection.init(spec.code_id, file, code_offset, code_size),
315 };
316 }
317
318 fn deinit(self: *Funcs) void {
319 const wasm = @fieldParentPtr(Wasm, "funcs", self);
320 self.func_types.deinit(wasm.base.allocator);
321 self.codesec.deinit(wasm.base.allocator);
322 }
323
324 /// Add a new function to the binary, first finding space for and writing
325 /// the code then writing the typeidx to the corresponding index in the
326 /// funcsec. Returns the function index used.
327 fn new(self: *Funcs, typeidx: u32, code: []const u8) !u32 {
328 const wasm = @fieldParentPtr(Wasm, "funcs", self);
329 const file = wasm.base.file.?;
330 const allocator = wasm.base.allocator;
331
332 assert(self.func_types.items.len == self.codesec.entries.items.len);
333
334 // TODO: consider nop-padding the code if there is a close but not perfect fit
335 const funcidx = try self.codesec.addEntry(file, allocator, code);
336
337 if (self.func_types.items.len < self.codesec.entries.items.len) {
338 // u32 vector length + funcs_count u32s in the vector
339 const current = 5 + @intCast(u32, self.func_types.items.len) * 5;
340 try self.funcsec.resize(file, current, current + 5);
341 try self.func_types.append(allocator, typeidx);
342
343 // Update the size in the section header and the item count of
344 // the contents vector.
345 const count = @intCast(u32, self.func_types.items.len);
346 var size_and_count: [10]u8 = undefined;
347 leb.writeUnsignedFixed(5, size_and_count[0..5], 5 + count * 5);
348 leb.writeUnsignedFixed(5, size_and_count[5..], count);
349 try file.pwriteAll(&size_and_count, self.funcsec.offset + 1);
350 } else {
351 // We are overwriting a dead function and may now free the type
352 wasm.types.free(self.func_types.items[funcidx]);
353 }
354
355 assert(self.func_types.items.len == self.codesec.entries.items.len);
356
357 var typeidx_leb: [5]u8 = undefined;
358 leb.writeUnsignedFixed(5, &typeidx_leb, typeidx);
359 try file.pwriteAll(&typeidx_leb, self.funcsec.offset + Section.header_size + 5 + funcidx * 5);
360
361 return funcidx;
362 }
363
364 fn free(self: *Funcs, funcidx: u32) void {
365 self.codesec.delEntry(funcidx);
366 }
367};
368
369/// Exports are tricky. We can't leave dead entries in the binary as they
370/// would obviously be visible from the execution environment. The simplest
371/// way to work around this is to re-emit the export section whenever
372/// something changes. This also makes it easier to ensure exported function
373/// and global indexes are updated as they change.
374const Exports = struct {
375 exportsec: Section,
376 /// Size in bytes of the contents of the section. Does not include
377 /// the "header" containing the section id and this value.
378 contents_size: u32,
379 /// If this is true, then exports will be rewritten on flush()
380 dirty: bool,
381
382 fn init(file: fs.File, offset: u64, initial_size: u64) !Exports {
383 return Exports{
384 .exportsec = (try VecSection.init(spec.exports_id, file, offset, initial_size)).section,
385 .contents_size = 5,
386 .dirty = false,
387 };
388 }
389
390 fn writeAll(self: *Exports, module: *Module) !void {
391 const wasm = @fieldParentPtr(Wasm, "exports", self);
392 const file = wasm.base.file.?;
393 var buf: [5]u8 = undefined;
394
395 // First ensure the section is the right size
396 var export_count: u32 = 0;
397 var new_contents_size: u32 = 5;
398 for (module.decl_exports.entries.items) |entry| {
399 for (entry.value) |e| {
400 export_count += 1;
401 new_contents_size += calcSize(e);
402 }
403 }
404 if (new_contents_size != self.contents_size) {
405 try self.exportsec.resize(file, self.contents_size, new_contents_size);
406 leb.writeUnsignedFixed(5, &buf, new_contents_size);
407 try file.pwriteAll(&buf, self.exportsec.offset + 1);
408 }
409
410 try file.seekTo(self.exportsec.offset + Section.header_size);
411 const writer = file.writer();
412
413 // Length of the exports vec
414 leb.writeUnsignedFixed(5, &buf, export_count);
415 try writer.writeAll(&buf);
416
417 for (module.decl_exports.entries.items) |entry|
418 for (entry.value) |e| try writeExport(writer, e);
419
420 self.dirty = false;
421 }
422
423 /// Return the total number of bytes an export will take.
424 /// TODO: fixed-width LEB128 is currently used for simplicity, but should
425 /// be replaced with proper variable-length LEB128 as it is inefficient.
426 fn calcSize(e: *Module.Export) u32 {
427 // LEB128 name length + name bytes + export type + LEB128 index
428 return 5 + @intCast(u32, e.options.name.len) + 1 + 5;
429 }
430
431 /// Write the data for a single export to the given file at a given offset.
432 /// TODO: fixed-width LEB128 is currently used for simplicity, but should
433 /// be replaced with proper variable-length LEB128 as it is inefficient.
434 fn writeExport(writer: anytype, e: *Module.Export) !void {
435 var buf: [5]u8 = undefined;
436
437 // Export name length + name
438 leb.writeUnsignedFixed(5, &buf, @intCast(u32, e.options.name.len));
439 try writer.writeAll(&buf);
440 try writer.writeAll(e.options.name);
441
442 switch (e.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
443 .Fn => {
444 // Type of the export
445 try writer.writeByte(0x00);
446 // Exported function index
447 leb.writeUnsignedFixed(5, &buf, e.exported_decl.fn_link.wasm.?.funcidx);
448 try writer.writeAll(&buf);
449 },
450 else => return error.TODOImplementNonFnDeclsForWasm,
451 }
452 }
453};
src-self-hosted/liveness.zig+82-45
......@@ -16,20 +16,42 @@ pub fn analyze(
1616 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
1717 defer table.deinit();
1818 try table.ensureCapacity(body.instructions.len);
19 try analyzeWithTable(arena, &table, body);
19 try analyzeWithTable(arena, &table, null, body);
2020}
2121
22fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), body: ir.Body) error{OutOfMemory}!void {
22fn analyzeWithTable(
23 arena: *std.mem.Allocator,
24 table: *std.AutoHashMap(*ir.Inst, void),
25 new_set: ?*std.AutoHashMap(*ir.Inst, void),
26 body: ir.Body,
27) error{OutOfMemory}!void {
2328 var i: usize = body.instructions.len;
2429
25 while (i != 0) {
26 i -= 1;
27 const base = body.instructions[i];
28 try analyzeInst(arena, table, base);
30 if (new_set) |ns| {
31 // We are only interested in doing this for instructions which are born
32 // before a conditional branch, so after obtaining the new set for
33 // each branch we prune the instructions which were born within.
34 while (i != 0) {
35 i -= 1;
36 const base = body.instructions[i];
37 _ = ns.remove(base);
38 try analyzeInst(arena, table, new_set, base);
39 }
40 } else {
41 while (i != 0) {
42 i -= 1;
43 const base = body.instructions[i];
44 try analyzeInst(arena, table, new_set, base);
45 }
2946 }
3047}
3148
32fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
49fn analyzeInst(
50 arena: *std.mem.Allocator,
51 table: *std.AutoHashMap(*ir.Inst, void),
52 new_set: ?*std.AutoHashMap(*ir.Inst, void),
53 base: *ir.Inst,
54) error{OutOfMemory}!void {
3355 if (table.contains(base)) {
3456 base.deaths = 0;
3557 } else {
......@@ -42,56 +64,70 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
4264 .constant => return,
4365 .block => {
4466 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);
67 try analyzeWithTable(arena, table, new_set, inst.body);
4668 // We let this continue so that it can possibly mark the block as
4769 // unreferenced below.
4870 },
71 .loop => {
72 const inst = base.castTag(.loop).?;
73 try analyzeWithTable(arena, table, new_set, inst.body);
74 return; // Loop has no operands and it is always unreferenced.
75 },
4976 .condbr => {
5077 const inst = base.castTag(.condbr).?;
51 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
52 defer true_table.deinit();
53 try true_table.ensureCapacity(inst.then_body.instructions.len);
54 try analyzeWithTable(arena, &true_table, inst.then_body);
55
56 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
57 defer false_table.deinit();
58 try false_table.ensureCapacity(inst.else_body.instructions.len);
59 try analyzeWithTable(arena, &false_table, inst.else_body);
6078
6179 // Each death that occurs inside one branch, but not the other, needs
6280 // to be added as a death immediately upon entering the other branch.
63 // During the iteration of the table, we additionally propagate the
64 // deaths to the parent table.
65 var true_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
66 defer true_entry_deaths.deinit();
67 var false_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
68 defer false_entry_deaths.deinit();
69 {
70 var it = false_table.iterator();
71 while (it.next()) |entry| {
72 const false_death = entry.key;
73 if (!true_table.contains(false_death)) {
74 try true_entry_deaths.append(false_death);
75 // Here we are only adding to the parent table if the following iteration
76 // would miss it.
77 try table.putNoClobber(false_death, {});
78 }
81
82 var then_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
83 defer then_table.deinit();
84 try analyzeWithTable(arena, table, &then_table, inst.then_body);
85
86 // Reset the table back to its state from before the branch.
87 for (then_table.items()) |entry| {
88 table.removeAssertDiscard(entry.key);
89 }
90
91 var else_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
92 defer else_table.deinit();
93 try analyzeWithTable(arena, table, &else_table, inst.else_body);
94
95 var then_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
96 defer then_entry_deaths.deinit();
97 var else_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
98 defer else_entry_deaths.deinit();
99
100 for (else_table.items()) |entry| {
101 const else_death = entry.key;
102 if (!then_table.contains(else_death)) {
103 try then_entry_deaths.append(else_death);
104 }
105 }
106 // This loop is the same, except it's for the then branch, and it additionally
107 // has to put its items back into the table to undo the reset.
108 for (then_table.items()) |entry| {
109 const then_death = entry.key;
110 if (!else_table.contains(then_death)) {
111 try else_entry_deaths.append(then_death);
79112 }
113 _ = try table.put(then_death, {});
80114 }
81 {
82 var it = true_table.iterator();
83 while (it.next()) |entry| {
84 const true_death = entry.key;
85 try table.putNoClobber(true_death, {});
86 if (!false_table.contains(true_death)) {
87 try false_entry_deaths.append(true_death);
88 }
115 // Now we have to correctly populate new_set.
116 if (new_set) |ns| {
117 try ns.ensureCapacity(ns.items().len + then_table.items().len + else_table.items().len);
118 for (then_table.items()) |entry| {
119 _ = ns.putAssumeCapacity(entry.key, {});
120 }
121 for (else_table.items()) |entry| {
122 _ = ns.putAssumeCapacity(entry.key, {});
89123 }
90124 }
91 inst.true_death_count = std.math.cast(@TypeOf(inst.true_death_count), true_entry_deaths.items.len) catch return error.OutOfMemory;
92 inst.false_death_count = std.math.cast(@TypeOf(inst.false_death_count), false_entry_deaths.items.len) catch return error.OutOfMemory;
93 const allocated_slice = try arena.alloc(*ir.Inst, true_entry_deaths.items.len + false_entry_deaths.items.len);
125 inst.then_death_count = std.math.cast(@TypeOf(inst.then_death_count), then_entry_deaths.items.len) catch return error.OutOfMemory;
126 inst.else_death_count = std.math.cast(@TypeOf(inst.else_death_count), else_entry_deaths.items.len) catch return error.OutOfMemory;
127 const allocated_slice = try arena.alloc(*ir.Inst, then_entry_deaths.items.len + else_entry_deaths.items.len);
94128 inst.deaths = allocated_slice.ptr;
129 std.mem.copy(*ir.Inst, inst.thenDeaths(), then_entry_deaths.items);
130 std.mem.copy(*ir.Inst, inst.elseDeaths(), else_entry_deaths.items);
95131
96132 // Continue on with the instruction analysis. The following code will find the condition
97133 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
......@@ -108,11 +144,12 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
108144 if (prev == null) {
109145 // Death.
110146 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
147 if (new_set) |ns| try ns.putNoClobber(operand, {});
111148 }
112149 }
113150 } else {
114151 @panic("Handle liveness analysis for instructions with many parameters");
115152 }
116153
117 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
154 std.log.scoped(.liveness).debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
118155}
src-self-hosted/main.zig+33-27
......@@ -30,6 +30,7 @@ const usage =
3030 \\ build-obj [source] Create object from source or assembly
3131 \\ fmt [source] Parse file and render in canonical zig format
3232 \\ targets List available compilation targets
33 \\ env Print lib path, std path, compiler id and version
3334 \\ version Print version number and exit
3435 \\ zen Print zen of zig and exit
3536 \\
......@@ -42,27 +43,33 @@ pub fn log(
4243 comptime format: []const u8,
4344 args: anytype,
4445) void {
45 if (@enumToInt(level) > @enumToInt(std.log.level))
46 return;
47
48 const scope_name = @tagName(scope);
49 const ok = comptime for (build_options.log_scopes) |log_scope| {
50 if (mem.eql(u8, log_scope, scope_name))
51 break true;
52 } else false;
46 // Hide anything more verbose than warn unless it was added with `-Dlog=foo`.
47 if (@enumToInt(level) > @enumToInt(std.log.level) or
48 @enumToInt(level) > @enumToInt(std.log.Level.warn))
49 {
50 const scope_name = @tagName(scope);
51 const ok = comptime for (build_options.log_scopes) |log_scope| {
52 if (mem.eql(u8, log_scope, scope_name))
53 break true;
54 } else false;
5355
54 if (!ok)
55 return;
56 if (!ok)
57 return;
58 }
5659
5760 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
5861
5962 // Print the message to stderr, silently ignoring any errors
60 std.debug.print(prefix ++ format, args);
63 std.debug.print(prefix ++ format ++ "\n", args);
6164}
6265
66var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
67
6368pub fn main() !void {
64 // TODO general purpose allocator in the zig std lib
65 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
69 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator;
70 defer if (!std.builtin.link_libc) {
71 _ = general_purpose_allocator.deinit();
72 };
6673 var arena_instance = std.heap.ArenaAllocator.init(gpa);
6774 defer arena_instance.deinit();
6875 const arena = &arena_instance.allocator;
......@@ -89,11 +96,9 @@ pub fn main() !void {
8996 const stdout = io.getStdOut().outStream();
9097 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
9198 } else if (mem.eql(u8, cmd, "version")) {
92 // Need to set up the build script to give the version as a comptime value.
93 // TODO when you solve this, also take a look at link.zig, there is a placeholder
94 // that says "TODO version here".
95 std.debug.print("TODO version command not implemented yet\n", .{});
96 return error.Unimplemented;
99 try std.io.getStdOut().writeAll(build_options.version ++ "\n");
100 } else if (mem.eql(u8, cmd, "env")) {
101 try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream());
97102 } else if (mem.eql(u8, cmd, "zen")) {
98103 try io.getStdOut().writeAll(info_zen);
99104 } else if (mem.eql(u8, cmd, "help")) {
......@@ -147,6 +152,7 @@ const usage_build_generic =
147152 \\ -ofmt=[mode] Override target object format
148153 \\ elf Executable and Linking Format
149154 \\ c Compile to C source code
155 \\ wasm WebAssembly
150156 \\ coff (planned) Common Object File Format (Windows)
151157 \\ pe (planned) Portable Executable (Windows)
152158 \\ macho (planned) macOS relocatables
......@@ -336,39 +342,39 @@ fn buildOutputType(
336342 } else if (mem.startsWith(u8, arg, "-l")) {
337343 try system_libs.append(arg[2..]);
338344 } else {
339 std.debug.print("unrecognized parameter: '{}'", .{arg});
345 std.debug.print("unrecognized parameter: '{}'\n", .{arg});
340346 process.exit(1);
341347 }
342348 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
343 std.debug.print("assembly files not supported yet", .{});
349 std.debug.print("assembly files not supported yet\n", .{});
344350 process.exit(1);
345351 } else if (mem.endsWith(u8, arg, ".o") or
346352 mem.endsWith(u8, arg, ".obj") or
347353 mem.endsWith(u8, arg, ".a") or
348354 mem.endsWith(u8, arg, ".lib"))
349355 {
350 std.debug.print("object files and static libraries not supported yet", .{});
356 std.debug.print("object files and static libraries not supported yet\n", .{});
351357 process.exit(1);
352358 } else if (mem.endsWith(u8, arg, ".c") or
353359 mem.endsWith(u8, arg, ".cpp"))
354360 {
355 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
361 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet\n", .{});
356362 process.exit(1);
357363 } else if (mem.endsWith(u8, arg, ".so") or
358364 mem.endsWith(u8, arg, ".dylib") or
359365 mem.endsWith(u8, arg, ".dll"))
360366 {
361 std.debug.print("linking against dynamic libraries not yet supported", .{});
367 std.debug.print("linking against dynamic libraries not yet supported\n", .{});
362368 process.exit(1);
363369 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
364370 if (root_src_file) |other| {
365 std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
371 std.debug.print("found another zig file '{}' after root source file '{}'\n", .{ arg, other });
366372 process.exit(1);
367373 } else {
368374 root_src_file = arg;
369375 }
370376 } else {
371 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
377 std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg});
372378 }
373379 }
374380 }
......@@ -385,7 +391,7 @@ fn buildOutputType(
385391 };
386392
387393 if (system_libs.items.len != 0) {
388 std.debug.print("linking against system libraries not yet supported", .{});
394 std.debug.print("linking against system libraries not yet supported\n", .{});
389395 process.exit(1);
390396 }
391397
......@@ -554,7 +560,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
554560 });
555561 }
556562 } else {
557 std.log.info(.compiler, "Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
563 std.log.scoped(.compiler).info("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
558564 }
559565
560566 if (zir_out_path) |zop| {
src-self-hosted/print_env.zig created+47
......@@ -0,0 +1,47 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const introspect = @import("introspect.zig");
4const Allocator = std.mem.Allocator;
5
6pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: anytype) !void {
7 const zig_lib_dir = introspect.resolveZigLibDir(gpa) catch |err| {
8 std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
9 std.process.exit(1);
10 };
11 defer gpa.free(zig_lib_dir);
12
13 const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_dir, "std" });
14 defer gpa.free(zig_std_dir);
15
16 const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa);
17 defer gpa.free(global_cache_dir);
18
19 const compiler_id_digest = try introspect.resolveCompilerId(gpa);
20 var compiler_id_buf: [compiler_id_digest.len * 2]u8 = undefined;
21 const compiler_id = std.fmt.bufPrint(&compiler_id_buf, "{x}", .{compiler_id_digest}) catch unreachable;
22
23 var bos = std.io.bufferedOutStream(stdout);
24 const bos_stream = bos.outStream();
25
26 var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
27 try jws.beginObject();
28
29 try jws.objectField("lib_dir");
30 try jws.emitString(zig_lib_dir);
31
32 try jws.objectField("std_dir");
33 try jws.emitString(zig_std_dir);
34
35 try jws.objectField("id");
36 try jws.emitString(compiler_id);
37
38 try jws.objectField("global_cache_dir");
39 try jws.emitString(global_cache_dir);
40
41 try jws.objectField("version");
42 try jws.emitString(build_options.version);
43
44 try jws.endObject();
45 try bos_stream.writeByte('\n');
46 try bos.flush();
47}
src-self-hosted/print_targets.zig+1-1
......@@ -67,7 +67,7 @@ pub fn cmdTargets(
6767) !void {
6868 const available_glibcs = blk: {
6969 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| {
70 std.debug.warn("unable to find zig installation directory: {}\n", .{@errorName(err)});
70 std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
7171 std.process.exit(1);
7272 };
7373 defer allocator.free(zig_lib_dir);
src-self-hosted/stage2.zig+29-3
......@@ -179,8 +179,7 @@ export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
179179 return 0;
180180}
181181
182fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
183 const allocator = std.heap.c_allocator;
182fn argvToArrayList(allocator: *Allocator, argc: c_int, argv: [*]const [*:0]const u8) !ArrayList([]const u8) {
184183 var args_list = std.ArrayList([]const u8).init(allocator);
185184 const argc_usize = @intCast(usize, argc);
186185 var arg_i: usize = 0;
......@@ -188,8 +187,16 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
188187 try args_list.append(mem.spanZ(argv[arg_i]));
189188 }
190189
191 const args = args_list.span()[2..];
190 return args_list;
191}
192192
193fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
194 const allocator = std.heap.c_allocator;
195
196 var args_list = try argvToArrayList(allocator, argc, argv);
197 defer args_list.deinit();
198
199 const args = args_list.span()[2..];
193200 return self_hosted_main.cmdFmt(allocator, args);
194201}
195202
......@@ -387,6 +394,25 @@ fn detectNativeCpuWithLLVM(
387394 return result;
388395}
389396
397export fn stage2_env(argc: c_int, argv: [*]const [*:0]const u8) c_int {
398 const allocator = std.heap.c_allocator;
399
400 var args_list = argvToArrayList(allocator, argc, argv) catch |err| {
401 std.debug.print("unable to parse arguments: {}\n", .{@errorName(err)});
402 return -1;
403 };
404 defer args_list.deinit();
405
406 const args = args_list.span()[2..];
407
408 @import("print_env.zig").cmdEnv(allocator, args, std.io.getStdOut().outStream()) catch |err| {
409 std.debug.print("unable to print info: {}\n", .{@errorName(err)});
410 return -1;
411 };
412
413 return 0;
414}
415
390416// ABI warning
391417export fn stage2_cmd_targets(
392418 zig_triple: ?[*:0]const u8,
src-self-hosted/test.zig+5-4
......@@ -407,8 +407,6 @@ pub const TestContext = struct {
407407 defer root_node.end();
408408
409409 for (self.cases.items) |case| {
410 std.testing.base_allocator_instance.reset();
411
412410 var prg_node = root_node.start(case.name, case.updates.items.len);
413411 prg_node.activate();
414412 defer prg_node.end();
......@@ -419,12 +417,11 @@ pub const TestContext = struct {
419417 progress.refresh_rate_ns = 0;
420418
421419 try self.runOneCase(std.testing.allocator, &prg_node, case);
422 try std.testing.allocator_instance.validate();
423420 }
424421 }
425422
426423 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
427 const target_info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
424 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
428425 const target = target_info.target;
429426
430427 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
......@@ -481,6 +478,10 @@ pub const TestContext = struct {
481478 for (all_errors.list) |err| {
482479 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
483480 }
481 if (case.cbe) {
482 const C = module.bin_file.cast(link.File.C).?;
483 std.debug.warn("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
484 }
484485 std.debug.warn("Test failed.\n", .{});
485486 std.process.exit(1);
486487 }
src-self-hosted/translate_c.zig+290-303
......@@ -61,7 +61,8 @@ const Scope = struct {
6161 pending_block: Block,
6262 cases: []*ast.Node,
6363 case_index: usize,
64 has_default: bool = false,
64 switch_label: ?[]const u8,
65 default_label: ?[]const u8,
6566 };
6667
6768 /// Used for the scope of condition expressions, for example `if (cond)`.
......@@ -73,7 +74,7 @@ const Scope = struct {
7374
7475 fn getBlockScope(self: *Condition, c: *Context) !*Block {
7576 if (self.block) |*b| return b;
76 self.block = try Block.init(c, &self.base, "blk");
77 self.block = try Block.init(c, &self.base, true);
7778 return &self.block.?;
7879 }
7980
......@@ -93,21 +94,22 @@ const Scope = struct {
9394 mangle_count: u32 = 0,
9495 lbrace: ast.TokenIndex,
9596
96 fn init(c: *Context, parent: *Scope, label: ?[]const u8) !Block {
97 return Block{
97 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
98 var blk = Block{
9899 .base = .{
99100 .id = .Block,
100101 .parent = parent,
101102 },
102103 .statements = std.ArrayList(*ast.Node).init(c.gpa),
103104 .variables = AliasList.init(c.gpa),
104 .label = if (label) |l| blk: {
105 const ll = try appendIdentifier(c, l);
106 _ = try appendToken(c, .Colon, ":");
107 break :blk ll;
108 } else null,
105 .label = null,
109106 .lbrace = try appendToken(c, .LBrace, "{"),
110107 };
108 if (labeled) {
109 blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
110 _ = try appendToken(c, .Colon, ":");
111 }
112 return blk;
111113 }
112114
113115 fn deinit(self: *Block) void {
......@@ -116,19 +118,31 @@ const Scope = struct {
116118 self.* = undefined;
117119 }
118120
119 fn complete(self: *Block, c: *Context) !*ast.Node.Block {
121 fn complete(self: *Block, c: *Context) !*ast.Node {
120122 // We reserve 1 extra statement if the parent is a Loop. This is in case of
121123 // do while, we want to put `if (cond) break;` at the end.
122124 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);
123 const node = try ast.Node.Block.alloc(c.arena, alloc_len);
124 node.* = .{
125 .statements_len = self.statements.items.len,
126 .lbrace = self.lbrace,
127 .rbrace = try appendToken(c, .RBrace, "}"),
128 .label = self.label,
129 };
130 mem.copy(*ast.Node, node.statements(), self.statements.items);
131 return node;
125 const rbrace = try appendToken(c, .RBrace, "}");
126 if (self.label) |label| {
127 const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);
128 node.* = .{
129 .statements_len = self.statements.items.len,
130 .lbrace = self.lbrace,
131 .rbrace = rbrace,
132 .label = label,
133 };
134 mem.copy(*ast.Node, node.statements(), self.statements.items);
135 return &node.base;
136 } else {
137 const node = try ast.Node.Block.alloc(c.arena, alloc_len);
138 node.* = .{
139 .statements_len = self.statements.items.len,
140 .lbrace = self.lbrace,
141 .rbrace = rbrace,
142 };
143 mem.copy(*ast.Node, node.statements(), self.statements.items);
144 return &node.base;
145 }
132146 }
133147
134148 /// Given the desired name, return a name that does not shadow anything from outer scopes.
......@@ -318,15 +332,9 @@ pub const Context = struct {
318332 return node;
319333 }
320334
321 fn createBlock(c: *Context, label: ?[]const u8, statements_len: ast.NodeIndex) !*ast.Node.Block {
322 const label_node = if (label) |l| blk: {
323 const ll = try appendIdentifier(c, l);
324 _ = try appendToken(c, .Colon, ":");
325 break :blk ll;
326 } else null;
335 fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block {
327336 const block_node = try ast.Node.Block.alloc(c.arena, statements_len);
328337 block_node.* = .{
329 .label = label_node,
330338 .lbrace = try appendToken(c, .LBrace, "{"),
331339 .statements_len = statements_len,
332340 .rbrace = undefined,
......@@ -577,7 +585,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
577585
578586 // actual function definition with body
579587 const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
580 var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null);
588 var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
581589 defer block_scope.deinit();
582590 var scope = &block_scope.base;
583591
......@@ -626,8 +634,48 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
626634 error.UnsupportedType,
627635 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
628636 };
637 // add return statement if the function didn't have one
638 blk: {
639 const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_type);
640
641 if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) break :blk;
642 const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
643 if (isCVoid(return_qt)) break :blk;
644
645 if (block_scope.statements.items.len > 0) {
646 var last = block_scope.statements.items[block_scope.statements.items.len - 1];
647 while (true) {
648 switch (last.tag) {
649 .Block, .LabeledBlock => {
650 const stmts = last.blockStatements();
651 if (stmts.len == 0) break;
652
653 last = stmts[stmts.len - 1];
654 },
655 // no extra return needed
656 .Return => break :blk,
657 else => break,
658 }
659 }
660 }
661
662 const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
663 .ltoken = try appendToken(rp.c, .Keyword_return, "return"),
664 .tag = .Return,
665 }, .{
666 .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, ZigClangQualType_getTypePtr(return_qt)) catch |err| switch (err) {
667 error.OutOfMemory => |e| return e,
668 error.UnsupportedTranslation,
669 error.UnsupportedType,
670 => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
671 },
672 });
673 _ = try appendToken(rp.c, .Semicolon, ";");
674 try block_scope.statements.append(&return_expr.base);
675 }
676
629677 const body_node = try block_scope.complete(rp.c);
630 proto_node.setTrailer("body_node", &body_node.base);
678 proto_node.setTrailer("body_node", body_node);
631679 return addTopLevelDecl(c, fn_name, &proto_node.base);
632680}
633681
......@@ -931,7 +979,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
931979 else => |e| return e,
932980 };
933981
934 const align_expr = blk: {
982 const align_expr = blk_2: {
935983 const alignment = ZigClangFieldDecl_getAlignedAttribute(field_decl, rp.c.clang_context);
936984 if (alignment != 0) {
937985 _ = try appendToken(rp.c, .Keyword_align, "align");
......@@ -940,9 +988,9 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
940988 const expr = try transCreateNodeInt(rp.c, alignment / 8);
941989 _ = try appendToken(rp.c, .RParen, ")");
942990
943 break :blk expr;
991 break :blk_2 expr;
944992 }
945 break :blk null;
993 break :blk_2 null;
946994 };
947995
948996 const field_node = try c.arena.create(ast.Node.ContainerField);
......@@ -1073,9 +1121,9 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
10731121
10741122 const field_name_tok = try appendIdentifier(c, field_name);
10751123
1076 const int_node = if (!pure_enum) blk: {
1124 const int_node = if (!pure_enum) blk_2: {
10771125 _ = try appendToken(c, .Colon, "=");
1078 break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
1126 break :blk_2 try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
10791127 } else
10801128 null;
10811129
......@@ -1233,7 +1281,7 @@ fn transStmt(
12331281 .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
12341282 .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
12351283 .NullStmtClass => {
1236 const block = try rp.c.createBlock(null, 0);
1284 const block = try rp.c.createBlock(0);
12371285 block.rbrace = try appendToken(rp.c, .RBrace, "}");
12381286 return &block.base;
12391287 },
......@@ -1307,14 +1355,14 @@ fn transBinaryOperator(
13071355 const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
13081356 if (expr) {
13091357 _ = try appendToken(rp.c, .Semicolon, ";");
1310 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, rhs);
1358 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
13111359 try block_scope.statements.append(&break_node.base);
13121360 const block_node = try block_scope.complete(rp.c);
13131361 const rparen = try appendToken(rp.c, .RParen, ")");
13141362 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
13151363 grouped_expr.* = .{
13161364 .lparen = lparen,
1317 .expr = &block_node.base,
1365 .expr = block_node,
13181366 .rparen = rparen,
13191367 };
13201368 return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
......@@ -1476,11 +1524,10 @@ fn transCompoundStmtInline(
14761524}
14771525
14781526fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {
1479 var block_scope = try Scope.Block.init(rp.c, scope, null);
1527 var block_scope = try Scope.Block.init(rp.c, scope, false);
14801528 defer block_scope.deinit();
14811529 try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
1482 const node = try block_scope.complete(rp.c);
1483 return &node.base;
1530 return try block_scope.complete(rp.c);
14841531}
14851532
14861533fn transCStyleCastExprClass(
......@@ -1684,6 +1731,14 @@ fn transBoolExpr(
16841731 lrvalue: LRValue,
16851732 grouped: bool,
16861733) TransError!*ast.Node {
1734 if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr)) == .IntegerLiteralClass) {
1735 var is_zero: bool = undefined;
1736 if (!ZigClangIntegerLiteral_isZero(@ptrCast(*const ZigClangIntegerLiteral, expr), &is_zero, rp.c.clang_context)) {
1737 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid integer literal", .{});
1738 }
1739 return try transCreateNodeBoolLiteral(rp.c, !is_zero);
1740 }
1741
16871742 const lparen = if (grouped)
16881743 try appendToken(rp.c, .LParen, "(")
16891744 else
......@@ -2380,7 +2435,7 @@ fn transZeroInitExpr(
23802435 ty: *const ZigClangType,
23812436) TransError!*ast.Node {
23822437 switch (ZigClangType_getTypeClass(ty)) {
2383 .Builtin => blk: {
2438 .Builtin => {
23842439 const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
23852440 switch (ZigClangBuiltinType_getKind(builtin_ty)) {
23862441 .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
......@@ -2539,7 +2594,7 @@ fn transDoWhileLoop(
25392594 // zig: if (!cond) break;
25402595 // zig: }
25412596 const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
2542 break :blk node.cast(ast.Node.Block).?;
2597 break :blk node.castTag(.Block).?;
25432598 } else blk: {
25442599 // the C statement is without a block, so we need to create a block to contain it.
25452600 // c: do
......@@ -2550,7 +2605,7 @@ fn transDoWhileLoop(
25502605 // zig: if (!cond) break;
25512606 // zig: }
25522607 new = true;
2553 const block = try rp.c.createBlock(null, 2);
2608 const block = try rp.c.createBlock(2);
25542609 block.statements_len = 1; // over-allocated so we can add another below
25552610 block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
25562611 break :blk block;
......@@ -2579,7 +2634,7 @@ fn transForLoop(
25792634 defer if (block_scope) |*bs| bs.deinit();
25802635
25812636 if (ZigClangForStmt_getInit(stmt)) |init| {
2582 block_scope = try Scope.Block.init(rp.c, scope, null);
2637 block_scope = try Scope.Block.init(rp.c, scope, false);
25832638 loop_scope.parent = &block_scope.?.base;
25842639 const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
25852640 try block_scope.?.statements.append(init_node);
......@@ -2609,8 +2664,7 @@ fn transForLoop(
26092664 while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
26102665 if (block_scope) |*bs| {
26112666 try bs.statements.append(&while_node.base);
2612 const node = try bs.complete(rp.c);
2613 return &node.base;
2667 return try bs.complete(rp.c);
26142668 } else {
26152669 _ = try appendToken(rp.c, .Semicolon, ";");
26162670 return &while_node.base;
......@@ -2665,17 +2719,19 @@ fn transSwitch(
26652719 .cases = switch_node.cases(),
26662720 .case_index = 0,
26672721 .pending_block = undefined,
2722 .default_label = null,
2723 .switch_label = null,
26682724 };
26692725
26702726 // tmp block that all statements will go before being picked up by a case or default
2671 var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null);
2727 var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);
26722728 defer block_scope.deinit();
26732729
26742730 // Note that we do not defer a deinit here; the switch_scope.pending_block field
26752731 // has its own memory management. This resource is freed inside `transCase` and
26762732 // then the final pending_block is freed at the bottom of this function with
26772733 // pending_block.deinit().
2678 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
2734 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
26792735 try switch_scope.pending_block.statements.append(&switch_node.base);
26802736
26812737 const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
......@@ -2690,11 +2746,19 @@ fn transSwitch(
26902746 switch_scope.pending_block.statements.appendAssumeCapacity(n);
26912747 }
26922748
2693 switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");
2694 _ = try appendToken(rp.c, .Colon, ":");
2695 if (!switch_scope.has_default) {
2749 if (switch_scope.default_label == null) {
2750 switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
2751 }
2752 if (switch_scope.switch_label) |l| {
2753 switch_scope.pending_block.label = try appendIdentifier(rp.c, l);
2754 _ = try appendToken(rp.c, .Colon, ":");
2755 }
2756 if (switch_scope.default_label == null) {
26962757 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
2697 else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch", null)).base;
2758 else_prong.expr = blk: {
2759 var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?);
2760 break :blk &(try br.finish(null)).base;
2761 };
26982762 _ = try appendToken(rp.c, .Comma, ",");
26992763
27002764 if (switch_scope.case_index >= switch_scope.cases.len)
......@@ -2708,7 +2772,7 @@ fn transSwitch(
27082772
27092773 const result_node = try switch_scope.pending_block.complete(rp.c);
27102774 switch_scope.pending_block.deinit();
2711 return &result_node.base;
2775 return result_node;
27122776}
27132777
27142778fn transCase(
......@@ -2718,7 +2782,7 @@ fn transCase(
27182782) TransError!*ast.Node {
27192783 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
27202784 const switch_scope = scope.getSwitch();
2721 const label = try std.fmt.allocPrint(rp.c.arena, "__case_{}", .{switch_scope.case_index - @boolToInt(switch_scope.has_default)});
2785 const label = try block_scope.makeMangledName(rp.c, "case");
27222786 _ = try appendToken(rp.c, .Semicolon, ";");
27232787
27242788 const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
......@@ -2738,7 +2802,10 @@ fn transCase(
27382802 try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
27392803
27402804 const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
2741 switch_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
2805 switch_prong.expr = blk: {
2806 var br = try CtrlFlow.init(rp.c, .Break, label);
2807 break :blk &(try br.finish(null)).base;
2808 };
27422809 _ = try appendToken(rp.c, .Comma, ",");
27432810
27442811 if (switch_scope.case_index >= switch_scope.cases.len)
......@@ -2755,9 +2822,9 @@ fn transCase(
27552822
27562823 const pending_node = try switch_scope.pending_block.complete(rp.c);
27572824 switch_scope.pending_block.deinit();
2758 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
2825 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
27592826
2760 try switch_scope.pending_block.statements.append(&pending_node.base);
2827 try switch_scope.pending_block.statements.append(pending_node);
27612828
27622829 return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
27632830}
......@@ -2769,12 +2836,14 @@ fn transDefault(
27692836) TransError!*ast.Node {
27702837 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
27712838 const switch_scope = scope.getSwitch();
2772 const label = "__default";
2773 switch_scope.has_default = true;
2839 switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
27742840 _ = try appendToken(rp.c, .Semicolon, ";");
27752841
27762842 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
2777 else_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
2843 else_prong.expr = blk: {
2844 var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);
2845 break :blk &(try br.finish(null)).base;
2846 };
27782847 _ = try appendToken(rp.c, .Comma, ",");
27792848
27802849 if (switch_scope.case_index >= switch_scope.cases.len)
......@@ -2782,7 +2851,7 @@ fn transDefault(
27822851 switch_scope.cases[switch_scope.case_index] = &else_prong.base;
27832852 switch_scope.case_index += 1;
27842853
2785 switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
2854 switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?);
27862855 _ = try appendToken(rp.c, .Colon, ":");
27872856
27882857 // take all pending statements
......@@ -2791,8 +2860,8 @@ fn transDefault(
27912860
27922861 const pending_node = try switch_scope.pending_block.complete(rp.c);
27932862 switch_scope.pending_block.deinit();
2794 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);
2795 try switch_scope.pending_block.statements.append(&pending_node.base);
2863 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
2864 try switch_scope.pending_block.statements.append(pending_node);
27962865
27972866 return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
27982867}
......@@ -2886,7 +2955,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
28862955 return transCompoundStmt(rp, scope, comp);
28872956 }
28882957 const lparen = try appendToken(rp.c, .LParen, "(");
2889 var block_scope = try Scope.Block.init(rp.c, scope, "blk");
2958 var block_scope = try Scope.Block.init(rp.c, scope, true);
28902959 defer block_scope.deinit();
28912960
28922961 var it = ZigClangCompoundStmt_body_begin(comp);
......@@ -2907,7 +2976,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
29072976 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
29082977 grouped_expr.* = .{
29092978 .lparen = lparen,
2910 .expr = &block_node.base,
2979 .expr = block_node,
29112980 .rparen = rparen,
29122981 };
29132982 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
......@@ -3081,7 +3150,7 @@ fn transUnaryExprOrTypeTraitExpr(
30813150 .AlignOf => "@alignOf",
30823151 .PreferredAlignOf,
30833152 .VecStep,
3084 .OpenMPRequiredSimdAlign,
3153 .OpenMPRequiredSimdAlign,
30853154 => return revertAndWarn(
30863155 rp,
30873156 error.UnsupportedTranslation,
......@@ -3201,7 +3270,7 @@ fn transCreatePreCrement(
32013270 // zig: _ref.* += 1;
32023271 // zig: break :blk _ref.*
32033272 // zig: })
3204 var block_scope = try Scope.Block.init(rp.c, scope, "blk");
3273 var block_scope = try Scope.Block.init(rp.c, scope, true);
32053274 defer block_scope.deinit();
32063275 const ref = try block_scope.makeMangledName(rp.c, "ref");
32073276
......@@ -3231,7 +3300,7 @@ fn transCreatePreCrement(
32313300 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
32323301 try block_scope.statements.append(assign);
32333302
3234 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
3303 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
32353304 try block_scope.statements.append(&break_node.base);
32363305 const block_node = try block_scope.complete(rp.c);
32373306 // semicolon must immediately follow rbrace because it is the last token in a block
......@@ -3239,7 +3308,7 @@ fn transCreatePreCrement(
32393308 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
32403309 grouped_expr.* = .{
32413310 .lparen = try appendToken(rp.c, .LParen, "("),
3242 .expr = &block_node.base,
3311 .expr = block_node,
32433312 .rparen = try appendToken(rp.c, .RParen, ")"),
32443313 };
32453314 return &grouped_expr.base;
......@@ -3275,7 +3344,7 @@ fn transCreatePostCrement(
32753344 // zig: _ref.* += 1;
32763345 // zig: break :blk _tmp
32773346 // zig: })
3278 var block_scope = try Scope.Block.init(rp.c, scope, "blk");
3347 var block_scope = try Scope.Block.init(rp.c, scope, true);
32793348 defer block_scope.deinit();
32803349 const ref = try block_scope.makeMangledName(rp.c, "ref");
32813350
......@@ -3333,7 +3402,7 @@ fn transCreatePostCrement(
33333402 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
33343403 grouped_expr.* = .{
33353404 .lparen = try appendToken(rp.c, .LParen, "("),
3336 .expr = &block_node.base,
3405 .expr = block_node,
33373406 .rparen = try appendToken(rp.c, .RParen, ")"),
33383407 };
33393408 return &grouped_expr.base;
......@@ -3450,7 +3519,7 @@ fn transCreateCompoundAssign(
34503519 // zig: _ref.* = _ref.* + rhs;
34513520 // zig: break :blk _ref.*
34523521 // zig: })
3453 var block_scope = try Scope.Block.init(rp.c, scope, "blk");
3522 var block_scope = try Scope.Block.init(rp.c, scope, true);
34543523 defer block_scope.deinit();
34553524 const ref = try block_scope.makeMangledName(rp.c, "ref");
34563525
......@@ -3518,13 +3587,13 @@ fn transCreateCompoundAssign(
35183587 try block_scope.statements.append(assign);
35193588 }
35203589
3521 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
3590 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
35223591 try block_scope.statements.append(&break_node.base);
35233592 const block_node = try block_scope.complete(rp.c);
35243593 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
35253594 grouped_expr.* = .{
35263595 .lparen = try appendToken(rp.c, .LParen, "("),
3527 .expr = &block_node.base,
3596 .expr = block_node,
35283597 .rparen = try appendToken(rp.c, .RParen, ")"),
35293598 };
35303599 return &grouped_expr.base;
......@@ -3594,8 +3663,16 @@ fn transCPtrCast(
35943663
35953664fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
35963665 const break_scope = scope.getBreakableScope();
3597 const label_text: ?[]const u8 = if (break_scope.id == .Switch) "__switch" else null;
3598 const br = try transCreateNodeBreak(rp.c, label_text, null);
3666 const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
3667 const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
3668 const block_scope = try scope.findBlockScope(rp.c);
3669 swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
3670 break :blk swtch.switch_label;
3671 } else
3672 null;
3673
3674 var cf = try CtrlFlow.init(rp.c, .Break, label_text);
3675 const br = try cf.finish(null);
35993676 _ = try appendToken(rp.c, .Semicolon, ";");
36003677 return &br.base;
36013678}
......@@ -3626,7 +3703,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
36263703 // })
36273704 const lparen = try appendToken(rp.c, .LParen, "(");
36283705
3629 var block_scope = try Scope.Block.init(rp.c, scope, "blk");
3706 var block_scope = try Scope.Block.init(rp.c, scope, true);
36303707 defer block_scope.deinit();
36313708
36323709 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
......@@ -3675,7 +3752,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
36753752 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
36763753 grouped_expr.* = .{
36773754 .lparen = lparen,
3678 .expr = &block_node.base,
3755 .expr = block_node,
36793756 .rparen = try appendToken(rp.c, .RParen, ")"),
36803757 };
36813758 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
......@@ -4074,8 +4151,7 @@ fn transCreateNodeAssign(
40744151 // zig: lhs = _tmp;
40754152 // zig: break :blk _tmp
40764153 // zig: })
4077 const label_name = "blk";
4078 var block_scope = try Scope.Block.init(rp.c, scope, label_name);
4154 var block_scope = try Scope.Block.init(rp.c, scope, true);
40794155 defer block_scope.deinit();
40804156
40814157 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
......@@ -4110,7 +4186,7 @@ fn transCreateNodeAssign(
41104186 try block_scope.statements.append(assign);
41114187
41124188 const break_node = blk: {
4113 var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, label_name);
4189 var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));
41144190 const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
41154191 break :blk try tmp_ctrl_flow.finish(rhs_expr);
41164192 };
......@@ -4119,7 +4195,7 @@ fn transCreateNodeAssign(
41194195 const block_node = try block_scope.complete(rp.c);
41204196 // semicolon must immediately follow rbrace because it is the last token in a block
41214197 _ = try appendToken(rp.c, .Semicolon, ";");
4122 return &block_node.base;
4198 return block_node;
41234199}
41244200
41254201fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
......@@ -4412,7 +4488,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
44124488
44134489 const block = try ast.Node.Block.alloc(c.arena, 1);
44144490 block.* = .{
4415 .label = null,
44164491 .lbrace = block_lbrace,
44174492 .statements_len = 1,
44184493 .rbrace = try appendToken(c, .RBrace, "}"),
......@@ -4487,23 +4562,12 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
44874562 return node;
44884563}
44894564
4490fn transCreateNodeBreakToken(
4491 c: *Context,
4492 label: ?ast.TokenIndex,
4493 rhs: ?*ast.Node,
4494) !*ast.Node.ControlFlowExpression {
4495 const other_token = label orelse return transCreateNodeBreak(c, null, rhs);
4496 const loc = c.token_locs.items[other_token];
4497 const label_name = c.source_buffer.items[loc.start..loc.end];
4498 return transCreateNodeBreak(c, label_name, rhs);
4499}
4500
45014565fn transCreateNodeBreak(
45024566 c: *Context,
4503 label: ?[]const u8,
4567 label: ?ast.TokenIndex,
45044568 rhs: ?*ast.Node,
45054569) !*ast.Node.ControlFlowExpression {
4506 var ctrl_flow = try CtrlFlow.init(c, .Break, label);
4570 var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
45074571 return ctrl_flow.finish(rhs);
45084572}
45094573
......@@ -4907,7 +4971,7 @@ fn finishTransFnProto(
49074971 const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
49084972 const extern_export_inline_tok = if (is_export)
49094973 try appendToken(rp.c, .Keyword_export, "export")
4910 else if (cc == .C and is_extern)
4974 else if (is_extern)
49114975 try appendToken(rp.c, .Keyword_extern, "extern")
49124976 else
49134977 null;
......@@ -5212,26 +5276,32 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
52125276 ZigClangErrorMsg_delete(errors.ptr, errors.len);
52135277}
52145278
5215const CTokIterator = struct {
5279const MacroCtx = struct {
52165280 source: []const u8,
52175281 list: []const CToken,
52185282 i: usize = 0,
5283 loc: ZigClangSourceLocation,
5284 name: []const u8,
52195285
5220 fn peek(self: *CTokIterator) ?CToken.Id {
5286 fn peek(self: *MacroCtx) ?CToken.Id {
52215287 if (self.i >= self.list.len) return null;
52225288 return self.list[self.i + 1].id;
52235289 }
52245290
5225 fn next(self: *CTokIterator) ?CToken.Id {
5291 fn next(self: *MacroCtx) ?CToken.Id {
52265292 if (self.i >= self.list.len) return null;
52275293 self.i += 1;
52285294 return self.list[self.i].id;
52295295 }
52305296
5231 fn slice(self: *CTokIterator, index: usize) []const u8 {
5232 const tok = self.list[index];
5297 fn slice(self: *MacroCtx) []const u8 {
5298 const tok = self.list[self.i];
52335299 return self.source[tok.start..tok.end];
52345300 }
5301
5302 fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
5303 return failDecl(c, self.loc, self.name, fmt, args);
5304 }
52355305};
52365306
52375307fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
......@@ -5278,18 +5348,21 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
52785348 try tok_list.append(tok);
52795349 }
52805350
5281 var tok_it = CTokIterator{
5351 var macro_ctx = MacroCtx{
52825352 .source = slice,
52835353 .list = tok_list.items,
5354 .name = mangled_name,
5355 .loc = begin_loc,
52845356 };
5285 assert(mem.eql(u8, tok_it.slice(0), name));
5357 assert(mem.eql(u8, macro_ctx.slice(), name));
52865358
52875359 var macro_fn = false;
5288 switch (tok_it.peek().?) {
5360 switch (macro_ctx.peek().?) {
52895361 .Identifier => {
52905362 // if it equals itself, ignore. for example, from stdio.h:
52915363 // #define stdin stdin
5292 if (mem.eql(u8, name, tok_it.slice(1))) {
5364 const tok = macro_ctx.list[1];
5365 if (mem.eql(u8, name, slice[tok.start..tok.end])) {
52935366 continue;
52945367 }
52955368 },
......@@ -5300,15 +5373,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
53005373 },
53015374 .LParen => {
53025375 // if the name is immediately followed by a '(' then it is a function
5303 macro_fn = tok_it.list[0].end == tok_it.list[1].start;
5376 macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start;
53045377 },
53055378 else => {},
53065379 }
53075380
53085381 (if (macro_fn)
5309 transMacroFnDefine(c, &tok_it, mangled_name, begin_loc)
5382 transMacroFnDefine(c, &macro_ctx)
53105383 else
5311 transMacroDefine(c, &tok_it, mangled_name, begin_loc)) catch |err| switch (err) {
5384 transMacroDefine(c, &macro_ctx)) catch |err| switch (err) {
53125385 error.ParseError => continue,
53135386 error.OutOfMemory => |e| return e,
53145387 };
......@@ -5318,24 +5391,18 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
53185391 }
53195392}
53205393
5321fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
5394fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
53225395 const scope = &c.global_scope.base;
53235396
53245397 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
53255398 const mut_tok = try appendToken(c, .Keyword_const, "const");
5326 const name_tok = try appendIdentifier(c, name);
5399 const name_tok = try appendIdentifier(c, m.name);
53275400 const eq_token = try appendToken(c, .Equal, "=");
53285401
5329 const init_node = try parseCExpr(c, it, source_loc, scope);
5330 const last = it.next().?;
5402 const init_node = try parseCExpr(c, m, scope);
5403 const last = m.next().?;
53315404 if (last != .Eof and last != .Nl)
5332 return failDecl(
5333 c,
5334 source_loc,
5335 name,
5336 "unable to translate C expr: unexpected token .{}",
5337 .{@tagName(last)},
5338 );
5405 return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
53395406
53405407 const semicolon_token = try appendToken(c, .Semicolon, ";");
53415408 const node = try ast.Node.VarDecl.create(c.arena, .{
......@@ -5347,45 +5414,33 @@ fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc
53475414 .eq_token = eq_token,
53485415 .init_node = init_node,
53495416 });
5350 _ = try c.global_scope.macro_table.put(name, &node.base);
5417 _ = try c.global_scope.macro_table.put(m.name, &node.base);
53515418}
53525419
5353fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
5354 var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
5420fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5421 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
53555422 defer block_scope.deinit();
53565423 const scope = &block_scope.base;
53575424
53585425 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
53595426 const inline_tok = try appendToken(c, .Keyword_inline, "inline");
53605427 const fn_tok = try appendToken(c, .Keyword_fn, "fn");
5361 const name_tok = try appendIdentifier(c, name);
5428 const name_tok = try appendIdentifier(c, m.name);
53625429 _ = try appendToken(c, .LParen, "(");
53635430
5364 if (it.next().? != .LParen) {
5365 return failDecl(
5366 c,
5367 source_loc,
5368 name,
5369 "unable to translate C expr: expected '('",
5370 .{},
5371 );
5431 if (m.next().? != .LParen) {
5432 return m.fail(c, "unable to translate C expr: expected '('", .{});
53725433 }
53735434
53745435 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
53755436 defer fn_params.deinit();
53765437
53775438 while (true) {
5378 if (it.next().? != .Identifier) {
5379 return failDecl(
5380 c,
5381 source_loc,
5382 name,
5383 "unable to translate C expr: expected identifier",
5384 .{},
5385 );
5439 if (m.next().? != .Identifier) {
5440 return m.fail(c, "unable to translate C expr: expected identifier", .{});
53865441 }
53875442
5388 const mangled_name = try block_scope.makeMangledName(c, it.slice(it.i));
5443 const mangled_name = try block_scope.makeMangledName(c, m.slice());
53895444 const param_name_tok = try appendIdentifier(c, mangled_name);
53905445 _ = try appendToken(c, .Colon, ":");
53915446
......@@ -5403,20 +5458,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
54035458 .param_type = .{ .any_type = &any_type.base },
54045459 };
54055460
5406 if (it.peek().? != .Comma)
5461 if (m.peek().? != .Comma)
54075462 break;
5408 _ = it.next();
5463 _ = m.next();
54095464 _ = try appendToken(c, .Comma, ",");
54105465 }
54115466
5412 if (it.next().? != .RParen) {
5413 return failDecl(
5414 c,
5415 source_loc,
5416 name,
5417 "unable to translate C expr: expected ')'",
5418 .{},
5419 );
5467 if (m.next().? != .RParen) {
5468 return m.fail(c, "unable to translate C expr: expected ')'", .{});
54205469 }
54215470
54225471 _ = try appendToken(c, .RParen, ")");
......@@ -5424,20 +5473,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
54245473 const type_of = try c.createBuiltinCall("@TypeOf", 1);
54255474
54265475 const return_kw = try appendToken(c, .Keyword_return, "return");
5427 const expr = try parseCExpr(c, it, source_loc, scope);
5428 const last = it.next().?;
5476 const expr = try parseCExpr(c, m, scope);
5477 const last = m.next().?;
54295478 if (last != .Eof and last != .Nl)
5430 return failDecl(
5431 c,
5432 source_loc,
5433 name,
5434 "unable to translate C expr: unexpected token .{}",
5435 .{@tagName(last)},
5436 );
5479 return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
54375480 _ = try appendToken(c, .Semicolon, ";");
5438 const type_of_arg = if (expr.tag != .Block) expr else blk: {
5439 const blk = @fieldParentPtr(ast.Node.Block, "base", expr);
5440 const blk_last = blk.statements()[blk.statements_len - 1];
5481 const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
5482 const stmts = expr.blockStatements();
5483 const blk_last = stmts[stmts.len - 1];
54415484 const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
54425485 break :blk br.getRHS().?;
54435486 };
......@@ -5460,42 +5503,35 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
54605503 .visib_token = pub_tok,
54615504 .extern_export_inline_token = inline_tok,
54625505 .name_token = name_tok,
5463 .body_node = &block_node.base,
5506 .body_node = block_node,
54645507 });
54655508 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
54665509
5467 _ = try c.global_scope.macro_table.put(name, &fn_proto.base);
5510 _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base);
54685511}
54695512
54705513const ParseError = Error || error{ParseError};
54715514
5472fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5473 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
5474 switch (it.next().?) {
5515fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
5516 const node = try parseCPrefixOpExpr(c, m, scope);
5517 switch (m.next().?) {
54755518 .QuestionMark => {
54765519 // must come immediately after expr
54775520 _ = try appendToken(c, .RParen, ")");
54785521 const if_node = try transCreateNodeIf(c);
54795522 if_node.condition = node;
5480 if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);
5481 if (it.next().? != .Colon) {
5482 try failDecl(
5483 c,
5484 source_loc,
5485 it.slice(0),
5486 "unable to translate C expr: expected ':'",
5487 .{},
5488 );
5523 if_node.body = try parseCPrimaryExpr(c, m, scope);
5524 if (m.next().? != .Colon) {
5525 try m.fail(c, "unable to translate C expr: expected ':'", .{});
54895526 return error.ParseError;
54905527 }
54915528 if_node.@"else" = try transCreateNodeElse(c);
5492 if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope);
5529 if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope);
54935530 return &if_node.base;
54945531 },
54955532 .Comma => {
54965533 _ = try appendToken(c, .Semicolon, ";");
5497 const label_name = "blk";
5498 var block_scope = try Scope.Block.init(c, scope, label_name);
5534 var block_scope = try Scope.Block.init(c, scope, true);
54995535 defer block_scope.deinit();
55005536
55015537 var last = node;
......@@ -5512,30 +5548,29 @@ fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation
55125548 };
55135549 try block_scope.statements.append(&op_node.base);
55145550
5515 last = try parseCPrefixOpExpr(c, it, source_loc, scope);
5551 last = try parseCPrefixOpExpr(c, m, scope);
55165552 _ = try appendToken(c, .Semicolon, ";");
5517 if (it.next().? != .Comma) {
5518 it.i -= 1;
5553 if (m.next().? != .Comma) {
5554 m.i -= 1;
55195555 break;
55205556 }
55215557 }
55225558
5523 const break_node = try transCreateNodeBreak(c, label_name, last);
5559 const break_node = try transCreateNodeBreak(c, block_scope.label, last);
55245560 try block_scope.statements.append(&break_node.base);
5525 const block_node = try block_scope.complete(c);
5526 return &block_node.base;
5561 return try block_scope.complete(c);
55275562 },
55285563 else => {
5529 it.i -= 1;
5564 m.i -= 1;
55305565 return node;
55315566 },
55325567 }
55335568}
55345569
5535fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
5536 var lit_bytes = it.slice(it.i);
5570fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
5571 var lit_bytes = m.slice();
55375572
5538 switch (it.list[it.i].id) {
5573 switch (m.list[m.i].id) {
55395574 .IntegerLiteral => |suffix| {
55405575 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
55415576 switch (lit_bytes[1]) {
......@@ -5596,8 +5631,8 @@ fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocati
55965631 }
55975632}
55985633
5599fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ![]const u8 {
5600 var source = source_bytes;
5634fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
5635 var source = m.slice();
56015636 for (source) |c, i| {
56025637 if (c == '\"' or c == '\'') {
56035638 source = source[i..];
......@@ -5669,11 +5704,11 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
56695704 bytes[i] = '?';
56705705 },
56715706 'u', 'U' => {
5672 try failDecl(ctx, source_loc, name, "macro tokenizing failed: TODO unicode escape sequences", .{});
5707 try m.fail(ctx, "macro tokenizing failed: TODO unicode escape sequences", .{});
56735708 return error.ParseError;
56745709 },
56755710 else => {
5676 try failDecl(ctx, source_loc, name, "macro tokenizing failed: unknown escape sequence", .{});
5711 try m.fail(ctx, "macro tokenizing failed: unknown escape sequence", .{});
56775712 return error.ParseError;
56785713 },
56795714 }
......@@ -5692,21 +5727,21 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
56925727 switch (c) {
56935728 '0'...'9' => {
56945729 num = std.math.mul(u8, num, 16) catch {
5695 try failDecl(ctx, source_loc, name, "macro tokenizing failed: hex literal overflowed", .{});
5730 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
56965731 return error.ParseError;
56975732 };
56985733 num += c - '0';
56995734 },
57005735 'a'...'f' => {
57015736 num = std.math.mul(u8, num, 16) catch {
5702 try failDecl(ctx, source_loc, name, "macro tokenizing failed: hex literal overflowed", .{});
5737 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
57035738 return error.ParseError;
57045739 };
57055740 num += c - 'a' + 10;
57065741 },
57075742 'A'...'F' => {
57085743 num = std.math.mul(u8, num, 16) catch {
5709 try failDecl(ctx, source_loc, name, "macro tokenizing failed: hex literal overflowed", .{});
5744 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
57105745 return error.ParseError;
57115746 };
57125747 num += c - 'A' + 10;
......@@ -5733,7 +5768,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
57335768 if (accept_digit) {
57345769 count += 1;
57355770 num = std.math.mul(u8, num, 8) catch {
5736 try failDecl(ctx, source_loc, name, "macro tokenizing failed: octal literal overflowed", .{});
5771 try m.fail(ctx, "macro tokenizing failed: octal literal overflowed", .{});
57375772 return error.ParseError;
57385773 };
57395774 num += c - '0';
......@@ -5756,13 +5791,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
57565791 return bytes[0..i];
57575792}
57585793
5759fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5760 const tok = it.next().?;
5761 const slice = it.slice(it.i);
5794fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
5795 const tok = m.next().?;
5796 const slice = m.slice();
57625797 switch (tok) {
57635798 .CharLiteral => {
57645799 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
5765 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
5800 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));
57665801 const node = try c.arena.create(ast.Node.OneToken);
57675802 node.* = .{
57685803 .base = .{ .tag = .CharLiteral },
......@@ -5780,7 +5815,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
57805815 }
57815816 },
57825817 .StringLiteral => {
5783 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
5818 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));
57845819 const node = try c.arena.create(ast.Node.OneToken);
57855820 node.* = .{
57865821 .base = .{ .tag = .StringLiteral },
......@@ -5789,7 +5824,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
57895824 return &node.base;
57905825 },
57915826 .IntegerLiteral, .FloatLiteral => {
5792 return parseCNumLit(c, it, source_loc);
5827 return parseCNumLit(c, m);
57935828 },
57945829 // eventually this will be replaced by std.c.parse which will handle these correctly
57955830 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
......@@ -5800,61 +5835,55 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
58005835 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
58015836 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
58025837 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
5803 .Keyword_unsigned => if (it.next()) |t| switch (t) {
5838 .Keyword_unsigned => if (m.next()) |t| switch (t) {
58045839 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
58055840 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
58065841 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
5807 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
5808 _ = it.next();
5842 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
5843 _ = m.next();
58095844 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
58105845 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
58115846 else => {
5812 it.i -= 1;
5847 m.i -= 1;
58135848 return transCreateNodeIdentifierUnchecked(c, "c_uint");
58145849 },
58155850 } else {
58165851 return transCreateNodeIdentifierUnchecked(c, "c_uint");
58175852 },
5818 .Keyword_signed => if (it.next()) |t| switch (t) {
5853 .Keyword_signed => if (m.next()) |t| switch (t) {
58195854 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
58205855 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
58215856 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
5822 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
5823 _ = it.next();
5857 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
5858 _ = m.next();
58245859 return transCreateNodeIdentifierUnchecked(c, "c_longlong");
58255860 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
58265861 else => {
5827 it.i -= 1;
5862 m.i -= 1;
58285863 return transCreateNodeIdentifierUnchecked(c, "c_int");
58295864 },
58305865 } else {
58315866 return transCreateNodeIdentifierUnchecked(c, "c_int");
58325867 },
58335868 .Identifier => {
5834 const mangled_name = scope.getAlias(it.slice(it.i));
5869 const mangled_name = scope.getAlias(slice);
58355870 return transCreateNodeIdentifier(c, mangled_name);
58365871 },
58375872 .LParen => {
5838 const inner_node = try parseCExpr(c, it, source_loc, scope);
5873 const inner_node = try parseCExpr(c, m, scope);
58395874
5840 const next_id = it.next().?;
5875 const next_id = m.next().?;
58415876 if (next_id != .RParen) {
5842 try failDecl(
5843 c,
5844 source_loc,
5845 it.slice(0),
5846 "unable to translate C expr: expected ')'' instead got: {}",
5847 .{@tagName(next_id)},
5848 );
5877 try m.fail(c, "unable to translate C expr: expected ')'' instead got: {}", .{@tagName(next_id)});
58495878 return error.ParseError;
58505879 }
58515880 var saw_l_paren = false;
58525881 var saw_integer_literal = false;
5853 switch (it.peek().?) {
5882 switch (m.peek().?) {
58545883 // (type)(to_cast)
58555884 .LParen => {
58565885 saw_l_paren = true;
5857 _ = it.next();
5886 _ = m.next();
58585887 },
58595888 // (type)identifier
58605889 .Identifier => {},
......@@ -5868,16 +5897,10 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
58685897 // hack to get zig fmt to render a comma in builtin calls
58695898 _ = try appendToken(c, .Comma, ",");
58705899
5871 const node_to_cast = try parseCExpr(c, it, source_loc, scope);
5900 const node_to_cast = try parseCExpr(c, m, scope);
58725901
5873 if (saw_l_paren and it.next().? != .RParen) {
5874 try failDecl(
5875 c,
5876 source_loc,
5877 it.slice(0),
5878 "unable to translate C expr: expected ')''",
5879 .{},
5880 );
5902 if (saw_l_paren and m.next().? != .RParen) {
5903 try m.fail(c, "unable to translate C expr: expected ')''", .{});
58815904 return error.ParseError;
58825905 }
58835906
......@@ -5905,13 +5928,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
59055928 return &group_node.base;
59065929 },
59075930 else => {
5908 try failDecl(
5909 c,
5910 source_loc,
5911 it.slice(0),
5912 "unable to translate C expr: unexpected token .{}",
5913 .{@tagName(tok)},
5914 );
5931 try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)});
59155932 return error.ParseError;
59165933 },
59175934 }
......@@ -6018,52 +6035,40 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
60186035 return &group_node.base;
60196036}
60206037
6021fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6022 var node = try parseCPrimaryExpr(c, it, source_loc, scope);
6038fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
6039 var node = try parseCPrimaryExpr(c, m, scope);
60236040 while (true) {
60246041 var op_token: ast.TokenIndex = undefined;
60256042 var op_id: ast.Node.Tag = undefined;
60266043 var bool_op = false;
6027 switch (it.next().?) {
6044 switch (m.next().?) {
60286045 .Period => {
6029 if (it.next().? != .Identifier) {
6030 try failDecl(
6031 c,
6032 source_loc,
6033 it.slice(0),
6034 "unable to translate C expr: expected identifier",
6035 .{},
6036 );
6046 if (m.next().? != .Identifier) {
6047 try m.fail(c, "unable to translate C expr: expected identifier", .{});
60376048 return error.ParseError;
60386049 }
60396050
6040 node = try transCreateNodeFieldAccess(c, node, it.slice(it.i));
6051 node = try transCreateNodeFieldAccess(c, node, m.slice());
60416052 continue;
60426053 },
60436054 .Arrow => {
6044 if (it.next().? != .Identifier) {
6045 try failDecl(
6046 c,
6047 source_loc,
6048 it.slice(0),
6049 "unable to translate C expr: expected identifier",
6050 .{},
6051 );
6055 if (m.next().? != .Identifier) {
6056 try m.fail(c, "unable to translate C expr: expected identifier", .{});
60526057 return error.ParseError;
60536058 }
60546059 const deref = try transCreateNodePtrDeref(c, node);
6055 node = try transCreateNodeFieldAccess(c, deref, it.slice(it.i));
6060 node = try transCreateNodeFieldAccess(c, deref, m.slice());
60566061 continue;
60576062 },
60586063 .Asterisk => {
6059 if (it.peek().? == .RParen) {
6064 if (m.peek().? == .RParen) {
60606065 // type *)
60616066
60626067 // hack to get zig fmt to render a comma in builtin calls
60636068 _ = try appendToken(c, .Comma, ",");
60646069
60656070 // last token of `node`
6066 const prev_id = it.list[it.i - 1].id;
6071 const prev_id = m.list[m.i - 1].id;
60676072
60686073 if (prev_id == .Keyword_void) {
60696074 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
......@@ -6134,17 +6139,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
61346139 },
61356140 .LBracket => {
61366141 const arr_node = try transCreateNodeArrayAccess(c, node);
6137 arr_node.index_expr = try parseCPrefixOpExpr(c, it, source_loc, scope);
6142 arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope);
61386143 arr_node.rtoken = try appendToken(c, .RBracket, "]");
61396144 node = &arr_node.base;
6140 if (it.next().? != .RBracket) {
6141 try failDecl(
6142 c,
6143 source_loc,
6144 it.slice(0),
6145 "unable to translate C expr: expected ']'",
6146 .{},
6147 );
6145 if (m.next().? != .RBracket) {
6146 try m.fail(c, "unable to translate C expr: expected ']'", .{});
61486147 return error.ParseError;
61496148 }
61506149 continue;
......@@ -6154,19 +6153,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
61546153 var call_params = std.ArrayList(*ast.Node).init(c.gpa);
61556154 defer call_params.deinit();
61566155 while (true) {
6157 const arg = try parseCPrefixOpExpr(c, it, source_loc, scope);
6156 const arg = try parseCPrefixOpExpr(c, m, scope);
61586157 try call_params.append(arg);
6159 switch (it.next().?) {
6158 switch (m.next().?) {
61606159 .Comma => _ = try appendToken(c, .Comma, ","),
61616160 .RParen => break,
61626161 else => {
6163 try failDecl(
6164 c,
6165 source_loc,
6166 it.slice(0),
6167 "unable to translate C expr: expected ',' or ')'",
6168 .{},
6169 );
6162 try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
61706163 return error.ParseError;
61716164 },
61726165 }
......@@ -6193,19 +6186,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
61936186 defer init_vals.deinit();
61946187
61956188 while (true) {
6196 const val = try parseCPrefixOpExpr(c, it, source_loc, scope);
6189 const val = try parseCPrefixOpExpr(c, m, scope);
61976190 try init_vals.append(val);
6198 switch (it.next().?) {
6191 switch (m.next().?) {
61996192 .Comma => _ = try appendToken(c, .Comma, ","),
62006193 .RBrace => break,
62016194 else => {
6202 try failDecl(
6203 c,
6204 source_loc,
6205 it.slice(0),
6206 "unable to translate C expr: expected ',' or '}}'",
6207 .{},
6208 );
6195 try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
62096196 return error.ParseError;
62106197 },
62116198 }
......@@ -6254,22 +6241,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
62546241 op_id = .ArrayCat;
62556242 op_token = try appendToken(c, .PlusPlus, "++");
62566243
6257 it.i -= 1;
6244 m.i -= 1;
62586245 },
62596246 .Identifier => {
62606247 op_id = .ArrayCat;
62616248 op_token = try appendToken(c, .PlusPlus, "++");
62626249
6263 it.i -= 1;
6250 m.i -= 1;
62646251 },
62656252 else => {
6266 it.i -= 1;
6253 m.i -= 1;
62676254 return node;
62686255 },
62696256 }
62706257 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
62716258 const lhs_node = try cast_fn(c, node);
6272 const rhs_node = try parseCPrefixOpExpr(c, it, source_loc, scope);
6259 const rhs_node = try parseCPrefixOpExpr(c, m, scope);
62736260 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
62746261 op_node.* = .{
62756262 .base = .{ .tag = op_id },
......@@ -6281,36 +6268,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
62816268 }
62826269}
62836270
6284fn parseCPrefixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6285 switch (it.next().?) {
6271fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
6272 switch (m.next().?) {
62866273 .Bang => {
62876274 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
6288 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6275 node.rhs = try parseCPrefixOpExpr(c, m, scope);
62896276 return &node.base;
62906277 },
62916278 .Minus => {
62926279 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
6293 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6280 node.rhs = try parseCPrefixOpExpr(c, m, scope);
62946281 return &node.base;
62956282 },
6296 .Plus => return try parseCPrefixOpExpr(c, it, source_loc, scope),
6283 .Plus => return try parseCPrefixOpExpr(c, m, scope),
62976284 .Tilde => {
62986285 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
6299 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6286 node.rhs = try parseCPrefixOpExpr(c, m, scope);
63006287 return &node.base;
63016288 },
63026289 .Asterisk => {
6303 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
6290 const node = try parseCPrefixOpExpr(c, m, scope);
63046291 return try transCreateNodePtrDeref(c, node);
63056292 },
63066293 .Ampersand => {
63076294 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
6308 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6295 node.rhs = try parseCPrefixOpExpr(c, m, scope);
63096296 return &node.base;
63106297 },
63116298 else => {
6312 it.i -= 1;
6313 return try parseCSuffixOpExpr(c, it, source_loc, scope);
6299 m.i -= 1;
6300 return try parseCSuffixOpExpr(c, m, scope);
63146301 },
63156302 }
63166303}
src-self-hosted/type.zig+299-46
......@@ -70,6 +70,11 @@ pub const Type = extern union {
7070 .single_mut_pointer => return .Pointer,
7171 .single_const_pointer_to_comptime_int => return .Pointer,
7272 .const_slice_u8 => return .Pointer,
73
74 .optional,
75 .optional_single_const_pointer,
76 .optional_single_mut_pointer,
77 => return .Optional,
7378 }
7479 }
7580
......@@ -102,8 +107,18 @@ pub const Type = extern union {
102107 return @fieldParentPtr(T, "base", self.ptr_otherwise);
103108 }
104109
110 pub fn castPointer(self: Type) ?*Payload.Pointer {
111 return switch (self.tag()) {
112 .single_const_pointer,
113 .single_mut_pointer,
114 .optional_single_const_pointer,
115 .optional_single_mut_pointer,
116 => @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise),
117 else => null,
118 };
119 }
120
105121 pub fn eql(a: Type, b: Type) bool {
106 //std.debug.warn("test {} == {}\n", .{ a, b });
107122 // As a shortcut, if the small tags / addresses match, we're done.
108123 if (a.tag_if_small_enough == b.tag_if_small_enough)
109124 return true;
......@@ -122,8 +137,8 @@ pub const Type = extern union {
122137 .Null => return true,
123138 .Pointer => {
124139 // Hot path for common case:
125 if (a.cast(Payload.SingleConstPointer)) |a_payload| {
126 if (b.cast(Payload.SingleConstPointer)) |b_payload| {
140 if (a.castPointer()) |a_payload| {
141 if (b.castPointer()) |b_payload| {
127142 return eql(a_payload.pointee_type, b_payload.pointee_type);
128143 }
129144 }
......@@ -180,9 +195,13 @@ pub const Type = extern union {
180195 }
181196 return true;
182197 },
198 .Optional => {
199 var buf_a: Payload.Pointer = undefined;
200 var buf_b: Payload.Pointer = undefined;
201 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
202 },
183203 .Float,
184204 .Struct,
185 .Optional,
186205 .ErrorUnion,
187206 .ErrorSet,
188207 .Enum,
......@@ -197,6 +216,74 @@ pub const Type = extern union {
197216 }
198217 }
199218
219 pub fn hash(self: Type) u32 {
220 var hasher = std.hash.Wyhash.init(0);
221 const zig_type_tag = self.zigTypeTag();
222 std.hash.autoHash(&hasher, zig_type_tag);
223 switch (zig_type_tag) {
224 .Type,
225 .Void,
226 .Bool,
227 .NoReturn,
228 .ComptimeFloat,
229 .ComptimeInt,
230 .Undefined,
231 .Null,
232 => {}, // The zig type tag is all that is needed to distinguish.
233
234 .Pointer => {
235 // TODO implement more pointer type hashing
236 },
237 .Int => {
238 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
239 if (self.isNamedInt()) {
240 std.hash.autoHash(&hasher, self.tag());
241 } else {
242 // Remaining cases are arbitrary sized integers.
243 // The target will not be branched upon, because we handled target-dependent cases above.
244 const info = self.intInfo(@as(Target, undefined));
245 std.hash.autoHash(&hasher, info.signed);
246 std.hash.autoHash(&hasher, info.bits);
247 }
248 },
249 .Array => {
250 std.hash.autoHash(&hasher, self.arrayLen());
251 std.hash.autoHash(&hasher, self.elemType().hash());
252 // TODO hash array sentinel
253 },
254 .Fn => {
255 std.hash.autoHash(&hasher, self.fnReturnType().hash());
256 std.hash.autoHash(&hasher, self.fnCallingConvention());
257 const params_len = self.fnParamLen();
258 std.hash.autoHash(&hasher, params_len);
259 var i: usize = 0;
260 while (i < params_len) : (i += 1) {
261 std.hash.autoHash(&hasher, self.fnParamType(i).hash());
262 }
263 },
264 .Optional => {
265 var buf: Payload.Pointer = undefined;
266 std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
267 },
268 .Float,
269 .Struct,
270 .ErrorUnion,
271 .ErrorSet,
272 .Enum,
273 .Union,
274 .BoundFn,
275 .Opaque,
276 .Frame,
277 .AnyFrame,
278 .Vector,
279 .EnumLiteral,
280 => {
281 // TODO implement more type hashing
282 },
283 }
284 return @truncate(u32, hasher.final());
285 }
286
200287 pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
201288 if (self.tag_if_small_enough < Tag.no_payload_count) {
202289 return Type{ .tag_if_small_enough = self.tag_if_small_enough };
......@@ -253,24 +340,6 @@ pub const Type = extern union {
253340 };
254341 return Type{ .ptr_otherwise = &new_payload.base };
255342 },
256 .single_const_pointer => {
257 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
258 const new_payload = try allocator.create(Payload.SingleConstPointer);
259 new_payload.* = .{
260 .base = payload.base,
261 .pointee_type = try payload.pointee_type.copy(allocator),
262 };
263 return Type{ .ptr_otherwise = &new_payload.base };
264 },
265 .single_mut_pointer => {
266 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", self.ptr_otherwise);
267 const new_payload = try allocator.create(Payload.SingleMutPointer);
268 new_payload.* = .{
269 .base = payload.base,
270 .pointee_type = try payload.pointee_type.copy(allocator),
271 };
272 return Type{ .ptr_otherwise = &new_payload.base };
273 },
274343 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
275344 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
276345 .function => {
......@@ -288,6 +357,12 @@ pub const Type = extern union {
288357 };
289358 return Type{ .ptr_otherwise = &new_payload.base };
290359 },
360 .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
361 .single_const_pointer,
362 .single_mut_pointer,
363 .optional_single_mut_pointer,
364 .optional_single_const_pointer,
365 => return self.copyPayloadSingleField(allocator, Payload.Pointer, "pointee_type"),
291366 }
292367 }
293368
......@@ -298,6 +373,14 @@ pub const Type = extern union {
298373 return Type{ .ptr_otherwise = &new_payload.base };
299374 }
300375
376 fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
377 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
378 const new_payload = try allocator.create(T);
379 new_payload.base = payload.base;
380 @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
381 return Type{ .ptr_otherwise = &new_payload.base };
382 }
383
301384 pub fn format(
302385 self: Type,
303386 comptime fmt: []const u8,
......@@ -373,13 +456,13 @@ pub const Type = extern union {
373456 continue;
374457 },
375458 .single_const_pointer => {
376 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
459 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
377460 try out_stream.writeAll("*const ");
378461 ty = payload.pointee_type;
379462 continue;
380463 },
381464 .single_mut_pointer => {
382 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);
465 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
383466 try out_stream.writeAll("*");
384467 ty = payload.pointee_type;
385468 continue;
......@@ -392,6 +475,24 @@ pub const Type = extern union {
392475 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
393476 return out_stream.print("u{}", .{payload.bits});
394477 },
478 .optional => {
479 const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
480 try out_stream.writeByte('?');
481 ty = payload.child_type;
482 continue;
483 },
484 .optional_single_const_pointer => {
485 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
486 try out_stream.writeAll("?*const ");
487 ty = payload.pointee_type;
488 continue;
489 },
490 .optional_single_mut_pointer => {
491 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
492 try out_stream.writeAll("?*");
493 ty = payload.pointee_type;
494 continue;
495 },
395496 }
396497 unreachable;
397498 }
......@@ -481,12 +582,16 @@ pub const Type = extern union {
481582 .single_const_pointer_to_comptime_int,
482583 .const_slice_u8,
483584 .array_u8_sentinel_0,
484 .array, // TODO check for zero bits
485 .single_const_pointer,
486 .single_mut_pointer,
487 .int_signed, // TODO check for zero bits
488 .int_unsigned, // TODO check for zero bits
585 .optional,
586 .optional_single_mut_pointer,
587 .optional_single_const_pointer,
489588 => true,
589 // TODO lazy types
590 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
591 .single_const_pointer => self.elemType().hasCodeGenBits(),
592 .single_mut_pointer => self.elemType().hasCodeGenBits(),
593 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,
594 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
490595
491596 .c_void,
492597 .void,
......@@ -533,6 +638,8 @@ pub const Type = extern union {
533638 .const_slice_u8,
534639 .single_const_pointer,
535640 .single_mut_pointer,
641 .optional_single_const_pointer,
642 .optional_single_mut_pointer,
536643 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
537644
538645 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
......@@ -565,6 +672,17 @@ pub const Type = extern union {
565672 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
566673 },
567674
675 .optional => {
676 var buf: Payload.Pointer = undefined;
677 const child_type = self.optionalChild(&buf);
678 if (!child_type.hasCodeGenBits()) return 1;
679
680 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
681 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
682
683 return child_type.abiAlignment(target);
684 },
685
568686 .c_void,
569687 .void,
570688 .type,
......@@ -615,6 +733,8 @@ pub const Type = extern union {
615733 .const_slice_u8,
616734 .single_const_pointer,
617735 .single_mut_pointer,
736 .optional_single_const_pointer,
737 .optional_single_mut_pointer,
618738 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
619739
620740 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
......@@ -644,6 +764,21 @@ pub const Type = extern union {
644764
645765 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
646766 },
767
768 .optional => {
769 var buf: Payload.Pointer = undefined;
770 const child_type = self.optionalChild(&buf);
771 if (!child_type.hasCodeGenBits()) return 1;
772
773 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
774 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
775
776 // Optional types are represented as a struct with the child type as the first
777 // field and a boolean as the second. Since the child type's abi alignment is
778 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
779 // to the child type's ABI alignment.
780 return child_type.abiAlignment(target) + child_type.abiSize(target);
781 },
647782 };
648783 }
649784
......@@ -692,6 +827,9 @@ pub const Type = extern union {
692827 .function,
693828 .int_unsigned,
694829 .int_signed,
830 .optional,
831 .optional_single_mut_pointer,
832 .optional_single_const_pointer,
695833 => false,
696834
697835 .single_const_pointer,
......@@ -748,6 +886,9 @@ pub const Type = extern union {
748886 .function,
749887 .int_unsigned,
750888 .int_signed,
889 .optional,
890 .optional_single_mut_pointer,
891 .optional_single_const_pointer,
751892 => false,
752893
753894 .const_slice_u8 => true,
......@@ -799,6 +940,9 @@ pub const Type = extern union {
799940 .int_unsigned,
800941 .int_signed,
801942 .single_mut_pointer,
943 .optional,
944 .optional_single_mut_pointer,
945 .optional_single_const_pointer,
802946 => false,
803947
804948 .single_const_pointer,
......@@ -856,10 +1000,29 @@ pub const Type = extern union {
8561000 .single_const_pointer,
8571001 .single_const_pointer_to_comptime_int,
8581002 .const_slice_u8,
1003 .optional,
1004 .optional_single_mut_pointer,
1005 .optional_single_const_pointer,
8591006 => false,
8601007 };
8611008 }
8621009
1010 /// Asserts that the type is an optional
1011 pub fn isPtrLikeOptional(self: Type) bool {
1012 switch (self.tag()) {
1013 .optional_single_const_pointer, .optional_single_mut_pointer => return true,
1014 .optional => {
1015 var buf: Payload.Pointer = undefined;
1016 const child_type = self.optionalChild(&buf);
1017 // optionals of zero sized pointers behave like bools
1018 if (!child_type.hasCodeGenBits()) return false;
1019
1020 return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();
1021 },
1022 else => unreachable,
1023 }
1024 }
1025
8631026 /// Asserts the type is a pointer or array type.
8641027 pub fn elemType(self: Type) Type {
8651028 return switch (self.tag()) {
......@@ -903,16 +1066,63 @@ pub const Type = extern union {
9031066 .function,
9041067 .int_unsigned,
9051068 .int_signed,
1069 .optional,
1070 .optional_single_const_pointer,
1071 .optional_single_mut_pointer,
9061072 => unreachable,
9071073
9081074 .array => self.cast(Payload.Array).?.elem_type,
909 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
910 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,
1075 .single_const_pointer => self.castPointer().?.pointee_type,
1076 .single_mut_pointer => self.castPointer().?.pointee_type,
9111077 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
9121078 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
9131079 };
9141080 }
9151081
1082 /// Asserts that the type is an optional.
1083 pub fn optionalChild(self: Type, buf: *Payload.Pointer) Type {
1084 return switch (self.tag()) {
1085 .optional => self.cast(Payload.Optional).?.child_type,
1086 .optional_single_mut_pointer => {
1087 buf.* = .{
1088 .base = .{ .tag = .single_mut_pointer },
1089 .pointee_type = self.castPointer().?.pointee_type,
1090 };
1091 return Type.initPayload(&buf.base);
1092 },
1093 .optional_single_const_pointer => {
1094 buf.* = .{
1095 .base = .{ .tag = .single_const_pointer },
1096 .pointee_type = self.castPointer().?.pointee_type,
1097 };
1098 return Type.initPayload(&buf.base);
1099 },
1100 else => unreachable,
1101 };
1102 }
1103
1104 /// Asserts that the type is an optional.
1105 /// Same as `optionalChild` but allocates the buffer if needed.
1106 pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
1107 return switch (self.tag()) {
1108 .optional => self.cast(Payload.Optional).?.child_type,
1109 .optional_single_mut_pointer, .optional_single_const_pointer => {
1110 const payload = try allocator.create(Payload.Pointer);
1111 payload.* = .{
1112 .base = .{
1113 .tag = if (self.tag() == .optional_single_const_pointer)
1114 .single_const_pointer
1115 else
1116 .single_mut_pointer,
1117 },
1118 .pointee_type = self.castPointer().?.pointee_type,
1119 };
1120 return Type.initPayload(&payload.base);
1121 },
1122 else => unreachable,
1123 };
1124 }
1125
9161126 /// Asserts the type is an array or vector.
9171127 pub fn arrayLen(self: Type) u64 {
9181128 return switch (self.tag()) {
......@@ -960,6 +1170,9 @@ pub const Type = extern union {
9601170 .const_slice_u8,
9611171 .int_unsigned,
9621172 .int_signed,
1173 .optional,
1174 .optional_single_mut_pointer,
1175 .optional_single_const_pointer,
9631176 => unreachable,
9641177
9651178 .array => self.cast(Payload.Array).?.len,
......@@ -1014,6 +1227,9 @@ pub const Type = extern union {
10141227 .const_slice_u8,
10151228 .int_unsigned,
10161229 .int_signed,
1230 .optional,
1231 .optional_single_mut_pointer,
1232 .optional_single_const_pointer,
10171233 => unreachable,
10181234
10191235 .array => return null,
......@@ -1065,6 +1281,9 @@ pub const Type = extern union {
10651281 .u16,
10661282 .u32,
10671283 .u64,
1284 .optional,
1285 .optional_single_mut_pointer,
1286 .optional_single_const_pointer,
10681287 => false,
10691288
10701289 .int_signed,
......@@ -1120,6 +1339,9 @@ pub const Type = extern union {
11201339 .i16,
11211340 .i32,
11221341 .i64,
1342 .optional,
1343 .optional_single_mut_pointer,
1344 .optional_single_const_pointer,
11231345 => false,
11241346
11251347 .int_unsigned,
......@@ -1165,6 +1387,9 @@ pub const Type = extern union {
11651387 .single_const_pointer_to_comptime_int,
11661388 .array_u8_sentinel_0,
11671389 .const_slice_u8,
1390 .optional,
1391 .optional_single_mut_pointer,
1392 .optional_single_const_pointer,
11681393 => unreachable,
11691394
11701395 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
......@@ -1228,6 +1453,9 @@ pub const Type = extern union {
12281453 .i32,
12291454 .u64,
12301455 .i64,
1456 .optional,
1457 .optional_single_mut_pointer,
1458 .optional_single_const_pointer,
12311459 => false,
12321460
12331461 .usize,
......@@ -1320,6 +1548,9 @@ pub const Type = extern union {
13201548 .c_ulonglong,
13211549 .int_unsigned,
13221550 .int_signed,
1551 .optional,
1552 .optional_single_mut_pointer,
1553 .optional_single_const_pointer,
13231554 => unreachable,
13241555 };
13251556 }
......@@ -1378,6 +1609,9 @@ pub const Type = extern union {
13781609 .c_ulonglong,
13791610 .int_unsigned,
13801611 .int_signed,
1612 .optional,
1613 .optional_single_mut_pointer,
1614 .optional_single_const_pointer,
13811615 => unreachable,
13821616 }
13831617 }
......@@ -1435,6 +1669,9 @@ pub const Type = extern union {
14351669 .c_ulonglong,
14361670 .int_unsigned,
14371671 .int_signed,
1672 .optional,
1673 .optional_single_mut_pointer,
1674 .optional_single_const_pointer,
14381675 => unreachable,
14391676 }
14401677 }
......@@ -1492,6 +1729,9 @@ pub const Type = extern union {
14921729 .c_ulonglong,
14931730 .int_unsigned,
14941731 .int_signed,
1732 .optional,
1733 .optional_single_mut_pointer,
1734 .optional_single_const_pointer,
14951735 => unreachable,
14961736 };
14971737 }
......@@ -1546,6 +1786,9 @@ pub const Type = extern union {
15461786 .c_ulonglong,
15471787 .int_unsigned,
15481788 .int_signed,
1789 .optional,
1790 .optional_single_mut_pointer,
1791 .optional_single_const_pointer,
15491792 => unreachable,
15501793 };
15511794 }
......@@ -1600,6 +1843,9 @@ pub const Type = extern union {
16001843 .c_ulonglong,
16011844 .int_unsigned,
16021845 .int_signed,
1846 .optional,
1847 .optional_single_mut_pointer,
1848 .optional_single_const_pointer,
16031849 => unreachable,
16041850 };
16051851 }
......@@ -1654,6 +1900,9 @@ pub const Type = extern union {
16541900 .single_const_pointer_to_comptime_int,
16551901 .array_u8_sentinel_0,
16561902 .const_slice_u8,
1903 .optional,
1904 .optional_single_mut_pointer,
1905 .optional_single_const_pointer,
16571906 => false,
16581907 };
16591908 }
......@@ -1698,6 +1947,9 @@ pub const Type = extern union {
16981947 .array_u8_sentinel_0,
16991948 .const_slice_u8,
17001949 .c_void,
1950 .optional,
1951 .optional_single_mut_pointer,
1952 .optional_single_const_pointer,
17011953 => return null,
17021954
17031955 .void => return Value.initTag(.void_value),
......@@ -1726,13 +1978,8 @@ pub const Type = extern union {
17261978 ty = array.elem_type;
17271979 continue;
17281980 },
1729 .single_const_pointer => {
1730 const ptr = ty.cast(Payload.SingleConstPointer).?;
1731 ty = ptr.pointee_type;
1732 continue;
1733 },
1734 .single_mut_pointer => {
1735 const ptr = ty.cast(Payload.SingleMutPointer).?;
1981 .single_const_pointer, .single_mut_pointer => {
1982 const ptr = ty.castPointer().?;
17361983 ty = ptr.pointee_type;
17371984 continue;
17381985 },
......@@ -1787,6 +2034,9 @@ pub const Type = extern union {
17872034 .array,
17882035 .single_const_pointer,
17892036 .single_mut_pointer,
2037 .optional,
2038 .optional_single_mut_pointer,
2039 .optional_single_const_pointer,
17902040 => return false,
17912041 };
17922042 }
......@@ -1847,6 +2097,9 @@ pub const Type = extern union {
18472097 int_signed,
18482098 int_unsigned,
18492099 function,
2100 optional,
2101 optional_single_mut_pointer,
2102 optional_single_const_pointer,
18502103
18512104 pub const last_no_payload_tag = Tag.const_slice_u8;
18522105 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -1868,14 +2121,8 @@ pub const Type = extern union {
18682121 len: u64,
18692122 };
18702123
1871 pub const SingleConstPointer = struct {
1872 base: Payload = Payload{ .tag = .single_const_pointer },
1873
1874 pointee_type: Type,
1875 };
1876
1877 pub const SingleMutPointer = struct {
1878 base: Payload = Payload{ .tag = .single_mut_pointer },
2124 pub const Pointer = struct {
2125 base: Payload,
18792126
18802127 pointee_type: Type,
18812128 };
......@@ -1899,6 +2146,12 @@ pub const Type = extern union {
18992146 return_type: Type,
19002147 cc: std.builtin.CallingConvention,
19012148 };
2149
2150 pub const Optional = struct {
2151 base: Payload = Payload{ .tag = .optional },
2152
2153 child_type: Type,
2154 };
19022155 };
19032156};
19042157
src-self-hosted/value.zig+76-1
......@@ -562,12 +562,87 @@ pub const Value = extern union {
562562 .bool_true => return 1,
563563
564564 .int_u64 => return self.cast(Payload.Int_u64).?.int,
565 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
565 .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
566566 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
567567 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
568568 }
569569 }
570570
571 /// Asserts the value is an integer and it fits in a i64
572 pub fn toSignedInt(self: Value) i64 {
573 switch (self.tag()) {
574 .ty,
575 .int_type,
576 .u8_type,
577 .i8_type,
578 .u16_type,
579 .i16_type,
580 .u32_type,
581 .i32_type,
582 .u64_type,
583 .i64_type,
584 .usize_type,
585 .isize_type,
586 .c_short_type,
587 .c_ushort_type,
588 .c_int_type,
589 .c_uint_type,
590 .c_long_type,
591 .c_ulong_type,
592 .c_longlong_type,
593 .c_ulonglong_type,
594 .c_longdouble_type,
595 .f16_type,
596 .f32_type,
597 .f64_type,
598 .f128_type,
599 .c_void_type,
600 .bool_type,
601 .void_type,
602 .type_type,
603 .anyerror_type,
604 .comptime_int_type,
605 .comptime_float_type,
606 .noreturn_type,
607 .null_type,
608 .undefined_type,
609 .fn_noreturn_no_args_type,
610 .fn_void_no_args_type,
611 .fn_naked_noreturn_no_args_type,
612 .fn_ccc_void_no_args_type,
613 .single_const_pointer_to_comptime_int_type,
614 .const_slice_u8_type,
615 .null_value,
616 .function,
617 .ref_val,
618 .decl_ref,
619 .elem_ptr,
620 .bytes,
621 .repeated,
622 .float_16,
623 .float_32,
624 .float_64,
625 .float_128,
626 .void_value,
627 .unreachable_value,
628 .empty_array,
629 => unreachable,
630
631 .undef => unreachable,
632
633 .zero,
634 .bool_false,
635 => return 0,
636
637 .bool_true => return 1,
638
639 .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
640 .int_i64 => return self.cast(Payload.Int_i64).?.int,
641 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(i64) catch unreachable,
642 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(i64) catch unreachable,
643 }
644 }
645
571646 pub fn toBool(self: Value) bool {
572647 return switch (self.tag()) {
573648 .bool_true => true,
src-self-hosted/zir.zig+331-83
......@@ -151,6 +151,11 @@ pub const Inst = struct {
151151 isnonnull,
152152 /// Return a boolean true if an optional is null. `x == null`
153153 isnull,
154 /// Return a boolean true if value is an error
155 iserr,
156 /// A labeled block of code that loops forever. At the end of the body it is implied
157 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
158 loop,
154159 /// Ambiguously remainder division or modulus. If the computation would possibly have
155160 /// a different value depending on whether the operation is remainder division or modulus,
156161 /// a compile error is emitted. Otherwise the computation is performed.
......@@ -189,6 +194,8 @@ pub const Inst = struct {
189194 single_const_ptr_type,
190195 /// Create a mutable pointer type based on the element type. `*T`
191196 single_mut_ptr_type,
197 /// Create a pointer type with attributes
198 ptr_type,
192199 /// Write a value to a pointer. For loading, see `deref`.
193200 store,
194201 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
......@@ -208,10 +215,19 @@ pub const Inst = struct {
208215 @"unreachable",
209216 /// Bitwise XOR. `^`
210217 xor,
218 /// Create an optional type '?T'
219 optional_type,
220 /// Unwraps an optional value 'lhs.?'
221 unwrap_optional_safe,
222 /// Same as previous, but without safety checks. Used for orelse, if and while
223 unwrap_optional_unsafe,
224 /// Gets the payload of an error union
225 unwrap_err_safe,
226 /// Same as previous, but without safety checks. Used for orelse, if and while
227 unwrap_err_unsafe,
211228
212229 pub fn Type(tag: Tag) type {
213230 return switch (tag) {
214 .arg,
215231 .breakpoint,
216232 .dbg_stmt,
217233 .returnvoid,
......@@ -227,6 +243,7 @@ pub const Inst = struct {
227243 .@"return",
228244 .isnull,
229245 .isnonnull,
246 .iserr,
230247 .ptrtoint,
231248 .alloc,
232249 .ensure_result_used,
......@@ -237,6 +254,11 @@ pub const Inst = struct {
237254 .typeof,
238255 .single_const_ptr_type,
239256 .single_mut_ptr_type,
257 .optional_type,
258 .unwrap_optional_safe,
259 .unwrap_optional_unsafe,
260 .unwrap_err_safe,
261 .unwrap_err_unsafe,
240262 => UnOp,
241263
242264 .add,
......@@ -268,6 +290,7 @@ pub const Inst = struct {
268290 .xor,
269291 => BinOp,
270292
293 .arg => Arg,
271294 .block => Block,
272295 .@"break" => Break,
273296 .breakvoid => BreakVoid,
......@@ -279,6 +302,7 @@ pub const Inst = struct {
279302 .declval_in_module => DeclValInModule,
280303 .coerce_result_block_ptr => CoerceResultBlockPtr,
281304 .compileerror => CompileError,
305 .loop => Loop,
282306 .@"const" => Const,
283307 .str => Str,
284308 .int => Int,
......@@ -292,6 +316,7 @@ pub const Inst = struct {
292316 .fntype => FnType,
293317 .elemptr => ElemPtr,
294318 .condbr => CondBr,
319 .ptr_type => PtrType,
295320 };
296321 }
297322
......@@ -347,6 +372,7 @@ pub const Inst = struct {
347372 .inttype,
348373 .isnonnull,
349374 .isnull,
375 .iserr,
350376 .mod_rem,
351377 .mul,
352378 .mulwrap,
......@@ -366,6 +392,12 @@ pub const Inst = struct {
366392 .subwrap,
367393 .typeof,
368394 .xor,
395 .optional_type,
396 .unwrap_optional_safe,
397 .unwrap_optional_unsafe,
398 .unwrap_err_safe,
399 .unwrap_err_unsafe,
400 .ptr_type,
369401 => false,
370402
371403 .@"break",
......@@ -376,6 +408,7 @@ pub const Inst = struct {
376408 .returnvoid,
377409 .unreach_nocheck,
378410 .@"unreachable",
411 .loop,
379412 => true,
380413 };
381414 }
......@@ -431,6 +464,16 @@ pub const Inst = struct {
431464 kw_args: struct {},
432465 };
433466
467 pub const Arg = struct {
468 pub const base_tag = Tag.arg;
469 base: Inst,
470
471 positionals: struct {
472 name: []const u8,
473 },
474 kw_args: struct {},
475 };
476
434477 pub const Block = struct {
435478 pub const base_tag = Tag.block;
436479 base: Inst,
......@@ -577,6 +620,16 @@ pub const Inst = struct {
577620 kw_args: struct {},
578621 };
579622
623 pub const Loop = struct {
624 pub const base_tag = Tag.loop;
625 base: Inst,
626
627 positionals: struct {
628 body: Module.Body,
629 },
630 kw_args: struct {},
631 };
632
580633 pub const FieldPtr = struct {
581634 pub const base_tag = Tag.fieldptr;
582635 base: Inst,
......@@ -774,6 +827,24 @@ pub const Inst = struct {
774827 },
775828 kw_args: struct {},
776829 };
830
831 pub const PtrType = struct {
832 pub const base_tag = Tag.ptr_type;
833 base: Inst,
834
835 positionals: struct {
836 child_type: *Inst,
837 },
838 kw_args: struct {
839 @"allowzero": bool = false,
840 @"align": ?*Inst = null,
841 align_bit_start: ?*Inst = null,
842 align_bit_end: ?*Inst = null,
843 @"const": bool = true,
844 @"volatile": bool = false,
845 sentinel: ?*Inst = null,
846 },
847 };
777848};
778849
779850pub const ErrorMsg = struct {
......@@ -785,12 +856,24 @@ pub const Module = struct {
785856 decls: []*Decl,
786857 arena: std.heap.ArenaAllocator,
787858 error_msg: ?ErrorMsg = null,
859 metadata: std.AutoHashMap(*Inst, MetaData),
860 body_metadata: std.AutoHashMap(*Body, BodyMetaData),
861
862 pub const MetaData = struct {
863 deaths: ir.Inst.DeathsInt,
864 };
865
866 pub const BodyMetaData = struct {
867 deaths: []*Inst,
868 };
788869
789870 pub const Body = struct {
790871 instructions: []*Inst,
791872 };
792873
793874 pub fn deinit(self: *Module, allocator: *Allocator) void {
875 self.metadata.deinit();
876 self.body_metadata.deinit();
794877 allocator.free(self.decls);
795878 self.arena.deinit();
796879 self.* = undefined;
......@@ -838,27 +921,25 @@ pub const Module = struct {
838921 .module = &self,
839922 .inst_table = InstPtrTable.init(allocator),
840923 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
924 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
841925 .arena = std.heap.ArenaAllocator.init(allocator),
842926 .indent = 2,
927 .next_instr_index = undefined,
843928 };
844929 defer write.arena.deinit();
845930 defer write.inst_table.deinit();
846931 defer write.block_table.deinit();
932 defer write.loop_table.deinit();
847933
848934 // First, build a map of *Inst to @ or % indexes
849935 try write.inst_table.ensureCapacity(self.decls.len);
850936
851937 for (self.decls) |decl, decl_i| {
852938 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
853
854 if (decl.inst.cast(Inst.Fn)) |fn_inst| {
855 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
856 try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined });
857 }
858 }
859939 }
860940
861941 for (self.decls) |decl, i| {
942 write.next_instr_index = 0;
862943 try stream.print("@{} ", .{decl.name});
863944 try write.writeInstToStream(stream, decl.inst);
864945 try stream.writeByte('\n');
......@@ -872,8 +953,10 @@ const Writer = struct {
872953 module: *const Module,
873954 inst_table: InstPtrTable,
874955 block_table: std.AutoHashMap(*Inst.Block, []const u8),
956 loop_table: std.AutoHashMap(*Inst.Loop, []const u8),
875957 arena: std.heap.ArenaAllocator,
876958 indent: usize,
959 next_instr_index: usize,
877960
878961 fn writeInstToStream(
879962 self: *Writer,
......@@ -904,7 +987,7 @@ const Writer = struct {
904987 if (i != 0) {
905988 try stream.writeAll(", ");
906989 }
907 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name));
990 try self.writeParamToStream(stream, &@field(inst.positionals, arg_field.name));
908991 }
909992
910993 comptime var need_comma = pos_fields.len != 0;
......@@ -914,13 +997,13 @@ const Writer = struct {
914997 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
915998 if (need_comma) try stream.writeAll(", ");
916999 try stream.print("{}=", .{arg_field.name});
917 try self.writeParamToStream(stream, non_optional);
1000 try self.writeParamToStream(stream, &non_optional);
9181001 need_comma = true;
9191002 }
9201003 } else {
9211004 if (need_comma) try stream.writeAll(", ");
9221005 try stream.print("{}=", .{arg_field.name});
923 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name));
1006 try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name));
9241007 need_comma = true;
9251008 }
9261009 }
......@@ -928,7 +1011,8 @@ const Writer = struct {
9281011 try stream.writeByte(')');
9291012 }
9301013
931 fn writeParamToStream(self: *Writer, stream: anytype, param: anytype) !void {
1014 fn writeParamToStream(self: *Writer, stream: anytype, param_ptr: anytype) !void {
1015 const param = param_ptr.*;
9321016 if (@typeInfo(@TypeOf(param)) == .Enum) {
9331017 return stream.writeAll(@tagName(param));
9341018 }
......@@ -946,15 +1030,36 @@ const Writer = struct {
9461030 },
9471031 Module.Body => {
9481032 try stream.writeAll("{\n");
949 for (param.instructions) |inst, i| {
1033 if (self.module.body_metadata.get(param_ptr)) |metadata| {
1034 if (metadata.deaths.len > 0) {
1035 try stream.writeByteNTimes(' ', self.indent);
1036 try stream.writeAll("; deaths={");
1037 for (metadata.deaths) |death, i| {
1038 if (i != 0) try stream.writeAll(", ");
1039 try self.writeInstParamToStream(stream, death);
1040 }
1041 try stream.writeAll("}\n");
1042 }
1043 }
1044
1045 for (param.instructions) |inst| {
1046 const my_i = self.next_instr_index;
1047 self.next_instr_index += 1;
1048 try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined });
9501049 try stream.writeByteNTimes(' ', self.indent);
951 try stream.print("%{} ", .{i});
1050 try stream.print("%{} ", .{my_i});
9521051 if (inst.cast(Inst.Block)) |block| {
953 const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{i});
1052 const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{my_i});
9541053 try self.block_table.put(block, name);
1054 } else if (inst.cast(Inst.Loop)) |loop| {
1055 const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{}", .{my_i});
1056 try self.loop_table.put(loop, name);
9551057 }
9561058 self.indent += 2;
9571059 try self.writeInstToStream(stream, inst);
1060 if (self.module.metadata.get(inst)) |metadata| {
1061 try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
1062 }
9581063 self.indent -= 2;
9591064 try stream.writeByte('\n');
9601065 }
......@@ -970,6 +1075,10 @@ const Writer = struct {
9701075 const name = self.block_table.get(param).?;
9711076 return std.zig.renderStringLiteral(name, stream);
9721077 },
1078 *Inst.Loop => {
1079 const name = self.loop_table.get(param).?;
1080 return std.zig.renderStringLiteral(name, stream);
1081 },
9731082 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
9741083 }
9751084 }
......@@ -1006,8 +1115,10 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
10061115 .decls = .{},
10071116 .unnamed_index = 0,
10081117 .block_table = std.StringHashMap(*Inst.Block).init(allocator),
1118 .loop_table = std.StringHashMap(*Inst.Loop).init(allocator),
10091119 };
10101120 defer parser.block_table.deinit();
1121 defer parser.loop_table.deinit();
10111122 errdefer parser.arena.deinit();
10121123
10131124 parser.parseRoot() catch |err| switch (err) {
......@@ -1021,6 +1132,8 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
10211132 .decls = parser.decls.toOwnedSlice(allocator),
10221133 .arena = parser.arena,
10231134 .error_msg = parser.error_msg,
1135 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1136 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
10241137 };
10251138}
10261139
......@@ -1034,6 +1147,7 @@ const Parser = struct {
10341147 error_msg: ?ErrorMsg = null,
10351148 unnamed_index: usize,
10361149 block_table: std.StringHashMap(*Inst.Block),
1150 loop_table: std.StringHashMap(*Inst.Loop),
10371151
10381152 const Body = struct {
10391153 instructions: std.ArrayList(*Inst),
......@@ -1245,6 +1359,8 @@ const Parser = struct {
12451359
12461360 if (InstType == Inst.Block) {
12471361 try self.block_table.put(inst_name, inst_specific);
1362 } else if (InstType == Inst.Loop) {
1363 try self.loop_table.put(inst_name, inst_specific);
12481364 }
12491365
12501366 if (@hasField(InstType, "ty")) {
......@@ -1356,6 +1472,10 @@ const Parser = struct {
13561472 const name = try self.parseStringLiteral();
13571473 return self.block_table.get(name).?;
13581474 },
1475 *Inst.Loop => {
1476 const name = try self.parseStringLiteral();
1477 return self.loop_table.get(name).?;
1478 },
13591479 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
13601480 }
13611481 return self.fail("TODO parse parameter {}", .{@typeName(T)});
......@@ -1421,8 +1541,14 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
14211541 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
14221542 .indent = 0,
14231543 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1544 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1545 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1546 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
14241547 };
1548 errdefer ctx.metadata.deinit();
1549 errdefer ctx.body_metadata.deinit();
14251550 defer ctx.block_table.deinit();
1551 defer ctx.loop_table.deinit();
14261552 defer ctx.decls.deinit(allocator);
14271553 defer ctx.names.deinit();
14281554 defer ctx.primitive_table.deinit();
......@@ -1433,7 +1559,50 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
14331559 return Module{
14341560 .decls = ctx.decls.toOwnedSlice(allocator),
14351561 .arena = ctx.arena,
1562 .metadata = ctx.metadata,
1563 .body_metadata = ctx.body_metadata,
1564 };
1565}
1566
1567/// For debugging purposes, prints a function representation to stderr.
1568pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
1569 const allocator = old_module.gpa;
1570 var ctx: EmitZIR = .{
1571 .allocator = allocator,
1572 .decls = .{},
1573 .arena = std.heap.ArenaAllocator.init(allocator),
1574 .old_module = &old_module,
1575 .next_auto_name = 0,
1576 .names = std.StringHashMap(void).init(allocator),
1577 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1578 .indent = 0,
1579 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1580 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1581 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1582 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
1583 };
1584 defer ctx.metadata.deinit();
1585 defer ctx.body_metadata.deinit();
1586 defer ctx.block_table.deinit();
1587 defer ctx.loop_table.deinit();
1588 defer ctx.decls.deinit(allocator);
1589 defer ctx.names.deinit();
1590 defer ctx.primitive_table.deinit();
1591 defer ctx.arena.deinit();
1592
1593 const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
1594 _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {
1595 std.debug.print("unable to dump function: {}\n", .{err});
1596 return;
1597 };
1598 var module = Module{
1599 .decls = ctx.decls.items,
1600 .arena = ctx.arena,
1601 .metadata = ctx.metadata,
1602 .body_metadata = ctx.body_metadata,
14361603 };
1604
1605 module.dump();
14371606}
14381607
14391608const EmitZIR = struct {
......@@ -1446,6 +1615,9 @@ const EmitZIR = struct {
14461615 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
14471616 indent: usize,
14481617 block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),
1618 loop_table: std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop),
1619 metadata: std.AutoHashMap(*Inst, Module.MetaData),
1620 body_metadata: std.AutoHashMap(*Module.Body, Module.BodyMetaData),
14491621
14501622 fn emit(self: *EmitZIR) !void {
14511623 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
......@@ -1545,7 +1717,7 @@ const EmitZIR = struct {
15451717 } else blk: {
15461718 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
15471719 };
1548 try new_body.inst_table.putNoClobber(inst, new_inst);
1720 _ = try new_body.inst_table.put(inst, new_inst);
15491721 return new_inst;
15501722 } else {
15511723 return new_body.inst_table.get(inst).?;
......@@ -1596,6 +1768,70 @@ const EmitZIR = struct {
15961768 return &declref_inst.base;
15971769 }
15981770
1771 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
1772 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1773 defer inst_table.deinit();
1774
1775 var instructions = std.ArrayList(*Inst).init(self.allocator);
1776 defer instructions.deinit();
1777
1778 switch (module_fn.analysis) {
1779 .queued => unreachable,
1780 .in_progress => unreachable,
1781 .success => |body| {
1782 try self.emitBody(body, &inst_table, &instructions);
1783 },
1784 .sema_failure => {
1785 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
1786 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1787 fail_inst.* = .{
1788 .base = .{
1789 .src = src,
1790 .tag = Inst.CompileError.base_tag,
1791 },
1792 .positionals = .{
1793 .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
1794 },
1795 .kw_args = .{},
1796 };
1797 try instructions.append(&fail_inst.base);
1798 },
1799 .dependency_failure => {
1800 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1801 fail_inst.* = .{
1802 .base = .{
1803 .src = src,
1804 .tag = Inst.CompileError.base_tag,
1805 },
1806 .positionals = .{
1807 .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
1808 },
1809 .kw_args = .{},
1810 };
1811 try instructions.append(&fail_inst.base);
1812 },
1813 }
1814
1815 const fn_type = try self.emitType(src, ty);
1816
1817 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1818 mem.copy(*Inst, arena_instrs, instructions.items);
1819
1820 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1821 fn_inst.* = .{
1822 .base = .{
1823 .src = src,
1824 .tag = Inst.Fn.base_tag,
1825 },
1826 .positionals = .{
1827 .fn_type = fn_type.inst,
1828 .body = .{ .instructions = arena_instrs },
1829 },
1830 .kw_args = .{},
1831 };
1832 return self.emitUnnamedDecl(&fn_inst.base);
1833 }
1834
15991835 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
16001836 const allocator = &self.arena.allocator;
16011837 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
......@@ -1659,68 +1895,7 @@ const EmitZIR = struct {
16591895 },
16601896 .Fn => {
16611897 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
1662
1663 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1664 defer inst_table.deinit();
1665
1666 var instructions = std.ArrayList(*Inst).init(self.allocator);
1667 defer instructions.deinit();
1668
1669 switch (module_fn.analysis) {
1670 .queued => unreachable,
1671 .in_progress => unreachable,
1672 .success => |body| {
1673 try self.emitBody(body, &inst_table, &instructions);
1674 },
1675 .sema_failure => {
1676 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
1677 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1678 fail_inst.* = .{
1679 .base = .{
1680 .src = src,
1681 .tag = Inst.CompileError.base_tag,
1682 },
1683 .positionals = .{
1684 .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
1685 },
1686 .kw_args = .{},
1687 };
1688 try instructions.append(&fail_inst.base);
1689 },
1690 .dependency_failure => {
1691 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1692 fail_inst.* = .{
1693 .base = .{
1694 .src = src,
1695 .tag = Inst.CompileError.base_tag,
1696 },
1697 .positionals = .{
1698 .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
1699 },
1700 .kw_args = .{},
1701 };
1702 try instructions.append(&fail_inst.base);
1703 },
1704 }
1705
1706 const fn_type = try self.emitType(src, typed_value.ty);
1707
1708 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1709 mem.copy(*Inst, arena_instrs, instructions.items);
1710
1711 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1712 fn_inst.* = .{
1713 .base = .{
1714 .src = src,
1715 .tag = Inst.Fn.base_tag,
1716 },
1717 .positionals = .{
1718 .fn_type = fn_type.inst,
1719 .body = .{ .instructions = arena_instrs },
1720 },
1721 .kw_args = .{},
1722 };
1723 return self.emitUnnamedDecl(&fn_inst.base);
1898 return self.emitFn(module_fn, src, typed_value.ty);
17241899 },
17251900 .Array => {
17261901 // TODO more checks to make sure this can be emitted as a string literal
......@@ -1751,7 +1926,7 @@ const EmitZIR = struct {
17511926 }
17521927 }
17531928
1754 fn emitNoOp(self: *EmitZIR, src: usize, tag: Inst.Tag) Allocator.Error!*Inst {
1929 fn emitNoOp(self: *EmitZIR, src: usize, old_inst: *ir.Inst.NoOp, tag: Inst.Tag) Allocator.Error!*Inst {
17551930 const new_inst = try self.arena.allocator.create(Inst.NoOp);
17561931 new_inst.* = .{
17571932 .base = .{
......@@ -1843,19 +2018,21 @@ const EmitZIR = struct {
18432018 const new_inst = switch (inst.tag) {
18442019 .constant => unreachable, // excluded from function bodies
18452020
1846 .arg => try self.emitNoOp(inst.src, .arg),
1847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
1848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
1849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
2021 .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint),
2022 .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck),
2023 .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid),
2024 .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt),
18512025
18522026 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
18532027 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
18542028 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
18552029 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
18562030 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
2031 .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr),
18572032 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
18582033 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
2034 .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
2035 .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as),
18592036
18602037 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
18612038 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
......@@ -1886,6 +2063,22 @@ const EmitZIR = struct {
18862063 break :blk &new_inst.base;
18872064 },
18882065
2066 .arg => blk: {
2067 const old_inst = inst.castTag(.arg).?;
2068 const new_inst = try self.arena.allocator.create(Inst.Arg);
2069 new_inst.* = .{
2070 .base = .{
2071 .src = inst.src,
2072 .tag = .arg,
2073 },
2074 .positionals = .{
2075 .name = try self.arena.allocator.dupe(u8, mem.spanZ(old_inst.name)),
2076 },
2077 .kw_args = .{},
2078 };
2079 break :blk &new_inst.base;
2080 },
2081
18892082 .block => blk: {
18902083 const old_inst = inst.castTag(.block).?;
18912084 const new_inst = try self.arena.allocator.create(Inst.Block);
......@@ -1911,6 +2104,31 @@ const EmitZIR = struct {
19112104 break :blk &new_inst.base;
19122105 },
19132106
2107 .loop => blk: {
2108 const old_inst = inst.castTag(.loop).?;
2109 const new_inst = try self.arena.allocator.create(Inst.Loop);
2110
2111 try self.loop_table.put(old_inst, new_inst);
2112
2113 var loop_body = std.ArrayList(*Inst).init(self.allocator);
2114 defer loop_body.deinit();
2115
2116 try self.emitBody(old_inst.body, inst_table, &loop_body);
2117
2118 new_inst.* = .{
2119 .base = .{
2120 .src = inst.src,
2121 .tag = Inst.Loop.base_tag,
2122 },
2123 .positionals = .{
2124 .body = .{ .instructions = loop_body.toOwnedSlice() },
2125 },
2126 .kw_args = .{},
2127 };
2128
2129 break :blk &new_inst.base;
2130 },
2131
19142132 .brvoid => blk: {
19152133 const old_inst = inst.cast(ir.Inst.BrVoid).?;
19162134 const new_block = self.block_table.get(old_inst.block).?;
......@@ -2019,10 +2237,24 @@ const EmitZIR = struct {
20192237 defer then_body.deinit();
20202238 defer else_body.deinit();
20212239
2240 const then_deaths = try self.arena.allocator.alloc(*Inst, old_inst.thenDeaths().len);
2241 const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len);
2242
2243 for (old_inst.thenDeaths()) |death, i| {
2244 then_deaths[i] = try self.resolveInst(new_body, death);
2245 }
2246 for (old_inst.elseDeaths()) |death, i| {
2247 else_deaths[i] = try self.resolveInst(new_body, death);
2248 }
2249
20222250 try self.emitBody(old_inst.then_body, inst_table, &then_body);
20232251 try self.emitBody(old_inst.else_body, inst_table, &else_body);
20242252
20252253 const new_inst = try self.arena.allocator.create(Inst.CondBr);
2254
2255 try self.body_metadata.put(&new_inst.positionals.then_body, .{ .deaths = then_deaths });
2256 try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths });
2257
20262258 new_inst.* = .{
20272259 .base = .{
20282260 .src = inst.src,
......@@ -2038,6 +2270,7 @@ const EmitZIR = struct {
20382270 break :blk &new_inst.base;
20392271 },
20402272 };
2273 try self.metadata.put(new_inst, .{ .deaths = inst.deaths });
20412274 try instructions.append(new_inst);
20422275 try inst_table.put(inst, new_inst);
20432276 }
......@@ -2142,6 +2375,21 @@ const EmitZIR = struct {
21422375 std.debug.panic("TODO implement emitType for {}", .{ty});
21432376 }
21442377 },
2378 .Optional => {
2379 var buf: Type.Payload.Pointer = undefined;
2380 const inst = try self.arena.allocator.create(Inst.UnOp);
2381 inst.* = .{
2382 .base = .{
2383 .src = src,
2384 .tag = .optional_type,
2385 },
2386 .positionals = .{
2387 .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst,
2388 },
2389 .kw_args = .{},
2390 };
2391 return self.emitUnnamedDecl(&inst.base);
2392 },
21452393 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
21462394 },
21472395 }
src-self-hosted/zir_sema.zig+172-25
......@@ -53,6 +53,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
5353 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
5454 .single_const_ptr_type => return analyzeInstSingleConstPtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?),
5555 .single_mut_ptr_type => return analyzeInstSingleMutPtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?),
56 .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
5657 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
5758 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
5859 .int => {
......@@ -60,14 +61,15 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
6061 return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
6162 },
6263 .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
64 .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
6365 .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
6466 .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
6567 .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
6668 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
6769 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
6870 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
69 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?),
70 .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?),
71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
72 .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
7173 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
7274 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
7375 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
......@@ -102,14 +104,29 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
102104 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
103105 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
104106 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
107 .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?, true),
105108 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
106109 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
110 .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
111 .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true),
112 .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),
113 .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true),
114 .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false),
107115 }
108116}
109117
110118pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
111 for (body.instructions) |src_inst| {
112 src_inst.analyzed_inst = try analyzeInst(mod, scope, src_inst);
119 for (body.instructions) |src_inst, i| {
120 const analyzed_inst = try analyzeInst(mod, scope, src_inst);
121 src_inst.analyzed_inst = analyzed_inst;
122 if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
123 for (body.instructions[i..]) |unreachable_inst| {
124 if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| {
125 return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{});
126 }
127 }
128 break;
129 }
113130 }
114131}
115132
......@@ -303,8 +320,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
303320
304321fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
305322 const operand = try resolveInst(mod, scope, inst.positionals.operand);
323 const ptr_type = try mod.singlePtrType(scope, inst.base.src, false, operand.ty);
324
325 if (operand.value()) |val| {
326 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
327 ref_payload.* = .{ .val = val };
328
329 return mod.constInst(scope, inst.base.src, .{
330 .ty = ptr_type,
331 .val = Value.initPayload(&ref_payload.base),
332 });
333 }
334
306335 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
307 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);
308336 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
309337}
310338
......@@ -333,7 +361,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
333361
334362fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
335363 const var_type = try resolveType(mod, scope, inst.positionals.operand);
336 const ptr_type = try mod.singleMutPtrType(scope, inst.base.src, var_type);
364 const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type);
337365 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
338366 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
339367}
......@@ -365,7 +393,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType)
365393 // TODO support C-style var args
366394 const param_count = fn_ty.fnParamLen();
367395 if (arg_index >= param_count) {
368 return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{
396 return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} argument(s)", .{
369397 arg_index,
370398 fn_ty,
371399 param_count,
......@@ -408,7 +436,7 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileE
408436 return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
409437}
410438
411fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
439fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
412440 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
413441 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
414442 const param_index = b.instructions.items.len;
......@@ -420,7 +448,42 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!
420448 });
421449 }
422450 const param_type = fn_ty.fnParamType(param_index);
423 return mod.addNoOp(b, inst.base.src, param_type, .arg);
451 const name = try scope.arena().dupeZ(u8, inst.positionals.name);
452 return mod.addArg(b, inst.base.src, param_type, name);
453}
454
455fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst {
456 const parent_block = scope.cast(Scope.Block).?;
457
458 // Reserve space for a Loop instruction so that generated Break instructions can
459 // point to it, even if it doesn't end up getting used because the code ends up being
460 // comptime evaluated.
461 const loop_inst = try parent_block.arena.create(Inst.Loop);
462 loop_inst.* = .{
463 .base = .{
464 .tag = Inst.Loop.base_tag,
465 .ty = Type.initTag(.noreturn),
466 .src = inst.base.src,
467 },
468 .body = undefined,
469 };
470
471 var child_block: Scope.Block = .{
472 .parent = parent_block,
473 .func = parent_block.func,
474 .decl = parent_block.decl,
475 .instructions = .{},
476 .arena = parent_block.arena,
477 };
478 defer child_block.instructions.deinit(mod.gpa);
479
480 try analyzeBody(mod, &child_block.base, inst.positionals.body);
481
482 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
483
484 try parent_block.instructions.append(mod.gpa, &loop_inst.base);
485 loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
486 return &loop_inst.base;
424487}
425488
426489fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
......@@ -445,7 +508,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
445508 .decl = parent_block.decl,
446509 .instructions = .{},
447510 .arena = parent_block.arena,
448 // TODO @as here is working around a miscompilation compiler bug :(
511 // TODO @as here is working around a stage1 miscompilation bug :(
449512 .label = @as(?Scope.Block.Label, Scope.Block.Label{
450513 .zir_block = inst,
451514 .results = .{},
......@@ -537,7 +600,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
537600 return mod.fail(
538601 scope,
539602 inst.positionals.func.src,
540 "expected at least {} arguments, found {}",
603 "expected at least {} argument(s), found {}",
541604 .{ fn_params_len, call_params_len },
542605 );
543606 }
......@@ -547,7 +610,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
547610 return mod.fail(
548611 scope,
549612 inst.positionals.func.src,
550 "expected {} arguments, found {}",
613 "expected {} argument(s), found {}",
551614 .{ fn_params_len, call_params_len },
552615 );
553616 }
......@@ -609,6 +672,69 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I
609672 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
610673}
611674
675fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
676 const child_type = try resolveType(mod, scope, optional.positionals.operand);
677
678 return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) {
679 .single_const_pointer => blk: {
680 const payload = try scope.arena().create(Type.Payload.Pointer);
681 payload.* = .{
682 .base = .{ .tag = .optional_single_const_pointer },
683 .pointee_type = child_type.elemType(),
684 };
685 break :blk &payload.base;
686 },
687 .single_mut_pointer => blk: {
688 const payload = try scope.arena().create(Type.Payload.Pointer);
689 payload.* = .{
690 .base = .{ .tag = .optional_single_mut_pointer },
691 .pointee_type = child_type.elemType(),
692 };
693 break :blk &payload.base;
694 },
695 else => blk: {
696 const payload = try scope.arena().create(Type.Payload.Optional);
697 payload.* = .{
698 .child_type = child_type,
699 };
700 break :blk &payload.base;
701 },
702 }));
703}
704
705fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
706 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
707 assert(operand.ty.zigTypeTag() == .Pointer);
708
709 if (operand.ty.elemType().zigTypeTag() != .Optional) {
710 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()});
711 }
712
713 const child_type = try operand.ty.elemType().optionalChildAlloc(scope.arena());
714 const child_pointer = try mod.singlePtrType(scope, unwrap.base.src, operand.ty.isConstPtr(), child_type);
715
716 if (operand.value()) |val| {
717 if (val.isNull()) {
718 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
719 }
720 return mod.constInst(scope, unwrap.base.src, .{
721 .ty = child_pointer,
722 .val = val,
723 });
724 }
725
726 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
727 if (safety_check and mod.wantSafety(scope)) {
728 const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand);
729 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
730 }
731 return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand);
732}
733
734fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
735 return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{});
736}
737
612738fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
613739 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
614740
......@@ -793,8 +919,11 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
793919 // required a larger index.
794920 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
795921
796 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
797 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
922 const type_payload = try scope.arena().create(Type.Payload.Pointer);
923 type_payload.* = .{
924 .base = .{ .tag = .single_const_pointer },
925 .pointee_type = array_ptr.ty.elemType().elemType(),
926 };
798927
799928 return mod.constInst(scope, inst.base.src, .{
800929 .ty = Type.initPayload(&type_payload.base),
......@@ -1046,6 +1175,10 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver
10461175 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
10471176}
10481177
1178fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
1179 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstIsErr", .{});
1180}
1181
10491182fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
10501183 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
10511184 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
......@@ -1083,18 +1216,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
10831216 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
10841217}
10851218
1086fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1087 return mod.analyzeUnreach(scope, unreach.base.src);
1088}
1089
1090fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1219fn analyzeInstUnreachable(
1220 mod: *Module,
1221 scope: *Scope,
1222 unreach: *zir.Inst.NoOp,
1223 safety_check: bool,
1224) InnerError!*Inst {
10911225 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
10921226 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1093 if (mod.wantSafety(scope)) {
1094 // TODO Once we have a panic function to call, call it here instead of this.
1095 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
1227 if (safety_check and mod.wantSafety(scope)) {
1228 return mod.safetyPanic(b, unreach.base.src, .unreach);
1229 } else {
1230 return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
10961231 }
1097 return mod.analyzeUnreach(scope, unreach.base.src);
10981232}
10991233
11001234fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
......@@ -1105,6 +1239,15 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
11051239
11061240fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
11071241 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1242 if (b.func) |func| {
1243 // Need to emit a compile error if returning void is not allowed.
1244 const void_inst = try mod.constVoid(scope, inst.base.src);
1245 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
1246 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
1247 if (casted_void.ty.zigTypeTag() != .Void) {
1248 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
1249 }
1250 }
11081251 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
11091252}
11101253
......@@ -1149,12 +1292,16 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr
11491292
11501293fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
11511294 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1152 const ty = try mod.singleConstPtrType(scope, inst.base.src, elem_type);
1295 const ty = try mod.singlePtrType(scope, inst.base.src, false, elem_type);
11531296 return mod.constType(scope, inst.base.src, ty);
11541297}
11551298
11561299fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
11571300 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1158 const ty = try mod.singleMutPtrType(scope, inst.base.src, elem_type);
1301 const ty = try mod.singlePtrType(scope, inst.base.src, true, elem_type);
11591302 return mod.constType(scope, inst.base.src, ty);
11601303}
1304
1305fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst {
1306 return mod.fail(scope, inst.base.src, "TODO implement ptr_type", .{});
1307}
src/all_types.hpp+3
......@@ -2438,6 +2438,7 @@ struct ScopeBlock {
24382438 LVal lval;
24392439 bool safety_off;
24402440 bool fast_math_on;
2441 bool name_used;
24412442};
24422443
24432444// This scope is created from every defer expression.
......@@ -2488,6 +2489,8 @@ struct ScopeLoop {
24882489 ZigList<IrBasicBlockSrc *> *incoming_blocks;
24892490 ResultLocPeerParent *peer_parent;
24902491 ScopeExpr *spill_scope;
2492
2493 bool name_used;
24912494};
24922495
24932496// This scope blocks certain things from working such as comptime continue
src/analyze.cpp+9-2
......@@ -7303,7 +7303,14 @@ void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {
73037303 case ZigTypeIdEnum:
73047304 {
73057305 TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);
7306 buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
7306 if(field != nullptr){
7307 buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
7308 } else {
7309 // untagged value in a non-exhaustive enum
7310 buf_appendf(buf, "%s.(", buf_ptr(&type_entry->name));
7311 bigint_append_buf(buf, &const_val->data.x_enum_tag, 10);
7312 buf_appendf(buf, ")");
7313 }
73077314 return;
73087315 }
73097316 case ZigTypeIdErrorUnion:
......@@ -8623,7 +8630,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
86238630 enum_type->llvm_type = get_llvm_type(g, tag_int_type);
86248631
86258632 // create debug type for tag
8626 uint64_t tag_debug_size_in_bits = tag_int_type->size_in_bits;
8633 uint64_t tag_debug_size_in_bits = 8*tag_int_type->abi_size;
86278634 uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
86288635 ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
86298636 ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),
src/codegen.cpp+15-22
......@@ -3481,8 +3481,9 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutableGen *exec
34813481static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToPtr *instruction) {
34823482 ZigType *wanted_type = instruction->base.value->type;
34833483 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
3484 const uint32_t align_bytes = get_ptr_align(g, wanted_type);
34843485
3485 if (ir_want_runtime_safety(g, &instruction->base)) {
3486 if (ir_want_runtime_safety(g, &instruction->base) && align_bytes > 1) {
34863487 ZigType *usize = g->builtin_types.entry_usize;
34873488 LLVMValueRef zero = LLVMConstNull(usize->llvm_type);
34883489
......@@ -3499,7 +3500,6 @@ static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable
34993500 }
35003501
35013502 {
3502 const uint32_t align_bytes = get_ptr_align(g, wanted_type);
35033503 LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false);
35043504 LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, "");
35053505 LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, "");
......@@ -5887,6 +5887,12 @@ static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutableGen *executable
58875887static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutableGen *executable,
58885888 IrInstGenReturnAddress *instruction)
58895889{
5890 if (target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) {
5891 // I got this error from LLVM 10:
5892 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address"
5893 return LLVMConstNull(get_llvm_type(g, instruction->base.value->type));
5894 }
5895
58905896 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type);
58915897 LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");
58925898 return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, "");
......@@ -7866,17 +7872,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val,
78667872 // TODO ^^ make an actual global variable
78677873}
78687874
7869static void validate_inline_fns(CodeGen *g) {
7870 for (size_t i = 0; i < g->inline_fns.length; i += 1) {
7871 ZigFn *fn_entry = g->inline_fns.at(i);
7872 LLVMValueRef fn_val = LLVMGetNamedFunction(g->module, fn_entry->llvm_name);
7873 if (fn_val != nullptr) {
7874 add_node_error(g, fn_entry->proto_node, buf_sprintf("unable to inline function"));
7875 }
7876 }
7877 report_errors_and_maybe_exit(g);
7878}
7879
78807875static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
78817876 bool is_extern = var->decl_node->data.variable_declaration.is_extern;
78827877 bool is_export = var->decl_node->data.variable_declaration.is_export;
......@@ -8354,8 +8349,6 @@ static void zig_llvm_emit_output(CodeGen *g) {
83548349 exit(1);
83558350 }
83568351
8357 validate_inline_fns(g);
8358
83598352 if (g->emit_bin) {
83608353 g->link_objects.append(&g->o_file_output_path);
83618354 if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) {
......@@ -10260,9 +10253,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
1026010253 gen_h->types_to_declare.append(type_entry);
1026110254 return;
1026210255 case ZigTypeIdStruct:
10263 for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
10264 TypeStructField *field = type_entry->data.structure.fields[i];
10265 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
10256 if(type_entry->data.structure.layout == ContainerLayoutExtern) {
10257 for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
10258 TypeStructField *field = type_entry->data.structure.fields[i];
10259 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
10260 }
1026610261 }
1026710262 gen_h->types_to_declare.append(type_entry);
1026810263 return;
......@@ -10695,21 +10690,19 @@ static void gen_h_file(CodeGen *g) {
1069510690 fprintf(out_h, "\n");
1069610691 }
1069710692
10698 fprintf(out_h, "%s", buf_ptr(&types_buf));
10699
1070010693 fprintf(out_h, "#ifdef __cplusplus\n");
1070110694 fprintf(out_h, "extern \"C\" {\n");
1070210695 fprintf(out_h, "#endif\n");
1070310696 fprintf(out_h, "\n");
1070410697
10698 fprintf(out_h, "%s", buf_ptr(&types_buf));
1070510699 fprintf(out_h, "%s\n", buf_ptr(&fns_buf));
10700 fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
1070610701
1070710702 fprintf(out_h, "#ifdef __cplusplus\n");
1070810703 fprintf(out_h, "} // extern \"C\"\n");
1070910704 fprintf(out_h, "#endif\n\n");
1071010705
10711 fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
10712
1071310706 fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));
1071410707
1071510708 if (fclose(out_h))
src/config.zig.in created+3
......@@ -0,0 +1,3 @@
1pub const version: []const u8 = "@ZIG_VERSION@";
2pub const log_scopes: []const []const u8 = &[_][]const u8{};
3pub const enable_tracy = false;
src/ir.cpp+124-27
......@@ -5477,6 +5477,25 @@ static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
54775477 return result;
54785478}
54795479
5480static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) {
5481 if (name == nullptr) return false;
5482
5483 for (;;) {
5484 if (scope == nullptr || scope->id == ScopeIdFnDef) {
5485 break;
5486 } else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) {
5487 Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name;
5488 if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) {
5489 ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name)));
5490 add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here"));
5491 return true;
5492 }
5493 }
5494 scope = scope->parent;
5495 }
5496 return false;
5497}
5498
54805499static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
54815500 ResultLoc *result_loc)
54825501{
......@@ -5485,6 +5504,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
54855504 ZigList<IrInstSrc *> incoming_values = {0};
54865505 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
54875506
5507 if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name))
5508 return irb->codegen->invalid_inst_src;
5509
54885510 ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
54895511
54905512 Scope *outer_block_scope = &scope_block->base;
......@@ -5496,6 +5518,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
54965518 }
54975519
54985520 if (block_node->data.block.statements.length == 0) {
5521 if (scope_block->name != nullptr) {
5522 add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
5523 }
54995524 // {}
55005525 return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
55015526 }
......@@ -5553,6 +5578,10 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
55535578 }
55545579 }
55555580
5581 if (scope_block->name != nullptr && scope_block->name_used == false) {
5582 add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
5583 }
5584
55565585 if (found_invalid_inst)
55575586 return irb->codegen->invalid_inst_src;
55585587
......@@ -6321,9 +6350,9 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
63216350 BuiltinFnEntry *builtin_fn = entry->value;
63226351 size_t actual_param_count = node->data.fn_call_expr.params.length;
63236352
6324 if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
6353 if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
63256354 add_node_error(irb->codegen, node,
6326 buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
6355 buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize,
63276356 builtin_fn->param_count, actual_param_count));
63286357 return irb->codegen->invalid_inst_src;
63296358 }
......@@ -8153,6 +8182,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
81538182 ZigList<IrInstSrc *> incoming_values = {0};
81548183 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
81558184
8185 if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name))
8186 return irb->codegen->invalid_inst_src;
8187
81568188 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
81578189 loop_scope->break_block = end_block;
81588190 loop_scope->continue_block = continue_block;
......@@ -8170,6 +8202,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
81708202 if (body_result == irb->codegen->invalid_inst_src)
81718203 return body_result;
81728204
8205 if (loop_scope->name != nullptr && loop_scope->name_used == false) {
8206 add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
8207 }
8208
81738209 if (!instr_is_unreachable(body_result)) {
81748210 ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
81758211 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
......@@ -8264,6 +8300,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
82648300 ZigList<IrInstSrc *> incoming_values = {0};
82658301 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
82668302
8303 if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name))
8304 return irb->codegen->invalid_inst_src;
8305
82678306 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
82688307 loop_scope->break_block = end_block;
82698308 loop_scope->continue_block = continue_block;
......@@ -8281,6 +8320,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
82818320 if (body_result == irb->codegen->invalid_inst_src)
82828321 return body_result;
82838322
8323 if (loop_scope->name != nullptr && loop_scope->name_used == false) {
8324 add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
8325 }
8326
82848327 if (!instr_is_unreachable(body_result)) {
82858328 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
82868329 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
......@@ -8354,6 +8397,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
83548397
83558398 Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
83568399
8400 if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name))
8401 return irb->codegen->invalid_inst_src;
8402
83578403 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
83588404 loop_scope->break_block = end_block;
83598405 loop_scope->continue_block = continue_block;
......@@ -8370,6 +8416,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
83708416 if (body_result == irb->codegen->invalid_inst_src)
83718417 return body_result;
83728418
8419 if (loop_scope->name != nullptr && loop_scope->name_used == false) {
8420 add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
8421 }
8422
83738423 if (!instr_is_unreachable(body_result)) {
83748424 ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
83758425 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
......@@ -8502,6 +8552,9 @@ static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNod
85028552 elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);
85038553 build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime);
85048554
8555 if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name))
8556 return irb->codegen->invalid_inst_src;
8557
85058558 ZigList<IrInstSrc *> incoming_values = {0};
85068559 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
85078560 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
......@@ -8521,6 +8574,10 @@ static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNod
85218574 if (body_result == irb->codegen->invalid_inst_src)
85228575 return irb->codegen->invalid_inst_src;
85238576
8577 if (loop_scope->name != nullptr && loop_scope->name_used == false) {
8578 add_node_error(irb->codegen, node, buf_sprintf("unused for label"));
8579 }
8580
85248581 if (!instr_is_unreachable(body_result)) {
85258582 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
85268583 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
......@@ -9465,6 +9522,7 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n
94659522 if (node->data.break_expr.name == nullptr ||
94669523 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
94679524 {
9525 this_loop_scope->name_used = true;
94689526 loop_scope = this_loop_scope;
94699527 break;
94709528 }
......@@ -9474,6 +9532,7 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n
94749532 (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
94759533 {
94769534 assert(this_block_scope->end_block != nullptr);
9535 this_block_scope->name_used = true;
94779536 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
94789537 }
94799538 } else if (search_scope->id == ScopeIdSuspend) {
......@@ -9541,6 +9600,7 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN
95419600 if (node->data.continue_expr.name == nullptr ||
95429601 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
95439602 {
9603 this_loop_scope->name_used = true;
95449604 loop_scope = this_loop_scope;
95459605 break;
95469606 }
......@@ -14037,7 +14097,8 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I
1403714097
1403814098 // If there is only one possible tag, then we know at comptime what it is.
1403914099 if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&
14040 enum_type->data.enumeration.src_field_count == 1)
14100 enum_type->data.enumeration.src_field_count == 1 &&
14101 !enum_type->data.enumeration.non_exhaustive)
1404114102 {
1404214103 IrInstGen *result = ir_const(ira, source_instr, tag_type);
1404314104 init_const_bigint(result->value, tag_type,
......@@ -14077,7 +14138,8 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
1407714138
1407814139 // If there is only 1 possible tag, then we know at comptime what it is.
1407914140 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
14080 wanted_type->data.enumeration.src_field_count == 1)
14141 wanted_type->data.enumeration.src_field_count == 1 &&
14142 !wanted_type->data.enumeration.non_exhaustive)
1408114143 {
1408214144 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
1408314145 result->value->special = ConstValSpecialStatic;
......@@ -14116,7 +14178,14 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
1411614178 if (!val)
1411714179 return ira->codegen->invalid_inst_gen;
1411814180 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
14119 assert(union_field != nullptr);
14181 if (union_field == nullptr) {
14182 Buf *int_buf = buf_alloc();
14183 bigint_append_buf(int_buf, &target->value->data.x_enum_tag, 10);
14184
14185 ir_add_error(ira, &target->base,
14186 buf_sprintf("no tag by value %s", buf_ptr(int_buf)));
14187 return ira->codegen->invalid_inst_gen;
14188 }
1412014189 ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);
1412114190 if (field_type == nullptr)
1412214191 return ira->codegen->invalid_inst_gen;
......@@ -14152,6 +14221,13 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
1415214221 return result;
1415314222 }
1415414223
14224 if (target->value->type->data.enumeration.non_exhaustive) {
14225 ir_add_error(ira, source_instr,
14226 buf_sprintf("runtime cast to union '%s' from non-exhustive enum",
14227 buf_ptr(&wanted_type->name)));
14228 return ira->codegen->invalid_inst_gen;
14229 }
14230
1415514231 // if the union has all fields 0 bits, we can do it
1415614232 // and in fact it's a noop cast because the union value is just the enum value
1415714233 if (wanted_type->data.unionation.gen_field_count == 0) {
......@@ -20127,7 +20203,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2012720203 if (fn_type_id->is_var_args) {
2012820204 if (call_param_count < src_param_count) {
2012920205 ErrorMsg *msg = ir_add_error_node(ira, source_node,
20130 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count));
20206 buf_sprintf("expected at least %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "",
20207 src_param_count, call_param_count));
2013120208 if (fn_proto_node) {
2013220209 add_error_note(ira->codegen, msg, fn_proto_node,
2013320210 buf_sprintf("declared here"));
......@@ -20136,7 +20213,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2013620213 }
2013720214 } else if (src_param_count != call_param_count) {
2013820215 ErrorMsg *msg = ir_add_error_node(ira, source_node,
20139 buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count));
20216 buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "",
20217 src_param_count, call_param_count));
2014020218 if (fn_proto_node) {
2014120219 add_error_note(ira->codegen, msg, fn_proto_node,
2014220220 buf_sprintf("declared here"));
......@@ -23755,7 +23833,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
2375523833 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
2375623834 return result;
2375723835 }
23758 if (tag_type->data.enumeration.src_field_count == 1) {
23836 if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {
2375923837 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
2376023838 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
2376123839 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
......@@ -23770,7 +23848,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
2377023848 case ZigTypeIdEnum: {
2377123849 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
2377223850 return ira->codegen->invalid_inst_gen;
23773 if (target_type->data.enumeration.src_field_count == 1) {
23851 if (target_type->data.enumeration.src_field_count == 1 && !target_type->data.enumeration.non_exhaustive) {
2377423852 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
2377523853 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);
2377623854 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
......@@ -25068,12 +25146,12 @@ static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) {
2506825146 zig_unreachable();
2506925147}
2507025148
25071static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
25072 Error err;
25149static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, ZigType *ptr_type_entry) {
2507325150 ZigType *attrs_type;
2507425151 BuiltinPtrSize size_enum_index;
2507525152 if (is_slice(ptr_type_entry)) {
25076 attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index]->type_entry;
25153 TypeStructField *ptr_field = ptr_type_entry->data.structure.fields[slice_ptr_index];
25154 attrs_type = resolve_struct_field_type(ira->codegen, ptr_field);
2507725155 size_enum_index = BuiltinPtrSizeSlice;
2507825156 } else if (ptr_type_entry->id == ZigTypeIdPointer) {
2507925157 attrs_type = ptr_type_entry;
......@@ -25082,9 +25160,6 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
2508225160 zig_unreachable();
2508325161 }
2508425162
25085 if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusSizeKnown)))
25086 return nullptr;
25087
2508825163 ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
2508925164 assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown));
2509025165
......@@ -25115,9 +25190,18 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
2511525190 fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile;
2511625191 // alignment: u32
2511725192 ensure_field_index(result->type, "alignment", 3);
25118 fields[3]->special = ConstValSpecialStatic;
2511925193 fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int;
25120 bigint_init_unsigned(&fields[3]->data.x_bigint, get_ptr_align(ira->codegen, attrs_type));
25194 if (attrs_type->data.pointer.explicit_alignment != 0) {
25195 fields[3]->special = ConstValSpecialStatic;
25196 bigint_init_unsigned(&fields[3]->data.x_bigint, attrs_type->data.pointer.explicit_alignment);
25197 } else {
25198 LazyValueAlignOf *lazy_align_of = heap::c_allocator.create<LazyValueAlignOf>();
25199 lazy_align_of->ira = ira; ira_ref(ira);
25200 fields[3]->special = ConstValSpecialLazy;
25201 fields[3]->data.x_lazy = &lazy_align_of->base;
25202 lazy_align_of->base.id = LazyValueIdAlignOf;
25203 lazy_align_of->target_type = ir_const_type(ira, source_instr, attrs_type->data.pointer.child_type);
25204 }
2512125205 // child: type
2512225206 ensure_field_index(result->type, "child", 4);
2512325207 fields[4]->special = ConstValSpecialStatic;
......@@ -25131,7 +25215,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
2513125215 // sentinel: anytype
2513225216 ensure_field_index(result->type, "sentinel", 6);
2513325217 fields[6]->special = ConstValSpecialStatic;
25134 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
25218 if (attrs_type->data.pointer.sentinel != nullptr) {
2513525219 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
2513625220 set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
2513725221 } else {
......@@ -25166,9 +25250,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2516625250 assert(type_entry != nullptr);
2516725251 assert(!type_is_invalid(type_entry));
2516825252
25169 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25170 return err;
25171
2517225253 auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
2517325254 if (entry != nullptr) {
2517425255 *out = entry->value;
......@@ -25232,7 +25313,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2523225313 }
2523325314 case ZigTypeIdPointer:
2523425315 {
25235 result = create_ptr_like_type_info(ira, type_entry);
25316 result = create_ptr_like_type_info(ira, source_instr, type_entry);
2523625317 if (result == nullptr)
2523725318 return ErrorSemanticAnalyzeFail;
2523825319 break;
......@@ -25318,6 +25399,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2531825399 }
2531925400 case ZigTypeIdEnum:
2532025401 {
25402 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25403 return err;
25404
2532125405 result = ira->codegen->pass1_arena->create<ZigValue>();
2532225406 result->special = ConstValSpecialStatic;
2532325407 result->type = ir_type_info_get_type(ira, "Enum", nullptr);
......@@ -25456,6 +25540,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2545625540 }
2545725541 case ZigTypeIdUnion:
2545825542 {
25543 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25544 return err;
25545
2545925546 result = ira->codegen->pass1_arena->create<ZigValue>();
2546025547 result->special = ConstValSpecialStatic;
2546125548 result->type = ir_type_info_get_type(ira, "Union", nullptr);
......@@ -25546,12 +25633,15 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2554625633 case ZigTypeIdStruct:
2554725634 {
2554825635 if (type_entry->data.structure.special == StructSpecialSlice) {
25549 result = create_ptr_like_type_info(ira, type_entry);
25636 result = create_ptr_like_type_info(ira, source_instr, type_entry);
2555025637 if (result == nullptr)
2555125638 return ErrorSemanticAnalyzeFail;
2555225639 break;
2555325640 }
2555425641
25642 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25643 return err;
25644
2555525645 result = ira->codegen->pass1_arena->create<ZigValue>();
2555625646 result->special = ConstValSpecialStatic;
2555725647 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
......@@ -28765,6 +28855,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2876528855 if (type_is_invalid(switch_type))
2876628856 return ira->codegen->invalid_inst_gen;
2876728857
28858 ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value;
28859 bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer &&
28860 original_value->type->data.pointer.child_type->id == ZigTypeIdUnion;
28861
2876828862 if (switch_type->id == ZigTypeIdEnum) {
2876928863 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
2877028864 field_prev_uses.init(switch_type->data.enumeration.src_field_count);
......@@ -28820,9 +28914,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2882028914 }
2882128915 }
2882228916 if (instruction->have_underscore_prong) {
28823 if (!switch_type->data.enumeration.non_exhaustive){
28917 if (!switch_type->data.enumeration.non_exhaustive) {
28918 ir_add_error(ira, &instruction->base.base,
28919 buf_sprintf("switch on exhaustive enum has `_` prong"));
28920 } else if (target_is_originally_union) {
2882428921 ir_add_error(ira, &instruction->base.base,
28825 buf_sprintf("switch on non-exhaustive enum has `_` prong"));
28922 buf_sprintf("`_` prong not allowed when switching on tagged union"));
2882628923 }
2882728924 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
2882828925 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
......@@ -28837,7 +28934,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2883728934 }
2883828935 }
2883928936 } else if (instruction->else_prong == nullptr) {
28840 if (switch_type->data.enumeration.non_exhaustive) {
28937 if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) {
2884128938 ir_add_error(ira, &instruction->base.base,
2884228939 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
2884328940 }
......@@ -30056,7 +30153,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3005630153 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
3005730154 }
3005830155 ir_add_error(ira, &arg_index_inst->base,
30059 buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
30156 buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " argument(s)",
3006030157 arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));
3006130158 return ira->codegen->invalid_inst_gen;
3006230159 }
src/main.cpp+3
......@@ -38,6 +38,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
3838 " builtin show the source code of @import(\"builtin\")\n"
3939 " cc use Zig as a drop-in C compiler\n"
4040 " c++ use Zig as a drop-in C++ compiler\n"
41 " env print lib path, std path, compiler id and version\n"
4142 " fmt parse files and render in canonical zig format\n"
4243 " id print the base64-encoded compiler id\n"
4344 " init-exe initialize a `zig build` application in the cwd\n"
......@@ -582,6 +583,8 @@ static int main0(int argc, char **argv) {
582583 return (term.how == TerminationIdClean) ? term.code : -1;
583584 } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
584585 return stage2_fmt(argc, argv);
586 } else if (argc >= 2 && strcmp(argv[1], "env") == 0) {
587 return stage2_env(argc, argv);
585588 } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) {
586589 emit_h = false;
587590 strip = true;
src/stage2.cpp+5
......@@ -27,6 +27,11 @@ void stage2_zen(const char **ptr, size_t *len) {
2727 stage2_panic(msg, strlen(msg));
2828}
2929
30int stage2_env(int argc, char** argv) {
31 const char *msg = "stage0 called stage2_env";
32 stage2_panic(msg, strlen(msg));
33}
34
3035void stage2_attach_segfault_handler(void) { }
3136
3237void stage2_panic(const char *ptr, size_t len) {
src/stage2.h+3
......@@ -141,6 +141,9 @@ ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
141141// ABI warning
142142ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
143143
144// ABI warning
145ZIG_EXTERN_C int stage2_env(int argc, char **argv);
146
144147// ABI warning
145148ZIG_EXTERN_C void stage2_attach_segfault_handler(void);
146149
src/tokenizer.cpp+1
......@@ -17,6 +17,7 @@
1717
1818#define WHITESPACE \
1919 ' ': \
20 case '\r': \
2021 case '\n'
2122
2223#define DIGIT_NON_ZERO \
src/zig_clang.cpp+13
......@@ -2619,6 +2619,19 @@ struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct Zi
26192619 return bitcast(casted->getBeginLoc());
26202620}
26212621
2622bool ZigClangIntegerLiteral_isZero(const struct ZigClangIntegerLiteral *self, bool *result, const struct ZigClangASTContext *ctx) {
2623 auto casted_self = reinterpret_cast<const clang::IntegerLiteral *>(self);
2624 auto casted_ctx = reinterpret_cast<const clang::ASTContext *>(ctx);
2625 clang::Expr::EvalResult eval_result;
2626 if (!casted_self->EvaluateAsInt(eval_result, *casted_ctx)) {
2627 return false;
2628 }
2629 const llvm::APSInt result_int = eval_result.Val.getInt();
2630 const llvm::APSInt zero(result_int.getBitWidth(), result_int.isUnsigned());
2631 *result = zero == result_int;
2632 return true;
2633}
2634
26222635const struct ZigClangExpr *ZigClangReturnStmt_getRetValue(const struct ZigClangReturnStmt *self) {
26232636 auto casted = reinterpret_cast<const clang::ReturnStmt *>(self);
26242637 return reinterpret_cast<const struct ZigClangExpr *>(casted->getRetValue());
src/zig_clang.h+1
......@@ -1142,6 +1142,7 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangCStyleCastExpr_getType(const struct
11421142
11431143ZIG_EXTERN_C bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *, struct ZigClangExprEvalResult *, const struct ZigClangASTContext *);
11441144ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct ZigClangIntegerLiteral *);
1145ZIG_EXTERN_C bool ZigClangIntegerLiteral_isZero(const struct ZigClangIntegerLiteral *, bool *, const struct ZigClangASTContext *);
11451146
11461147ZIG_EXTERN_C const struct ZigClangExpr *ZigClangReturnStmt_getRetValue(const struct ZigClangReturnStmt *);
11471148
test/compile_errors.zig+107-38
......@@ -2,6 +2,29 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("duplicate/unused labels",
6 \\comptime {
7 \\ blk: { blk: while (false) {} }
8 \\ blk: while (false) { blk: for (@as([0]void, undefined)) |_| {} }
9 \\ blk: for (@as([0]void, undefined)) |_| { blk: {} }
10 \\}
11 \\comptime {
12 \\ blk: {}
13 \\ blk: while(false) {}
14 \\ blk: for(@as([0]void, undefined)) |_| {}
15 \\}
16 , &[_][]const u8{
17 "tmp.zig:2:17: error: redeclaration of label 'blk'",
18 "tmp.zig:2:10: note: previous declaration is here",
19 "tmp.zig:3:31: error: redeclaration of label 'blk'",
20 "tmp.zig:3:10: note: previous declaration is here",
21 "tmp.zig:4:51: error: redeclaration of label 'blk'",
22 "tmp.zig:4:10: note: previous declaration is here",
23 "tmp.zig:7:10: error: unused block label",
24 "tmp.zig:8:10: error: unused while label",
25 "tmp.zig:9:10: error: unused for label",
26 });
27
528 cases.addTest("@alignCast of zero sized types",
629 \\export fn foo() void {
730 \\ const a: *void = undefined;
......@@ -28,6 +51,46 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2851 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
2952 });
3053
54 cases.addTest("invalid non-exhaustive enum to union",
55 \\const E = enum(u8) {
56 \\ a,
57 \\ b,
58 \\ _,
59 \\};
60 \\const U = union(E) {
61 \\ a,
62 \\ b,
63 \\};
64 \\export fn foo() void {
65 \\ var e = @intToEnum(E, 15);
66 \\ var u: U = e;
67 \\}
68 \\export fn bar() void {
69 \\ const e = @intToEnum(E, 15);
70 \\ var u: U = e;
71 \\}
72 , &[_][]const u8{
73 "tmp.zig:12:16: error: runtime cast to union 'U' from non-exhustive enum",
74 "tmp.zig:16:16: error: no tag by value 15",
75 });
76
77 cases.addTest("switching with exhaustive enum has '_' prong ",
78 \\const E = enum{
79 \\ a,
80 \\ b,
81 \\};
82 \\pub export fn entry() void {
83 \\ var e: E = .b;
84 \\ switch (e) {
85 \\ .a => {},
86 \\ .b => {},
87 \\ _ => {},
88 \\ }
89 \\}
90 , &[_][]const u8{
91 "tmp.zig:7:5: error: switch on exhaustive enum has `_` prong",
92 });
93
3194 cases.addTest("invalid pointer with @Type",
3295 \\export fn entry() void {
3396 \\ _ = @Type(.{ .Pointer = .{
......@@ -541,6 +604,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
541604 \\ b,
542605 \\ _,
543606 \\};
607 \\const U = union(E) {
608 \\ a: i32,
609 \\ b: u32,
610 \\};
544611 \\pub export fn entry() void {
545612 \\ var e: E = .b;
546613 \\ switch (e) { // error: switch not handling the tag `b`
......@@ -551,10 +618,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
551618 \\ .a => {},
552619 \\ .b => {},
553620 \\ }
621 \\ var u = U{.a = 2};
622 \\ switch (u) { // error: `_` prong not allowed when switching on tagged union
623 \\ .a => {},
624 \\ .b => {},
625 \\ _ => {},
626 \\ }
554627 \\}
555628 , &[_][]const u8{
556 "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",
557 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
629 "tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch",
630 "tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
631 "tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union",
558632 });
559633
560634 cases.add("switch expression - unreachable else prong (bool)",
......@@ -682,7 +756,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
682756 \\ for (arr) |bits| _ = @popCount(bits);
683757 \\}
684758 , &[_][]const u8{
685 "tmp.zig:3:26: error: expected 2 arguments, found 1",
759 "tmp.zig:3:26: error: expected 2 argument(s), found 1",
686760 });
687761
688762 cases.addTest("@call rejects non comptime-known fn - always_inline",
......@@ -4080,7 +4154,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
40804154 \\}
40814155 \\fn b(a: i32, b: i32, c: i32) void { }
40824156 , &[_][]const u8{
4083 "tmp.zig:2:6: error: expected 3 arguments, found 1",
4157 "tmp.zig:2:6: error: expected 3 argument(s), found 1",
40844158 });
40854159
40864160 cases.add("invalid type",
......@@ -4693,7 +4767,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
46934767 \\
46944768 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }
46954769 , &[_][]const u8{
4696 "tmp.zig:20:34: error: expected 1 arguments, found 0",
4770 "tmp.zig:20:34: error: expected 1 argument(s), found 0",
46974771 });
46984772
46994773 cases.add("missing function name",
......@@ -5475,7 +5549,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
54755549 \\}
54765550 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }
54775551 , &[_][]const u8{
5478 "tmp.zig:6:15: error: expected 2 arguments, found 3",
5552 "tmp.zig:6:15: error: expected 2 argument(s), found 3",
54795553 });
54805554
54815555 cases.add("assign through constant pointer",
......@@ -6128,32 +6202,33 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
61286202 "tmp.zig:2:15: error: expected error union type, found '?i32'",
61296203 });
61306204
6131 cases.add("inline fn calls itself indirectly",
6132 \\export fn foo() void {
6133 \\ bar();
6134 \\}
6135 \\inline fn bar() void {
6136 \\ baz();
6137 \\ quux();
6138 \\}
6139 \\inline fn baz() void {
6140 \\ bar();
6141 \\ quux();
6142 \\}
6143 \\extern fn quux() void;
6144 , &[_][]const u8{
6145 "tmp.zig:4:1: error: unable to inline function",
6146 });
6147
6148 cases.add("save reference to inline function",
6149 \\export fn foo() void {
6150 \\ quux(@ptrToInt(bar));
6151 \\}
6152 \\inline fn bar() void { }
6153 \\extern fn quux(usize) void;
6154 , &[_][]const u8{
6155 "tmp.zig:4:1: error: unable to inline function",
6156 });
6205 // TODO test this in stage2, but we won't even try in stage1
6206 //cases.add("inline fn calls itself indirectly",
6207 // \\export fn foo() void {
6208 // \\ bar();
6209 // \\}
6210 // \\inline fn bar() void {
6211 // \\ baz();
6212 // \\ quux();
6213 // \\}
6214 // \\inline fn baz() void {
6215 // \\ bar();
6216 // \\ quux();
6217 // \\}
6218 // \\extern fn quux() void;
6219 //, &[_][]const u8{
6220 // "tmp.zig:4:1: error: unable to inline function",
6221 //});
6222
6223 //cases.add("save reference to inline function",
6224 // \\export fn foo() void {
6225 // \\ quux(@ptrToInt(bar));
6226 // \\}
6227 // \\inline fn bar() void { }
6228 // \\extern fn quux(usize) void;
6229 //, &[_][]const u8{
6230 // "tmp.zig:4:1: error: unable to inline function",
6231 //});
61576232
61586233 cases.add("signed integer division",
61596234 \\export fn foo(a: i32, b: i32) i32 {
......@@ -6641,12 +6716,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
66416716 "tmp.zig:9:13: error: type '*MyType' does not support field access",
66426717 });
66436718
6644 cases.add("carriage return special case", "fn test() bool {\r\n" ++
6645 " true\r\n" ++
6646 "}\r\n", &[_][]const u8{
6647 "tmp.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported",
6648 });
6649
66506719 cases.add("invalid legacy unicode escape",
66516720 \\export fn entry() void {
66526721 \\ const a = '\U1234';
test/run_translated_c.zig-1
......@@ -15,7 +15,6 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1515 \\ }
1616 \\ if (s0 != 1) abort();
1717 \\ if (s1 != 10) abort();
18 \\ return 0;
1918 \\}
2019 , "");
2120
test/stage1/behavior/enum.zig+37
......@@ -85,6 +85,43 @@ test "empty non-exhaustive enum" {
8585 comptime S.doTheTest(42);
8686}
8787
88test "single field non-exhaustive enum" {
89 const S = struct {
90 const E = enum(u8) {
91 a,
92 _,
93 };
94 fn doTheTest(y: u8) void {
95 var e: E = .a;
96 expect(switch (e) {
97 .a => true,
98 _ => false,
99 });
100 e = @intToEnum(E, 12);
101 expect(switch (e) {
102 .a => false,
103 _ => true,
104 });
105
106 expect(switch (e) {
107 .a => false,
108 else => true,
109 });
110 e = .a;
111 expect(switch (e) {
112 .a => true,
113 else => false,
114 });
115
116 expect(@enumToInt(@intToEnum(E, y)) == y);
117 expect(@typeInfo(E).Enum.fields.len == 1);
118 expect(@typeInfo(E).Enum.is_exhaustive == false);
119 }
120 };
121 S.doTheTest(23);
122 comptime S.doTheTest(23);
123}
124
88125test "enum type" {
89126 const foo1 = Foo{ .One = 13 };
90127 const foo2 = Foo{
test/stage1/behavior/union.zig+23
......@@ -690,3 +690,26 @@ test "method call on an empty union" {
690690 S.doTheTest();
691691 comptime S.doTheTest();
692692}
693
694test "switching on non exhaustive union" {
695 const S = struct {
696 const E = enum(u8) {
697 a,
698 b,
699 _,
700 };
701 const U = union(E) {
702 a: i32,
703 b: u32,
704 };
705 fn doTheTest() void {
706 var a = U{ .a = 2 };
707 switch (a) {
708 .a => |val| expect(val == 2),
709 .b => unreachable,
710 }
711 }
712 };
713 S.doTheTest();
714 comptime S.doTheTest();
715}
test/stage2/cbe.zig+92-11
......@@ -10,36 +10,45 @@ const linux_x64 = std.zig.CrossTarget{
1010
1111pub fn addCases(ctx: *TestContext) !void {
1212 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {}
13 \\export fn _start() noreturn {
14 \\ unreachable;
15 \\}
1416 ,
15 \\noreturn void _start(void) {}
17 \\zig_noreturn void _start(void) {
18 \\ zig_unreachable();
19 \\}
1620 \\
1721 );
1822 ctx.c("less empty start function", linux_x64,
19 \\fn main() noreturn {}
23 \\fn main() noreturn {
24 \\ unreachable;
25 \\}
2026 \\
2127 \\export fn _start() noreturn {
2228 \\ main();
2329 \\}
2430 ,
25 \\noreturn void main(void);
31 \\zig_noreturn void main(void);
2632 \\
27 \\noreturn void _start(void) {
33 \\zig_noreturn void _start(void) {
2834 \\ main();
2935 \\}
3036 \\
31 \\noreturn void main(void) {}
37 \\zig_noreturn void main(void) {
38 \\ zig_unreachable();
39 \\}
3240 \\
3341 );
3442 // TODO: implement return values
3543 // TODO: figure out a way to prevent asm constants from being generated
3644 ctx.c("inline asm", linux_x64,
37 \\fn exitGood() void {
45 \\fn exitGood() noreturn {
3846 \\ asm volatile ("syscall"
3947 \\ :
4048 \\ : [number] "{rax}" (231),
4149 \\ [arg1] "{rdi}" (0)
4250 \\ );
51 \\ unreachable;
4352 \\}
4453 \\
4554 \\export fn _start() noreturn {
......@@ -48,21 +57,93 @@ pub fn addCases(ctx: *TestContext) !void {
4857 ,
4958 \\#include <stddef.h>
5059 \\
51 \\void exitGood(void);
60 \\zig_noreturn void exitGood(void);
5261 \\
5362 \\const char *const exitGood__anon_0 = "{rax}";
5463 \\const char *const exitGood__anon_1 = "{rdi}";
5564 \\const char *const exitGood__anon_2 = "syscall";
5665 \\
57 \\noreturn void _start(void) {
66 \\zig_noreturn void _start(void) {
5867 \\ exitGood();
5968 \\}
6069 \\
61 \\void exitGood(void) {
70 \\zig_noreturn void exitGood(void) {
6271 \\ register size_t rax_constant __asm__("rax") = 231;
6372 \\ register size_t rdi_constant __asm__("rdi") = 0;
6473 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
65 \\ return;
74 \\ zig_unreachable();
75 \\}
76 \\
77 );
78 ctx.c("exit with parameter", linux_x64,
79 \\export fn _start() noreturn {
80 \\ exit(0);
81 \\}
82 \\
83 \\fn exit(code: usize) noreturn {
84 \\ asm volatile ("syscall"
85 \\ :
86 \\ : [number] "{rax}" (231),
87 \\ [arg1] "{rdi}" (code)
88 \\ );
89 \\ unreachable;
90 \\}
91 \\
92 ,
93 \\#include <stddef.h>
94 \\
95 \\zig_noreturn void exit(size_t arg0);
96 \\
97 \\const char *const exit__anon_0 = "{rax}";
98 \\const char *const exit__anon_1 = "{rdi}";
99 \\const char *const exit__anon_2 = "syscall";
100 \\
101 \\zig_noreturn void _start(void) {
102 \\ exit(0);
103 \\}
104 \\
105 \\zig_noreturn void exit(size_t arg0) {
106 \\ register size_t rax_constant __asm__("rax") = 231;
107 \\ register size_t rdi_constant __asm__("rdi") = arg0;
108 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
109 \\ zig_unreachable();
110 \\}
111 \\
112 );
113 ctx.c("exit with u8 parameter", linux_x64,
114 \\export fn _start() noreturn {
115 \\ exit(0);
116 \\}
117 \\
118 \\fn exit(code: u8) noreturn {
119 \\ asm volatile ("syscall"
120 \\ :
121 \\ : [number] "{rax}" (231),
122 \\ [arg1] "{rdi}" (code)
123 \\ );
124 \\ unreachable;
125 \\}
126 \\
127 ,
128 \\#include <stddef.h>
129 \\#include <stdint.h>
130 \\
131 \\zig_noreturn void exit(uint8_t arg0);
132 \\
133 \\const char *const exit__anon_0 = "{rax}";
134 \\const char *const exit__anon_1 = "{rdi}";
135 \\const char *const exit__anon_2 = "syscall";
136 \\
137 \\zig_noreturn void _start(void) {
138 \\ exit(0);
139 \\}
140 \\
141 \\zig_noreturn void exit(uint8_t arg0) {
142 \\ const size_t __temp_0 = (size_t)arg0;
143 \\ register size_t rax_constant __asm__("rax") = 231;
144 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;
145 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
146 \\ zig_unreachable();
66147 \\}
67148 \\
68149 );
test/stage2/compare_output.zig+145-9
......@@ -12,17 +12,22 @@ const linux_riscv64 = std.zig.CrossTarget{
1212 .os_tag = .linux,
1313};
1414
15pub fn addCases(ctx: *TestContext) !void {
16 if (std.Target.current.os.tag != .linux or
17 std.Target.current.cpu.arch != .x86_64)
18 {
19 // TODO implement self-hosted PE (.exe file) linking
20 // TODO implement more ZIR so we don't depend on x86_64-linux
21 return;
22 }
15const wasi = std.zig.CrossTarget{
16 .cpu_arch = .wasm32,
17 .os_tag = .wasi,
18};
2319
20pub fn addCases(ctx: *TestContext) !void {
2421 {
2522 var case = ctx.exe("hello world with updates", linux_x64);
23
24 case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
25
26 case.addError(
27 \\export fn _start() noreturn {
28 \\}
29 , &[_][]const u8{":2:1: error: expected noreturn, found void"});
30
2631 // Regular old hello world
2732 case.addCompareOutput(
2833 \\export fn _start() noreturn {
......@@ -123,7 +128,7 @@ pub fn addCases(ctx: *TestContext) !void {
123128 \\
124129 );
125130 }
126
131
127132 {
128133 var case = ctx.exe("hello world", linux_riscv64);
129134 // Regular old hello world
......@@ -438,5 +443,136 @@ pub fn addCases(ctx: *TestContext) !void {
438443 ,
439444 "",
440445 );
446
447 // Optionals
448 case.addCompareOutput(
449 \\export fn _start() noreturn {
450 \\ const a: u32 = 2;
451 \\ const b: ?u32 = a;
452 \\ const c = b.?;
453 \\ if (c != 2) unreachable;
454 \\
455 \\ exit();
456 \\}
457 \\
458 \\fn exit() noreturn {
459 \\ asm volatile ("syscall"
460 \\ :
461 \\ : [number] "{rax}" (231),
462 \\ [arg1] "{rdi}" (0)
463 \\ : "rcx", "r11", "memory"
464 \\ );
465 \\ unreachable;
466 \\}
467 ,
468 "",
469 );
470
471 // While loops
472 case.addCompareOutput(
473 \\export fn _start() noreturn {
474 \\ var i: u32 = 0;
475 \\ while (i < 4) : (i += 1) print();
476 \\ assert(i == 4);
477 \\
478 \\ exit();
479 \\}
480 \\
481 \\fn print() void {
482 \\ asm volatile ("syscall"
483 \\ :
484 \\ : [number] "{rax}" (1),
485 \\ [arg1] "{rdi}" (1),
486 \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
487 \\ [arg3] "{rdx}" (6)
488 \\ : "rcx", "r11", "memory"
489 \\ );
490 \\ return;
491 \\}
492 \\
493 \\pub fn assert(ok: bool) void {
494 \\ if (!ok) unreachable; // assertion failure
495 \\}
496 \\
497 \\fn exit() noreturn {
498 \\ asm volatile ("syscall"
499 \\ :
500 \\ : [number] "{rax}" (231),
501 \\ [arg1] "{rdi}" (0)
502 \\ : "rcx", "r11", "memory"
503 \\ );
504 \\ unreachable;
505 \\}
506 ,
507 "hello\nhello\nhello\nhello\n",
508 );
509
510 // Labeled blocks (no conditional branch)
511 case.addCompareOutput(
512 \\export fn _start() noreturn {
513 \\ assert(add(3, 4) == 20);
514 \\
515 \\ exit();
516 \\}
517 \\
518 \\fn add(a: u32, b: u32) u32 {
519 \\ const x: u32 = blk: {
520 \\ const c = a + b; // 7
521 \\ const d = a + c; // 10
522 \\ const e = d + b; // 14
523 \\ break :blk e;
524 \\ };
525 \\ const y = x + a; // 17
526 \\ const z = y + a; // 20
527 \\ return z;
528 \\}
529 \\
530 \\pub fn assert(ok: bool) void {
531 \\ if (!ok) unreachable; // assertion failure
532 \\}
533 \\
534 \\fn exit() noreturn {
535 \\ asm volatile ("syscall"
536 \\ :
537 \\ : [number] "{rax}" (231),
538 \\ [arg1] "{rdi}" (0)
539 \\ : "rcx", "r11", "memory"
540 \\ );
541 \\ unreachable;
542 \\}
543 ,
544 "",
545 );
546 }
547
548 {
549 var case = ctx.exe("wasm returns", wasi);
550
551 case.addCompareOutput(
552 \\export fn _start() u32 {
553 \\ return 42;
554 \\}
555 ,
556 "42\n",
557 );
558
559 case.addCompareOutput(
560 \\export fn _start() i64 {
561 \\ return 42;
562 \\}
563 ,
564 "42\n",
565 );
566
567 case.addCompareOutput(
568 \\export fn _start() f32 {
569 \\ return 42.0;
570 \\}
571 ,
572 // This is what you get when you take the bits of the IEE-754
573 // representation of 42.0 and reinterpret them as an unsigned
574 // integer. Guess that's a bug in wasmtime.
575 "1109917696\n",
576 );
441577 }
442578}
test/stage2/zir.zig+9-9
......@@ -28,7 +28,7 @@ pub fn addCases(ctx: *TestContext) !void {
2828 \\@unnamed$5 = export(@unnamed$4, "entry")
2929 \\@unnamed$6 = fntype([], @void, cc=C)
3030 \\@entry = fn(@unnamed$6, {
31 \\ %0 = returnvoid()
31 \\ %0 = returnvoid() ; deaths=0b1000000000000000
3232 \\})
3333 \\
3434 );
......@@ -75,7 +75,7 @@ pub fn addCases(ctx: *TestContext) !void {
7575 \\@3 = int(3)
7676 \\@unnamed$6 = fntype([], @void, cc=C)
7777 \\@entry = fn(@unnamed$6, {
78 \\ %0 = returnvoid()
78 \\ %0 = returnvoid() ; deaths=0b1000000000000000
7979 \\})
8080 \\@entry__anon_1 = str("2\x08\x01\n")
8181 \\@9 = declref("9__anon_0")
......@@ -117,18 +117,18 @@ pub fn addCases(ctx: *TestContext) !void {
117117 \\@unnamed$5 = export(@unnamed$4, "entry")
118118 \\@unnamed$6 = fntype([], @void, cc=C)
119119 \\@entry = fn(@unnamed$6, {
120 \\ %0 = call(@a, [], modifier=auto)
121 \\ %1 = returnvoid()
120 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
121 \\ %1 = returnvoid() ; deaths=0b1000000000000000
122122 \\})
123123 \\@unnamed$8 = fntype([], @void, cc=C)
124124 \\@a = fn(@unnamed$8, {
125 \\ %0 = call(@b, [], modifier=auto)
126 \\ %1 = returnvoid()
125 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
126 \\ %1 = returnvoid() ; deaths=0b1000000000000000
127127 \\})
128128 \\@unnamed$10 = fntype([], @void, cc=C)
129129 \\@b = fn(@unnamed$10, {
130 \\ %0 = call(@a, [], modifier=auto)
131 \\ %1 = returnvoid()
130 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
131 \\ %1 = returnvoid() ; deaths=0b1000000000000000
132132 \\})
133133 \\
134134 );
......@@ -193,7 +193,7 @@ pub fn addCases(ctx: *TestContext) !void {
193193 \\@unnamed$5 = export(@unnamed$4, "entry")
194194 \\@unnamed$6 = fntype([], @void, cc=C)
195195 \\@entry = fn(@unnamed$6, {
196 \\ %0 = returnvoid()
196 \\ %0 = returnvoid() ; deaths=0b1000000000000000
197197 \\})
198198 \\
199199 );
test/translate_c.zig+79-50
......@@ -3,12 +3,33 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("missing return stmt",
7 \\int foo() {}
8 \\int bar() {
9 \\ int a = 2;
10 \\}
11 \\int baz() {
12 \\ return 0;
13 \\}
14 , &[_][]const u8{
15 \\pub export fn foo() c_int {
16 \\ return 0;
17 \\}
18 \\pub export fn bar() c_int {
19 \\ var a: c_int = 2;
20 \\ return 0;
21 \\}
22 \\pub export fn baz() c_int {
23 \\ return 0;
24 \\}
25 });
26
627 cases.add("alignof",
7 \\int main() {
28 \\void main() {
829 \\ int a = _Alignof(int);
930 \\}
1031 , &[_][]const u8{
11 \\pub export fn main() c_int {
32 \\pub export fn main() void {
1233 \\ var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));
1334 \\}
1435 });
......@@ -99,10 +120,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
99120 \\}
100121 , &[_][]const u8{
101122 \\pub export fn foo() void {
102 \\ while (@as(c_int, 0) != 0) while (@as(c_int, 0) != 0) {};
103 \\ while (true) while (@as(c_int, 0) != 0) {};
123 \\ while (false) while (false) {};
124 \\ while (true) while (false) {};
104125 \\ while (true) while (true) {
105 \\ if (!(@as(c_int, 0) != 0)) break;
126 \\ if (!false) break;
106127 \\ };
107128 \\}
108129 });
......@@ -539,6 +560,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
539560 \\ c = (a * b);
540561 \\ c = @divTrunc(a, b);
541562 \\ c = @rem(a, b);
563 \\ return 0;
542564 \\}
543565 \\pub export fn u() c_uint {
544566 \\ var a: c_uint = undefined;
......@@ -549,6 +571,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
549571 \\ c = (a *% b);
550572 \\ c = (a / b);
551573 \\ c = (a % b);
574 \\ return 0;
552575 \\}
553576 });
554577
......@@ -1260,11 +1283,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
12601283 \\void __attribute__((cdecl)) foo4(float *a);
12611284 \\void __attribute__((thiscall)) foo5(float *a);
12621285 , &[_][]const u8{
1263 \\pub fn foo1(a: [*c]f32) callconv(.Fastcall) void;
1264 \\pub fn foo2(a: [*c]f32) callconv(.Stdcall) void;
1265 \\pub fn foo3(a: [*c]f32) callconv(.Vectorcall) void;
1286 \\pub extern fn foo1(a: [*c]f32) callconv(.Fastcall) void;
1287 \\pub extern fn foo2(a: [*c]f32) callconv(.Stdcall) void;
1288 \\pub extern fn foo3(a: [*c]f32) callconv(.Vectorcall) void;
12661289 \\pub extern fn foo4(a: [*c]f32) void;
1267 \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void;
1290 \\pub extern fn foo5(a: [*c]f32) callconv(.Thiscall) void;
12681291 });
12691292
12701293 cases.addWithTarget("Calling convention", CrossTarget.parse(.{
......@@ -1274,8 +1297,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
12741297 \\void __attribute__((pcs("aapcs"))) foo1(float *a);
12751298 \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);
12761299 , &[_][]const u8{
1277 \\pub fn foo1(a: [*c]f32) callconv(.AAPCS) void;
1278 \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
1300 \\pub extern fn foo1(a: [*c]f32) callconv(.AAPCS) void;
1301 \\pub extern fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
12791302 });
12801303
12811304 cases.addWithTarget("Calling convention", CrossTarget.parse(.{
......@@ -1284,7 +1307,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
12841307 }) catch unreachable,
12851308 \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);
12861309 , &[_][]const u8{
1287 \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void;
1310 \\pub extern fn foo1(a: [*c]f32) callconv(.Vectorcall) void;
12881311 });
12891312
12901313 cases.add("Parameterless function prototypes",
......@@ -1596,13 +1619,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
15961619 });
15971620
15981621 cases.add("worst-case assign",
1599 \\int foo() {
1622 \\void foo() {
16001623 \\ int a;
16011624 \\ int b;
16021625 \\ a = b = 2;
16031626 \\}
16041627 , &[_][]const u8{
1605 \\pub export fn foo() c_int {
1628 \\pub export fn foo() void {
16061629 \\ var a: c_int = undefined;
16071630 \\ var b: c_int = undefined;
16081631 \\ a = blk: {
......@@ -1634,8 +1657,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16341657 , &[_][]const u8{
16351658 \\pub export fn foo() c_int {
16361659 \\ var a: c_int = 5;
1637 \\ while (@as(c_int, 2) != 0) a = 2;
1638 \\ while (@as(c_int, 4) != 0) {
1660 \\ while (true) a = 2;
1661 \\ while (true) {
16391662 \\ var a_1: c_int = 4;
16401663 \\ a_1 = 9;
16411664 \\ _ = @as(c_int, 6);
......@@ -1644,17 +1667,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16441667 \\ while (true) {
16451668 \\ var a_1: c_int = 2;
16461669 \\ a_1 = 12;
1647 \\ if (!(@as(c_int, 4) != 0)) break;
1670 \\ if (!true) break;
16481671 \\ }
16491672 \\ while (true) {
16501673 \\ a = 7;
1651 \\ if (!(@as(c_int, 4) != 0)) break;
1674 \\ if (!true) break;
16521675 \\ }
1676 \\ return 0;
16531677 \\}
16541678 });
16551679
16561680 cases.add("for loops",
1657 \\int foo() {
1681 \\void foo() {
16581682 \\ for (int i = 2, b = 4; i + 2; i = 2) {
16591683 \\ int a = 2;
16601684 \\ a = 6, 5, 7;
......@@ -1662,7 +1686,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16621686 \\ char i = 2;
16631687 \\}
16641688 , &[_][]const u8{
1665 \\pub export fn foo() c_int {
1689 \\pub export fn foo() void {
16661690 \\ {
16671691 \\ var i: c_int = 2;
16681692 \\ var b: c_int = 4;
......@@ -1679,8 +1703,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16791703
16801704 cases.add("shadowing primitive types",
16811705 \\unsigned anyerror = 2;
1706 \\#define noreturn _Noreturn
16821707 , &[_][]const u8{
16831708 \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2));
1709 ,
1710
1711 \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn");
16841712 });
16851713
16861714 cases.add("floats",
......@@ -1702,13 +1730,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17021730 \\}
17031731 , &[_][]const u8{
17041732 \\pub export fn bar() c_int {
1705 \\ if ((if (@as(c_int, 2) != 0) @as(c_int, 5) else (if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2);
1706 \\ return if (@as(c_int, 2) != 0) @as(c_int, 5) else if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6);
1733 \\ if ((if (true) @as(c_int, 5) else (if (true) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2);
1734 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
17071735 \\}
17081736 });
17091737
17101738 cases.add("switch on int",
1711 \\int switch_fn(int i) {
1739 \\void switch_fn(int i) {
17121740 \\ int res = 0;
17131741 \\ switch (i) {
17141742 \\ case 0:
......@@ -1723,19 +1751,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17231751 \\ }
17241752 \\}
17251753 , &[_][]const u8{
1726 \\pub export fn switch_fn(arg_i: c_int) c_int {
1754 \\pub export fn switch_fn(arg_i: c_int) void {
17271755 \\ var i = arg_i;
17281756 \\ var res: c_int = 0;
1729 \\ __switch: {
1730 \\ __case_2: {
1731 \\ __default: {
1732 \\ __case_1: {
1733 \\ __case_0: {
1757 \\ @"switch": {
1758 \\ case_2: {
1759 \\ default: {
1760 \\ case_1: {
1761 \\ case: {
17341762 \\ switch (i) {
1735 \\ @as(c_int, 0) => break :__case_0,
1736 \\ @as(c_int, 1)...@as(c_int, 3) => break :__case_1,
1737 \\ else => break :__default,
1738 \\ @as(c_int, 4) => break :__case_2,
1763 \\ @as(c_int, 0) => break :case,
1764 \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1,
1765 \\ else => break :default,
1766 \\ @as(c_int, 4) => break :case_2,
17391767 \\ }
17401768 \\ }
17411769 \\ res = 1;
......@@ -1743,7 +1771,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17431771 \\ res = 2;
17441772 \\ }
17451773 \\ res = (@as(c_int, 3) * i);
1746 \\ break :__switch;
1774 \\ break :@"switch";
17471775 \\ }
17481776 \\ res = 5;
17491777 \\ }
......@@ -1783,13 +1811,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17831811 });
17841812
17851813 cases.add("assign",
1786 \\int max(int a) {
1814 \\void max(int a) {
17871815 \\ int tmp;
17881816 \\ tmp = a;
17891817 \\ a = tmp;
17901818 \\}
17911819 , &[_][]const u8{
1792 \\pub export fn max(arg_a: c_int) c_int {
1820 \\pub export fn max(arg_a: c_int) void {
17931821 \\ var a = arg_a;
17941822 \\ var tmp: c_int = undefined;
17951823 \\ tmp = a;
......@@ -2078,7 +2106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20782106 \\ int b;
20792107 \\}a;
20802108 \\float b = 2.0f;
2081 \\int foo(void) {
2109 \\void foo(void) {
20822110 \\ struct Foo *c;
20832111 \\ a.b;
20842112 \\ c->b;
......@@ -2089,7 +2117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20892117 \\};
20902118 \\pub extern var a: struct_Foo;
20912119 \\pub export var b: f32 = 2;
2092 \\pub export fn foo() c_int {
2120 \\pub export fn foo() void {
20932121 \\ var c: [*c]struct_Foo = undefined;
20942122 \\ _ = a.b;
20952123 \\ _ = c.*.b;
......@@ -2200,11 +2228,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
22002228 \\ if (a < b) return b;
22012229 \\ if (a < b) return b else return a;
22022230 \\ if (a < b) {} else {}
2231 \\ return 0;
22032232 \\}
22042233 });
22052234
22062235 cases.add("if statements",
2207 \\int foo() {
2236 \\void foo() {
22082237 \\ if (2) {
22092238 \\ int a = 2;
22102239 \\ }
......@@ -2213,8 +2242,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
22132242 \\ }
22142243 \\}
22152244 , &[_][]const u8{
2216 \\pub export fn foo() c_int {
2217 \\ if (@as(c_int, 2) != 0) {
2245 \\pub export fn foo() void {
2246 \\ if (true) {
22182247 \\ var a: c_int = 2;
22192248 \\ }
22202249 \\ if ((blk: {
......@@ -2748,8 +2777,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27482777 \\}
27492778 , &[_][]const u8{
27502779 \\pub fn foo() callconv(.C) void {
2751 \\ if (@as(c_int, 1) != 0) while (true) {
2752 \\ if (!(@as(c_int, 0) != 0)) break;
2780 \\ if (true) while (true) {
2781 \\ if (!false) break;
27532782 \\ };
27542783 \\}
27552784 });
......@@ -2778,11 +2807,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27782807 \\ var x = arg_x;
27792808 \\ return blk: {
27802809 \\ const tmp = x;
2781 \\ (blk: {
2810 \\ (blk_1: {
27822811 \\ const ref = &p;
2783 \\ const tmp_1 = ref.*;
2812 \\ const tmp_2 = ref.*;
27842813 \\ ref.* += 1;
2785 \\ break :blk tmp_1;
2814 \\ break :blk_1 tmp_2;
27862815 \\ }).?.* = tmp;
27872816 \\ break :blk tmp;
27882817 \\ };
......@@ -2807,12 +2836,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28072836 });
28082837
28092838 cases.add("arg name aliasing decl which comes after",
2810 \\int foo(int bar) {
2839 \\void foo(int bar) {
28112840 \\ bar = 2;
28122841 \\}
28132842 \\int bar = 4;
28142843 , &[_][]const u8{
2815 \\pub export fn foo(arg_bar_1: c_int) c_int {
2844 \\pub export fn foo(arg_bar_1: c_int) void {
28162845 \\ var bar_1 = arg_bar_1;
28172846 \\ bar_1 = 2;
28182847 \\}
......@@ -2820,12 +2849,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28202849 });
28212850
28222851 cases.add("arg name aliasing macro which comes after",
2823 \\int foo(int bar) {
2852 \\void foo(int bar) {
28242853 \\ bar = 2;
28252854 \\}
28262855 \\#define bar 4
28272856 , &[_][]const u8{
2828 \\pub export fn foo(arg_bar_1: c_int) c_int {
2857 \\pub export fn foo(arg_bar_1: c_int) void {
28292858 \\ var bar_1 = arg_bar_1;
28302859 \\ bar_1 = 2;
28312860 \\}
tools/process_headers.zig+11-10
......@@ -248,7 +248,7 @@ const Contents = struct {
248248};
249249
250250const HashToContents = std.StringHashMap(Contents);
251const TargetToHash = std.HashMap(DestTarget, []const u8, DestTarget.hash, DestTarget.eql);
251const TargetToHash = std.HashMap(DestTarget, []const u8, DestTarget.hash, DestTarget.eql, true);
252252const PathTable = std.StringHashMap(*TargetToHash);
253253
254254const LibCVendor = enum {
......@@ -339,7 +339,7 @@ pub fn main() !void {
339339 try dir_stack.append(target_include_dir);
340340
341341 while (dir_stack.popOrNull()) |full_dir_name| {
342 var dir = std.fs.cwd().openDirList(full_dir_name) catch |err| switch (err) {
342 var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {
343343 error.FileNotFound => continue :search,
344344 error.AccessDenied => continue :search,
345345 else => return err,
......@@ -354,7 +354,8 @@ pub fn main() !void {
354354 .Directory => try dir_stack.append(full_path),
355355 .File => {
356356 const rel_path = try std.fs.path.relative(allocator, target_include_dir, full_path);
357 const raw_bytes = try std.io.readFileAlloc(allocator, full_path);
357 const max_size = 2 * 1024 * 1024 * 1024;
358 const raw_bytes = try std.fs.cwd().readFileAlloc(allocator, full_path, max_size);
358359 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
359360 total_bytes += raw_bytes.len;
360361 const hash = try allocator.alloc(u8, 32);
......@@ -365,14 +366,14 @@ pub fn main() !void {
365366 const gop = try hash_to_contents.getOrPut(hash);
366367 if (gop.found_existing) {
367368 max_bytes_saved += raw_bytes.len;
368 gop.kv.value.hit_count += 1;
369 gop.entry.value.hit_count += 1;
369370 std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{
370371 libc_target.name,
371372 rel_path,
372373 raw_bytes.len,
373374 });
374375 } else {
375 gop.kv.value = Contents{
376 gop.entry.value = Contents{
376377 .bytes = trimmed,
377378 .hit_count = 1,
378379 .hash = hash,
......@@ -380,13 +381,13 @@ pub fn main() !void {
380381 };
381382 }
382383 const path_gop = try path_table.getOrPut(rel_path);
383 const target_to_hash = if (path_gop.found_existing) path_gop.kv.value else blk: {
384 const target_to_hash = if (path_gop.found_existing) path_gop.entry.value else blk: {
384385 const ptr = try allocator.create(TargetToHash);
385386 ptr.* = TargetToHash.init(allocator);
386 path_gop.kv.value = ptr;
387 path_gop.entry.value = ptr;
387388 break :blk ptr;
388389 };
389 assert((try target_to_hash.put(dest_target, hash)) == null);
390 try target_to_hash.putNoClobber(dest_target, hash);
390391 },
391392 else => std.debug.warn("warning: weird file: {}\n", .{full_path}),
392393 }
......@@ -410,7 +411,7 @@ pub fn main() !void {
410411 {
411412 var hash_it = path_kv.value.iterator();
412413 while (hash_it.next()) |hash_kv| {
413 const contents = &hash_to_contents.get(hash_kv.value).?.value;
414 const contents = &hash_to_contents.get(hash_kv.value).?;
414415 try contents_list.append(contents);
415416 }
416417 }
......@@ -432,7 +433,7 @@ pub fn main() !void {
432433 }
433434 var hash_it = path_kv.value.iterator();
434435 while (hash_it.next()) |hash_kv| {
435 const contents = &hash_to_contents.get(hash_kv.value).?.value;
436 const contents = &hash_to_contents.get(hash_kv.value).?;
436437 if (contents.is_generic) continue;
437438
438439 const dest_target = hash_kv.key;