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")...@@ -326,10 +326,15 @@ set(LIBUNWIND_FILES_DEST "${ZIG_LIB_DIR}/libunwind")
326set(LIBCXX_FILES_DEST "${ZIG_LIB_DIR}/libcxx")326set(LIBCXX_FILES_DEST "${ZIG_LIB_DIR}/libcxx")
327set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std")327set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std")
328set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h")328set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h")
329set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig")
329configure_file (330configure_file (
330 "${CMAKE_SOURCE_DIR}/src/config.h.in"331 "${CMAKE_SOURCE_DIR}/src/config.h.in"
331 "${ZIG_CONFIG_H_OUT}"332 "${ZIG_CONFIG_H_OUT}"
332)333)
334configure_file (
335 "${CMAKE_SOURCE_DIR}/src/config.zig.in"
336 "${ZIG_CONFIG_ZIG_OUT}"
337)
333338
334include_directories(339include_directories(
335 ${CMAKE_SOURCE_DIR}340 ${CMAKE_SOURCE_DIR}
...@@ -472,6 +477,8 @@ set(BUILD_LIBSTAGE2_ARGS "build-lib"...@@ -472,6 +477,8 @@ set(BUILD_LIBSTAGE2_ARGS "build-lib"
472 --bundle-compiler-rt477 --bundle-compiler-rt
473 -fPIC478 -fPIC
474 -lc479 -lc
480 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}"
481 --pkg-end
475)482)
476483
477if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")484if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
build.zig+24-1
...@@ -10,6 +10,8 @@ const io = std.io;...@@ -10,6 +10,8 @@ const io = std.io;
10const fs = std.fs;10const fs = std.fs;
11const InstallDirectoryOptions = std.build.InstallDirectoryOptions;11const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
1212
13const zig_version = std.builtin.Version{ .major = 0, .minor = 6, .patch = 0 };
14
13pub fn build(b: *Builder) !void {15pub fn build(b: *Builder) !void {
14 b.setPreferredReleaseMode(.ReleaseFast);16 b.setPreferredReleaseMode(.ReleaseFast);
15 const mode = b.standardReleaseOptions();17 const mode = b.standardReleaseOptions();
...@@ -75,10 +77,31 @@ pub fn build(b: *Builder) !void {...@@ -75,10 +77,31 @@ pub fn build(b: *Builder) !void {
75 }77 }
76 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");78 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;79 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
80 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};85 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
82 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);105 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
83 exe.addBuildOption(bool, "enable_tracy", tracy != null);106 exe.addBuildOption(bool, "enable_tracy", tracy != null);
84 if (tracy) |tracy_path| {107 if (tracy) |tracy_path| {
ci/azure/linux_script+1-1
...@@ -14,7 +14,7 @@ sudo apt-get remove -y llvm-*...@@ -14,7 +14,7 @@ sudo apt-get remove -y llvm-*
14sudo rm -rf /usr/local/*14sudo rm -rf /usr/local/*
15sudo 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 tidy15sudo 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"
18wget https://ziglang.org/deps/$QEMUBASE.tar.xz18wget https://ziglang.org/deps/$QEMUBASE.tar.xz
19tar xf $QEMUBASE.tar.xz19tar xf $QEMUBASE.tar.xz
20PATH=$PWD/$QEMUBASE/bin:$PATH20PATH=$PWD/$QEMUBASE/bin:$PATH
doc/langref.html.in+33-11
...@@ -248,7 +248,7 @@ pub fn main() !void {...@@ -248,7 +248,7 @@ pub fn main() !void {
248 </p>248 </p>
249 <p>249 <p>
250 Following the <code>hello.zig</code> Zig code sample, the {#link|Zig Build System#} is used250 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
252 <code>hello</code> program is executed showing its output <code>Hello, world!</code>. The252 <code>hello</code> program is executed showing its output <code>Hello, world!</code>. The
253 lines beginning with <code>$</code> represent command line prompts and a command.253 lines beginning with <code>$</code> represent command line prompts and a command.
254 Everything else is program output.254 Everything else is program output.
...@@ -293,7 +293,7 @@ pub fn main() !void {...@@ -293,7 +293,7 @@ pub fn main() !void {
293 <p>293 <p>
294 In Zig, a function's block of statements and expressions are surrounded by <code>{</code> and294 In Zig, a function's block of statements and expressions are surrounded by <code>{</code> and
295 <code>}</code> curly-braces. Inside of the <code>main</code> function are expressions that perform295 <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.
297 </p>297 </p>
298 <p>298 <p>
299 First, a constant identifier, <code>stdout</code>, is initialized to represent standard output's299 First, a constant identifier, <code>stdout</code>, is initialized to represent standard output's
...@@ -325,7 +325,7 @@ pub fn main() !void {...@@ -325,7 +325,7 @@ pub fn main() !void {
325 represents writing data to a file. When the disk is full, a write to the file will fail.325 represents writing data to a file. When the disk is full, a write to the file will fail.
326 However, we typically do not expect writing text to the standard output to fail. To avoid having326 However, we typically do not expect writing text to the standard output to fail. To avoid having
327 to handle the failure case of printing to standard output, you can use alternate functions: the327 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.
329 This documentation will use the latter option to print to standard error (stderr) and silently return329 This documentation will use the latter option to print to standard error (stderr) and silently return
330 on failure. The next code sample, <code>hello_again.zig</code> demonstrates the use of330 on failure. The next code sample, <code>hello_again.zig</code> demonstrates the use of
331 <code>std.debug.print</code>.331 <code>std.debug.print</code>.
...@@ -5135,6 +5135,22 @@ test "float widening" {...@@ -5135,6 +5135,22 @@ test "float widening" {
5135 var c: f64 = b;5135 var c: f64 = b;
5136 var d: f128 = c;5136 var d: f128 = c;
5137 assert(d == a);5137 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;
5138}5154}
5139 {#code_end#}5155 {#code_end#}
5140 {#header_close#}5156 {#header_close#}
...@@ -8179,7 +8195,7 @@ const expect = std.testing.expect;...@@ -8179,7 +8195,7 @@ const expect = std.testing.expect;
8179test "@src" {8195test "@src" {
8180 doTheTest();8196 doTheTest();
8181}8197}
8182 8198
8183fn doTheTest() void {8199fn doTheTest() void {
8184 const src = @src();8200 const src = @src();
81858201
...@@ -9299,10 +9315,8 @@ fn concat(allocator: *Allocator, a: []const u8, b: []const u8) ![]u8 {...@@ -9299,10 +9315,8 @@ fn concat(allocator: *Allocator, a: []const u8, b: []const u8) ![]u8 {
9299 which will also do perform basic leak detection.9315 which will also do perform basic leak detection.
9300 </p>9316 </p>
9301 <p>9317 <p>
9302 Currently Zig has no general purpose allocator, but there is9318 Zig has a general purpose allocator available to be imported
9303 <a href="https://github.com/andrewrk/zig-general-purpose-allocator/">one under active development</a>.9319 with {#syntax#}std.heap.GeneralPurposeAllocator{#endsyntax#}. However, it is still recommended to
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
9306 follow the {#link|Choosing an Allocator#} guide.9320 follow the {#link|Choosing an Allocator#} guide.
9307 </p>9321 </p>
93089322
...@@ -9357,9 +9371,17 @@ pub fn main() !void {...@@ -9357,9 +9371,17 @@ pub fn main() !void {
9357 is handled correctly? In this case, use {#syntax#}std.testing.FailingAllocator{#endsyntax#}.9371 is handled correctly? In this case, use {#syntax#}std.testing.FailingAllocator{#endsyntax#}.
9358 </li>9372 </li>
9359 <li>9373 <li>
9360 Finally, if none of the above apply, you need a general purpose allocator. Zig does not9374 Are you writing a test? In this case, use {#syntax#}std.testing.allocator{#endsyntax#}.
9361 yet have a general purpose allocator in the standard library,9375 </li>
9362 <a href="https://github.com/andrewrk/zig-general-purpose-allocator/">but one is being actively developed</a>.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>
9363 You can also consider {#link|Implementing an Allocator#}.9385 You can also consider {#link|Implementing an Allocator#}.
9364 </li>9386 </li>
9365 </ol>9387 </ol>
lib/std/array_list.zig+1
...@@ -263,6 +263,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -263,6 +263,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
263 if (better_capacity >= new_capacity) break;263 if (better_capacity >= new_capacity) break;
264 }264 }
265265
266 // TODO This can be optimized to avoid needlessly copying undefined memory.
266 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);267 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
267 self.items.ptr = new_memory.ptr;268 self.items.ptr = new_memory.ptr;
268 self.capacity = new_memory.len;269 self.capacity = new_memory.len;
lib/std/atomic/queue.zig+1-1
...@@ -22,7 +22,7 @@ pub fn Queue(comptime T: type) type {...@@ -22,7 +22,7 @@ pub fn Queue(comptime T: type) type {
22 return Self{22 return Self{
23 .head = null,23 .head = null,
24 .tail = null,24 .tail = null,
25 .mutex = std.Mutex.init(),25 .mutex = std.Mutex{},
26 };26 };
27 }27 }
2828
lib/std/builtin.zig+27
...@@ -52,6 +52,25 @@ pub const subsystem: ?SubSystem = blk: {...@@ -52,6 +52,25 @@ pub const subsystem: ?SubSystem = blk: {
52pub const StackTrace = struct {52pub const StackTrace = struct {
53 index: usize,53 index: usize,
54 instruction_addresses: []usize,54 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 }
55};74};
5675
57/// This data structure is used by the Zig language code generation and76/// This data structure is used by the Zig language code generation and
...@@ -428,6 +447,14 @@ pub const Version = struct {...@@ -428,6 +447,14 @@ pub const Version = struct {
428 if (self.max.order(ver) == .lt) return false;447 if (self.max.order(ver) == .lt) return false;
429 return true;448 return true;
430 }449 }
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 }
431 };458 };
432459
433 pub fn order(lhs: Version, rhs: Version) std.math.Order {460 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(...@@ -91,6 +91,10 @@ pub extern "c" fn sendfile(
91 count: usize,91 count: usize,
92) isize;92) 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
94pub const pthread_attr_t = extern struct {98pub const pthread_attr_t = extern struct {
95 __size: [56]u8,99 __size: [56]u8,
96 __align: c_long,100 __align: c_long,
lib/std/cache_hash.zig+18-12
...@@ -188,12 +188,14 @@ pub const CacheHash = struct {...@@ -188,12 +188,14 @@ pub const CacheHash = struct {
188 };188 };
189189
190 var iter = mem.tokenize(line, " ");190 var iter = mem.tokenize(line, " ");
191 const size = iter.next() orelse return error.InvalidFormat;
191 const inode = iter.next() orelse return error.InvalidFormat;192 const inode = iter.next() orelse return error.InvalidFormat;
192 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;193 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
193 const digest_str = iter.next() orelse return error.InvalidFormat;194 const digest_str = iter.next() orelse return error.InvalidFormat;
194 const file_path = iter.rest();195 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;
197 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;199 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
198 base64_decoder.decode(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;200 base64_decoder.decode(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
199201
...@@ -216,10 +218,11 @@ pub const CacheHash = struct {...@@ -216,10 +218,11 @@ pub const CacheHash = struct {
216 defer this_file.close();218 defer this_file.close();
217219
218 const actual_stat = try this_file.stat();220 const actual_stat = try this_file.stat();
221 const size_match = actual_stat.size == cache_hash_file.stat.size;
219 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;222 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
220 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;223 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) {
223 self.manifest_dirty = true;226 self.manifest_dirty = true;
224227
225 cache_hash_file.stat = actual_stat;228 cache_hash_file.stat = actual_stat;
...@@ -392,7 +395,7 @@ pub const CacheHash = struct {...@@ -392,7 +395,7 @@ pub const CacheHash = struct {
392395
393 for (self.files.items) |file| {396 for (self.files.items) |file| {
394 base64_encoder.encode(encoded_digest[0..], &file.bin_digest);397 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 });
396 }399 }
397400
398 try self.manifest_file.?.pwriteAll(contents.items, 0);401 try self.manifest_file.?.pwriteAll(contents.items, 0);
...@@ -479,9 +482,10 @@ test "cache file and then recall it" {...@@ -479,9 +482,10 @@ test "cache file and then recall it" {
479 const temp_file = "test.txt";482 const temp_file = "test.txt";
480 const temp_manifest_dir = "temp_manifest_dir";483 const temp_manifest_dir = "temp_manifest_dir";
481484
485 const ts = std.time.nanoTimestamp();
482 try cwd.writeFile(temp_file, "Hello, world!\n");486 try cwd.writeFile(temp_file, "Hello, world!\n");
483487
484 while (isProblematicTimestamp(std.time.nanoTimestamp())) {488 while (isProblematicTimestamp(ts)) {
485 std.time.sleep(1);489 std.time.sleep(1);
486 }490 }
487491
...@@ -545,9 +549,13 @@ test "check that changing a file makes cache fail" {...@@ -545,9 +549,13 @@ test "check that changing a file makes cache fail" {
545 const original_temp_file_contents = "Hello, world!\n";549 const original_temp_file_contents = "Hello, world!\n";
546 const updated_temp_file_contents = "Hello, world; but updated!\n";550 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();
548 try cwd.writeFile(temp_file, original_temp_file_contents);556 try cwd.writeFile(temp_file, original_temp_file_contents);
549557
550 while (isProblematicTimestamp(std.time.nanoTimestamp())) {558 while (isProblematicTimestamp(ts)) {
551 std.time.sleep(1);559 std.time.sleep(1);
552 }560 }
553561
...@@ -571,10 +579,6 @@ test "check that changing a file makes cache fail" {...@@ -571,10 +579,6 @@ test "check that changing a file makes cache fail" {
571579
572 try cwd.writeFile(temp_file, updated_temp_file_contents);580 try cwd.writeFile(temp_file, updated_temp_file_contents);
573581
574 while (isProblematicTimestamp(std.time.nanoTimestamp())) {
575 std.time.sleep(1);
576 }
577
578 {582 {
579 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);583 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
580 defer ch.release();584 defer ch.release();
...@@ -594,7 +598,7 @@ test "check that changing a file makes cache fail" {...@@ -594,7 +598,7 @@ test "check that changing a file makes cache fail" {
594 testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));598 testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
595599
596 try cwd.deleteTree(temp_manifest_dir);600 try cwd.deleteTree(temp_manifest_dir);
597 try cwd.deleteFile(temp_file);601 try cwd.deleteTree(temp_file);
598}602}
599603
600test "no file inputs" {604test "no file inputs" {
...@@ -643,10 +647,11 @@ test "CacheHashes with files added after initial hash work" {...@@ -643,10 +647,11 @@ test "CacheHashes with files added after initial hash work" {
643 const temp_file2 = "cache_hash_post_file_test2.txt";647 const temp_file2 = "cache_hash_post_file_test2.txt";
644 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";648 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
645649
650 const ts1 = std.time.nanoTimestamp();
646 try cwd.writeFile(temp_file1, "Hello, world!\n");651 try cwd.writeFile(temp_file1, "Hello, world!\n");
647 try cwd.writeFile(temp_file2, "Hello world the second!\n");652 try cwd.writeFile(temp_file2, "Hello world the second!\n");
648653
649 while (isProblematicTimestamp(std.time.nanoTimestamp())) {654 while (isProblematicTimestamp(ts1)) {
650 std.time.sleep(1);655 std.time.sleep(1);
651 }656 }
652657
...@@ -680,9 +685,10 @@ test "CacheHashes with files added after initial hash work" {...@@ -680,9 +685,10 @@ test "CacheHashes with files added after initial hash work" {
680 testing.expect(mem.eql(u8, &digest1, &digest2));685 testing.expect(mem.eql(u8, &digest1, &digest2));
681686
682 // Modify the file added after initial hash687 // Modify the file added after initial hash
688 const ts2 = std.time.nanoTimestamp();
683 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");689 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
684690
685 while (isProblematicTimestamp(std.time.nanoTimestamp())) {691 while (isProblematicTimestamp(ts2)) {
686 std.time.sleep(1);692 std.time.sleep(1);
687 }693 }
688694
lib/std/crypto.zig+23-5
...@@ -29,17 +29,29 @@ pub const HmacSha1 = hmac.HmacSha1;...@@ -29,17 +29,29 @@ pub const HmacSha1 = hmac.HmacSha1;
29pub const HmacSha256 = hmac.HmacSha256;29pub const HmacSha256 = hmac.HmacSha256;
30pub const HmacBlake2s256 = hmac.HmacBlake2s256;30pub const HmacBlake2s256 = hmac.HmacBlake2s256;
3131
32const import_chaCha20 = @import("crypto/chacha20.zig");32pub const chacha20 = @import("crypto/chacha20.zig");
33pub const chaCha20IETF = import_chaCha20.chaCha20IETF;33pub const chaCha20IETF = chacha20.chaCha20IETF;
34pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;34pub const chaCha20With64BitNonce = chacha20.chaCha20With64BitNonce;
35pub const xChaCha20IETF = chacha20.xChaCha20IETF;
3536
36pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;37pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
37pub const X25519 = @import("crypto/x25519.zig").X25519;
3838
39const import_aes = @import("crypto/aes.zig");39const import_aes = @import("crypto/aes.zig");
40pub const AES128 = import_aes.AES128;40pub const AES128 = import_aes.AES128;
41pub const AES256 = import_aes.AES256;41pub 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
43const std = @import("std.zig");55const std = @import("std.zig");
44pub const randomBytes = std.os.getrandom;56pub const randomBytes = std.os.getrandom;
4557
...@@ -55,7 +67,13 @@ test "crypto" {...@@ -55,7 +67,13 @@ test "crypto" {
55 _ = @import("crypto/sha1.zig");67 _ = @import("crypto/sha1.zig");
56 _ = @import("crypto/sha2.zig");68 _ = @import("crypto/sha2.zig");
57 _ = @import("crypto/sha3.zig");69 _ = @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");
59}77}
6078
61test "issue #4532: no index out of bounds" {79test "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...@@ -90,7 +90,6 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
90 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;90 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
91 prng.random.bytes(out[0..]);91 prng.random.bytes(out[0..]);
9292
93 var offset: usize = 0;
94 var timer = try Timer.start();93 var timer = try Timer.start();
95 const start = timer.lap();94 const start = timer.lap();
96 {95 {
...@@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c...@@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
107 return throughput;106 return throughput;
108}107}
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
110fn usage() void {133fn usage() void {
111 std.debug.warn(134 std.debug.warn(
112 \\throughput_test [options]135 \\throughput_test [options]
...@@ -183,4 +206,11 @@ pub fn main() !void {...@@ -183,4 +206,11 @@ pub fn main() !void {
183 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });206 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
184 }207 }
185 }208 }
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 }
186}216}
lib/std/crypto/chacha20.zig+225-57
...@@ -25,12 +25,24 @@ fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {...@@ -25,12 +25,24 @@ fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
25 };25 };
26}26}
2727
28// The chacha family of ciphers are based on the salsa family.28fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
29fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {29 var ctx: [16]u32 = undefined;
30 assert(out.len >= 64);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 {
34 for (x) |_, i|46 for (x) |_, i|
35 x[i] = input[i];47 x[i] = input[i];
3648
...@@ -59,33 +71,27 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {...@@ -59,33 +71,27 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
59 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));71 x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
60 }72 }
61 }73 }
74}
6275
76fn hashToBytes(out: []u8, x: [16]u32) void {
63 for (x) |_, i| {77 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]);
65 }79 }
66}80}
6781
68fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {82fn 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);
70 var remaining: usize = if (in.len > out.len) in.len else out.len;84 var remaining: usize = if (in.len > out.len) in.len else out.len;
71 var cursor: usize = 0;85 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
85 while (true) {87 while (true) {
88 var x: [16]u32 = undefined;
86 var buf: [64]u8 = undefined;89 var buf: [64]u8 = undefined;
87 salsa20_wordtobyte(buf[0..], ctx);90 chacha20Core(x[0..], ctx);
8891 for (x) |_, i| {
92 x[i] +%= ctx[i];
93 }
94 hashToBytes(buf[0..], x);
89 if (remaining < 64) {95 if (remaining < 64) {
90 var i: usize = 0;96 var i: usize = 0;
91 while (i < remaining) : (i += 1)97 while (i < remaining) : (i += 1)
...@@ -104,6 +110,20 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo...@@ -104,6 +110,20 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo
104 }110 }
105}111}
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
107/// ChaCha20 avoids the possibility of timing attacks, as there are no branches127/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
108/// on secret key data.128/// on secret key data.
109///129///
...@@ -116,23 +136,12 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce:...@@ -116,23 +136,12 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce:
116 assert(in.len >= out.len);136 assert(in.len >= out.len);
117 assert((in.len >> 6) + counter <= maxInt(u32));137 assert((in.len >> 6) + counter <= maxInt(u32));
118138
119 var k: [8]u32 = undefined;
120 var c: [4]u32 = undefined;139 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
131 c[0] = counter;140 c[0] = counter;
132 c[1] = mem.readIntLittle(u32, nonce[0..4]);141 c[1] = mem.readIntLittle(u32, nonce[0..4]);
133 c[2] = mem.readIntLittle(u32, nonce[4..8]);142 c[2] = mem.readIntLittle(u32, nonce[4..8]);
134 c[3] = mem.readIntLittle(u32, nonce[8..12]);143 c[3] = mem.readIntLittle(u32, nonce[8..12]);
135 chaCha20_internal(out, in, k, c);144 chaCha20_internal(out, in, keyToWords(key), c);
136}145}
137146
138/// This is the original ChaCha20 before RFC 7539, which recommends using the147/// 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]...@@ -143,18 +152,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
143 assert(counter +% (in.len >> 6) >= counter);152 assert(counter +% (in.len >> 6) >= counter);
144153
145 var cursor: usize = 0;154 var cursor: usize = 0;
146 var k: [8]u32 = undefined;155 const k = keyToWords(key);
147 var c: [4]u32 = undefined;156 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
158 c[0] = @truncate(u32, counter);157 c[0] = @truncate(u32, counter);
159 c[1] = @truncate(u32, counter >> 32);158 c[1] = @truncate(u32, counter >> 32);
160 c[2] = mem.readIntLittle(u32, nonce[0..4]);159 c[2] = mem.readIntLittle(u32, nonce[0..4]);
...@@ -437,15 +436,15 @@ test "crypto.chacha20 test vector 5" {...@@ -437,15 +436,15 @@ test "crypto.chacha20 test vector 5" {
437436
438pub const chacha20poly1305_tag_size = 16;437pub const chacha20poly1305_tag_size = 16;
439438
440pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {439pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
441 assert(dst.len >= plaintext.len + chacha20poly1305_tag_size);440 assert(ciphertext.len >= plaintext.len);
442441
443 // derive poly1305 key442 // derive poly1305 key
444 var polyKey = [_]u8{0} ** 32;443 var polyKey = [_]u8{0} ** 32;
445 chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce);444 chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce);
446445
447 // encrypt plaintext446 // encrypt plaintext
448 chaCha20IETF(dst[0..plaintext.len], plaintext, 1, key, nonce);447 chaCha20IETF(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
449448
450 // construct mac449 // construct mac
451 var mac = Poly1305.init(polyKey[0..]);450 var mac = Poly1305.init(polyKey[0..]);
...@@ -455,7 +454,7 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,...@@ -455,7 +454,7 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,
455 const padding = 16 - (data.len % 16);454 const padding = 16 - (data.len % 16);
456 mac.update(zeros[0..padding]);455 mac.update(zeros[0..padding]);
457 }456 }
458 mac.update(dst[0..plaintext.len]);457 mac.update(ciphertext[0..plaintext.len]);
459 if (plaintext.len % 16 != 0) {458 if (plaintext.len % 16 != 0) {
460 const zeros = [_]u8{0} ** 16;459 const zeros = [_]u8{0} ** 16;
461 const padding = 16 - (plaintext.len % 16);460 const padding = 16 - (plaintext.len % 16);
...@@ -465,19 +464,17 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,...@@ -465,19 +464,17 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,
465 mem.writeIntLittle(u64, lens[0..8], data.len);464 mem.writeIntLittle(u64, lens[0..8], data.len);
466 mem.writeIntLittle(u64, lens[8..16], plaintext.len);465 mem.writeIntLittle(u64, lens[8..16], plaintext.len);
467 mac.update(lens[0..]);466 mac.update(lens[0..]);
468 mac.final(dst[plaintext.len..]);467 mac.final(tag);
469}468}
470469
471/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.470pub fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
472pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []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);
473 if (msgAndTag.len < chacha20poly1305_tag_size) {472}
474 return error.InvalidMessage;
475 }
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 {
477 // split ciphertext and tag476 // split ciphertext and tag
478 assert(dst.len >= msgAndTag.len - chacha20poly1305_tag_size);477 assert(dst.len >= ciphertext.len);
479 var ciphertext = msgAndTag[0 .. msgAndTag.len - chacha20poly1305_tag_size];
480 var polyTag = msgAndTag[ciphertext.len..];
481478
482 // derive poly1305 key479 // derive poly1305 key
483 var polyKey = [_]u8{0} ** 32;480 var polyKey = [_]u8{0} ** 32;
...@@ -510,7 +507,7 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,...@@ -510,7 +507,7 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
510 // See https://github.com/ziglang/zig/issues/1776507 // See https://github.com/ziglang/zig/issues/1776
511 var acc: u8 = 0;508 var acc: u8 = 0;
512 for (computedTag) |_, i| {509 for (computedTag) |_, i| {
513 acc |= (computedTag[i] ^ polyTag[i]);510 acc |= (computedTag[i] ^ tag[i]);
514 }511 }
515 if (acc != 0) {512 if (acc != 0) {
516 return error.AuthenticationFailed;513 return error.AuthenticationFailed;
...@@ -520,6 +517,75 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,...@@ -520,6 +517,75 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
520 chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce);517 chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
521}518}
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
523test "seal" {589test "seal" {
524 {590 {
525 const plaintext = "";591 const plaintext = "";
...@@ -636,3 +702,105 @@ test "open" {...@@ -636,3 +702,105 @@ test "open" {
636 testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce));702 testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce));
637 }703 }
638}704}
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 {...@@ -269,7 +269,7 @@ pub const Aead = struct {
269 /// npub: public nonce269 /// npub: public nonce
270 /// k: private key270 /// k: private key
271 /// NOTE: the check of the authentication tag is currently not done in constant time271 /// 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 {
273 assert(c.len == m.len);273 assert(c.len == m.len);
274274
275 var state = Aead.init(ad, npub, k);275 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;...@@ -19,9 +19,6 @@ const windows = std.os.windows;
1919
20pub const leb = @import("debug/leb128.zig");20pub 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
25pub const runtime_safety = switch (builtin.mode) {22pub const runtime_safety = switch (builtin.mode) {
26 .Debug, .ReleaseSafe => true,23 .Debug, .ReleaseSafe => true,
27 .ReleaseFast, .ReleaseSmall => false,24 .ReleaseFast, .ReleaseSmall => false,
...@@ -50,7 +47,7 @@ pub const LineInfo = struct {...@@ -50,7 +47,7 @@ pub const LineInfo = struct {
50 }47 }
51};48};
5249
53var stderr_mutex = std.Mutex.init();50var stderr_mutex = std.Mutex{};
5451
55/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for52/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
56/// "printf debugging".53/// "printf debugging".
...@@ -235,7 +232,7 @@ pub fn panic(comptime format: []const u8, args: anytype) noreturn {...@@ -235,7 +232,7 @@ pub fn panic(comptime format: []const u8, args: anytype) noreturn {
235var panicking: u8 = 0;232var panicking: u8 = 0;
236233
237// Locked to avoid interleaving panic messages from multiple threads.234// Locked to avoid interleaving panic messages from multiple threads.
238var panic_mutex = std.Mutex.init();235var panic_mutex = std.Mutex{};
239236
240/// Counts how many times the panic handler is invoked by this thread.237/// Counts how many times the panic handler is invoked by this thread.
241/// This is used to catch and handle panics triggered by the panic handler.238/// 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...@@ -322,7 +322,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: anytype, form_id: u64, e
322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
323 FORM_block2 => parseFormValueBlock(allocator, in_stream, endian, 2),323 FORM_block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
324 FORM_block4 => parseFormValueBlock(allocator, in_stream, endian, 4),324 FORM_block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
325 FORM_block => x: {325 FORM_block => {
326 const block_len = try nosuspend leb.readULEB128(usize, in_stream);326 const block_len = try nosuspend leb.readULEB128(usize, in_stream);
327 return parseFormValueBlockLen(allocator, in_stream, block_len);327 return parseFormValueBlockLen(allocator, in_stream, block_len);
328 },328 },
lib/std/dwarf_bits.zig+4-4
...@@ -69,7 +69,7 @@ pub const TAG_lo_user = 0x4080;...@@ -69,7 +69,7 @@ pub const TAG_lo_user = 0x4080;
69pub const TAG_hi_user = 0xffff;69pub const TAG_hi_user = 0xffff;
7070
71// SGI/MIPS Extensions.71// SGI/MIPS Extensions.
72pub const DW_TAG_MIPS_loop = 0x4081;72pub const TAG_MIPS_loop = 0x4081;
7373
74// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .74// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .
75pub const TAG_HP_array_descriptor = 0x4090;75pub const TAG_HP_array_descriptor = 0x4090;
...@@ -263,9 +263,9 @@ pub const AT_MIPS_has_inlines = 0x200b;...@@ -263,9 +263,9 @@ pub const AT_MIPS_has_inlines = 0x200b;
263263
264// HP extensions.264// HP extensions.
265pub const AT_HP_block_index = 0x2000;265pub const AT_HP_block_index = 0x2000;
266pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.266pub const AT_HP_unmodifiable = 0x2001; // Same as AT_MIPS_fde.
267pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.267pub const AT_HP_prologue = 0x2005; // Same as AT_MIPS_loop_unroll.
268pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.268pub const AT_HP_epilogue = 0x2008; // Same as AT_MIPS_stride.
269pub const AT_HP_actuals_stmt_list = 0x2010;269pub const AT_HP_actuals_stmt_list = 0x2010;
270pub const AT_HP_proc_per_section = 0x2011;270pub const AT_HP_proc_per_section = 0x2011;
271pub const AT_HP_raw_data_ptr = 0x2012;271pub const AT_HP_raw_data_ptr = 0x2012;
lib/std/fmt.zig+28-4
...@@ -88,8 +88,6 @@ pub fn format(...@@ -88,8 +88,6 @@ pub fn format(
88 if (args.len > ArgSetType.bit_count) {88 if (args.len > ArgSetType.bit_count) {
89 @compileError("32 arguments max are supported per format call");89 @compileError("32 arguments max are supported per format call");
90 }90 }
91 if (args.len == 0)
92 return writer.writeAll(fmt);
9391
94 const State = enum {92 const State = enum {
95 Start,93 Start,
...@@ -562,13 +560,25 @@ fn formatFloatValue(...@@ -562,13 +560,25 @@ fn formatFloatValue(
562 options: FormatOptions,560 options: FormatOptions,
563 writer: anytype,561 writer: anytype,
564) !void {562) !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
565 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {567 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 };
567 } else if (comptime std.mem.eql(u8, fmt, "d")) {572 } 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 };
569 } else {577 } else {
570 @compileError("Unknown format string: '" ++ fmt ++ "'");578 @compileError("Unknown format string: '" ++ fmt ++ "'");
571 }579 }
580
581 return formatBuf(buf_stream.getWritten(), options, writer);
572}582}
573583
574pub fn formatText(584pub fn formatText(
...@@ -1793,3 +1803,17 @@ test "padding" {...@@ -1793,3 +1803,17 @@ test "padding" {
1793 try testFmt("==================Filled", "{:=>24}", .{"Filled"});1803 try testFmt("==================Filled", "{:=>24}", .{"Filled"});
1794 try testFmt(" Centered ", "{:^24}", .{"Centered"});1804 try testFmt(" Centered ", "{:^24}", .{"Centered"});
1795}1805}
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 {...@@ -926,6 +926,123 @@ pub const Dir = struct {
926 return self.openDir(sub_path, open_dir_options);926 return self.openDir(sub_path, open_dir_options);
927 }927 }
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
929 /// Changes the current working directory to the open directory handle.1046 /// Changes the current working directory to the open directory handle.
930 /// This modifies global state and can have surprising effects in multi-1047 /// This modifies global state and can have surprising effects in multi-
931 /// threaded applications. Most applications and especially libraries should1048 /// threaded applications. Most applications and especially libraries should
...@@ -2060,7 +2177,7 @@ pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {...@@ -2060,7 +2177,7 @@ pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
2060}2177}
20612178
2062/// `realpath`, except caller must free the returned memory.2179/// `realpath`, except caller must free the returned memory.
2063/// TODO integrate with `Dir`2180/// See also `Dir.realpath`.
2064pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {2181pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
2065 // Use of MAX_PATH_BYTES here is valid as the realpath function does not2182 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
2066 // have a variant that takes an arbitrary-size buffer.2183 // have a variant that takes an arbitrary-size buffer.
lib/std/fs/file.zig+2-7
...@@ -607,15 +607,10 @@ pub const File = struct {...@@ -607,15 +607,10 @@ pub const File = struct {
607 }607 }
608 }608 }
609609
610 pub const CopyRangeError = PWriteError || PReadError;610 pub const CopyRangeError = os.CopyFileRangeError;
611611
612 pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {612 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 APIs613 return os.copy_file_range(in.handle, in_offset, out.handle, out_offset, len, 0);
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);
619 }614 }
620615
621 /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it616 /// 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" {...@@ -109,17 +109,57 @@ test "Dir.Iterator" {
109 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));109 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
110}110}
111111
112fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {112fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
113 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;113 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
114}114}
115115
116fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {116fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
117 for (entries.items) |entry| {117 for (entries.items) |entry| {
118 if (entry_eql(entry, el)) return true;118 if (entryEql(entry, el)) return true;
119 }119 }
120 return false;120 return false;
121}121}
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
123test "readAllAlloc" {163test "readAllAlloc" {
124 var tmp_dir = tmpDir(.{});164 var tmp_dir = tmpDir(.{});
125 defer tmp_dir.cleanup();165 defer tmp_dir.cleanup();
...@@ -167,12 +207,7 @@ test "directory operations on files" {...@@ -167,12 +207,7 @@ test "directory operations on files" {
167 testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));207 testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));
168208
169 if (builtin.os.tag != .wasi) {209 if (builtin.os.tag != .wasi) {
170 // TODO: use Dir's realpath function once that exists210 const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_file_name);
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 };
176 defer testing.allocator.free(absolute_path);211 defer testing.allocator.free(absolute_path);
177212
178 testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));213 testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));
...@@ -206,12 +241,7 @@ test "file operations on directories" {...@@ -206,12 +241,7 @@ test "file operations on directories" {
206 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));241 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));
207242
208 if (builtin.os.tag != .wasi) {243 if (builtin.os.tag != .wasi) {
209 // TODO: use Dir's realpath function once that exists244 const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_dir_name);
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 };
215 defer testing.allocator.free(absolute_path);245 defer testing.allocator.free(absolute_path);
216246
217 testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));247 testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));
...@@ -328,6 +358,32 @@ test "sendfile" {...@@ -328,6 +358,32 @@ test "sendfile" {
328 testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));358 testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
329}359}
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
331test "fs.copyFile" {387test "fs.copyFile" {
332 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";388 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
333 const src_file = "tmp_test_copy_file.txt";389 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 {...@@ -129,7 +129,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
129 }129 }
130 },130 },
131131
132 .Union => |info| blk: {132 .Union => |info| {
133 if (info.tag_type) |tag_type| {133 if (info.tag_type) |tag_type| {
134 const tag = meta.activeTag(key);134 const tag = meta.activeTag(key);
135 const s = hash(hasher, tag, strat);135 const s = hash(hasher, tag, strat);
lib/std/heap.zig+103-37
...@@ -12,6 +12,7 @@ const maxInt = std.math.maxInt;...@@ -12,6 +12,7 @@ const maxInt = std.math.maxInt;
12pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;12pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
13pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;13pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
14pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;14pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
15pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
1516
16const Allocator = mem.Allocator;17const Allocator = mem.Allocator;
1718
...@@ -36,7 +37,7 @@ var c_allocator_state = Allocator{...@@ -36,7 +37,7 @@ var c_allocator_state = Allocator{
36 .resizeFn = cResize,37 .resizeFn = cResize,
37};38};
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 {
40 assert(ptr_align <= @alignOf(c_longdouble));41 assert(ptr_align <= @alignOf(c_longdouble));
41 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);42 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);
42 if (len_align == 0) {43 if (len_align == 0) {
...@@ -53,7 +54,14 @@ fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocato...@@ -53,7 +54,14 @@ fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocato
53 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];54 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
54}55}
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 {
57 if (new_len == 0) {65 if (new_len == 0) {
58 c.free(buf.ptr);66 c.free(buf.ptr);
59 return 0;67 return 0;
...@@ -88,8 +96,6 @@ var wasm_page_allocator_state = Allocator{...@@ -88,8 +96,6 @@ var wasm_page_allocator_state = Allocator{
88 .resizeFn = WasmPageAllocator.resize,96 .resizeFn = WasmPageAllocator.resize,
89};97};
9098
91pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator");
92
93/// Verifies that the adjusted length will still map to the full length99/// Verifies that the adjusted length will still map to the full length
94pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {100pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
95 const aligned_len = mem.alignAllocLen(full_len, len, len_align);101 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 {...@@ -97,10 +103,13 @@ pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
97 return aligned_len;103 return aligned_len;
98}104}
99105
106/// TODO Utilize this on Windows.
107pub var next_mmap_addr_hint: ?[*]align(mem.page_size) u8 = null;
108
100const PageAllocator = struct {109const 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 {
102 assert(n > 0);111 assert(n > 0);
103 const alignedLen = mem.alignForward(n, mem.page_size);112 const aligned_len = mem.alignForward(n, mem.page_size);
104113
105 if (builtin.os.tag == .windows) {114 if (builtin.os.tag == .windows) {
106 const w = os.windows;115 const w = os.windows;
...@@ -112,14 +121,14 @@ const PageAllocator = struct {...@@ -112,14 +121,14 @@ const PageAllocator = struct {
112 // see https://devblogs.microsoft.com/oldnewthing/?p=42223121 // see https://devblogs.microsoft.com/oldnewthing/?p=42223
113 const addr = w.VirtualAlloc(122 const addr = w.VirtualAlloc(
114 null,123 null,
115 alignedLen,124 aligned_len,
116 w.MEM_COMMIT | w.MEM_RESERVE,125 w.MEM_COMMIT | w.MEM_RESERVE,
117 w.PAGE_READWRITE,126 w.PAGE_READWRITE,
118 ) catch return error.OutOfMemory;127 ) catch return error.OutOfMemory;
119128
120 // If the allocation is sufficiently aligned, use it.129 // If the allocation is sufficiently aligned, use it.
121 if (@ptrToInt(addr) & (alignment - 1) == 0) {130 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)];
123 }132 }
124133
125 // If it wasn't, actually do an explicitely aligned allocation.134 // If it wasn't, actually do an explicitely aligned allocation.
...@@ -146,20 +155,24 @@ const PageAllocator = struct {...@@ -146,20 +155,24 @@ const PageAllocator = struct {
146 // until it succeeds.155 // until it succeeds.
147 const ptr = w.VirtualAlloc(156 const ptr = w.VirtualAlloc(
148 @intToPtr(*c_void, aligned_addr),157 @intToPtr(*c_void, aligned_addr),
149 alignedLen,158 aligned_len,
150 w.MEM_COMMIT | w.MEM_RESERVE,159 w.MEM_COMMIT | w.MEM_RESERVE,
151 w.PAGE_READWRITE,160 w.PAGE_READWRITE,
152 ) catch continue;161 ) 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)];
155 }164 }
156 }165 }
157166
158 const maxDropLen = alignment - std.math.min(alignment, mem.page_size);167 const max_drop_len = alignment - std.math.min(alignment, mem.page_size);
159 const allocLen = if (maxDropLen <= alignedLen - n) alignedLen else mem.alignForward(alignedLen + maxDropLen, 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);
160 const slice = os.mmap(173 const slice = os.mmap(
161 null,174 hint,
162 allocLen,175 alloc_len,
163 os.PROT_READ | os.PROT_WRITE,176 os.PROT_READ | os.PROT_WRITE,
164 os.MAP_PRIVATE | os.MAP_ANONYMOUS,177 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
165 -1,178 -1,
...@@ -168,25 +181,36 @@ const PageAllocator = struct {...@@ -168,25 +181,36 @@ const PageAllocator = struct {
168 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));181 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
169182
170 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);183 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);
184 const result_ptr = @alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr));
171185
172 // Unmap the extra bytes that were only requested in order to guarantee186 // Unmap the extra bytes that were only requested in order to guarantee
173 // that the range of memory we were provided had a proper alignment in187 // that the range of memory we were provided had a proper alignment in
174 // it somewhere. The extra bytes could be at the beginning, or end, or both.188 // it somewhere. The extra bytes could be at the beginning, or end, or both.
175 const dropLen = aligned_addr - @ptrToInt(slice.ptr);189 const drop_len = aligned_addr - @ptrToInt(slice.ptr);
176 if (dropLen != 0) {190 if (drop_len != 0) {
177 os.munmap(slice[0..dropLen]);191 os.munmap(slice[0..drop_len]);
178 }192 }
179193
180 // Unmap extra pages194 // Unmap extra pages
181 const alignedBufferLen = allocLen - dropLen;195 const aligned_buffer_len = alloc_len - drop_len;
182 if (alignedBufferLen > alignedLen) {196 if (aligned_buffer_len > aligned_len) {
183 os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]);197 os.munmap(result_ptr[aligned_len..aligned_buffer_len]);
184 }198 }
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)];
187 }204 }
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 {
190 const new_size_aligned = mem.alignForward(new_size, mem.page_size);214 const new_size_aligned = mem.alignForward(new_size, mem.page_size);
191215
192 if (builtin.os.tag == .windows) {216 if (builtin.os.tag == .windows) {
...@@ -201,7 +225,7 @@ const PageAllocator = struct {...@@ -201,7 +225,7 @@ const PageAllocator = struct {
201 w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE);225 w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE);
202 return 0;226 return 0;
203 }227 }
204 if (new_size < buf_unaligned.len) {228 if (new_size <= buf_unaligned.len) {
205 const base_addr = @ptrToInt(buf_unaligned.ptr);229 const base_addr = @ptrToInt(buf_unaligned.ptr);
206 const old_addr_end = base_addr + buf_unaligned.len;230 const old_addr_end = base_addr + buf_unaligned.len;
207 const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size);231 const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size);
...@@ -216,10 +240,10 @@ const PageAllocator = struct {...@@ -216,10 +240,10 @@ const PageAllocator = struct {
216 }240 }
217 return alignPageAllocLen(new_size_aligned, new_size, len_align);241 return alignPageAllocLen(new_size_aligned, new_size, len_align);
218 }242 }
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) {
220 return alignPageAllocLen(new_size_aligned, new_size, len_align);245 return alignPageAllocLen(new_size_aligned, new_size, len_align);
221 }246 }
222 // new_size > buf_unaligned.len not implemented
223 return error.OutOfMemory;247 return error.OutOfMemory;
224 }248 }
225249
...@@ -229,6 +253,7 @@ const PageAllocator = struct {...@@ -229,6 +253,7 @@ const PageAllocator = struct {
229253
230 if (new_size_aligned < buf_aligned_len) {254 if (new_size_aligned < buf_aligned_len) {
231 const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned);255 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
232 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);257 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
233 if (new_size_aligned == 0)258 if (new_size_aligned == 0)
234 return 0;259 return 0;
...@@ -236,6 +261,7 @@ const PageAllocator = struct {...@@ -236,6 +261,7 @@ const PageAllocator = struct {
236 }261 }
237262
238 // TODO: call mremap263 // TODO: call mremap
264 // TODO: if the next_mmap_addr_hint is within the remapped range, update it
239 return error.OutOfMemory;265 return error.OutOfMemory;
240 }266 }
241};267};
...@@ -332,7 +358,7 @@ const WasmPageAllocator = struct {...@@ -332,7 +358,7 @@ const WasmPageAllocator = struct {
332 return mem.alignForward(memsize, mem.page_size) / mem.page_size;358 return mem.alignForward(memsize, mem.page_size) / mem.page_size;
333 }359 }
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 {
336 const page_count = nPages(len);362 const page_count = nPages(len);
337 const page_idx = try allocPages(page_count, alignment);363 const page_idx = try allocPages(page_count, alignment);
338 return @intToPtr([*]u8, page_idx * mem.page_size)[0..alignPageAllocLen(page_count * mem.page_size, len, len_align)];364 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 {...@@ -385,7 +411,14 @@ const WasmPageAllocator = struct {
385 }411 }
386 }412 }
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 {
389 const aligned_len = mem.alignForward(buf.len, mem.page_size);422 const aligned_len = mem.alignForward(buf.len, mem.page_size);
390 if (new_len > aligned_len) return error.OutOfMemory;423 if (new_len > aligned_len) return error.OutOfMemory;
391 const current_n = nPages(aligned_len);424 const current_n = nPages(aligned_len);
...@@ -425,7 +458,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -425,7 +458,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {
425 return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len);458 return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len);
426 }459 }
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 {
429 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);468 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
430469
431 const amt = n + ptr_align - 1 + @sizeOf(usize);470 const amt = n + ptr_align - 1 + @sizeOf(usize);
...@@ -452,7 +491,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -452,7 +491,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {
452 return buf;491 return buf;
453 }492 }
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 {
456 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);502 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
457 if (new_size == 0) {503 if (new_size == 0) {
458 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void, getRecordPtr(buf).*));504 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void, getRecordPtr(buf).*));
...@@ -524,7 +570,7 @@ pub const FixedBufferAllocator = struct {...@@ -524,7 +570,7 @@ pub const FixedBufferAllocator = struct {
524 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;570 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
525 }571 }
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 {
528 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);574 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
529 const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align);575 const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align);
530 const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr);576 const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr);
...@@ -538,7 +584,14 @@ pub const FixedBufferAllocator = struct {...@@ -538,7 +584,14 @@ pub const FixedBufferAllocator = struct {
538 return result;584 return result;
539 }585 }
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 {
542 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);595 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
543 assert(self.ownsSlice(buf)); // sanity check596 assert(self.ownsSlice(buf)); // sanity check
544597
...@@ -588,7 +641,7 @@ pub const ThreadSafeFixedBufferAllocator = blk: {...@@ -588,7 +641,7 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
588 };641 };
589 }642 }
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 {
592 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);645 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
593 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);646 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
594 while (true) {647 while (true) {
...@@ -636,18 +689,31 @@ pub fn StackFallbackAllocator(comptime size: usize) type {...@@ -636,18 +689,31 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
636 return &self.allocator;689 return &self.allocator;
637 }690 }
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 {
640 const self = @fieldParentPtr(Self, "allocator", allocator);699 const self = @fieldParentPtr(Self, "allocator", allocator);
641 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch700 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
642 return fallback_allocator.alloc(len, ptr_align);701 return fallback_allocator.alloc(len, ptr_align);
643 }702 }
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 {
646 const self = @fieldParentPtr(Self, "allocator", allocator);712 const self = @fieldParentPtr(Self, "allocator", allocator);
647 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {713 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);
649 } else {715 } else {
650 try self.fallback_allocator.callResizeFn(buf, new_len);716 try self.fallback_allocator.resize(buf, new_len);
651 }717 }
652 }718 }
653 };719 };
...@@ -932,7 +998,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator....@@ -932,7 +998,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.
932 slice[60] = 0x34;998 slice[60] = 0x34;
933999
934 // realloc to a smaller size but with a larger alignment1000 // 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);
936 testing.expect(slice[0] == 0x12);1002 testing.expect(slice[0] == 0x12);
937 testing.expect(slice[60] == 0x34);1003 testing.expect(slice[60] == 0x34);
938}1004}
lib/std/heap/arena_allocator.zig+2-2
...@@ -49,7 +49,7 @@ pub const ArenaAllocator = struct {...@@ -49,7 +49,7 @@ pub const ArenaAllocator = struct {
49 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);49 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);
50 const big_enough_len = prev_len + actual_min_size;50 const big_enough_len = prev_len + actual_min_size;
51 const len = big_enough_len + big_enough_len / 2;51 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());
53 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));53 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));
54 buf_node.* = BufNode{54 buf_node.* = BufNode{
55 .data = buf,55 .data = buf,
...@@ -60,7 +60,7 @@ pub const ArenaAllocator = struct {...@@ -60,7 +60,7 @@ pub const ArenaAllocator = struct {
60 return buf_node;60 return buf_node;
61 }61 }
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 {
64 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);64 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
6565
66 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align);66 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 {...@@ -23,10 +23,16 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
23 };23 };
24 }24 }
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 {
27 const self = @fieldParentPtr(Self, "allocator", allocator);33 const self = @fieldParentPtr(Self, "allocator", allocator);
28 self.out_stream.print("alloc : {}", .{len}) catch {};34 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);
30 if (result) |buff| {36 if (result) |buff| {
31 self.out_stream.print(" success!\n", .{}) catch {};37 self.out_stream.print(" success!\n", .{}) catch {};
32 } else |err| {38 } else |err| {
...@@ -35,7 +41,14 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {...@@ -35,7 +41,14 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
35 return result;41 return result;
36 }42 }
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 {
39 const self = @fieldParentPtr(Self, "allocator", allocator);52 const self = @fieldParentPtr(Self, "allocator", allocator);
40 if (new_len == 0) {53 if (new_len == 0) {
41 self.out_stream.print("free : {}\n", .{buf.len}) catch {};54 self.out_stream.print("free : {}\n", .{buf.len}) catch {};
...@@ -44,7 +57,7 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {...@@ -44,7 +57,7 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
44 } else {57 } else {
45 self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};58 self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
46 }59 }
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| {
48 if (new_len > buf.len) {61 if (new_len > buf.len) {
49 self.out_stream.print(" success!\n", .{}) catch {};62 self.out_stream.print(" success!\n", .{}) catch {};
50 }63 }
...@@ -74,9 +87,9 @@ test "LoggingAllocator" {...@@ -74,9 +87,9 @@ test "LoggingAllocator" {
74 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;87 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;
7588
76 var a = try allocator.alloc(u8, 10);89 var a = try allocator.alloc(u8, 10);
77 a.len = allocator.shrinkBytes(a, 5, 0);90 a = allocator.shrink(a, 5);
78 std.debug.assert(a.len == 5);91 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));
80 allocator.free(a);93 allocator.free(a);
8194
82 std.testing.expectEqualSlices(u8,95 std.testing.expectEqualSlices(u8,
lib/std/json.zig+1-1
...@@ -1742,7 +1742,7 @@ test "parse into tagged union" {...@@ -1742,7 +1742,7 @@ test "parse into tagged union" {
1742 A: struct { x: u32 },1742 A: struct { x: u32 },
1743 B: struct { y: u32 },1743 B: struct { y: u32 },
1744 };1744 };
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{}));
1746 }1746 }
1747}1747}
17481748
lib/std/log.zig+142-88
...@@ -2,12 +2,20 @@ const std = @import("std.zig");...@@ -2,12 +2,20 @@ const std = @import("std.zig");
2const builtin = std.builtin;2const builtin = std.builtin;
3const root = @import("root");3const root = @import("root");
44
5//! std.log is standardized interface for logging which allows for the logging5//! std.log is a standardized interface for logging which allows for the logging
6//! of programs and libraries using this interface to be formatted and filtered6//! of programs and libraries using this interface to be formatted and filtered
7//! by the implementer of the root.log function.7//! by the implementer of the root.log function.
8//!8//!
9//! The scope parameter should be used to give context to the logging. For9//! Each log message has an associated scope enum, which can be used to give
10//! example, a library called 'libfoo' might use .libfoo as its scope.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.
11//!19//!
12//! An example root.log might look something like this:20//! An example root.log might look something like this:
13//!21//!
...@@ -25,9 +33,9 @@ const root = @import("root");...@@ -25,9 +33,9 @@ const root = @import("root");
25//! args: anytype,33//! args: anytype,
26//! ) void {34//! ) void {
27//! // Ignore all non-critical logging from sources other than35//! // Ignore all non-critical logging from sources other than
28//! // .my_project and .nice_library36//! // .my_project, .nice_library and .default
29//! const scope_prefix = "(" ++ switch (scope) {37//! const scope_prefix = "(" ++ switch (scope) {
30//! .my_project, .nice_library => @tagName(scope),38//! .my_project, .nice_library, .default => @tagName(scope),
31//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))39//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
32//! @tagName(scope)40//! @tagName(scope)
33//! else41//! else
...@@ -44,16 +52,24 @@ const root = @import("root");...@@ -44,16 +52,24 @@ const root = @import("root");
44//! }52//! }
45//!53//!
46//! pub fn main() void {54//! pub fn main() void {
47//! // Won't be printed as log_level is .warn55//! // Using the default scope:
48//! std.log.info(.my_project, "Starting up.", .{});56//! std.log.info("Just a simple informational log message", .{}); // Won't be printed as log_level is .warn
49//! std.log.err(.nice_library, "Something went very wrong, sorry.", .{});57//! std.log.warn("Flux capacitor is starting to overheat", .{});
50//! // Won't be printed as it gets filtered out by our log function58//!
51//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1", .{});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
52//! }67//! }
53//! ```68//! ```
54//! Which produces the following output:69//! Which produces the following output:
55//! ```70//! ```
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
57//! ```73//! ```
5874
59pub const Level = enum {75pub const Level = enum {
...@@ -115,88 +131,126 @@ fn log(...@@ -115,88 +131,126 @@ fn log(
115 }131 }
116}132}
117133
118/// Log an emergency message to stderr. This log level is intended to be used134/// Returns a scoped logging namespace that logs all messages using the scope
119/// for conditions that cannot be handled and is usually followed by a panic.135/// provided here.
120pub fn emerg(136pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
121 comptime scope: @Type(.EnumLiteral),137 return struct {
122 comptime format: []const u8,138 /// Log an emergency message. This log level is intended to be used
123 args: anytype,139 /// for conditions that cannot be handled and is usually followed by a panic.
124) void {140 pub fn emerg(
125 @setCold(true);141 comptime format: []const u8,
126 log(.emerg, scope, format, args);142 args: anytype,
127}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 for148 /// Log an alert message. This log level is intended to be used for
130/// conditions that should be corrected immediately (e.g. database corruption).149 /// conditions that should be corrected immediately (e.g. database corruption).
131pub fn alert(150 pub fn alert(
132 comptime scope: @Type(.EnumLiteral),151 comptime format: []const u8,
133 comptime format: []const u8,152 args: anytype,
134 args: anytype,153 ) void {
135) void {154 @setCold(true);
136 @setCold(true);155 log(.alert, scope, format, args);
137 log(.alert, scope, format, args);156 }
138}
139157
140/// Log a critical message to stderr. This log level is intended to be used158 /// Log a critical message. This log level is intended to be used
141/// when a bug has been detected or something has gone wrong and it will have159 /// when a bug has been detected or something has gone wrong and it will have
142/// an effect on the operation of the program.160 /// an effect on the operation of the program.
143pub fn crit(161 pub fn crit(
144 comptime scope: @Type(.EnumLiteral),162 comptime format: []const u8,
145 comptime format: []const u8,163 args: anytype,
146 args: anytype,164 ) void {
147) void {165 @setCold(true);
148 @setCold(true);166 log(.crit, scope, format, args);
149 log(.crit, scope, format, args);167 }
150}
151168
152/// Log an error message to stderr. This log level is intended to be used when169 /// Log an error message. This log level is intended to be used when
153/// a bug has been detected or something has gone wrong but it is recoverable.170 /// a bug has been detected or something has gone wrong but it is recoverable.
154pub fn err(171 pub fn err(
155 comptime scope: @Type(.EnumLiteral),172 comptime format: []const u8,
156 comptime format: []const u8,173 args: anytype,
157 args: anytype,174 ) void {
158) void {175 @setCold(true);
159 @setCold(true);176 log(.err, scope, format, args);
160 log(.err, scope, format, args);177 }
161}
162178
163/// Log a warning message to stderr. This log level is intended to be used if179 /// Log a warning message. This log level is intended to be used if
164/// it is uncertain whether something has gone wrong or not, but the180 /// it is uncertain whether something has gone wrong or not, but the
165/// circumstances would be worth investigating.181 /// circumstances would be worth investigating.
166pub fn warn(182 pub fn warn(
167 comptime scope: @Type(.EnumLiteral),183 comptime format: []const u8,
168 comptime format: []const u8,184 args: anytype,
169 args: anytype,185 ) void {
170) void {186 log(.warn, scope, format, args);
171 log(.warn, scope, format, args);187 }
172}
173188
174/// Log a notice message to stderr. This log level is intended to be used for189 /// Log a notice message. This log level is intended to be used for
175/// non-error but significant conditions.190 /// non-error but significant conditions.
176pub fn notice(191 pub fn notice(
177 comptime scope: @Type(.EnumLiteral),192 comptime format: []const u8,
178 comptime format: []const u8,193 args: anytype,
179 args: anytype,194 ) void {
180) void {195 log(.notice, scope, format, args);
181 log(.notice, scope, format, args);196 }
182}
183197
184/// Log an info message to stderr. This log level is intended to be used for198 /// Log an info message. This log level is intended to be used for
185/// general messages about the state of the program.199 /// general messages about the state of the program.
186pub fn info(200 pub fn info(
187 comptime scope: @Type(.EnumLiteral),201 comptime format: []const u8,
188 comptime format: []const u8,202 args: anytype,
189 args: anytype,203 ) void {
190) void {204 log(.info, scope, format, args);
191 log(.info, scope, format, args);205 }
192}
193206
194/// Log a debug message to stderr. This log level is intended to be used for207 /// Log a debug message. This log level is intended to be used for
195/// messages which are only useful for debugging.208 /// messages which are only useful for debugging.
196pub fn debug(209 pub fn debug(
197 comptime scope: @Type(.EnumLiteral),210 comptime format: []const u8,
198 comptime format: []const u8,211 args: anytype,
199 args: anytype,212 ) void {
200) void {213 log(.debug, scope, format, args);
201 log(.debug, scope, format, args);214 }
215 };
202}216}
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" {...@@ -747,6 +747,7 @@ test "math.negateCast" {
747747
748/// Cast an integer to a different integer type. If the value doesn't fit,748/// Cast an integer to a different integer type. If the value doesn't fit,
749/// return an error.749/// return an error.
750/// TODO make this an optional not an error.
750pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) {751pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) {
751 comptime assert(@typeInfo(T) == .Int); // must pass an integer752 comptime assert(@typeInfo(T) == .Int); // must pass an integer
752 comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer753 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) {...@@ -837,6 +838,10 @@ pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
837 return @intCast(T, x);838 return @intCast(T, x);
838}839}
839840
841pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T {
842 return ceilPowerOfTwo(T, value) catch unreachable;
843}
844
840test "math.ceilPowerOfTwoPromote" {845test "math.ceilPowerOfTwoPromote" {
841 testCeilPowerOfTwoPromote();846 testCeilPowerOfTwoPromote();
842 comptime testCeilPowerOfTwoPromote();847 comptime testCeilPowerOfTwoPromote();
lib/std/mem.zig+32-387
...@@ -8,391 +8,13 @@ const meta = std.meta;...@@ -8,391 +8,13 @@ const meta = std.meta;
8const trait = meta.trait;8const trait = meta.trait;
9const testing = std.testing;9const testing = std.testing;
1010
11// https://github.com/ziglang/zig/issues/256411/// https://github.com/ziglang/zig/issues/2564
12pub const page_size = switch (builtin.arch) {12pub const page_size = switch (builtin.arch) {
13 .wasm32, .wasm64 => 64 * 1024,13 .wasm32, .wasm64 => 64 * 1024,
14 else => 4 * 1024,14 else => 4 * 1024,
15};15};
1616
17pub const Allocator = struct {17pub const Allocator = @import("mem/Allocator.zig");
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};
39618
397/// Detects and asserts if the std.mem.Allocator interface is violated by the caller19/// Detects and asserts if the std.mem.Allocator interface is violated by the caller
398/// or the allocator.20/// or the allocator.
...@@ -415,7 +37,13 @@ pub fn ValidationAllocator(comptime T: type) type {...@@ -415,7 +37,13 @@ pub fn ValidationAllocator(comptime T: type) type {
415 if (*T == *Allocator) return &self.underlying_allocator;37 if (*T == *Allocator) return &self.underlying_allocator;
416 return &self.underlying_allocator.allocator;38 return &self.underlying_allocator.allocator;
417 }39 }
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 {
419 assert(n > 0);47 assert(n > 0);
420 assert(mem.isValidAlign(ptr_align));48 assert(mem.isValidAlign(ptr_align));
421 if (len_align != 0) {49 if (len_align != 0) {
...@@ -424,7 +52,8 @@ pub fn ValidationAllocator(comptime T: type) type {...@@ -424,7 +52,8 @@ pub fn ValidationAllocator(comptime T: type) type {
424 }52 }
42553
426 const self = @fieldParentPtr(@This(), "allocator", allocator);54 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);
428 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));57 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));
429 if (len_align == 0) {58 if (len_align == 0) {
430 assert(result.len == n);59 assert(result.len == n);
...@@ -434,14 +63,22 @@ pub fn ValidationAllocator(comptime T: type) type {...@@ -434,14 +63,22 @@ pub fn ValidationAllocator(comptime T: type) type {
434 }63 }
435 return result;64 return result;
436 }65 }
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 {
438 assert(buf.len > 0);74 assert(buf.len > 0);
439 if (len_align != 0) {75 if (len_align != 0) {
440 assert(mem.isAlignedAnyAlign(new_len, len_align));76 assert(mem.isAlignedAnyAlign(new_len, len_align));
441 assert(new_len >= len_align);77 assert(new_len >= len_align);
442 }78 }
443 const self = @fieldParentPtr(@This(), "allocator", allocator);79 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);
445 if (len_align == 0) {82 if (len_align == 0) {
446 assert(result == new_len);83 assert(result == new_len);
447 } else {84 } else {
...@@ -481,7 +118,7 @@ var failAllocator = Allocator{...@@ -481,7 +118,7 @@ var failAllocator = Allocator{
481 .allocFn = failAllocatorAlloc,118 .allocFn = failAllocatorAlloc,
482 .resizeFn = Allocator.noResize,119 .resizeFn = Allocator.noResize,
483};120};
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 {
485 return error.OutOfMemory;122 return error.OutOfMemory;
486}123}
487124
...@@ -977,7 +614,7 @@ test "spanZ" {...@@ -977,7 +614,7 @@ test "spanZ" {
977}614}
978615
979/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,616/// 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.
981/// In the case of a sentinel-terminated array, it uses the array length.618/// In the case of a sentinel-terminated array, it uses the array length.
982/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.619/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
983pub fn len(value: anytype) usize {620pub fn len(value: anytype) usize {
...@@ -996,6 +633,9 @@ pub fn len(value: anytype) usize {...@@ -996,6 +633,9 @@ pub fn len(value: anytype) usize {
996 .C => indexOfSentinel(info.child, 0, value),633 .C => indexOfSentinel(info.child, 0, value),
997 .Slice => value.len,634 .Slice => value.len,
998 },635 },
636 .Struct => |info| if (info.is_tuple) {
637 return info.fields.len;
638 } else @compileError("invalid type given to std.mem.len"),
999 else => @compileError("invalid type given to std.mem.len"),639 else => @compileError("invalid type given to std.mem.len"),
1000 };640 };
1001}641}
...@@ -1021,6 +661,11 @@ test "len" {...@@ -1021,6 +661,11 @@ test "len" {
1021 const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 };661 const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 };
1022 testing.expect(len(vector) == 2);662 testing.expect(len(vector) == 2);
1023 }663 }
664 {
665 const tuple = .{ 1, 2 };
666 testing.expect(len(tuple) == 2);
667 testing.expect(tuple[0] == 1);
668 }
1024}669}
1025670
1026/// Takes a pointer to an array, an array, a sentinel-terminated pointer,671/// 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...@@ -2038,7 +1683,7 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
2038 var replacements: usize = 0;1683 var replacements: usize = 0;
2039 while (slide < input.len) {1684 while (slide < input.len) {
2040 if (mem.indexOf(T, input[slide..], needle) == @as(usize, 0)) {1685 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);
2042 i += replacement.len;1687 i += replacement.len;
2043 slide += needle.len;1688 slide += needle.len;
2044 replacements += 1;1689 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 {...@@ -269,19 +269,21 @@ pub fn isIndexable(comptime T: type) bool {
269 }269 }
270 return true;270 return true;
271 }271 }
272 return comptime is(.Array)(T) or is(.Vector)(T);272 return comptime is(.Array)(T) or is(.Vector)(T) or isTuple(T);
273}273}
274274
275test "std.meta.trait.isIndexable" {275test "std.meta.trait.isIndexable" {
276 const array = [_]u8{0} ** 10;276 const array = [_]u8{0} ** 10;
277 const slice = @as([]const u8, &array);277 const slice = @as([]const u8, &array);
278 const vector: meta.Vector(2, u32) = [_]u32{0} ** 2;278 const vector: meta.Vector(2, u32) = [_]u32{0} ** 2;
279 const tuple = .{ 1, 2, 3 };
279280
280 testing.expect(isIndexable(@TypeOf(array)));281 testing.expect(isIndexable(@TypeOf(array)));
281 testing.expect(isIndexable(@TypeOf(&array)));282 testing.expect(isIndexable(@TypeOf(&array)));
282 testing.expect(isIndexable(@TypeOf(slice)));283 testing.expect(isIndexable(@TypeOf(slice)));
283 testing.expect(!isIndexable(meta.Child(@TypeOf(slice))));284 testing.expect(!isIndexable(meta.Child(@TypeOf(slice))));
284 testing.expect(isIndexable(@TypeOf(vector)));285 testing.expect(isIndexable(@TypeOf(vector)));
286 testing.expect(isIndexable(@TypeOf(tuple)));
285}287}
286288
287pub fn isNumber(comptime T: type) bool {289pub fn isNumber(comptime T: type) bool {
lib/std/mutex.zig+127-143
...@@ -15,8 +15,7 @@ const ResetEvent = std.ResetEvent;...@@ -15,8 +15,7 @@ const ResetEvent = std.ResetEvent;
15/// deadlock detection.15/// deadlock detection.
16///16///
17/// Example usage:17/// Example usage:
18/// var m = Mutex.init();18/// var m = Mutex{};
19/// defer m.deinit();
20///19///
21/// const lock = m.acquire();20/// const lock = m.acquire();
22/// defer lock.release();21/// defer lock.release();
...@@ -30,141 +29,13 @@ const ResetEvent = std.ResetEvent;...@@ -30,141 +29,13 @@ const ResetEvent = std.ResetEvent;
30/// // ... lock not acquired29/// // ... lock not acquired
31/// }30/// }
32pub const Mutex = if (builtin.single_threaded)31pub const Mutex = if (builtin.single_threaded)
33 struct {32 Dummy
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 }
76else if (builtin.os.tag == .windows)33else if (builtin.os.tag == .windows)
77// https://locklessinc.com/articles/keyed_events/34 WindowsMutex
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 }
164else if (builtin.link_libc or builtin.os.tag == .linux)35else if (builtin.link_libc or builtin.os.tag == .linux)
165// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs36// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
166 struct {37 struct {
167 state: usize,38 state: usize = 0,
16839
169 /// number of times to spin trying to acquire the lock.40 /// number of times to spin trying to acquire the lock.
170 /// https://webkit.org/blog/6161/locking-in-webkit/41 /// https://webkit.org/blog/6161/locking-in-webkit/
...@@ -179,14 +50,6 @@ else if (builtin.link_libc or builtin.os.tag == .linux)...@@ -179,14 +50,6 @@ else if (builtin.link_libc or builtin.os.tag == .linux)
179 event: ResetEvent,50 event: ResetEvent,
180 };51 };
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
190 pub fn tryAcquire(self: *Mutex) ?Held {53 pub fn tryAcquire(self: *Mutex) ?Held {
191 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)54 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
192 return null;55 return null;
...@@ -298,6 +161,128 @@ else if (builtin.link_libc or builtin.os.tag == .linux)...@@ -298,6 +161,128 @@ else if (builtin.link_libc or builtin.os.tag == .linux)
298else161else
299 SpinLock;162 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
301const TestContext = struct {286const TestContext = struct {
302 mutex: *Mutex,287 mutex: *Mutex,
303 data: i128,288 data: i128,
...@@ -306,8 +291,7 @@ const TestContext = struct {...@@ -306,8 +291,7 @@ const TestContext = struct {
306};291};
307292
308test "std.Mutex" {293test "std.Mutex" {
309 var mutex = Mutex.init();294 var mutex = Mutex{};
310 defer mutex.deinit();
311295
312 var context = TestContext{296 var context = TestContext{
313 .mutex = &mutex,297 .mutex = &mutex,
lib/std/net.zig+9-10
...@@ -77,23 +77,23 @@ pub const Address = extern union {...@@ -77,23 +77,23 @@ pub const Address = extern union {
77 }77 }
7878
79 pub fn parseIp6(buf: []const u8, port: u16) !Address {79 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) };
81 }81 }
8282
83 pub fn resolveIp6(buf: []const u8, port: u16) !Address {83 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) };
85 }85 }
8686
87 pub fn parseIp4(buf: []const u8, port: u16) !Address {87 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) };
89 }89 }
9090
91 pub fn initIp4(addr: [4]u8, port: u16) Address {91 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) };
93 }93 }
9494
95 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {95 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) };
97 }97 }
9898
99 pub fn initUnix(path: []const u8) !Address {99 pub fn initUnix(path: []const u8) !Address {
...@@ -136,8 +136,8 @@ pub const Address = extern union {...@@ -136,8 +136,8 @@ pub const Address = extern union {
136 /// on the address family.136 /// on the address family.
137 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {137 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
138 switch (addr.family) {138 switch (addr.family) {
139 os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, 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).*} },140 os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).* } },
141 else => unreachable,141 else => unreachable,
142 }142 }
143 }143 }
...@@ -193,7 +193,7 @@ pub const Ip4Address = extern struct {...@@ -193,7 +193,7 @@ pub const Ip4Address = extern struct {
193 .sa = .{193 .sa = .{
194 .port = mem.nativeToBig(u16, port),194 .port = mem.nativeToBig(u16, port),
195 .addr = undefined,195 .addr = undefined,
196 }196 },
197 };197 };
198 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);198 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);
199199
...@@ -240,7 +240,7 @@ pub const Ip4Address = extern struct {...@@ -240,7 +240,7 @@ pub const Ip4Address = extern struct {
240 }240 }
241241
242 pub fn init(addr: [4]u8, port: u16) Ip4Address {242 pub fn init(addr: [4]u8, port: u16) Ip4Address {
243 return Ip4Address {243 return Ip4Address{
244 .sa = os.sockaddr_in{244 .sa = os.sockaddr_in{
245 .port = mem.nativeToBig(u16, port),245 .port = mem.nativeToBig(u16, port),
246 .addr = @ptrCast(*align(1) const u32, &addr).*,246 .addr = @ptrCast(*align(1) const u32, &addr).*,
...@@ -598,7 +598,6 @@ pub const Ip6Address = extern struct {...@@ -598,7 +598,6 @@ pub const Ip6Address = extern struct {
598 }598 }
599};599};
600600
601
602pub fn connectUnixSocket(path: []const u8) !fs.File {601pub fn connectUnixSocket(path: []const u8) !fs.File {
603 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;602 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
604 const sockfd = try os.socket(603 const sockfd = try os.socket(
lib/std/once.zig+1-1
...@@ -10,7 +10,7 @@ pub fn once(comptime f: fn () void) Once(f) {...@@ -10,7 +10,7 @@ pub fn once(comptime f: fn () void) Once(f) {
10pub fn Once(comptime f: fn () void) type {10pub fn Once(comptime f: fn () void) type {
11 return struct {11 return struct {
12 done: bool = false,12 done: bool = false,
13 mutex: std.Mutex = std.Mutex.init(),13 mutex: std.Mutex = std.Mutex{},
1414
15 /// Call the function `f`.15 /// Call the function `f`.
16 /// If `call` is invoked multiple times `f` will be executed only the16 /// 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...@@ -4025,23 +4025,15 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
4025 const pathname_w = try windows.cStrToPrefixedFileW(pathname);4025 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
4026 return realpathW(pathname_w.span(), out_buffer);4026 return realpathW(pathname_w.span(), out_buffer);
4027 }4027 }
4028 if (builtin.os.tag == .linux and !builtin.link_libc) {4028 if (!builtin.link_libc) {
4029 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {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) {
4030 error.FileLocksNotSupported => unreachable,4031 error.FileLocksNotSupported => unreachable,
4031 else => |e| return e,4032 else => |e| return e,
4032 };4033 };
4033 defer close(fd);4034 defer close(fd);
40344035
4035 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;4036 return getFdPath(fd, out_buffer);
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;
4045 }4037 }
4046 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {4038 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
4047 EINVAL => unreachable,4039 EINVAL => unreachable,
...@@ -4060,7 +4052,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP...@@ -4060,7 +4052,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
4060}4052}
40614053
4062/// Same as `realpath` except `pathname` is UTF16LE-encoded.4054/// Same as `realpath` except `pathname` is UTF16LE-encoded.
4063/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
4064pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4055pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4065 const w = windows;4056 const w = windows;
40664057
...@@ -4094,17 +4085,51 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat...@@ -4094,17 +4085,51 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
4094 };4085 };
4095 defer w.CloseHandle(h_file);4086 defer w.CloseHandle(h_file);
40964087
4097 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;4088 return getFdPath(h_file, out_buffer);
4098 const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);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 // Trust that Windows gives us valid UTF-16LE.
4101 // We strip it to make this function consistent across platforms.4102 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
4102 const prefix = [_]u16{ '\\', '\\', '?', '\\' };4103 return out_buffer[0..end_index];
4103 const start_index = if (mem.startsWith(u16, wide_slice, &prefix)) prefix.len else 0;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.4123 const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
4106 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable;4124 switch (err) {
4107 return out_buffer[0..end_index];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 }
4108}4133}
41094134
4110/// Spurious wakeups are possible and no precision of timing is guaranteed.4135/// Spurious wakeups are possible and no precision of timing is guaranteed.
...@@ -4932,6 +4957,85 @@ pub fn sendfile(...@@ -4932,6 +4957,85 @@ pub fn sendfile(
4932 return total_written;4957 return total_written;
4933}4958}
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
4935pub const PollError = error{5039pub const PollError = error{
4936 /// The kernel had no space to allocate file descriptor tables.5040 /// The kernel had no space to allocate file descriptor tables.
4937 SystemResources,5041 SystemResources,
...@@ -5204,8 +5308,8 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {...@@ -5204,8 +5308,8 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
5204 }5308 }
5205}5309}
52065310
5207pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: i32) !fd_t {5311pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
5208 const rc = system.signalfd4(fd, mask, flags);5312 const rc = system.signalfd(fd, mask, flags);
5209 switch (errno(rc)) {5313 switch (errno(rc)) {
5210 0 => return @intCast(fd_t, rc),5314 0 => return @intCast(fd_t, rc),
5211 EBADF, EINVAL => unreachable,5315 EBADF, EINVAL => unreachable,
lib/std/os/bits/linux.zig+1
...@@ -19,6 +19,7 @@ pub usingnamespace switch (builtin.arch) {...@@ -19,6 +19,7 @@ pub usingnamespace switch (builtin.arch) {
19};19};
2020
21pub usingnamespace @import("linux/netlink.zig");21pub usingnamespace @import("linux/netlink.zig");
22pub const bpf = @import("linux/bpf.zig");
2223
23const is_mips = builtin.arch.isMIPS();24const 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;...@@ -261,4 +261,4 @@ pub const O_LARGEFILE = 0;
261pub const O_NOATIME = 0o1000000;261pub const O_NOATIME = 0o1000000;
262pub const O_PATH = 0o10000000;262pub const O_PATH = 0o10000000;
263pub const O_TMPFILE = 0o20200000;263pub 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 {...@@ -1200,13 +1200,19 @@ pub fn ioctl(fd: fd_t, request: u32, arg: usize) usize {
1200 return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg);1200 return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg);
1201}1201}
12021202
1203pub fn signalfd4(fd: fd_t, mask: *const sigset_t, flags: i32) usize {1203pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) usize {
1204 return syscall4(1204 return syscall4(.signalfd4, @bitCast(usize, @as(isize, fd)), @ptrToInt(mask), NSIG / 8, flags);
1205 .signalfd4,1205}
1206 @bitCast(usize, @as(isize, fd)),1206
1207 @ptrToInt(mask),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 @bitCast(usize, @as(usize, NSIG / 8)),1208 return syscall6(
1209 @intCast(usize, flags),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,
1210 );1216 );
1211}1217}
12121218
lib/std/os/test.zig+10-1
...@@ -112,8 +112,11 @@ test "openat smoke test" {...@@ -112,8 +112,11 @@ test "openat smoke test" {
112test "symlink with relative paths" {112test "symlink with relative paths" {
113 if (builtin.os.tag == .wasi) return error.SkipZigTest;113 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
115 // First, try relative paths in cwd119 // First, try relative paths in cwd
116 var cwd = fs.cwd();
117 try cwd.writeFile("file.txt", "nonsense");120 try cwd.writeFile("file.txt", "nonsense");
118121
119 if (builtin.os.tag == .windows) {122 if (builtin.os.tag == .windows) {
...@@ -519,3 +522,9 @@ test "fcntl" {...@@ -519,3 +522,9 @@ test "fcntl" {
519 expect((flags & os.FD_CLOEXEC) != 0);522 expect((flags & os.FD_CLOEXEC) != 0);
520 }523 }
521}524}
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 {...@@ -51,7 +51,7 @@ pub const OpenFileOptions = struct {
51 open_dir: bool = false,51 open_dir: bool = false,
52 /// If false, tries to open path as a reparse point without dereferencing it.52 /// If false, tries to open path as a reparse point without dereferencing it.
53 /// Defaults to true.53 /// Defaults to true.
54 follow_symlinks: bool = true, 54 follow_symlinks: bool = true,
55};55};
5656
57/// TODO when share_access_nonblocking is false, this implementation uses57/// TODO when share_access_nonblocking is false, this implementation uses
...@@ -897,30 +897,156 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {...@@ -897,30 +897,156 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
897}897}
898898
899pub const GetFinalPathNameByHandleError = error{899pub const GetFinalPathNameByHandleError = error{
900 BadPathName,
900 FileNotFound,901 FileNotFound,
901 SystemResources,
902 NameTooLong,902 NameTooLong,
903 Unexpected,903 Unexpected,
904};904};
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(
907 hFile: HANDLE,923 hFile: HANDLE,
908 buf_ptr: [*]u16,924 fmt: GetFinalPathNameByHandleFormat,
909 buf_len: DWORD,925 out_buffer: []u16,
910 flags: DWORD,926) GetFinalPathNameByHandleError![]u16 {
911) GetFinalPathNameByHandleError![:0]u16 {927 // Get normalized path; doesn't include volume name though.
912 const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags);928 var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
913 if (rc == 0) {929 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
914 switch (kernel32.GetLastError()) {930
915 .FILE_NOT_FOUND => return error.FileNotFound,931 // Get NT volume name.
916 .PATH_NOT_FOUND => return error.FileNotFound,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
917 .NOT_ENOUGH_MEMORY => return error.SystemResources,933 try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
918 .FILENAME_EXCED_RANGE => return error.NameTooLong,934
919 .INVALID_PARAMETER => unreachable,935 const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
920 else => |err| return unexpectedError(err),936 const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0 .. file_name.FileNameLength / 2];
921 }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),
922 }1049 }
923 return buf_ptr[0..rc :0];
924}1050}
9251051
926pub const GetFileSizeError = error{Unexpected};1052pub const GetFileSizeError = error{Unexpected};
lib/std/os/windows/bits.zig+18
...@@ -1573,3 +1573,21 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;...@@ -1573,3 +1573,21 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
15731573
1574pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;1574pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
1575pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;1575pub 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 {...@@ -536,7 +536,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
536 // normalize x and y536 // normalize x and y
537 if (ex == 0) {537 if (ex == 0) {
538 i = ux << exp_bits;538 i = ux << exp_bits;
539 while (i >> bits_minus_1 == 0) : (b: {539 while (i >> bits_minus_1 == 0) : ({
540 ex -= 1;540 ex -= 1;
541 i <<= 1;541 i <<= 1;
542 }) {}542 }) {}
...@@ -547,7 +547,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {...@@ -547,7 +547,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
547 }547 }
548 if (ey == 0) {548 if (ey == 0) {
549 i = uy << exp_bits;549 i = uy << exp_bits;
550 while (i >> bits_minus_1 == 0) : (b: {550 while (i >> bits_minus_1 == 0) : ({
551 ey -= 1;551 ey -= 1;
552 i <<= 1;552 i <<= 1;
553 }) {}553 }) {}
...@@ -573,7 +573,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {...@@ -573,7 +573,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
573 return 0 * x;573 return 0 * x;
574 ux = i;574 ux = i;
575 }575 }
576 while (ux >> digits == 0) : (b: {576 while (ux >> digits == 0) : ({
577 ux <<= 1;577 ux <<= 1;
578 ex -= 1;578 ex -= 1;
579 }) {}579 }) {}
lib/std/special/compiler_rt/floatditf.zig+1-1
...@@ -18,7 +18,7 @@ pub fn __floatditf(arg: i64) callconv(.C) f128 {...@@ -18,7 +18,7 @@ pub fn __floatditf(arg: i64) callconv(.C) f128 {
18 var aAbs = @bitCast(u64, arg);18 var aAbs = @bitCast(u64, arg);
19 if (arg < 0) {19 if (arg < 0) {
20 sign = 1 << 127;20 sign = 1 << 127;
21 aAbs = ~@bitCast(u64, arg)+ 1;21 aAbs = ~@bitCast(u64, arg) + 1;
22 }22 }
2323
24 // Exponent of (fp_t)a is the width of abs(a).24 // 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");...@@ -4,6 +4,8 @@ const builtin = @import("builtin");
44
5pub const io_mode: io.Mode = builtin.test_io_mode;5pub const io_mode: io.Mode = builtin.test_io_mode;
66
7var log_err_count: usize = 0;
8
7pub fn main() anyerror!void {9pub fn main() anyerror!void {
8 const test_fn_list = builtin.test_functions;10 const test_fn_list = builtin.test_functions;
9 var ok_count: usize = 0;11 var ok_count: usize = 0;
...@@ -19,15 +21,21 @@ pub fn main() anyerror!void {...@@ -19,15 +21,21 @@ pub fn main() anyerror!void {
19 // ignores the alignment of the slice.21 // ignores the alignment of the slice.
20 async_frame_buffer = &[_]u8{};22 async_frame_buffer = &[_]u8{};
2123
24 var leaks: usize = 0;
22 for (test_fn_list) |test_fn, i| {25 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 }
24 std.testing.log_level = .warn;32 std.testing.log_level = .warn;
2533
26 var test_node = root_node.start(test_fn.name, null);34 var test_node = root_node.start(test_fn.name, null);
27 test_node.activate();35 test_node.activate();
28 progress.refresh();36 progress.refresh();
29 if (progress.terminal == null) {37 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 });
31 }39 }
32 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {40 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
33 .evented => blk: {41 .evented => blk: {
...@@ -42,24 +50,20 @@ pub fn main() anyerror!void {...@@ -42,24 +50,20 @@ pub fn main() anyerror!void {
42 skip_count += 1;50 skip_count += 1;
43 test_node.end();51 test_node.end();
44 progress.log("{}...SKIP (async test)\n", .{test_fn.name});52 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", .{});
46 continue;54 continue;
47 },55 },
48 } else test_fn.func();56 } else test_fn.func();
49 if (result) |_| {57 if (result) |_| {
50 ok_count += 1;58 ok_count += 1;
51 test_node.end();59 test_node.end();
52 std.testing.allocator_instance.validate() catch |err| switch (err) {60 if (progress.terminal == null) std.debug.print("OK\n", .{});
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", .{});
57 } else |err| switch (err) {61 } else |err| switch (err) {
58 error.SkipZigTest => {62 error.SkipZigTest => {
59 skip_count += 1;63 skip_count += 1;
60 test_node.end();64 test_node.end();
61 progress.log("{}...SKIP\n", .{test_fn.name});65 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", .{});
63 },67 },
64 else => {68 else => {
65 progress.log("", .{});69 progress.log("", .{});
...@@ -69,9 +73,18 @@ pub fn main() anyerror!void {...@@ -69,9 +73,18 @@ pub fn main() anyerror!void {
69 }73 }
70 root_node.end();74 root_node.end();
71 if (ok_count == test_fn_list.len) {75 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});
73 } else {77 } 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);
75 }88 }
76}89}
7790
...@@ -81,6 +94,9 @@ pub fn log(...@@ -81,6 +94,9 @@ pub fn log(
81 comptime format: []const u8,94 comptime format: []const u8,
82 args: anytype,95 args: anytype,
83) void {96) void {
97 if (@enumToInt(message_level) <= @enumToInt(std.log.Level.err)) {
98 log_err_count += 1;
99 }
84 if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {100 if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
85 std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);101 std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);
86 }102 }
lib/std/std.zig+2-1
...@@ -13,7 +13,8 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM...@@ -13,7 +13,8 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
13pub const DynLib = @import("dynamic_library.zig").DynLib;13pub const DynLib = @import("dynamic_library.zig").DynLib;
14pub const HashMap = hash_map.HashMap;14pub const HashMap = hash_map.HashMap;
15pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;15pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
16pub const Mutex = @import("mutex.zig").Mutex;16pub const mutex = @import("mutex.zig");
17pub const Mutex = mutex.Mutex;
17pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;18pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
18pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;19pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
19pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;20pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
lib/std/target.zig+28
...@@ -100,6 +100,14 @@ pub const Target = struct {...@@ -100,6 +100,14 @@ pub const Target = struct {
100 pub fn includesVersion(self: Range, ver: WindowsVersion) bool {100 pub fn includesVersion(self: Range, ver: WindowsVersion) bool {
101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
102 }102 }
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 }
103 };111 };
104112
105 /// This function is defined to serialize a Zig source code representation of this113 /// This function is defined to serialize a Zig source code representation of this
...@@ -135,6 +143,12 @@ pub const Target = struct {...@@ -135,6 +143,12 @@ pub const Target = struct {
135 pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {143 pub fn includesVersion(self: LinuxVersionRange, ver: Version) bool {
136 return self.range.includesVersion(ver);144 return self.range.includesVersion(ver);
137 }145 }
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 }
138 };152 };
139153
140 /// The version ranges here represent the minimum OS version to be supported154 /// The version ranges here represent the minimum OS version to be supported
...@@ -158,6 +172,8 @@ pub const Target = struct {...@@ -158,6 +172,8 @@ pub const Target = struct {
158 ///172 ///
159 /// Binaries built with a given maximum version will continue to function on newer operating system173 /// Binaries built with a given maximum version will continue to function on newer operating system
160 /// versions. However, such a binary may not take full advantage of the newer operating system APIs.174 /// versions. However, such a binary may not take full advantage of the newer operating system APIs.
175 ///
176 /// See `Os.isAtLeast`.
161 pub const VersionRange = union {177 pub const VersionRange = union {
162 none: void,178 none: void,
163 semver: Version.Range,179 semver: Version.Range,
...@@ -273,6 +289,18 @@ pub const Target = struct {...@@ -273,6 +289,18 @@ pub const Target = struct {
273 };289 };
274 }290 }
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
276 pub fn requiresLibC(os: Os) bool {304 pub fn requiresLibC(os: Os) bool {
277 return switch (os.tag) {305 return switch (os.tag) {
278 .freebsd,306 .freebsd,
lib/std/target/powerpc.zig+28-28
...@@ -447,8 +447,8 @@ pub const all_features = blk: {...@@ -447,8 +447,8 @@ pub const all_features = blk: {
447};447};
448448
449pub const cpu = struct {449pub const cpu = struct {
450 pub const @"440" = CpuModel{450 pub const @"ppc440" = CpuModel{
451 .name = "440",451 .name = "ppc440",
452 .llvm_name = "440",452 .llvm_name = "440",
453 .features = featureSet(&[_]Feature{453 .features = featureSet(&[_]Feature{
454 .booke,454 .booke,
...@@ -459,8 +459,8 @@ pub const cpu = struct {...@@ -459,8 +459,8 @@ pub const cpu = struct {
459 .msync,459 .msync,
460 }),460 }),
461 };461 };
462 pub const @"450" = CpuModel{462 pub const @"ppc450" = CpuModel{
463 .name = "450",463 .name = "ppc450",
464 .llvm_name = "450",464 .llvm_name = "450",
465 .features = featureSet(&[_]Feature{465 .features = featureSet(&[_]Feature{
466 .booke,466 .booke,
...@@ -471,70 +471,70 @@ pub const cpu = struct {...@@ -471,70 +471,70 @@ pub const cpu = struct {
471 .msync,471 .msync,
472 }),472 }),
473 };473 };
474 pub const @"601" = CpuModel{474 pub const @"ppc601" = CpuModel{
475 .name = "601",475 .name = "ppc601",
476 .llvm_name = "601",476 .llvm_name = "601",
477 .features = featureSet(&[_]Feature{477 .features = featureSet(&[_]Feature{
478 .fpu,478 .fpu,
479 }),479 }),
480 };480 };
481 pub const @"602" = CpuModel{481 pub const @"ppc602" = CpuModel{
482 .name = "602",482 .name = "ppc602",
483 .llvm_name = "602",483 .llvm_name = "602",
484 .features = featureSet(&[_]Feature{484 .features = featureSet(&[_]Feature{
485 .fpu,485 .fpu,
486 }),486 }),
487 };487 };
488 pub const @"603" = CpuModel{488 pub const @"ppc603" = CpuModel{
489 .name = "603",489 .name = "ppc603",
490 .llvm_name = "603",490 .llvm_name = "603",
491 .features = featureSet(&[_]Feature{491 .features = featureSet(&[_]Feature{
492 .fres,492 .fres,
493 .frsqrte,493 .frsqrte,
494 }),494 }),
495 };495 };
496 pub const @"603e" = CpuModel{496 pub const @"ppc603e" = CpuModel{
497 .name = "603e",497 .name = "ppc603e",
498 .llvm_name = "603e",498 .llvm_name = "603e",
499 .features = featureSet(&[_]Feature{499 .features = featureSet(&[_]Feature{
500 .fres,500 .fres,
501 .frsqrte,501 .frsqrte,
502 }),502 }),
503 };503 };
504 pub const @"603ev" = CpuModel{504 pub const @"ppc603ev" = CpuModel{
505 .name = "603ev",505 .name = "ppc603ev",
506 .llvm_name = "603ev",506 .llvm_name = "603ev",
507 .features = featureSet(&[_]Feature{507 .features = featureSet(&[_]Feature{
508 .fres,508 .fres,
509 .frsqrte,509 .frsqrte,
510 }),510 }),
511 };511 };
512 pub const @"604" = CpuModel{512 pub const @"ppc604" = CpuModel{
513 .name = "604",513 .name = "ppc604",
514 .llvm_name = "604",514 .llvm_name = "604",
515 .features = featureSet(&[_]Feature{515 .features = featureSet(&[_]Feature{
516 .fres,516 .fres,
517 .frsqrte,517 .frsqrte,
518 }),518 }),
519 };519 };
520 pub const @"604e" = CpuModel{520 pub const @"ppc604e" = CpuModel{
521 .name = "604e",521 .name = "ppc604e",
522 .llvm_name = "604e",522 .llvm_name = "604e",
523 .features = featureSet(&[_]Feature{523 .features = featureSet(&[_]Feature{
524 .fres,524 .fres,
525 .frsqrte,525 .frsqrte,
526 }),526 }),
527 };527 };
528 pub const @"620" = CpuModel{528 pub const @"ppc620" = CpuModel{
529 .name = "620",529 .name = "ppc620",
530 .llvm_name = "620",530 .llvm_name = "620",
531 .features = featureSet(&[_]Feature{531 .features = featureSet(&[_]Feature{
532 .fres,532 .fres,
533 .frsqrte,533 .frsqrte,
534 }),534 }),
535 };535 };
536 pub const @"7400" = CpuModel{536 pub const @"ppc7400" = CpuModel{
537 .name = "7400",537 .name = "ppc7400",
538 .llvm_name = "7400",538 .llvm_name = "7400",
539 .features = featureSet(&[_]Feature{539 .features = featureSet(&[_]Feature{
540 .altivec,540 .altivec,
...@@ -542,8 +542,8 @@ pub const cpu = struct {...@@ -542,8 +542,8 @@ pub const cpu = struct {
542 .frsqrte,542 .frsqrte,
543 }),543 }),
544 };544 };
545 pub const @"7450" = CpuModel{545 pub const @"ppc7450" = CpuModel{
546 .name = "7450",546 .name = "ppc7450",
547 .llvm_name = "7450",547 .llvm_name = "7450",
548 .features = featureSet(&[_]Feature{548 .features = featureSet(&[_]Feature{
549 .altivec,549 .altivec,
...@@ -551,16 +551,16 @@ pub const cpu = struct {...@@ -551,16 +551,16 @@ pub const cpu = struct {
551 .frsqrte,551 .frsqrte,
552 }),552 }),
553 };553 };
554 pub const @"750" = CpuModel{554 pub const @"ppc750" = CpuModel{
555 .name = "750",555 .name = "ppc750",
556 .llvm_name = "750",556 .llvm_name = "750",
557 .features = featureSet(&[_]Feature{557 .features = featureSet(&[_]Feature{
558 .fres,558 .fres,
559 .frsqrte,559 .frsqrte,
560 }),560 }),
561 };561 };
562 pub const @"970" = CpuModel{562 pub const @"ppc970" = CpuModel{
563 .name = "970",563 .name = "ppc970",
564 .llvm_name = "970",564 .llvm_name = "970",
565 .features = featureSet(&[_]Feature{565 .features = featureSet(&[_]Feature{
566 .@"64bit",566 .@"64bit",
lib/std/testing.zig+14-16
...@@ -1,18 +1,16 @@...@@ -1,18 +1,16 @@
1const std = @import("std.zig");1const 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;
5pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator;4pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator;
65
7/// This should only be used in temporary test programs.6/// This should only be used in temporary test programs.
8pub const allocator = &allocator_instance.allocator;7pub const allocator = &allocator_instance.allocator;
9pub var allocator_instance = LeakCountAllocator.init(&base_allocator_instance.allocator);8pub var allocator_instance = std.heap.GeneralPurposeAllocator(.{}){};
109
11pub const failing_allocator = &failing_allocator_instance.allocator;10pub const failing_allocator = &failing_allocator_instance.allocator;
12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);11pub 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..]));13pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
1614
17/// TODO https://github.com/ziglang/zig/issues/573815/// TODO https://github.com/ziglang/zig/issues/5738
18pub var log_level = std.log.Level.warn;16pub var log_level = std.log.Level.warn;
...@@ -326,22 +324,22 @@ test "expectEqual vector" {...@@ -326,22 +324,22 @@ test "expectEqual vector" {
326324
327pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {325pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {
328 if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {326 if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
329 warn("\n====== expected this output: =========\n", .{});327 print("\n====== expected this output: =========\n", .{});
330 printWithVisibleNewlines(expected);328 printWithVisibleNewlines(expected);
331 warn("\n======== instead found this: =========\n", .{});329 print("\n======== instead found this: =========\n", .{});
332 printWithVisibleNewlines(actual);330 printWithVisibleNewlines(actual);
333 warn("\n======================================\n", .{});331 print("\n======================================\n", .{});
334332
335 var diff_line_number: usize = 1;333 var diff_line_number: usize = 1;
336 for (expected[0..diff_index]) |value| {334 for (expected[0..diff_index]) |value| {
337 if (value == '\n') diff_line_number += 1;335 if (value == '\n') diff_line_number += 1;
338 }336 }
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", .{});
342 printIndicatorLine(expected, diff_index);340 printIndicatorLine(expected, diff_index);
343341
344 warn("found:\n", .{});342 print("found:\n", .{});
345 printIndicatorLine(actual, diff_index);343 printIndicatorLine(actual, diff_index);
346344
347 @panic("test failure");345 @panic("test failure");
...@@ -362,9 +360,9 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {...@@ -362,9 +360,9 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
362 {360 {
363 var i: usize = line_begin_index;361 var i: usize = line_begin_index;
364 while (i < indicator_index) : (i += 1)362 while (i < indicator_index) : (i += 1)
365 warn(" ", .{});363 print(" ", .{});
366 }364 }
367 warn("^\n", .{});365 print("^\n", .{});
368}366}
369367
370fn printWithVisibleNewlines(source: []const u8) void {368fn printWithVisibleNewlines(source: []const u8) void {
...@@ -372,15 +370,15 @@ fn printWithVisibleNewlines(source: []const u8) void {...@@ -372,15 +370,15 @@ fn printWithVisibleNewlines(source: []const u8) void {
372 while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) {370 while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) {
373 printLine(source[i .. i + nl]);371 printLine(source[i .. i + nl]);
374 }372 }
375 warn("{}␃\n", .{source[i..]}); // End of Text symbol (ETX)373 print("{}␃\n", .{source[i..]}); // End of Text symbol (ETX)
376}374}
377375
378fn printLine(line: []const u8) void {376fn printLine(line: []const u8) void {
379 if (line.len != 0) switch (line[line.len - 1]) {377 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,
381 else => {},379 else => {},
382 };380 };
383 warn("{}\n", .{line});381 print("{}\n", .{line});
384}382}
385383
386test "" {384test "" {
lib/std/testing/failing_allocator.zig+17-4
...@@ -45,21 +45,34 @@ pub const FailingAllocator = struct {...@@ -45,21 +45,34 @@ pub const FailingAllocator = struct {
45 };45 };
46 }46 }
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 {
49 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);55 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
50 if (self.index == self.fail_index) {56 if (self.index == self.fail_index) {
51 return error.OutOfMemory;57 return error.OutOfMemory;
52 }58 }
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);
54 self.allocated_bytes += result.len;60 self.allocated_bytes += result.len;
55 self.allocations += 1;61 self.allocations += 1;
56 self.index += 1;62 self.index += 1;
57 return result;63 return result;
58 }64 }
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 {
61 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);74 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| {
63 std.debug.assert(new_len > old_mem.len);76 std.debug.assert(new_len > old_mem.len);
64 return e;77 return e;
65 };78 };
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 {...@@ -526,18 +526,19 @@ pub const Node = struct {
526 Comptime,526 Comptime,
527 Nosuspend,527 Nosuspend,
528 Block,528 Block,
529 LabeledBlock,
529530
530 // Misc531 // Misc
531 DocComment,532 DocComment,
532 SwitchCase,533 SwitchCase, // TODO make this not a child of AST Node
533 SwitchElse,534 SwitchElse, // TODO make this not a child of AST Node
534 Else,535 Else, // TODO make this not a child of AST Node
535 Payload,536 Payload, // TODO make this not a child of AST Node
536 PointerPayload,537 PointerPayload, // TODO make this not a child of AST Node
537 PointerIndexPayload,538 PointerIndexPayload, // TODO make this not a child of AST Node
538 ContainerField,539 ContainerField,
539 ErrorTag,540 ErrorTag, // TODO make this not a child of AST Node
540 FieldInitializer,541 FieldInitializer, // TODO make this not a child of AST Node
541542
542 pub fn Type(tag: Tag) type {543 pub fn Type(tag: Tag) type {
543 return switch (tag) {544 return switch (tag) {
...@@ -654,6 +655,7 @@ pub const Node = struct {...@@ -654,6 +655,7 @@ pub const Node = struct {
654 .Comptime => Comptime,655 .Comptime => Comptime,
655 .Nosuspend => Nosuspend,656 .Nosuspend => Nosuspend,
656 .Block => Block,657 .Block => Block,
658 .LabeledBlock => LabeledBlock,
657 .DocComment => DocComment,659 .DocComment => DocComment,
658 .SwitchCase => SwitchCase,660 .SwitchCase => SwitchCase,
659 .SwitchElse => SwitchElse,661 .SwitchElse => SwitchElse,
...@@ -666,6 +668,13 @@ pub const Node = struct {...@@ -666,6 +668,13 @@ pub const Node = struct {
666 .FieldInitializer => FieldInitializer,668 .FieldInitializer => FieldInitializer,
667 };669 };
668 }670 }
671
672 pub fn isBlock(tag: Tag) bool {
673 return switch (tag) {
674 .Block, .LabeledBlock => true,
675 else => false,
676 };
677 }
669 };678 };
670679
671 /// Prefer `castTag` to this.680 /// Prefer `castTag` to this.
...@@ -729,6 +738,7 @@ pub const Node = struct {...@@ -729,6 +738,7 @@ pub const Node = struct {
729 .Root,738 .Root,
730 .ContainerField,739 .ContainerField,
731 .Block,740 .Block,
741 .LabeledBlock,
732 .Payload,742 .Payload,
733 .PointerPayload,743 .PointerPayload,
734 .PointerIndexPayload,744 .PointerIndexPayload,
...@@ -739,6 +749,7 @@ pub const Node = struct {...@@ -739,6 +749,7 @@ pub const Node = struct {
739 .DocComment,749 .DocComment,
740 .TestDecl,750 .TestDecl,
741 => return false,751 => return false,
752
742 .While => {753 .While => {
743 const while_node = @fieldParentPtr(While, "base", n);754 const while_node = @fieldParentPtr(While, "base", n);
744 if (while_node.@"else") |@"else"| {755 if (while_node.@"else") |@"else"| {
...@@ -746,7 +757,7 @@ pub const Node = struct {...@@ -746,7 +757,7 @@ pub const Node = struct {
746 continue;757 continue;
747 }758 }
748759
749 return while_node.body.tag != .Block;760 return !while_node.body.tag.isBlock();
750 },761 },
751 .For => {762 .For => {
752 const for_node = @fieldParentPtr(For, "base", n);763 const for_node = @fieldParentPtr(For, "base", n);
...@@ -755,7 +766,7 @@ pub const Node = struct {...@@ -755,7 +766,7 @@ pub const Node = struct {
755 continue;766 continue;
756 }767 }
757768
758 return for_node.body.tag != .Block;769 return !for_node.body.tag.isBlock();
759 },770 },
760 .If => {771 .If => {
761 const if_node = @fieldParentPtr(If, "base", n);772 const if_node = @fieldParentPtr(If, "base", n);
...@@ -764,7 +775,7 @@ pub const Node = struct {...@@ -764,7 +775,7 @@ pub const Node = struct {
764 continue;775 continue;
765 }776 }
766777
767 return if_node.body.tag != .Block;778 return !if_node.body.tag.isBlock();
768 },779 },
769 .Else => {780 .Else => {
770 const else_node = @fieldParentPtr(Else, "base", n);781 const else_node = @fieldParentPtr(Else, "base", n);
...@@ -773,29 +784,40 @@ pub const Node = struct {...@@ -773,29 +784,40 @@ pub const Node = struct {
773 },784 },
774 .Defer => {785 .Defer => {
775 const defer_node = @fieldParentPtr(Defer, "base", n);786 const defer_node = @fieldParentPtr(Defer, "base", n);
776 return defer_node.expr.tag != .Block;787 return !defer_node.expr.tag.isBlock();
777 },788 },
778 .Comptime => {789 .Comptime => {
779 const comptime_node = @fieldParentPtr(Comptime, "base", n);790 const comptime_node = @fieldParentPtr(Comptime, "base", n);
780 return comptime_node.expr.tag != .Block;791 return !comptime_node.expr.tag.isBlock();
781 },792 },
782 .Suspend => {793 .Suspend => {
783 const suspend_node = @fieldParentPtr(Suspend, "base", n);794 const suspend_node = @fieldParentPtr(Suspend, "base", n);
784 if (suspend_node.body) |body| {795 if (suspend_node.body) |body| {
785 return body.tag != .Block;796 return !body.tag.isBlock();
786 }797 }
787798
788 return true;799 return true;
789 },800 },
790 .Nosuspend => {801 .Nosuspend => {
791 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);802 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
792 return nosuspend_node.expr.tag != .Block;803 return !nosuspend_node.expr.tag.isBlock();
793 },804 },
794 else => return true,805 else => return true,
795 }806 }
796 }807 }
797 }808 }
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
799 pub fn dump(self: *Node, indent: usize) void {821 pub fn dump(self: *Node, indent: usize) void {
800 {822 {
801 var i: usize = 0;823 var i: usize = 0;
...@@ -1460,7 +1482,6 @@ pub const Node = struct {...@@ -1460,7 +1482,6 @@ pub const Node = struct {
1460 statements_len: NodeIndex,1482 statements_len: NodeIndex,
1461 lbrace: TokenIndex,1483 lbrace: TokenIndex,
1462 rbrace: TokenIndex,1484 rbrace: TokenIndex,
1463 label: ?TokenIndex,
14641485
1465 /// After this the caller must initialize the statements list.1486 /// After this the caller must initialize the statements list.
1466 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {1487 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {
...@@ -1483,10 +1504,6 @@ pub const Node = struct {...@@ -1483,10 +1504,6 @@ pub const Node = struct {
1483 }1504 }
14841505
1485 pub fn firstToken(self: *const Block) TokenIndex {1506 pub fn firstToken(self: *const Block) TokenIndex {
1486 if (self.label) |label| {
1487 return label;
1488 }
1489
1490 return self.lbrace;1507 return self.lbrace;
1491 }1508 }
14921509
...@@ -1509,6 +1526,57 @@ pub const Node = struct {...@@ -1509,6 +1526,57 @@ pub const Node = struct {
1509 }1526 }
1510 };1527 };
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
1512 pub const Defer = struct {1580 pub const Defer = struct {
1513 base: Node = Node{ .tag = .Defer },1581 base: Node = Node{ .tag = .Defer },
1514 defer_token: TokenIndex,1582 defer_token: TokenIndex,
lib/std/zig/parse.zig+37-29
...@@ -364,9 +364,10 @@ const Parser = struct {...@@ -364,9 +364,10 @@ const Parser = struct {
364 const name_node = try p.expectNode(parseStringLiteralSingle, .{364 const name_node = try p.expectNode(parseStringLiteralSingle, .{
365 .ExpectedStringLiteral = .{ .token = p.tok_i },365 .ExpectedStringLiteral = .{ .token = p.tok_i },
366 });366 });
367 const block_node = try p.expectNode(parseBlock, .{367 const block_node = (try p.parseBlock(null)) orelse {
368 .ExpectedLBrace = .{ .token = p.tok_i },368 try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } });
369 });369 return error.ParseError;
370 };
370371
371 const test_node = try p.arena.allocator.create(Node.TestDecl);372 const test_node = try p.arena.allocator.create(Node.TestDecl);
372 test_node.* = .{373 test_node.* = .{
...@@ -540,12 +541,14 @@ const Parser = struct {...@@ -540,12 +541,14 @@ const Parser = struct {
540 if (p.eatToken(.Semicolon)) |_| {541 if (p.eatToken(.Semicolon)) |_| {
541 break :blk null;542 break :blk null;
542 }543 }
543 break :blk try p.expectNodeRecoverable(parseBlock, .{544 const body_block = (try p.parseBlock(null)) orelse {
544 // Since parseBlock only return error.ParseError on545 // Since parseBlock only return error.ParseError on
545 // a missing '}' we can assume this function was546 // a missing '}' we can assume this function was
546 // supposed to end here.547 // supposed to end here.
547 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },548 try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });
548 });549 break :blk null;
550 };
551 break :blk body_block;
549 },552 },
550 .as_type => null,553 .as_type => null,
551 };554 };
...@@ -823,10 +826,7 @@ const Parser = struct {...@@ -823,10 +826,7 @@ const Parser = struct {
823 var colon: TokenIndex = undefined;826 var colon: TokenIndex = undefined;
824 const label_token = p.parseBlockLabel(&colon);827 const label_token = p.parseBlockLabel(&colon);
825828
826 if (try p.parseBlock()) |node| {829 if (try p.parseBlock(label_token)) |node| return node;
827 node.cast(Node.Block).?.label = label_token;
828 return node;
829 }
830830
831 if (try p.parseLoopStatement()) |node| {831 if (try p.parseLoopStatement()) |node| {
832 if (node.cast(Node.For)) |for_node| {832 if (node.cast(Node.For)) |for_node| {
...@@ -1003,14 +1003,13 @@ const Parser = struct {...@@ -1003,14 +1003,13 @@ const Parser = struct {
1003 fn parseBlockExpr(p: *Parser) Error!?*Node {1003 fn parseBlockExpr(p: *Parser) Error!?*Node {
1004 var colon: TokenIndex = undefined;1004 var colon: TokenIndex = undefined;
1005 const label_token = p.parseBlockLabel(&colon);1005 const label_token = p.parseBlockLabel(&colon);
1006 const block_node = (try p.parseBlock()) orelse {1006 const block_node = (try p.parseBlock(label_token)) orelse {
1007 if (label_token) |label| {1007 if (label_token) |label| {
1008 p.putBackToken(label + 1); // ":"1008 p.putBackToken(label + 1); // ":"
1009 p.putBackToken(label); // IDENTIFIER1009 p.putBackToken(label); // IDENTIFIER
1010 }1010 }
1011 return null;1011 return null;
1012 };1012 };
1013 block_node.cast(Node.Block).?.label = label_token;
1014 return block_node;1013 return block_node;
1015 }1014 }
10161015
...@@ -1177,7 +1176,7 @@ const Parser = struct {...@@ -1177,7 +1176,7 @@ const Parser = struct {
1177 p.putBackToken(token); // IDENTIFIER1176 p.putBackToken(token); // IDENTIFIER
1178 }1177 }
11791178
1180 if (try p.parseBlock()) |node| return node;1179 if (try p.parseBlock(null)) |node| return node;
1181 if (try p.parseCurlySuffixExpr()) |node| return node;1180 if (try p.parseCurlySuffixExpr()) |node| return node;
11821181
1183 return null;1182 return null;
...@@ -1189,7 +1188,7 @@ const Parser = struct {...@@ -1189,7 +1188,7 @@ const Parser = struct {
1189 }1188 }
11901189
1191 /// Block <- LBRACE Statement* RBRACE1190 /// Block <- LBRACE Statement* RBRACE
1192 fn parseBlock(p: *Parser) !?*Node {1191 fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node {
1193 const lbrace = p.eatToken(.LBrace) orelse return null;1192 const lbrace = p.eatToken(.LBrace) orelse return null;
11941193
1195 var statements = std.ArrayList(*Node).init(p.gpa);1194 var statements = std.ArrayList(*Node).init(p.gpa);
...@@ -1211,16 +1210,26 @@ const Parser = struct {...@@ -1211,16 +1210,26 @@ const Parser = struct {
12111210
1212 const statements_len = @intCast(NodeIndex, statements.items.len);1211 const statements_len = @intCast(NodeIndex, statements.items.len);
12131212
1214 const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len);1213 if (label_token) |label| {
1215 block_node.* = .{1214 const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len);
1216 .label = null,1215 block_node.* = .{
1217 .lbrace = lbrace,1216 .label = label,
1218 .statements_len = statements_len,1217 .lbrace = lbrace,
1219 .rbrace = rbrace,1218 .statements_len = statements_len,
1220 };1219 .rbrace = rbrace,
1221 std.mem.copy(*Node, block_node.statements(), statements.items);1220 };
12221221 std.mem.copy(*Node, block_node.statements(), statements.items);
1223 return &block_node.base;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 }
1224 }1233 }
12251234
1226 /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)1235 /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
...@@ -1658,11 +1667,8 @@ const Parser = struct {...@@ -1658,11 +1667,8 @@ const Parser = struct {
1658 var colon: TokenIndex = undefined;1667 var colon: TokenIndex = undefined;
1659 const label = p.parseBlockLabel(&colon);1668 const label = p.parseBlockLabel(&colon);
16601669
1661 if (label) |token| {1670 if (label) |label_token| {
1662 if (try p.parseBlock()) |node| {1671 if (try p.parseBlock(label_token)) |node| return node;
1663 node.cast(Node.Block).?.label = token;
1664 return node;
1665 }
1666 }1672 }
16671673
1668 if (try p.parseLoopTypeExpr()) |node| {1674 if (try p.parseLoopTypeExpr()) |node| {
...@@ -3440,6 +3446,7 @@ const Parser = struct {...@@ -3440,6 +3446,7 @@ const Parser = struct {
3440 }3446 }
3441 }3447 }
34423448
3449 /// TODO Delete this function. I don't like the inversion of control.
3443 fn expectNode(3450 fn expectNode(
3444 p: *Parser,3451 p: *Parser,
3445 parseFn: NodeParseFn,3452 parseFn: NodeParseFn,
...@@ -3449,6 +3456,7 @@ const Parser = struct {...@@ -3449,6 +3456,7 @@ const Parser = struct {
3449 return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError;3456 return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError;
3450 }3457 }
34513458
3459 /// TODO Delete this function. I don't like the inversion of control.
3452 fn expectNodeRecoverable(3460 fn expectNodeRecoverable(
3453 p: *Parser,3461 p: *Parser,
3454 parseFn: NodeParseFn,3462 parseFn: NodeParseFn,
lib/std/zig/render.zig+32-9
...@@ -392,28 +392,50 @@ fn renderExpression(...@@ -392,28 +392,50 @@ fn renderExpression(
392 return renderToken(tree, stream, any_type.token, indent, start_col, space);392 return renderToken(tree, stream, any_type.token, indent, start_col, space);
393 },393 },
394394
395 .Block => {395 .Block, .LabeledBlock => {
396 const block = @fieldParentPtr(ast.Node.Block, "base", base);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
398 if (block.label) |label| {421 if (block.label) |label| {
399 try renderToken(tree, stream, label, indent, start_col, Space.None);422 try renderToken(tree, stream, label, indent, start_col, Space.None);
400 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);423 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);
401 }424 }
402425
403 if (block.statements_len == 0) {426 if (block.statements.len == 0) {
404 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);427 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);
405 return renderToken(tree, stream, block.rbrace, indent, start_col, space);428 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
406 } else {429 } else {
407 const block_indent = indent + indent_delta;430 const block_indent = indent + indent_delta;
408 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);431 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);
409432
410 const block_statements = block.statements();433 for (block.statements) |statement, i| {
411 for (block_statements) |statement, i| {
412 try stream.writeByteNTimes(' ', block_indent);434 try stream.writeByteNTimes(' ', block_indent);
413 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);435 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);
414436
415 if (i + 1 < block_statements.len) {437 if (i + 1 < block.statements.len) {
416 try renderExtraNewline(tree, stream, start_col, block_statements[i + 1]);438 try renderExtraNewline(tree, stream, start_col, block.statements[i + 1]);
417 }439 }
418 }440 }
419441
...@@ -1841,7 +1863,7 @@ fn renderExpression(...@@ -1841,7 +1863,7 @@ fn renderExpression(
18411863
1842 const rparen = tree.nextToken(for_node.array_expr.lastToken());1864 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();
1845 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());1867 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
1846 const body_on_same_line = body_is_block or src_one_line_to_body;1868 const body_on_same_line = body_is_block or src_one_line_to_body;
18471869
...@@ -2385,7 +2407,7 @@ fn renderTokenOffset(...@@ -2385,7 +2407,7 @@ fn renderTokenOffset(
2385 }2407 }
2386 }2408 }
23872409
2388 if (next_token_id != .LineComment) blk: {2410 if (next_token_id != .LineComment) {
2389 switch (space) {2411 switch (space) {
2390 Space.None, Space.NoNewline => return,2412 Space.None, Space.NoNewline => return,
2391 Space.Newline => {2413 Space.Newline => {
...@@ -2578,6 +2600,7 @@ fn renderDocCommentsToken(...@@ -2578,6 +2600,7 @@ fn renderDocCommentsToken(
2578fn nodeIsBlock(base: *const ast.Node) bool {2600fn nodeIsBlock(base: *const ast.Node) bool {
2579 return switch (base.tag) {2601 return switch (base.tag) {
2580 .Block,2602 .Block,
2603 .LabeledBlock,
2581 .If,2604 .If,
2582 .For,2605 .For,
2583 .While,2606 .While,
src-self-hosted/Module.zig+226-77
...@@ -6,7 +6,7 @@ const Value = @import("value.zig").Value;...@@ -6,7 +6,7 @@ const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;6const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");7const TypedValue = @import("TypedValue.zig");
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const log = std.log;9const log = std.log.scoped(.module);
10const BigIntConst = std.math.big.int.Const;10const BigIntConst = std.math.big.int.Const;
11const BigIntMutable = std.math.big.int.Mutable;11const BigIntMutable = std.math.big.int.Mutable;
12const Target = std.Target;12const Target = std.Target;
...@@ -177,14 +177,14 @@ pub const Decl = struct {...@@ -177,14 +177,14 @@ pub const Decl = struct {
177177
178 /// Represents the position of the code in the output file.178 /// Represents the position of the code in the output file.
179 /// This is populated regardless of semantic analysis and code generation.179 /// 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
182 /// Represents the function in the linked output file, if the `Decl` is a function.182 /// Represents the function in the linked output file, if the `Decl` is a function.
183 /// This is stored here and not in `Fn` because `Decl` survives across updates but183 /// This is stored here and not in `Fn` because `Decl` survives across updates but
184 /// `Fn` does not.184 /// `Fn` does not.
185 /// TODO Look into making `Fn` a longer lived structure and moving this field there185 /// TODO Look into making `Fn` a longer lived structure and moving this field there
186 /// to save on memory usage.186 /// to save on memory usage.
187 fn_link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,187 fn_link: link.File.LinkFn,
188188
189 contents_hash: std.zig.SrcHash,189 contents_hash: std.zig.SrcHash,
190190
...@@ -301,6 +301,23 @@ pub const Fn = struct {...@@ -301,6 +301,23 @@ pub const Fn = struct {
301 body: zir.Module.Body,301 body: zir.Module.Body,
302 arena: std.heap.ArenaAllocator.State,302 arena: std.heap.ArenaAllocator.State,
303 };303 };
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 }
304};321};
305322
306pub const Scope = struct {323pub const Scope = struct {
...@@ -720,6 +737,13 @@ pub const Scope = struct {...@@ -720,6 +737,13 @@ pub const Scope = struct {
720 arena: *Allocator,737 arena: *Allocator,
721 /// The first N instructions in a function body ZIR are arg instructions.738 /// The first N instructions in a function body ZIR are arg instructions.
722 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},739 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 };
723 };747 };
724748
725 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.749 /// 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 {...@@ -949,10 +973,8 @@ pub fn update(self: *Module) !void {
949 try self.deleteDecl(decl);973 try self.deleteDecl(decl);
950 }974 }
951975
952 if (self.totalErrorCount() == 0) {976 // This is needed before reading the error flags.
953 // This is needed before reading the error flags.977 try self.bin_file.flush(self);
954 try self.bin_file.flush();
955 }
956978
957 self.link_error_flags = self.bin_file.errorFlags();979 self.link_error_flags = self.bin_file.errorFlags();
958980
...@@ -1057,7 +1079,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1057,7 +1079,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1057 // lifetime annotations in the ZIR.1079 // lifetime annotations in the ZIR.
1058 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);1080 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1059 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;1081 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});
1061 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);1083 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
1062 }1084 }
10631085
...@@ -1119,7 +1141,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {...@@ -1119,7 +1141,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1119 .complete => return,1141 .complete => return,
11201142
1121 .outdated => blk: {1143 .outdated => blk: {
1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});1144 log.debug("re-analyzing {}\n", .{decl.name});
11231145
1124 // The exports this Decl performs will be re-discovered, so we remove them here1146 // The exports this Decl performs will be re-discovered, so we remove them here
1125 // prior to re-analysis.1147 // prior to re-analysis.
...@@ -1303,14 +1325,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1303,14 +1325,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1303 for (fn_proto.params()) |param, i| {1325 for (fn_proto.params()) |param, i| {
1304 const name_token = param.name_token.?;1326 const name_token = param.name_token.?;
1305 const src = tree.token_locs[name_token].start;1327 const src = tree.token_locs[name_token].start;
1306 const param_name = tree.tokenSlice(name_token);1328 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
1307 const arg = try gen_scope_arena.allocator.create(zir.Inst.NoOp);1329 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
1308 arg.* = .{1330 arg.* = .{
1309 .base = .{1331 .base = .{
1310 .tag = .arg,1332 .tag = .arg,
1311 .src = src,1333 .src = src,
1312 },1334 },
1313 .positionals = .{},1335 .positionals = .{
1336 .name = param_name,
1337 },
1314 .kw_args = .{},1338 .kw_args = .{},
1315 };1339 };
1316 gen_scope.instructions.items[i] = &arg.base;1340 gen_scope.instructions.items[i] = &arg.base;
...@@ -1328,8 +1352,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1328,8 +1352,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13281352
1329 try astgen.blockExpr(self, params_scope, body_block);1353 try astgen.blockExpr(self, params_scope, body_block);
13301354
1331 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or1355 if (gen_scope.instructions.items.len == 0 or
1332 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))1356 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
1333 {1357 {
1334 const src = tree.token_locs[body_block.rbrace].start;1358 const src = tree.token_locs[body_block.rbrace].start;
1335 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);1359 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
...@@ -1538,10 +1562,16 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1538,10 +1562,16 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1538 if (!srcHashEql(decl.contents_hash, contents_hash)) {1562 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1539 try self.markOutdatedDecl(decl);1563 try self.markOutdatedDecl(decl);
1540 decl.contents_hash = contents_hash;1564 decl.contents_hash = contents_hash;
1541 } else if (decl.fn_link.len != 0) {1565 } else switch (self.bin_file.tag) {
1542 // TODO Look into detecting when this would be unnecessary by storing enough state1566 .elf => if (decl.fn_link.elf.len != 0) {
1543 // in `Decl` to notice that the line number did not change.1567 // TODO Look into detecting when this would be unnecessary by storing enough state
1544 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });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 => {},
1545 }1575 }
1546 }1576 }
1547 } else {1577 } else {
...@@ -1553,6 +1583,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1553,6 +1583,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1553 }1583 }
1554 }1584 }
1555 }1585 }
1586 } else {
1587 std.debug.panic("TODO: analyzeRootSrcFile {}", .{src_decl.tag});
1556 }1588 }
1557 // TODO also look for global variable declarations1589 // TODO also look for global variable declarations
1558 // TODO also look for comptime blocks and exported globals1590 // TODO also look for comptime blocks and exported globals
...@@ -1560,7 +1592,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1560,7 +1592,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1560 // Handle explicitly deleted decls from the source code. Not to be confused1592 // Handle explicitly deleted decls from the source code. Not to be confused
1561 // with when we delete decls because they are no longer referenced.1593 // with when we delete decls because they are no longer referenced.
1562 for (deleted_decls.items()) |entry| {1594 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});
1564 try self.deleteDecl(entry.key);1596 try self.deleteDecl(entry.key);
1565 }1597 }
1566}1598}
...@@ -1613,7 +1645,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1613,7 +1645,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1613 // Handle explicitly deleted decls from the source code. Not to be confused1645 // Handle explicitly deleted decls from the source code. Not to be confused
1614 // with when we delete decls because they are no longer referenced.1646 // with when we delete decls because they are no longer referenced.
1615 for (deleted_decls.items()) |entry| {1647 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});
1617 try self.deleteDecl(entry.key);1649 try self.deleteDecl(entry.key);
1618 }1650 }
1619}1651}
...@@ -1625,7 +1657,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -1625,7 +1657,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
1625 // not be present in the set, and this does nothing.1657 // not be present in the set, and this does nothing.
1626 decl.scope.removeDecl(decl);1658 decl.scope.removeDecl(decl);
16271659
1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});1660 log.debug("deleting decl '{}'\n", .{decl.name});
1629 const name_hash = decl.fullyQualifiedNameHash();1661 const name_hash = decl.fullyQualifiedNameHash();
1630 self.decl_table.removeAssertDiscard(name_hash);1662 self.decl_table.removeAssertDiscard(name_hash);
1631 // Remove itself from its dependencies, because we are about to destroy the decl pointer.1663 // 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 {...@@ -1712,17 +1744,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1712 const fn_zir = func.analysis.queued;1744 const fn_zir = func.analysis.queued;
1713 defer fn_zir.arena.promote(self.gpa).deinit();1745 defer fn_zir.arena.promote(self.gpa).deinit();
1714 func.analysis = .{ .in_progress = {} };1746 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
1717 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);1749 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
17181750
1719 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1751 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1720 func.analysis = .{ .success = .{ .instructions = instructions } };1752 func.analysis = .{ .success = .{ .instructions = instructions } };
1721 log.debug(.module, "set {} to success\n", .{decl.name});1753 log.debug("set {} to success\n", .{decl.name});
1722}1754}
17231755
1724fn markOutdatedDecl(self: *Module, decl: *Decl) !void {1756fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1725 log.debug(.module, "mark {} outdated\n", .{decl.name});1757 log.debug("mark {} outdated\n", .{decl.name});
1726 try self.work_queue.writeItem(.{ .analyze_decl = decl });1758 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1727 if (self.failed_decls.remove(decl)) |entry| {1759 if (self.failed_decls.remove(decl)) |entry| {
1728 entry.value.destroy(self.gpa);1760 entry.value.destroy(self.gpa);
...@@ -1745,7 +1777,18 @@ fn allocateNewDecl(...@@ -1745,7 +1777,18 @@ fn allocateNewDecl(
1745 .analysis = .unreferenced,1777 .analysis = .unreferenced,
1746 .deletion_flag = false,1778 .deletion_flag = false,
1747 .contents_hash = contents_hash,1779 .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 },
1749 .generation = 0,1792 .generation = 0,
1750 };1793 };
1751 return new_decl;1794 return new_decl;
...@@ -1926,6 +1969,20 @@ pub fn addBinOp(...@@ -1926,6 +1969,20 @@ pub fn addBinOp(
1926 return &inst.base;1969 return &inst.base;
1927}1970}
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
1929pub fn addBr(1986pub fn addBr(
1930 self: *Module,1987 self: *Module,
1931 scope_block: *Scope.Block,1988 scope_block: *Scope.Block,
...@@ -2152,8 +2209,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn...@@ -2152,8 +2209,11 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
2152 };2209 };
21532210
2154 const decl_tv = try decl.typedValue();2211 const decl_tv = try decl.typedValue();
2155 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);2212 const ty_payload = try scope.arena().create(Type.Payload.Pointer);
2156 ty_payload.* = .{ .pointee_type = decl_tv.ty };2213 ty_payload.* = .{
2214 .base = .{ .tag = .single_const_pointer },
2215 .pointee_type = decl_tv.ty,
2216 };
2157 const val_payload = try scope.arena().create(Value.Payload.DeclRef);2217 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
2158 val_payload.* = .{ .decl = decl };2218 val_payload.* = .{ .decl = decl };
21592219
...@@ -2195,11 +2255,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {...@@ -2195,11 +2255,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {
2195 };2255 };
2196}2256}
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
2203pub fn analyzeIsNull(2258pub fn analyzeIsNull(
2204 self: *Module,2259 self: *Module,
2205 scope: *Scope,2260 scope: *Scope,
...@@ -2382,6 +2437,15 @@ pub fn cmpNumeric(...@@ -2382,6 +2437,15 @@ pub fn cmpNumeric(
2382 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);2437 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
2383}2438}
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
2385fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {2449fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
2386 if (signed) {2450 if (signed) {
2387 const int_payload = try scope.arena().create(Type.Payload.IntSigned);2451 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...@@ -2452,6 +2516,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2452 }2516 }
2453 assert(inst.ty.zigTypeTag() != .Undefined);2517 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
2455 // *[N]T to []T2535 // *[N]T to []T
2456 if (inst.ty.isSinglePointer() and dest_type.isSlice() and2536 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2457 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))2537 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
...@@ -2466,39 +2546,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2466,39 +2546,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2466 }2546 }
24672547
2468 // comptime known number to other number2548 // comptime known number to other number
2469 if (inst.value()) |val| {2549 if (try self.coerceNum(scope, dest_type, inst)) |some|
2470 const src_zig_tag = inst.ty.zigTypeTag();2550 return some;
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 }
25022551
2503 // integer widening2552 // integer widening
2504 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {2553 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...@@ -2527,7 +2576,43 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2527 }2576 }
2528 }2577 }
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;
2531}2616}
25322617
2533pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {2618pub 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:...@@ -2774,7 +2859,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
2774 val_payload.* = .{ .val = lhs_val + rhs_val };2859 val_payload.* = .{ .val = lhs_val + rhs_val };
2775 break :blk &val_payload.base;2860 break :blk &val_payload.base;
2776 },2861 },
2777 128 => blk: {2862 128 => {
2778 return self.fail(scope, src, "TODO Implement addition for big floats", .{});2863 return self.fail(scope, src, "TODO Implement addition for big floats", .{});
2779 },2864 },
2780 else => unreachable,2865 else => unreachable,
...@@ -2808,7 +2893,7 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:...@@ -2808,7 +2893,7 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
2808 val_payload.* = .{ .val = lhs_val - rhs_val };2893 val_payload.* = .{ .val = lhs_val - rhs_val };
2809 break :blk &val_payload.base;2894 break :blk &val_payload.base;
2810 },2895 },
2811 128 => blk: {2896 128 => {
2812 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});2897 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
2813 },2898 },
2814 else => unreachable,2899 else => unreachable,
...@@ -2817,15 +2902,12 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:...@@ -2817,15 +2902,12 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
2817 return Value.initPayload(val_payload);2902 return Value.initPayload(val_payload);
2818}2903}
28192904
2820pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {2905pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) error{OutOfMemory}!Type {
2821 const type_payload = try scope.arena().create(Type.Payload.SingleMutPointer);2906 const type_payload = try scope.arena().create(Type.Payload.Pointer);
2822 type_payload.* = .{ .pointee_type = elem_ty };2907 type_payload.* = .{
2823 return Type.initPayload(&type_payload.base);2908 .base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer },
2824}2909 .pointee_type = elem_ty,
28252910 };
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 };
2829 return Type.initPayload(&type_payload.base);2911 return Type.initPayload(&type_payload.base);
2830}2912}
28312913
...@@ -2860,3 +2942,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2860,3 +2942,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2860 });2942 });
2861 }2943 }
2862}2944}
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...@@ -47,21 +47,34 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
47/// Turn Zig AST into untyped ZIR istructions.47/// Turn Zig AST into untyped ZIR istructions.
48pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {48pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
49 switch (node.tag) {49 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.
50 .VarDecl => unreachable, // Handled in `blockExpr`.54 .VarDecl => unreachable, // Handled in `blockExpr`.
51 .Assign => unreachable, // Handled in `blockExpr`.55 .SwitchCase => unreachable, // Handled in `switchExpr`.
52 .AssignBitAnd => unreachable, // Handled in `blockExpr`.56 .SwitchElse => unreachable, // Handled in `switchExpr`.
53 .AssignBitOr => unreachable, // Handled in `blockExpr`.57 .Else => unreachable, // Handled explicitly the control flow expression functions.
54 .AssignBitShiftLeft => unreachable, // Handled in `blockExpr`.58 .Payload => unreachable, // Handled explicitly.
55 .AssignBitShiftRight => unreachable, // Handled in `blockExpr`.59 .PointerPayload => unreachable, // Handled explicitly.
56 .AssignBitXor => unreachable, // Handled in `blockExpr`.60 .PointerIndexPayload => unreachable, // Handled explicitly.
57 .AssignDiv => unreachable, // Handled in `blockExpr`.61 .ErrorTag => unreachable, // Handled explicitly.
58 .AssignSub => unreachable, // Handled in `blockExpr`.62 .FieldInitializer => unreachable, // Handled explicitly.
59 .AssignSubWrap => unreachable, // Handled in `blockExpr`.63
60 .AssignMod => unreachable, // Handled in `blockExpr`.64 .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
61 .AssignAdd => unreachable, // Handled in `blockExpr`.65 .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
62 .AssignAddWrap => unreachable, // Handled in `blockExpr`.66 .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
63 .AssignMul => unreachable, // Handled in `blockExpr`.67 .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
64 .AssignMulWrap => unreachable, // Handled in `blockExpr`.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
66 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),79 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
67 .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),80 .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...@@ -96,41 +109,186 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
96 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),109 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
97 .Return => return ret(mod, scope, node.castTag(.Return).?),110 .Return => return ret(mod, scope, node.castTag(.Return).?),
98 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),111 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
112 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
99 .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)),113 .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)),
100 .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),114 .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
101 .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),115 .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).?)),
102 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),117 .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
103 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),118 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
104 .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),119 .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
105 .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),120 .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", .{}),
107 }168 }
108}169}
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 {
111 const tracy = trace(@src());221 const tracy = trace(@src());
112 defer tracy.end();222 defer tracy.end();
113223
114 if (block_node.label) |label| {224 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
115 return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{});225}
116 }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
118 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);279 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
119 defer block_arena.deinit();280 defer block_arena.deinit();
120281
121 var scope = parent_scope;282 var scope = parent_scope;
122 for (block_node.statements()) |statement| {283 for (statements) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;284 const src = tree.token_locs[statement.firstToken()].start;
124 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);285 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
125 switch (statement.tag) {286 switch (statement.tag) {
126 .VarDecl => {287 .VarDecl => {
127 const var_decl_node = statement.castTag(.VarDecl).?;288 const var_decl_node = statement.castTag(.VarDecl).?;
128 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);289 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
129 },290 },
130 .Assign => {291 .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
131 const ass = statement.castTag(.Assign).?;
132 try assign(mod, scope, ass);
133 },
134 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),292 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
135 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),293 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
136 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),294 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
...@@ -177,76 +335,49 @@ fn varDecl(...@@ -177,76 +335,49 @@ fn varDecl(
177 // Depending on the type of AST the initialization expression is, we may need an lvalue335 // Depending on the type of AST the initialization expression is, we may need an lvalue
178 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as336 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
179 // the variable, no memory location needed.337 // the variable, no memory location needed.
180 if (nodeMayNeedMemoryLocation(init_node)) {338 const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
181 if (node.getTrailer("type_node")) |type_node| {339 if (node.getTrailer("type_node")) |type_node| {
182 const type_inst = try typeExpr(mod, scope, type_node);340 const type_inst = try typeExpr(mod, scope, type_node);
183 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);341 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
184 const result_loc: ResultLoc = .{ .ptr = alloc };342 break :r 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;
194 } else {343 } else {
195 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);344 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
196 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };345 break :r 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;
206 }346 }
207 } else {347 } else r: {
208 const result_loc: ResultLoc = if (node.getTrailer("type_node")) |type_node|348 if (node.getTrailer("type_node")) |type_node|
209 .{ .ty = try typeExpr(mod, scope, type_node) }349 break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
210 else350 else
211 .none;351 break :r .none;
212 const init_inst = try expr(mod, scope, result_loc, init_node);352 };
213 const sub_scope = try block_arena.create(Scope.LocalVal);353 const init_inst = try expr(mod, scope, result_loc, init_node);
214 sub_scope.* = .{354 const sub_scope = try block_arena.create(Scope.LocalVal);
215 .parent = scope,355 sub_scope.* = .{
216 .gen_zir = scope.getGenZIR(),356 .parent = scope,
217 .name = ident_name,357 .gen_zir = scope.getGenZIR(),
218 .inst = init_inst,358 .name = ident_name,
219 };359 .inst = init_inst,
220 return &sub_scope.base;360 };
221 }361 return &sub_scope.base;
222 },362 },
223 .Keyword_var => {363 .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: {
225 const type_inst = try typeExpr(mod, scope, type_node);365 const type_inst = try typeExpr(mod, scope, type_node);
226 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);366 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
227 const result_loc: ResultLoc = .{ .ptr = alloc };367 break :a .{ .alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst), .result_loc = .{ .ptr = alloc } };
228 const init_inst = try expr(mod, scope, result_loc, init_node);368 } else a: {
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 {
238 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);369 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
239 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };370 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
240 const init_inst = try expr(mod, scope, result_loc, init_node);371 };
241 const sub_scope = try block_arena.create(Scope.LocalPtr);372 const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
242 sub_scope.* = .{373 const sub_scope = try block_arena.create(Scope.LocalPtr);
243 .parent = scope,374 sub_scope.* = .{
244 .gen_zir = scope.getGenZIR(),375 .parent = scope,
245 .name = ident_name,376 .gen_zir = scope.getGenZIR(),
246 .ptr = alloc,377 .name = ident_name,
247 };378 .ptr = var_data.alloc,
248 return &sub_scope.base;379 };
249 }380 return &sub_scope.base;
250 },381 },
251 else => unreachable,382 else => unreachable,
252 }383 }
...@@ -256,7 +387,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne...@@ -256,7 +387,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne
256 if (infix_node.lhs.castTag(.Identifier)) |ident| {387 if (infix_node.lhs.castTag(.Identifier)) |ident| {
257 // This intentionally does not support @"_" syntax.388 // This intentionally does not support @"_" syntax.
258 const ident_name = scope.tree().tokenSlice(ident.token);389 const ident_name = scope.tree().tokenSlice(ident.token);
259 if (std.mem.eql(u8, ident_name, "_")) {390 if (mem.eql(u8, ident_name, "_")) {
260 _ = try expr(mod, scope, .discard, infix_node.rhs);391 _ = try expr(mod, scope, .discard, infix_node.rhs);
261 return;392 return;
262 }393 }
...@@ -294,12 +425,90 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr...@@ -294,12 +425,90 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr
294 return addZIRUnOp(mod, scope, src, .boolnot, operand);425 return addZIRUnOp(mod, scope, src, .boolnot, operand);
295}426}
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
297/// Identifier token -> String (allocated in scope.arena())506/// 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 {
299 const tree = scope.tree();508 const tree = scope.tree();
300509
301 const ident_name = tree.tokenSlice(token);510 const ident_name = tree.tokenSlice(token);
302 if (std.mem.startsWith(u8, ident_name, "@")) {511 if (mem.startsWith(u8, ident_name, "@")) {
303 const raw_string = ident_name[1..];512 const raw_string = ident_name[1..];
304 var bad_index: usize = undefined;513 var bad_index: usize = undefined;
305 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {514 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
...@@ -359,13 +568,77 @@ fn simpleBinOp(...@@ -359,13 +568,77 @@ fn simpleBinOp(
359 return rlWrap(mod, scope, rl, result);568 return rlWrap(mod, scope, rl, result);
360}569}
361570
362fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {571const CondKind = union(enum) {
363 if (if_node.payload) |payload| {572 bool,
364 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});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", .{});
365 }633 }
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 };
366 if (if_node.@"else") |else_node| {639 if (if_node.@"else") |else_node| {
367 if (else_node.payload) |payload| {640 if (else_node.payload) |payload| {
368 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});641 cond_kind = .{ .err_union = null };
369 }642 }
370 }643 }
371 var block_scope: Scope.GenZIR = .{644 var block_scope: Scope.GenZIR = .{
...@@ -378,11 +651,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -378,11 +651,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
378651
379 const tree = scope.tree();652 const tree = scope.tree();
380 const if_src = tree.token_locs[if_node.if_token].start;653 const if_src = tree.token_locs[if_node.if_token].start;
381 const bool_type = try addZIRInstConst(mod, scope, if_src, .{654 const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
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);
386655
387 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{656 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
388 .condition = cond,657 .condition = cond,
...@@ -393,6 +662,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -393,6 +662,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
393 const block = try addZIRInstBlock(mod, scope, if_src, .{662 const block = try addZIRInstBlock(mod, scope, if_src, .{
394 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),663 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
395 });664 });
665
666 const then_src = tree.token_locs[if_node.body.lastToken()].start;
396 var then_scope: Scope.GenZIR = .{667 var then_scope: Scope.GenZIR = .{
397 .parent = scope,668 .parent = scope,
398 .decl = block_scope.decl,669 .decl = block_scope.decl,
...@@ -401,6 +672,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -401,6 +672,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
401 };672 };
402 defer then_scope.instructions.deinit(mod.gpa);673 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
404 // Most result location types can be forwarded directly; however678 // Most result location types can be forwarded directly; however
405 // if we need to write to a pointer which has an inferred type,679 // if we need to write to a pointer which has an inferred type,
406 // proper type inference requires peer type resolution on the if's680 // 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...@@ -410,10 +684,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
410 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },684 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
411 };685 };
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);
414 if (!then_result.tag.isNoReturn()) {688 if (!then_result.tag.isNoReturn()) {
415 const then_src = tree.token_locs[if_node.body.lastToken()].start;689 _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
416 _ = try addZIRInst(mod, &then_scope.base, then_src, zir.Inst.Break, .{
417 .block = block,690 .block = block,
418 .operand = then_result,691 .operand = then_result,
419 }, .{});692 }, .{});
...@@ -431,10 +704,13 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -431,10 +704,13 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
431 defer else_scope.instructions.deinit(mod.gpa);704 defer else_scope.instructions.deinit(mod.gpa);
432705
433 if (if_node.@"else") |else_node| {706 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);
435 if (!else_result.tag.isNoReturn()) {712 if (!else_result.tag.isNoReturn()) {
436 const else_src = tree.token_locs[else_node.body.lastToken()].start;713 _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
437 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
438 .block = block,714 .block = block,
439 .operand = else_result,715 .operand = else_result,
440 }, .{});716 }, .{});
...@@ -454,6 +730,133 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -454,6 +730,133 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
454 return &block.base;730 return &block.base;
455}731}
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
457fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {860fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
458 const tree = scope.tree();861 const tree = scope.tree();
459 const src = tree.token_locs[cfe.ltoken].start;862 const src = tree.token_locs[cfe.ltoken].start;
...@@ -510,7 +913,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -510,7 +913,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
510 const int_type_payload = try scope.arena().create(Value.Payload.IntType);913 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
511 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };914 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
512 const result = try addZIRInstConst(mod, scope, src, .{915 const result = try addZIRInstConst(mod, scope, src, .{
513 .ty = Type.initTag(.comptime_int),916 .ty = Type.initTag(.type),
514 .val = Value.initPayload(&int_type_payload.base),917 .val = Value.initPayload(&int_type_payload.base),
515 });918 });
516 return rlWrap(mod, scope, rl, result);919 return rlWrap(mod, scope, rl, result);
...@@ -852,6 +1255,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built...@@ -852,6 +1255,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
852 return simpleCast(mod, scope, rl, call, .intcast);1255 return simpleCast(mod, scope, rl, call, .intcast);
853 } else if (mem.eql(u8, builtin_name, "@bitCast")) {1256 } else if (mem.eql(u8, builtin_name, "@bitCast")) {
854 return bitCast(mod, scope, rl, call);1257 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));
855 } else {1261 } else {
856 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});1262 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
857 }1263 }
...@@ -1022,6 +1428,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {...@@ -1022,6 +1428,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
1022 .Slice,1428 .Slice,
1023 .Deref,1429 .Deref,
1024 .ArrayAccess,1430 .ArrayAccess,
1431 .Block,
1025 => return false,1432 => return false,
10261433
1027 // Forward the question to a sub-expression.1434 // Forward the question to a sub-expression.
...@@ -1048,11 +1455,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {...@@ -1048,11 +1455,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
1048 .Switch,1455 .Switch,
1049 .Call,1456 .Call,
1050 .BuiltinCall, // TODO some of these can return false1457 .BuiltinCall, // TODO some of these can return false
1458 .LabeledBlock,
1051 => return true,1459 => return true,
10521460
1053 // Depending on AST properties, they may need memory locations.1461 // Depending on AST properties, they may need memory locations.
1054 .If => return node.castTag(.If).?.@"else" != null,1462 .If => return node.castTag(.If).?.@"else" != null,
1055 .Block => return node.castTag(.Block).?.label != null,
1056 }1463 }
1057 }1464 }
1058}1465}
...@@ -1094,6 +1501,15 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1094,6 +1501,15 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1094 }1501 }
1095}1502}
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
1097pub fn addZIRInstSpecial(1513pub fn addZIRInstSpecial(
1098 mod: *Module,1514 mod: *Module,
1099 scope: *Scope,1515 scope: *Scope,
...@@ -1211,3 +1627,9 @@ pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module...@@ -1211,3 +1627,9 @@ pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module
1211 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;1627 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
1212 return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});1628 return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
1213}1629}
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 @@...@@ -1,8 +1,15 @@
1#if __STDC_VERSION__ >= 201112L1#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn2#define zig_noreturn _Noreturn
3#elif __GNUC__ && !__STRICT_ANSI__3#elif __GNUC__
4#define noreturn __attribute__ ((noreturn))4#define zig_noreturn __attribute__ ((noreturn))
5#elif _MSC_VER
6#define zig_noreturn __declspec(noreturn)
5#else7#else
6#define noreturn8#define zig_noreturn
7#endif9#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...@@ -1141,6 +1141,7 @@ pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigC
11411141
1142pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool;1142pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool;
1143pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation;1143pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation;
1144pub extern fn ZigClangIntegerLiteral_isZero(*const ZigClangIntegerLiteral, *bool, *const ZigClangASTContext) bool;
11441145
1145pub extern fn ZigClangReturnStmt_getRetValue(*const ZigClangReturnStmt) ?*const ZigClangExpr;1146pub 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;...@@ -20,7 +20,21 @@ const leb128 = std.debug.leb;
2020
21/// The codegen-related data that is stored in `ir.Inst.Block` instructions.21/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
22pub const BlockData = struct {22pub 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,
24};38};
2539
26pub const Reloc = union(enum) {40pub const Reloc = union(enum) {
...@@ -50,6 +64,8 @@ pub fn generateSymbol(...@@ -50,6 +64,8 @@ pub fn generateSymbol(
50 typed_value: TypedValue,64 typed_value: TypedValue,
51 code: *std.ArrayList(u8),65 code: *std.ArrayList(u8),
52 dbg_line: *std.ArrayList(u8),66 dbg_line: *std.ArrayList(u8),
67 dbg_info: *std.ArrayList(u8),
68 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
53) GenerateSymbolError!Result {69) GenerateSymbolError!Result {
54 const tracy = trace(@src());70 const tracy = trace(@src());
55 defer tracy.end();71 defer tracy.end();
...@@ -57,61 +73,62 @@ pub fn generateSymbol(...@@ -57,61 +73,62 @@ pub fn generateSymbol(
57 switch (typed_value.ty.zigTypeTag()) {73 switch (typed_value.ty.zigTypeTag()) {
58 .Fn => {74 .Fn => {
59 switch (bin_file.base.options.target.cpu.arch) {75 switch (bin_file.base.options.target.cpu.arch) {
60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),76 .wasm32 => unreachable, // has its own code path
61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),77 .wasm64 => unreachable, // has its own code path
62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),78 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),79 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),80 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),81 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),82 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),83 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),84 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),85 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),86 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),87 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),88 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),89 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),90 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),91 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),92 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),93 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),94 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),95 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),96 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),97 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),98 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),99 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),100 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),101 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),102 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),103 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),104 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),105 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),106 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),107 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),108 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),109 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),110 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),111 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),112 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),113 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),114 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),115 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),116 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),117 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),118 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),119 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),120 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),121 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),122 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),123 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),124 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),125 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),126 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
111 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."),127 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."),
112 }128 }
113 },129 },
114 .Array => {130 .Array => {
131 // TODO populate .debug_info for the array
115 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {132 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
116 if (typed_value.ty.arraySentinel()) |sentinel| {133 if (typed_value.ty.arraySentinel()) |sentinel| {
117 try code.ensureCapacity(code.items.len + payload.data.len + 1);134 try code.ensureCapacity(code.items.len + payload.data.len + 1);
...@@ -120,7 +137,7 @@ pub fn generateSymbol(...@@ -120,7 +137,7 @@ pub fn generateSymbol(
120 switch (try generateSymbol(bin_file, src, .{137 switch (try generateSymbol(bin_file, src, .{
121 .ty = typed_value.ty.elemType(),138 .ty = typed_value.ty.elemType(),
122 .val = sentinel,139 .val = sentinel,
123 }, code, dbg_line)) {140 }, code, dbg_line, dbg_info, dbg_info_type_relocs)) {
124 .appended => return Result{ .appended = {} },141 .appended => return Result{ .appended = {} },
125 .externally_managed => |slice| {142 .externally_managed => |slice| {
126 code.appendSliceAssumeCapacity(slice);143 code.appendSliceAssumeCapacity(slice);
...@@ -134,7 +151,7 @@ pub fn generateSymbol(...@@ -134,7 +151,7 @@ pub fn generateSymbol(
134 }151 }
135 return Result{152 return Result{
136 .fail = try ErrorMsg.create(153 .fail = try ErrorMsg.create(
137 bin_file.allocator,154 bin_file.base.allocator,
138 src,155 src,
139 "TODO implement generateSymbol for more kinds of arrays",156 "TODO implement generateSymbol for more kinds of arrays",
140 .{},157 .{},
...@@ -142,29 +159,36 @@ pub fn generateSymbol(...@@ -142,29 +159,36 @@ pub fn generateSymbol(
142 };159 };
143 },160 },
144 .Pointer => {161 .Pointer => {
162 // TODO populate .debug_info for the pointer
163
145 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {164 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
146 const decl = payload.decl;165 const decl = payload.decl;
147 if (decl.analysis != .complete) return error.AnalysisFail;166 if (decl.analysis != .complete) return error.AnalysisFail;
148 assert(decl.link.local_sym_index != 0);167 assert(decl.link.elf.local_sym_index != 0);
149 // TODO handle the dependency of this symbol on the decl's vaddr.168 // TODO handle the dependency of this symbol on the decl's vaddr.
150 // If the decl changes vaddr, then this symbol needs to get regenerated.169 // 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;
152 const endian = bin_file.base.options.target.cpu.arch.endian();171 const endian = bin_file.base.options.target.cpu.arch.endian();
153 switch (bin_file.ptr_width) {172 switch (bin_file.base.options.target.cpu.arch.ptrBitWidth()) {
154 .p32 => {173 16 => {
174 try code.resize(2);
175 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
176 },
177 32 => {
155 try code.resize(4);178 try code.resize(4);
156 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);179 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
157 },180 },
158 .p64 => {181 64 => {
159 try code.resize(8);182 try code.resize(8);
160 mem.writeInt(u64, code.items[0..8], vaddr, endian);183 mem.writeInt(u64, code.items[0..8], vaddr, endian);
161 },184 },
185 else => unreachable,
162 }186 }
163 return Result{ .appended = {} };187 return Result{ .appended = {} };
164 }188 }
165 return Result{189 return Result{
166 .fail = try ErrorMsg.create(190 .fail = try ErrorMsg.create(
167 bin_file.allocator,191 bin_file.base.allocator,
168 src,192 src,
169 "TODO implement generateSymbol for pointer {}",193 "TODO implement generateSymbol for pointer {}",
170 .{typed_value.val},194 .{typed_value.val},
...@@ -172,6 +196,8 @@ pub fn generateSymbol(...@@ -172,6 +196,8 @@ pub fn generateSymbol(
172 };196 };
173 },197 },
174 .Int => {198 .Int => {
199 // TODO populate .debug_info for the integer
200
175 const info = typed_value.ty.intInfo(bin_file.base.options.target);201 const info = typed_value.ty.intInfo(bin_file.base.options.target);
176 if (info.bits == 8 and !info.signed) {202 if (info.bits == 8 and !info.signed) {
177 const x = typed_value.val.toUnsignedInt();203 const x = typed_value.val.toUnsignedInt();
...@@ -180,7 +206,7 @@ pub fn generateSymbol(...@@ -180,7 +206,7 @@ pub fn generateSymbol(
180 }206 }
181 return Result{207 return Result{
182 .fail = try ErrorMsg.create(208 .fail = try ErrorMsg.create(
183 bin_file.allocator,209 bin_file.base.allocator,
184 src,210 src,
185 "TODO implement generateSymbol for int type '{}'",211 "TODO implement generateSymbol for int type '{}'",
186 .{typed_value.ty},212 .{typed_value.ty},
...@@ -190,7 +216,7 @@ pub fn generateSymbol(...@@ -190,7 +216,7 @@ pub fn generateSymbol(
190 else => |t| {216 else => |t| {
191 return Result{217 return Result{
192 .fail = try ErrorMsg.create(218 .fail = try ErrorMsg.create(
193 bin_file.allocator,219 bin_file.base.allocator,
194 src,220 src,
195 "TODO implement generateSymbol for type '{}'",221 "TODO implement generateSymbol for type '{}'",
196 .{@tagName(t)},222 .{@tagName(t)},
...@@ -213,6 +239,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -213,6 +239,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
213 mod_fn: *const Module.Fn,239 mod_fn: *const Module.Fn,
214 code: *std.ArrayList(u8),240 code: *std.ArrayList(u8),
215 dbg_line: *std.ArrayList(u8),241 dbg_line: *std.ArrayList(u8),
242 dbg_info: *std.ArrayList(u8),
243 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
216 err_msg: ?*ErrorMsg,244 err_msg: ?*ErrorMsg,
217 args: []MCValue,245 args: []MCValue,
218 ret_mcv: MCValue,246 ret_mcv: MCValue,
...@@ -382,15 +410,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -382,15 +410,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
382 typed_value: TypedValue,410 typed_value: TypedValue,
383 code: *std.ArrayList(u8),411 code: *std.ArrayList(u8),
384 dbg_line: *std.ArrayList(u8),412 dbg_line: *std.ArrayList(u8),
413 dbg_info: *std.ArrayList(u8),
414 dbg_info_type_relocs: *link.File.Elf.DbgInfoTypeRelocsTable,
385 ) GenerateSymbolError!Result {415 ) GenerateSymbolError!Result {
386 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;416 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
387417
388 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;418 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);
391 defer {421 defer {
392 assert(branch_stack.items.len == 1);422 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);
394 branch_stack.deinit();424 branch_stack.deinit();
395 }425 }
396 const branch = try branch_stack.addOne();426 const branch = try branch_stack.addOne();
...@@ -413,12 +443,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -413,12 +443,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
413 };443 };
414444
415 var function = Self{445 var function = Self{
416 .gpa = bin_file.allocator,446 .gpa = bin_file.base.allocator,
417 .target = &bin_file.base.options.target,447 .target = &bin_file.base.options.target,
418 .bin_file = bin_file,448 .bin_file = bin_file,
419 .mod_fn = module_fn,449 .mod_fn = module_fn,
420 .code = code,450 .code = code,
421 .dbg_line = dbg_line,451 .dbg_line = dbg_line,
452 .dbg_info = dbg_info,
453 .dbg_info_type_relocs = dbg_info_type_relocs,
422 .err_msg = null,454 .err_msg = null,
423 .args = undefined, // populated after `resolveCallingConventionValues`455 .args = undefined, // populated after `resolveCallingConventionValues`
424 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`456 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -432,7 +464,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -432,7 +464,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
432 .rbrace_src = src_data.rbrace_src,464 .rbrace_src = src_data.rbrace_src,
433 .source = src_data.source,465 .source = src_data.source,
434 };466 };
435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);467 defer function.exitlude_jump_relocs.deinit(bin_file.base.allocator);
436468
437 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {469 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
438 error.CodegenFail => return Result{ .fail = function.err_msg.? },470 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -536,7 +568,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -536,7 +568,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
536 }568 }
537569
538 fn genBody(self: *Self, body: ir.Body) InnerError!void {570 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;
540 for (body.instructions) |inst| {573 for (body.instructions) |inst| {
541 const new_inst = try self.genFuncInst(inst);574 const new_inst = try self.genFuncInst(inst);
542 try inst_table.putNoClobber(self.gpa, inst, new_inst);575 try inst_table.putNoClobber(self.gpa, inst, new_inst);
...@@ -596,6 +629,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -596,6 +629,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
596 }629 }
597 }630 }
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
599 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {649 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
600 switch (inst.tag) {650 switch (inst.tag) {
601 .add => return self.genAdd(inst.castTag(.add).?),651 .add => return self.genAdd(inst.castTag(.add).?),
...@@ -621,7 +671,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -621,7 +671,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
621 .intcast => return self.genIntCast(inst.castTag(.intcast).?),671 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
622 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),672 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
623 .isnull => return self.genIsNull(inst.castTag(.isnull).?),673 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
674 .iserr => return self.genIsErr(inst.castTag(.iserr).?),
624 .load => return self.genLoad(inst.castTag(.load).?),675 .load => return self.genLoad(inst.castTag(.load).?),
676 .loop => return self.genLoop(inst.castTag(.loop).?),
625 .not => return self.genNot(inst.castTag(.not).?),677 .not => return self.genNot(inst.castTag(.not).?),
626 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),678 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
627 .ref => return self.genRef(inst.castTag(.ref).?),679 .ref => return self.genRef(inst.castTag(.ref).?),
...@@ -630,6 +682,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -630,6 +682,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
630 .store => return self.genStore(inst.castTag(.store).?),682 .store => return self.genStore(inst.castTag(.store).?),
631 .sub => return self.genSub(inst.castTag(.sub).?),683 .sub => return self.genSub(inst.castTag(.sub).?),
632 .unreach => return MCValue{ .unreach = {} },684 .unreach => return MCValue{ .unreach = {} },
685 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
686 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
633 }687 }
634 }688 }
635689
...@@ -779,6 +833,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -779,6 +833,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779 }833 }
780 }834 }
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
782 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {861 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
783 const elem_ty = inst.base.ty;862 const elem_ty = inst.base.ty;
784 if (!elem_ty.hasCodeGenBits())863 if (!elem_ty.hasCodeGenBits())
...@@ -995,7 +1074,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -995,7 +1074,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
995 }1074 }
996 }1075 }
9971076
998 fn genArg(self: *Self, inst: *ir.Inst.NoOp) !MCValue {1077 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
999 if (FreeRegInt == u0) {1078 if (FreeRegInt == u0) {
1000 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1079 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
1001 }1080 }
...@@ -1008,10 +1087,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1008,10 +1087,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1008 const result = self.args[self.arg_index];1087 const result = self.args[self.arg_index];
1009 self.arg_index += 1;1088 self.arg_index += 1;
10101089
1090 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
1011 switch (result) {1091 switch (result) {
1012 .register => |reg| {1092 .register => |reg| {
1013 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });1093 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
1014 branch.markRegUsed(reg);1094 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
1015 },1104 },
1016 else => {},1105 else => {},
1017 }1106 }
...@@ -1024,11 +1113,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1024,11 +1113,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1024 try self.code.append(0xcc); // int31113 try self.code.append(0xcc); // int3
1025 },1114 },
1026 .riscv64 => {1115 .riscv64 => {
1027 const full = @bitCast(u32, instructions.CallBreak{1116 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
1028 .mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
1029 });
1030
1031 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1032 },1117 },
1033 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1118 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
1034 }1119 }
...@@ -1080,7 +1165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1080,7 +1165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1080 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];1165 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1081 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1166 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1082 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1167 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);
1084 // ff 14 25 xx xx xx xx call [addr]1169 // ff 14 25 xx xx xx xx call [addr]
1085 try self.code.ensureCapacity(self.code.items.len + 7);1170 try self.code.ensureCapacity(self.code.items.len + 7);
1086 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });1171 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
...@@ -1101,15 +1186,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1101,15 +1186,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1101 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];1186 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1102 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1187 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1103 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1188 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
1106 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });1191 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1107 const jalr = instructions.Jalr{1192 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
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));
1113 } else {1193 } else {
1114 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});1194 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1115 }1195 }
...@@ -1166,12 +1246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1166,12 +1246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1166 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);1246 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1167 },1247 },
1168 .riscv64 => {1248 .riscv64 => {
1169 const jalr = instructions.Jalr{1249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
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));
1175 },1250 },
1176 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1251 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1177 }1252 }
...@@ -1226,6 +1301,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1226,6 +1301,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1226 }1301 }
12271302
1228 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {1303 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
1304 // TODO Rework this so that the arch-independent logic isn't buried and duplicated.
1229 switch (arch) {1305 switch (arch) {
1230 .x86_64 => {1306 .x86_64 => {
1231 try self.code.ensureCapacity(self.code.items.len + 6);1307 try self.code.ensureCapacity(self.code.items.len + 6);
...@@ -1278,6 +1354,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1278,6 +1354,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1278 }1354 }
12791355
1280 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {1356 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
1281 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });1358 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
1282 const reloc = Reloc{ .rel32 = self.code.items.len };1359 const reloc = Reloc{ .rel32 = self.code.items.len };
1283 self.code.items.len += 4;1360 self.code.items.len += 4;
...@@ -1301,17 +1378,56 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1301,17 +1378,56 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1301 }1378 }
1302 }1379 }
13031380
1304 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {1381 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1305 if (inst.base.ty.hasCodeGenBits()) {1382 switch (arch) {
1306 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});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}),
1307 }1410 }
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 };
1309 defer inst.codegen.relocs.deinit(self.gpa);1424 defer inst.codegen.relocs.deinit(self.gpa);
1425
1310 try self.genBody(inst.body);1426 try self.genBody(inst.body);
13111427
1312 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);1428 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);
1315 }1431 }
13161432
1317 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {1433 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
...@@ -1331,13 +1447,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1331,13 +1447,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1331 }1447 }
13321448
1333 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {1449 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
1334 if (!inst.operand.ty.hasCodeGenBits())1450 if (inst.operand.ty.hasCodeGenBits()) {
1335 return self.brVoid(inst.base.src, inst.block);1451 const operand = try self.resolveInst(inst.operand);
13361452 const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
1337 const operand = try self.resolveInst(inst.operand);1453 if (block_mcv == .none) {
1338 switch (arch) {1454 inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
1339 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),1455 } else {
1456 try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
1457 }
1340 }1458 }
1459 return self.brVoid(inst.base.src, inst.block);
1341 }1460 }
13421461
1343 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {1462 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
...@@ -1379,11 +1498,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1379,11 +1498,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1379 }1498 }
13801499
1381 if (mem.eql(u8, inst.asm_source, "ecall")) {1500 if (mem.eql(u8, inst.asm_source, "ecall")) {
1382 const full = @bitCast(u32, instructions.CallBreak{1501 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
1383 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
1384 });
1385
1386 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1387 } else {1502 } else {
1388 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});1503 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
1389 }1504 }
...@@ -1590,36 +1705,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1590,36 +1705,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1590 .immediate => |unsigned_x| {1705 .immediate => |unsigned_x| {
1591 const x = @bitCast(i64, unsigned_x);1706 const x = @bitCast(i64, unsigned_x);
1592 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {1707 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1593 const instruction = @bitCast(u32, instructions.Addi{1708 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
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);
1601 return;1709 return;
1602 }1710 }
1603 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {1711 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1604 const split = @bitCast(packed struct {1712 const lo12 = @truncate(i12, x);
1605 low12: i12,1713 const carry: i32 = if (lo12 < 0) 1 else 0;
1606 up20: i20,1714 const hi20 = @truncate(i20, (x >> 12) +% carry);
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);
16151715
1616 const addi = @bitCast(u32, instructions.Addi{1716 // TODO: add test case for 32-bit immediate
1617 .mode = @enumToInt(instructions.Addi.Mode.addi),1717 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
1618 .imm = @truncate(i12, split.low12),1718 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
1619 .rs1 = reg.id(),
1620 .rd = reg.id(),
1621 });
1622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
1623 return;1719 return;
1624 }1720 }
1625 // li rd, immediate1721 // li rd, immediate
...@@ -1631,14 +1727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1631,14 +1727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1631 // If the type is a pointer, it means the pointer address is at this memory location.1727 // If the type is a pointer, it means the pointer address is at this memory location.
1632 try self.genSetReg(src, reg, .{ .immediate = addr });1728 try self.genSetReg(src, reg, .{ .immediate = addr });
16331729
1634 const ld = @bitCast(u32, instructions.Load{1730 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
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);
1642 // LOAD imm=[i12 offset = 0], rs1 =1731 // LOAD imm=[i12 offset = 0], rs1 =
16431732
1644 // return self.fail("TODO implement genSetReg memory for riscv64");1733 // return self.fail("TODO implement genSetReg memory for riscv64");
...@@ -1919,9 +2008,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1919,9 +2008,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1919 return mcv;2008 return mcv;
1920 }2009 }
19212010
1922 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {2011 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
1923 if (typed_value.val.isUndef())2012 if (typed_value.val.isUndef())
1924 return MCValue.undef;2013 return MCValue{ .undef = {} };
1925 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2014 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1926 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2015 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1927 switch (typed_value.ty.zigTypeTag()) {2016 switch (typed_value.ty.zigTypeTag()) {
...@@ -1929,7 +2018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1929,7 +2018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1929 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {2018 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1930 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];2019 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1931 const decl = payload.decl;2020 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;
1933 return MCValue{ .memory = got_addr };2022 return MCValue{ .memory = got_addr };
1934 }2023 }
1935 return self.fail(src, "TODO codegen more kinds of const pointers", .{});2024 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
...@@ -1946,6 +2035,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1946,6 +2035,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1946 },2035 },
1947 .ComptimeInt => unreachable, // semantic analysis prevents this2036 .ComptimeInt => unreachable, // semantic analysis prevents this
1948 .ComptimeFloat => unreachable, // semantic analysis prevents this2037 .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 },
1949 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),2053 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1950 }2054 }
1951 }2055 }
...@@ -2051,10 +2155,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2051,10 +2155,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2051 };2155 };
2052 }2156 }
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 {
2055 @setCold(true);2159 @setCold(true);
2056 assert(self.err_msg == null);2160 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);
2058 return error.CodegenFail;2162 return error.CodegenFail;
2059 }2163 }
20602164
src-self-hosted/codegen/c.zig+174-73
...@@ -11,46 +11,64 @@ const C = link.File.C;...@@ -11,46 +11,64 @@ const C = link.File.C;
11const Decl = Module.Decl;11const Decl = Module.Decl;
12const mem = std.mem;12const mem = std.mem;
1313
14/// Maps a name from Zig source to C. This will always give the same output for14/// Maps a name from Zig source to C. Currently, this will always give the same
15/// any given input.15/// output for any given input, sometimes resulting in broken identifiers.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
17 return allocator.dupe(u8, name);17 return allocator.dupe(u8, name);
18}18}
1919
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {20fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {
21 if (T.tag() == .usize) {21 switch (T.zigTypeTag()) {
22 file.need_stddef = true;22 .NoReturn => {
23 try writer.writeAll("size_t");23 try writer.writeAll("zig_noreturn void");
24 } else {24 },
25 switch (T.zigTypeTag()) {25 .Void => try writer.writeAll("void"),
26 .NoReturn => {26 .Int => {
27 file.need_noreturn = true;27 if (T.tag() == .u8) {
28 try writer.writeAll("noreturn void");28 ctx.file.need_stdint = true;
29 },29 try writer.writeAll("uint8_t");
30 .Void => try writer.writeAll("void"),30 } else if (T.tag() == .usize) {
31 .Int => {31 ctx.file.need_stddef = true;
32 if (T.tag() == .u8) {32 try writer.writeAll("size_t");
33 file.need_stdint = true;33 } else {
34 try writer.writeAll("uint8_t");34 return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{});
35 } else {35 }
36 return file.fail(src, "TODO implement int types", .{});36 },
37 }37 else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
38 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
41 }38 }
42}39}
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 {
45 const tv = decl.typed_value.most_recent.typed_value;53 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());54 try renderType(ctx, writer, tv.ty.fnReturnType());
47 const name = try map(file.allocator, mem.spanZ(decl.name));55 const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);56 defer ctx.file.base.allocator.free(name);
49 try writer.print(" {}(", .{name});57 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)58 var param_len = tv.ty.fnParamLen();
51 try writer.writeAll("void)")59 if (param_len == 0)
52 else60 try writer.writeAll("void")
53 return file.fail(decl.src(), "TODO implement parameters", .{});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(')');
54}72}
5573
56pub fn generate(file: *C, decl: *Decl) !void {74pub fn generate(file: *C, decl: *Decl) !void {
...@@ -64,8 +82,8 @@ pub fn generate(file: *C, decl: *Decl) !void {...@@ -64,8 +82,8 @@ pub fn generate(file: *C, decl: *Decl) !void {
64fn genArray(file: *C, decl: *Decl) !void {82fn genArray(file: *C, decl: *Decl) !void {
65 const tv = decl.typed_value.most_recent.typed_value;83 const tv = decl.typed_value.most_recent.typed_value;
66 // TODO: prevent inline asm constants from being emitted84 // TODO: prevent inline asm constants from being emitted
67 const name = try map(file.allocator, mem.span(decl.name));85 const name = try map(file.base.allocator, mem.span(decl.name));
68 defer file.allocator.free(name);86 defer file.base.allocator.free(name);
69 if (tv.val.cast(Value.Payload.Bytes)) |payload|87 if (tv.val.cast(Value.Payload.Bytes)) |payload|
70 if (tv.ty.arraySentinel()) |sentinel|88 if (tv.ty.arraySentinel()) |sentinel|
71 if (sentinel.toUnsignedInt() == 0)89 if (sentinel.toUnsignedInt() == 0)
...@@ -78,11 +96,40 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -78,11 +96,40 @@ fn genArray(file: *C, decl: *Decl) !void {
78 return file.fail(decl.src(), "TODO non-byte arrays", .{});96 return file.fail(decl.src(), "TODO non-byte arrays", .{});
79}97}
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
81fn genFn(file: *C, decl: *Decl) !void {121fn genFn(file: *C, decl: *Decl) !void {
82 const writer = file.main.writer();122 const writer = file.main.writer();
83 const tv = decl.typed_value.most_recent.typed_value;123 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
87 try writer.writeAll(" {");134 try writer.writeAll(" {");
88135
...@@ -91,13 +138,19 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -91,13 +138,19 @@ fn genFn(file: *C, decl: *Decl) !void {
91 if (instructions.len > 0) {138 if (instructions.len > 0) {
92 try writer.writeAll("\n");139 try writer.writeAll("\n");
93 for (instructions) |inst| {140 for (instructions) |inst| {
94 switch (inst.tag) {141 if (switch (inst.tag) {
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),142 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
96 .call => try genCall(file, inst.castTag(.call).?, decl),143 .call => try genCall(&ctx, inst.castTag(.call).?),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),144 .ret => try genRet(&ctx, inst.castTag(.ret).?),
98 .retvoid => try file.main.writer().print(" return;\n", .{}),145 .retvoid => try genRetVoid(&ctx),
99 .dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),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).?),
100 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),151 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
152 }) |name| {
153 try ctx.inst_map.putNoClobber(inst, name);
101 }154 }
102 }155 }
103 }156 }
...@@ -105,13 +158,40 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -105,13 +158,40 @@ fn genFn(file: *C, decl: *Decl) !void {
105 try writer.writeAll("}\n\n");158 try writer.writeAll("}\n\n");
106}159}
107160
108fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {161fn genArg(ctx: *Context) !?[]u8 {
109 return file.fail(decl.src(), "TODO return {}", .{expected_return_type});162 const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
163 ctx.argdex += 1;
164 return name;
110}165}
111166
112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {167fn genRetVoid(ctx: *Context) !?[]u8 {
113 const writer = file.main.writer();168 try ctx.file.main.writer().print(" return;\n", .{});
114 const header = file.header.writer();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();
115 try writer.writeAll(" ");195 try writer.writeAll(" ");
116 if (inst.func.castTag(.constant)) |func_inst| {196 if (inst.func.castTag(.constant)) |func_inst| {
117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {197 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
...@@ -122,52 +202,77 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -122,52 +202,77 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
122 try writer.print("(void)", .{});202 try writer.print("(void)", .{});
123 }203 }
124 const tname = mem.spanZ(target.name);204 const tname = mem.spanZ(target.name);
125 if (file.called.get(tname) == null) {205 if (ctx.file.called.get(tname) == null) {
126 try file.called.put(tname, void{});206 try ctx.file.called.put(tname, void{});
127 try renderFunctionSignature(file, header, target);207 try renderFunctionSignature(ctx, header, target);
128 try header.writeAll(";\n");208 try header.writeAll(";\n");
129 }209 }
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");
131 } else {224 } else {
132 return file.fail(decl.src(), "TODO non-function call target?", .{});225 return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});
133 }
134 if (inst.args.len != 0) {
135 return file.fail(decl.src(), "TODO function arguments", .{});
136 }226 }
137 } else {227 } 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?", .{});
139 }229 }
230 return null;
140}231}
141232
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {233fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
143 // TODO emit #line directive here with line number and filename234 // TODO emit #line directive here with line number and filename
235 return null;
144}236}
145237
146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {238fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
147 const writer = file.main.writer();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();
148 try writer.writeAll(" ");250 try writer.writeAll(" ");
149 for (as.inputs) |i, index| {251 for (as.inputs) |i, index| {
150 if (i[0] == '{' and i[i.len - 1] == '}') {252 if (i[0] == '{' and i[i.len - 1] == '}') {
151 const reg = i[1 .. i.len - 1];253 const reg = i[1 .. i.len - 1];
152 const arg = as.args[index];254 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
153 if (arg.castTag(.constant)) |c| {259 if (arg.castTag(.constant)) |c| {
154 if (c.val.tag() == .int_u64) {260 try renderValue(ctx, writer, arg.ty, c.val);
155 try writer.writeAll("register ");261 try writer.writeAll(";\n ");
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 }
161 } else {262 } 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});
163 }268 }
164 } else {269 } 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", .{});
166 }271 }
167 }272 }
168 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });273 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
169 if (as.output) |o| {274 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", .{});
171 }276 }
172 if (as.inputs.len > 0) {277 if (as.inputs.len > 0) {
173 if (as.output == null) {278 if (as.output == null) {
...@@ -181,12 +286,7 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {...@@ -181,12 +286,7 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
181 if (index > 0) {286 if (index > 0) {
182 try writer.writeAll(", ");287 try writer.writeAll(", ");
183 }288 }
184 if (arg.castTag(.constant)) |c| {289 try writer.print("\"\"({}_constant)", .{reg});
185 try writer.print("\"\"({}_constant)", .{reg});
186 } else {
187 // This is blocked by the earlier test
188 unreachable;
189 }
190 } else {290 } else {
191 // This is blocked by the earlier test291 // This is blocked by the earlier test
192 unreachable;292 unreachable;
...@@ -194,4 +294,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {...@@ -194,4 +294,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
194 }294 }
195 }295 }
196 try writer.writeAll(");\n");296 try writer.writeAll(");\n");
297 return null;
197}298}
src-self-hosted/codegen/riscv64.zig+383-42
...@@ -1,56 +1,398 @@...@@ -1,56 +1,398 @@
1const std = @import("std");1const std = @import("std");
2const DW = std.dwarf;
23
3pub const instructions = struct {4// TODO: this is only tagged to facilitate the monstrosity.
4 pub const CallBreak = packed struct {5// Once packed structs work make it packed.
5 pub const Mode = packed enum(u12) { ecall, ebreak };6pub const Instruction = union(enum) {
6 opcode: u7 = 0b1110011,7 R: packed struct {
7 unused1: u5 = 0,8 opcode: u7,
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,
16 rd: u5,9 rd: u5,
17 mode: u3, //: Mode10 funct3: u3,
18 rs1: u5,11 rs1: u5,
19 imm: i12,12 rs2: u5,
20 };13 funct7: u7,
21 pub const Lui = packed struct {14 },
22 opcode: u7 = 0b0110111,15 I: packed struct {
16 opcode: u7,
23 rd: u5,17 rd: u5,
24 imm: i20,18 funct3: u3,
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
32 rs1: u5,19 rs1: u5,
33 offset: i12,20 imm0_11: u12,
34 };21 },
35 // I-type22 S: packed struct {
36 pub const Jalr = packed struct {23 opcode: u7,
37 opcode: u7 = 0b1100111,24 imm0_4: u5,
38 rd: u5,25 funct3: u3,
39 mode: u3 = 0,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,
40 rs1: u5,35 rs1: u5,
41 offset: i12,36 rs2: u5,
42 };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);
43};381};
44382
45// zig fmt: off383// zig fmt: off
46pub const RawRegister = enum(u8) {384pub const RawRegister = enum(u5) {
47 x0, x1, x2, x3, x4, x5, x6, x7,385 x0, x1, x2, x3, x4, x5, x6, x7,
48 x8, x9, x10, x11, x12, x13, x14, x15,386 x8, x9, x10, x11, x12, x13, x14, x15,
49 x16, x17, x18, x19, x20, x21, x22, x23,387 x16, x17, x18, x19, x20, x21, x22, x23,
50 x24, x25, x26, x27, x28, x29, x30, x31,388 x24, x25, x26, x27, x28, x29, x30, x31,
389
390 pub fn dwarfLocOp(reg: RawRegister) u8 {
391 return @enumToInt(reg) + DW.OP_reg0;
392 }
51};393};
52394
53pub const Register = enum(u8) {395pub const Register = enum(u5) {
54 // 64 bit registers396 // 64 bit registers
55 zero, // zero397 zero, // zero
56 ra, // return address. caller saved398 ra, // return address. caller saved
...@@ -71,11 +413,6 @@ pub const Register = enum(u8) {...@@ -71,11 +413,6 @@ pub const Register = enum(u8) {
71 return null;413 return null;
72 }414 }
73415
74 /// Returns the register's id.
75 pub fn id(self: @This()) u5 {
76 return @truncate(u5, @enumToInt(self));
77 }
78
79 /// Returns the index into `callee_preserved_regs`.416 /// Returns the index into `callee_preserved_regs`.
80 pub fn allocIndex(self: Register) ?u4 {417 pub fn allocIndex(self: Register) ?u4 {
81 inline for(callee_preserved_regs) |cpreg, i| {418 inline for(callee_preserved_regs) |cpreg, i| {
...@@ -83,6 +420,10 @@ pub const Register = enum(u8) {...@@ -83,6 +420,10 @@ pub const Register = enum(u8) {
83 }420 }
84 return null;421 return null;
85 }422 }
423
424 pub fn dwarfLocOp(reg: Register) u8 {
425 return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
426 }
86};427};
87428
88// zig fmt: on429// 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 @@...@@ -1,3 +1,6 @@
1const std = @import("std");
2const DW = std.dwarf;
3
1// zig fmt: off4// zig fmt: off
2pub const Register = enum(u8) {5pub const Register = enum(u8) {
3 // 0 through 7, 32-bit registers. id is int value6 // 0 through 7, 32-bit registers. id is int value
...@@ -37,8 +40,84 @@ pub const Register = enum(u8) {...@@ -37,8 +40,84 @@ pub const Register = enum(u8) {
37 else => null,40 else => null,
38 };41 };
39 }42 }
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 }
40};73};
4174
42// zig fmt: on75// zig fmt: on
4376
44pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };77pub 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 @@...@@ -1,4 +1,6 @@
1const std = @import("std");
1const Type = @import("../Type.zig");2const Type = @import("../Type.zig");
3const DW = std.dwarf;
24
3// zig fmt: off5// zig fmt: off
46
...@@ -101,6 +103,30 @@ pub const Register = enum(u8) {...@@ -101,6 +103,30 @@ pub const Register = enum(u8) {
101 pub fn to8(self: Register) Register {103 pub fn to8(self: Register) Register {
102 return @intToEnum(Register, @as(u8, self.id()) + 48);104 return @intToEnum(Register, @as(u8, self.id()) + 48);
103 }105 }
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 }
104};130};
105131
106// zig fmt: on132// zig fmt: on
...@@ -109,3 +135,86 @@ pub const Register = enum(u8) {...@@ -109,3 +135,86 @@ pub const Register = enum(u8) {
109pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };135pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
110pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };136pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
111pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };137pub 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 @@...@@ -3,8 +3,7 @@
3const std = @import("std");3const std = @import("std");
4const mem = std.mem;4const mem = std.mem;
5const fs = std.fs;5const fs = std.fs;
66const CacheHash = std.cache_hash.CacheHash;
7const warn = std.debug.warn;
87
9/// Caller must free result8/// Caller must free result
10pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {9pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
...@@ -63,7 +62,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {...@@ -63,7 +62,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
6362
64pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {63pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
65 return findZigLibDir(allocator) catch |err| {64 return findZigLibDir(allocator) catch |err| {
66 warn(65 std.debug.print(
67 \\Unable to find zig lib directory: {}.66 \\Unable to find zig lib directory: {}.
68 \\Reinstall Zig or use --zig-install-prefix.67 \\Reinstall Zig or use --zig-install-prefix.
69 \\68 \\
...@@ -73,7 +72,64 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {...@@ -73,7 +72,64 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
73 };72 };
74}73}
7574
76/// Caller must free result75/// Caller owns returned memory.
77pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 {76pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
78 return std.mem.dupe(allocator, u8, "zig-cache");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;
79}135}
src-self-hosted/ir.zig+47-5
...@@ -68,8 +68,10 @@ pub const Inst = struct {...@@ -68,8 +68,10 @@ pub const Inst = struct {
68 dbg_stmt,68 dbg_stmt,
69 isnonnull,69 isnonnull,
70 isnull,70 isnull,
71 iserr,
71 /// Read a value from a pointer.72 /// Read a value from a pointer.
72 load,73 load,
74 loop,
73 ptrtoint,75 ptrtoint,
74 ref,76 ref,
75 ret,77 ret,
...@@ -81,13 +83,14 @@ pub const Inst = struct {...@@ -81,13 +83,14 @@ pub const Inst = struct {
81 not,83 not,
82 floatcast,84 floatcast,
83 intcast,85 intcast,
86 unwrap_optional,
87 wrap_optional,
8488
85 pub fn Type(tag: Tag) type {89 pub fn Type(tag: Tag) type {
86 return switch (tag) {90 return switch (tag) {
87 .alloc,91 .alloc,
88 .retvoid,92 .retvoid,
89 .unreach,93 .unreach,
90 .arg,
91 .breakpoint,94 .breakpoint,
92 .dbg_stmt,95 .dbg_stmt,
93 => NoOp,96 => NoOp,
...@@ -98,10 +101,13 @@ pub const Inst = struct {...@@ -98,10 +101,13 @@ pub const Inst = struct {
98 .not,101 .not,
99 .isnonnull,102 .isnonnull,
100 .isnull,103 .isnull,
104 .iserr,
101 .ptrtoint,105 .ptrtoint,
102 .floatcast,106 .floatcast,
103 .intcast,107 .intcast,
104 .load,108 .load,
109 .unwrap_optional,
110 .wrap_optional,
105 => UnOp,111 => UnOp,
106112
107 .add,113 .add,
...@@ -115,6 +121,7 @@ pub const Inst = struct {...@@ -115,6 +121,7 @@ pub const Inst = struct {
115 .store,121 .store,
116 => BinOp,122 => BinOp,
117123
124 .arg => Arg,
118 .assembly => Assembly,125 .assembly => Assembly,
119 .block => Block,126 .block => Block,
120 .br => Br,127 .br => Br,
...@@ -122,6 +129,7 @@ pub const Inst = struct {...@@ -122,6 +129,7 @@ pub const Inst = struct {
122 .call => Call,129 .call => Call,
123 .condbr => CondBr,130 .condbr => CondBr,
124 .constant => Constant,131 .constant => Constant,
132 .loop => Loop,
125 };133 };
126 }134 }
127135
...@@ -253,6 +261,20 @@ pub const Inst = struct {...@@ -253,6 +261,20 @@ pub const Inst = struct {
253 }261 }
254 };262 };
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
256 pub const Assembly = struct {278 pub const Assembly = struct {
257 pub const base_tag = Tag.assembly;279 pub const base_tag = Tag.assembly;
258280
...@@ -354,11 +376,11 @@ pub const Inst = struct {...@@ -354,11 +376,11 @@ pub const Inst = struct {
354 then_body: Body,376 then_body: Body,
355 else_body: Body,377 else_body: Body,
356 /// Set of instructions whose lifetimes end at the start of one of the branches.378 /// 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]`.379 /// The `then` branch is first: `deaths[0..then_death_count]`.
358 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.380 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
359 deaths: [*]*Inst = undefined,381 deaths: [*]*Inst = undefined,
360 true_death_count: u32 = 0,382 then_death_count: u32 = 0,
361 false_death_count: u32 = 0,383 else_death_count: u32 = 0,
362384
363 pub fn operandCount(self: *const CondBr) usize {385 pub fn operandCount(self: *const CondBr) usize {
364 return 1;386 return 1;
...@@ -372,6 +394,12 @@ pub const Inst = struct {...@@ -372,6 +394,12 @@ pub const Inst = struct {
372394
373 return null;395 return null;
374 }396 }
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 }
375 };403 };
376404
377 pub const Constant = struct {405 pub const Constant = struct {
...@@ -387,6 +415,20 @@ pub const Inst = struct {...@@ -387,6 +415,20 @@ pub const Inst = struct {
387 return null;415 return null;
388 }416 }
389 };417 };
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 };
390};432};
391433
392pub const Body = struct {434pub const Body = struct {
src-self-hosted/link.zig+674-267
...@@ -8,12 +8,16 @@ const fs = std.fs;...@@ -8,12 +8,16 @@ const fs = std.fs;
8const elf = std.elf;8const elf = std.elf;
9const codegen = @import("codegen.zig");9const codegen = @import("codegen.zig");
10const c_codegen = @import("codegen/c.zig");10const c_codegen = @import("codegen/c.zig");
11const log = std.log;11const log = std.log.scoped(.link);
12const DW = std.dwarf;12const DW = std.dwarf;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;14const leb128 = std.debug.leb;
15const Package = @import("Package.zig");15const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;16const 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
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.22// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off23// zig fmt: off
...@@ -36,9 +40,26 @@ pub const Options = struct {...@@ -36,9 +40,26 @@ pub const Options = struct {
36 program_code_size_hint: u64 = 256 * 1024,40 program_code_size_hint: u64 = 256 * 1024,
37};41};
3842
43
39pub const File = struct {44pub 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
40 tag: Tag,59 tag: Tag,
41 options: Options,60 options: Options,
61 file: ?fs.File,
62 allocator: *Allocator,
4263
43 /// Attempts incremental linking, if the file already exists. If64 /// Attempts incremental linking, if the file already exists. If
44 /// incremental linking fails, falls back to truncating the file and65 /// incremental linking fails, falls back to truncating the file and
...@@ -49,8 +70,8 @@ pub const File = struct {...@@ -49,8 +70,8 @@ pub const File = struct {
49 .unknown => unreachable,70 .unknown => unreachable,
50 .coff => return error.TODOImplementCoff,71 .coff => return error.TODOImplementCoff,
51 .elf => return Elf.openPath(allocator, dir, sub_path, options),72 .elf => return Elf.openPath(allocator, dir, sub_path, options),
52 .macho => return error.TODOImplementMacho,73 .macho => return MachO.openPath(allocator, dir, sub_path, options),
53 .wasm => return error.TODOImplementWasm,74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
54 .c => return C.openPath(allocator, dir, sub_path, options),75 .c => return C.openPath(allocator, dir, sub_path, options),
55 .hex => return error.TODOImplementHex,76 .hex => return error.TODOImplementHex,
56 .raw => return error.TODOImplementRaw,77 .raw => return error.TODOImplementRaw,
...@@ -66,43 +87,61 @@ pub const File = struct {...@@ -66,43 +87,61 @@ pub const File = struct {
6687
67 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {88 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
68 switch (base.tag) {89 switch (base.tag) {
69 .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),90 .elf, .macho => {
70 .c => {},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 => {},
71 }99 }
72 }100 }
73101
74 pub fn makeExecutable(base: *File) !void {102 pub fn makeExecutable(base: *File) !void {
75 switch (base.tag) {103 switch (base.tag) {
76 .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
77 .c => unreachable,104 .c => unreachable,
105 .wasm => {},
106 else => if (base.file) |f| {
107 f.close();
108 base.file = null;
109 },
78 }110 }
79 }111 }
80112
81 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {113 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
82 switch (base.tag) {114 switch (base.tag) {
83 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),115 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
84 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),117 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
118 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
85 }119 }
86 }120 }
87121
88 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {122 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
89 switch (base.tag) {123 switch (base.tag) {
90 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),124 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
91 .c => {},125 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126 .c, .wasm => {},
92 }127 }
93 }128 }
94129
95 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {130 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
96 switch (base.tag) {131 switch (base.tag) {
97 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),132 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
98 .c => {},133 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
134 .c, .wasm => {},
99 }135 }
100 }136 }
101137
102 pub fn deinit(base: *File) void {138 pub fn deinit(base: *File) void {
139 if (base.file) |f| f.close();
103 switch (base.tag) {140 switch (base.tag) {
104 .elf => @fieldParentPtr(Elf, "base", base).deinit(),141 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
142 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
105 .c => @fieldParentPtr(C, "base", base).deinit(),143 .c => @fieldParentPtr(C, "base", base).deinit(),
144 .wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
106 }145 }
107 }146 }
108147
...@@ -111,37 +150,53 @@ pub const File = struct {...@@ -111,37 +150,53 @@ pub const File = struct {
111 .elf => {150 .elf => {
112 const parent = @fieldParentPtr(Elf, "base", base);151 const parent = @fieldParentPtr(Elf, "base", base);
113 parent.deinit();152 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);
115 },159 },
116 .c => {160 .c => {
117 const parent = @fieldParentPtr(C, "base", base);161 const parent = @fieldParentPtr(C, "base", base);
118 parent.deinit();162 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);
120 },169 },
121 }170 }
122 }171 }
123172
124 pub fn flush(base: *File) !void {173 pub fn flush(base: *File, module: *Module) !void {
125 const tracy = trace(@src());174 const tracy = trace(@src());
126 defer tracy.end();175 defer tracy.end();
127176
128 try switch (base.tag) {177 try switch (base.tag) {
129 .elf => @fieldParentPtr(Elf, "base", base).flush(),178 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
130 .c => @fieldParentPtr(C, "base", base).flush(),179 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
180 .c => @fieldParentPtr(C, "base", base).flush(module),
181 .wasm => @fieldParentPtr(Wasm, "base", base).flush(module),
131 };182 };
132 }183 }
133184
134 pub fn freeDecl(base: *File, decl: *Module.Decl) void {185 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
135 switch (base.tag) {186 switch (base.tag) {
136 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),187 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
188 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
137 .c => unreachable,189 .c => unreachable,
190 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
138 }191 }
139 }192 }
140193
141 pub fn errorFlags(base: *File) ErrorFlags {194 pub fn errorFlags(base: *File) ErrorFlags {
142 return switch (base.tag) {195 return switch (base.tag) {
143 .elf => @fieldParentPtr(Elf, "base", base).error_flags,196 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
144 .c => return .{ .no_entry_point_found = false },198 .c => return .{ .no_entry_point_found = false },
199 .wasm => return ErrorFlags{},
145 };200 };
146 }201 }
147202
...@@ -154,13 +209,17 @@ pub const File = struct {...@@ -154,13 +209,17 @@ pub const File = struct {
154 ) !void {209 ) !void {
155 switch (base.tag) {210 switch (base.tag) {
156 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),211 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
212 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
157 .c => return {},213 .c => return {},
214 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
158 }215 }
159 }216 }
160217
161 pub const Tag = enum {218 pub const Tag = enum {
162 elf,219 elf,
220 macho,
163 c,221 c,
222 wasm,
164 };223 };
165224
166 pub const ErrorFlags = struct {225 pub const ErrorFlags = struct {
...@@ -172,15 +231,13 @@ pub const File = struct {...@@ -172,15 +231,13 @@ pub const File = struct {
172231
173 base: File,232 base: File,
174233
175 allocator: *Allocator,
176 header: std.ArrayList(u8),234 header: std.ArrayList(u8),
177 constants: std.ArrayList(u8),235 constants: std.ArrayList(u8),
178 main: std.ArrayList(u8),236 main: std.ArrayList(u8),
179 file: ?fs.File,237
180 called: std.StringHashMap(void),238 called: std.StringHashMap(void),
181 need_stddef: bool = false,239 need_stddef: bool = false,
182 need_stdint: bool = false,240 need_stdint: bool = false,
183 need_noreturn: bool = false,
184 error_msg: *Module.ErrorMsg = undefined,241 error_msg: *Module.ErrorMsg = undefined,
185242
186 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {243 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
...@@ -196,9 +253,9 @@ pub const File = struct {...@@ -196,9 +253,9 @@ pub const File = struct {
196 .base = .{253 .base = .{
197 .tag = .c,254 .tag = .c,
198 .options = options,255 .options = options,
256 .file = file,
257 .allocator = allocator,
199 },258 },
200 .allocator = allocator,
201 .file = file,
202 .main = std.ArrayList(u8).init(allocator),259 .main = std.ArrayList(u8).init(allocator),
203 .header = std.ArrayList(u8).init(allocator),260 .header = std.ArrayList(u8).init(allocator),
204 .constants = std.ArrayList(u8).init(allocator),261 .constants = std.ArrayList(u8).init(allocator),
...@@ -208,8 +265,8 @@ pub const File = struct {...@@ -208,8 +265,8 @@ pub const File = struct {
208 return &c_file.base;265 return &c_file.base;
209 }266 }
210267
211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {268 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{AnalysisFail, OutOfMemory} {
212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);269 self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
213 return error.AnalysisFail;270 return error.AnalysisFail;
214 }271 }
215272
...@@ -218,8 +275,6 @@ pub const File = struct {...@@ -218,8 +275,6 @@ pub const File = struct {
218 self.header.deinit();275 self.header.deinit();
219 self.constants.deinit();276 self.constants.deinit();
220 self.called.deinit();277 self.called.deinit();
221 if (self.file) |f|
222 f.close();
223 }278 }
224279
225 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {280 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
...@@ -231,8 +286,8 @@ pub const File = struct {...@@ -231,8 +286,8 @@ pub const File = struct {
231 };286 };
232 }287 }
233288
234 pub fn flush(self: *File.C) !void {289 pub fn flush(self: *File.C, module: *Module) !void {
235 const writer = self.file.?.writer();290 const writer = self.base.file.?.writer();
236 try writer.writeAll(@embedFile("cbe.h"));291 try writer.writeAll(@embedFile("cbe.h"));
237 var includes = false;292 var includes = false;
238 if (self.need_stddef) {293 if (self.need_stddef) {
...@@ -259,8 +314,8 @@ pub const File = struct {...@@ -259,8 +314,8 @@ pub const File = struct {
259 }314 }
260 }315 }
261 try writer.writeAll(self.main.items);316 try writer.writeAll(self.main.items);
262 self.file.?.close();317 self.base.file.?.close();
263 self.file = null;318 self.base.file = null;
264 }319 }
265 };320 };
266321
...@@ -269,9 +324,6 @@ pub const File = struct {...@@ -269,9 +324,6 @@ pub const File = struct {
269324
270 base: File,325 base: File,
271326
272 allocator: *Allocator,
273 file: ?fs.File,
274 owns_file_handle: bool,
275 ptr_width: enum { p32, p64 },327 ptr_width: enum { p32, p64 },
276328
277 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.329 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
...@@ -309,26 +361,27 @@ pub const File = struct {...@@ -309,26 +361,27 @@ pub const File = struct {
309 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and361 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
310 /// write them at the end. These are only the local symbols. The length of this array362 /// write them at the end. These are only the local symbols. The length of this array
311 /// is the value used for sh_info in the .symtab section.363 /// is the value used for sh_info in the .symtab section.
312 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},364 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
313 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = 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){},367 local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
316 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},368 global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
317 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},369 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
318370
319 /// Same order as in the file. The value is the absolute vaddr value.371 /// Same order as in the file. The value is the absolute vaddr value.
320 /// If the vaddr of the executable program header changes, the entire372 /// If the vaddr of the executable program header changes, the entire
321 /// offset table needs to be rewritten.373 /// offset table needs to be rewritten.
322 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},374 offset_table: std.ArrayListUnmanaged(u64) = .{},
323375
324 phdr_table_dirty: bool = false,376 phdr_table_dirty: bool = false,
325 shdr_table_dirty: bool = false,377 shdr_table_dirty: bool = false,
326 shstrtab_dirty: bool = false,378 shstrtab_dirty: bool = false,
327 debug_strtab_dirty: bool = false,379 debug_strtab_dirty: bool = false,
328 offset_table_count_dirty: bool = false,380 offset_table_count_dirty: bool = false,
329 debug_info_section_dirty: bool = false,
330 debug_abbrev_section_dirty: bool = false,381 debug_abbrev_section_dirty: bool = false,
331 debug_aranges_section_dirty: bool = false,382 debug_aranges_section_dirty: bool = false,
383
384 debug_info_header_dirty: bool = false,
332 debug_line_header_dirty: bool = false,385 debug_line_header_dirty: bool = false,
333386
334 error_flags: ErrorFlags = ErrorFlags{},387 error_flags: ErrorFlags = ErrorFlags{},
...@@ -348,7 +401,7 @@ pub const File = struct {...@@ -348,7 +401,7 @@ pub const File = struct {
348 /// overcapacity can be negative. A simple way to have negative overcapacity is to401 /// overcapacity can be negative. A simple way to have negative overcapacity is to
349 /// allocate a fresh text block, which will have ideal capacity, and then grow it402 /// allocate a fresh text block, which will have ideal capacity, and then grow it
350 /// by 1 byte. It will then have -1 overcapacity.403 /// 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) = .{},
352 last_text_block: ?*TextBlock = null,405 last_text_block: ?*TextBlock = null,
353406
354 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.407 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
...@@ -357,6 +410,12 @@ pub const File = struct {...@@ -357,6 +410,12 @@ pub const File = struct {
357 dbg_line_fn_first: ?*SrcFn = null,410 dbg_line_fn_first: ?*SrcFn = null,
358 dbg_line_fn_last: ?*SrcFn = null,411 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
360 /// `alloc_num / alloc_den` is the factor of padding when allocating.419 /// `alloc_num / alloc_den` is the factor of padding when allocating.
361 const alloc_num = 4;420 const alloc_num = 4;
362 const alloc_den = 3;421 const alloc_den = 3;
...@@ -367,6 +426,17 @@ pub const File = struct {...@@ -367,6 +426,17 @@ pub const File = struct {
367 const minimum_text_block_size = 64;426 const minimum_text_block_size = 64;
368 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;427 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
370 pub const TextBlock = struct {440 pub const TextBlock = struct {
371 /// Each decl always gets a local symbol with the fully qualified name.441 /// Each decl always gets a local symbol with the fully qualified name.
372 /// The vaddr and size are found here directly.442 /// The vaddr and size are found here directly.
...@@ -382,11 +452,24 @@ pub const File = struct {...@@ -382,11 +452,24 @@ pub const File = struct {
382 prev: ?*TextBlock,452 prev: ?*TextBlock,
383 next: ?*TextBlock,453 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
385 pub const empty = TextBlock{464 pub const empty = TextBlock{
386 .local_sym_index = 0,465 .local_sym_index = 0,
387 .offset_table_index = undefined,466 .offset_table_index = undefined,
388 .prev = null,467 .prev = null,
389 .next = null,468 .next = null,
469 .dbg_info_prev = null,
470 .dbg_info_next = null,
471 .dbg_info_off = undefined,
472 .dbg_info_len = undefined,
390 };473 };
391474
392 /// Returns how much room there is to grow in virtual address space.475 /// Returns how much room there is to grow in virtual address space.
...@@ -454,7 +537,6 @@ pub const File = struct {...@@ -454,7 +537,6 @@ pub const File = struct {
454 else => |e| return e,537 else => |e| return e,
455 };538 };
456539
457 elf_file.owns_file_handle = true;
458 return &elf_file.base;540 return &elf_file.base;
459 }541 }
460542
...@@ -467,12 +549,11 @@ pub const File = struct {...@@ -467,12 +549,11 @@ pub const File = struct {
467 }549 }
468 var self: Elf = .{550 var self: Elf = .{
469 .base = .{551 .base = .{
552 .file = file,
470 .tag = .elf,553 .tag = .elf,
471 .options = options,554 .options = options,
555 .allocator = allocator,
472 },556 },
473 .allocator = allocator,
474 .file = file,
475 .owns_file_handle = false,
476 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {557 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
477 32 => .p32,558 32 => .p32,
478 64 => .p64,559 64 => .p64,
...@@ -499,16 +580,15 @@ pub const File = struct {...@@ -499,16 +580,15 @@ pub const File = struct {
499 .base = .{580 .base = .{
500 .tag = .elf,581 .tag = .elf,
501 .options = options,582 .options = options,
583 .allocator = allocator,
584 .file = file,
502 },585 },
503 .allocator = allocator,
504 .file = file,
505 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {586 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
506 32 => .p32,587 32 => .p32,
507 64 => .p64,588 64 => .p64,
508 else => return error.UnsupportedELFArchitecture,589 else => return error.UnsupportedELFArchitecture,
509 },590 },
510 .shdr_table_dirty = true,591 .shdr_table_dirty = true,
511 .owns_file_handle = false,
512 };592 };
513 errdefer self.deinit();593 errdefer self.deinit();
514594
...@@ -542,39 +622,19 @@ pub const File = struct {...@@ -542,39 +622,19 @@ pub const File = struct {
542 }622 }
543623
544 pub fn deinit(self: *Elf) void {624 pub fn deinit(self: *Elf) void {
545 self.sections.deinit(self.allocator);625 self.sections.deinit(self.base.allocator);
546 self.program_headers.deinit(self.allocator);626 self.program_headers.deinit(self.base.allocator);
547 self.shstrtab.deinit(self.allocator);627 self.shstrtab.deinit(self.base.allocator);
548 self.debug_strtab.deinit(self.allocator);628 self.debug_strtab.deinit(self.base.allocator);
549 self.local_symbols.deinit(self.allocator);629 self.local_symbols.deinit(self.base.allocator);
550 self.global_symbols.deinit(self.allocator);630 self.global_symbols.deinit(self.base.allocator);
551 self.global_symbol_free_list.deinit(self.allocator);631 self.global_symbol_free_list.deinit(self.base.allocator);
552 self.local_symbol_free_list.deinit(self.allocator);632 self.local_symbol_free_list.deinit(self.base.allocator);
553 self.offset_table_free_list.deinit(self.allocator);633 self.offset_table_free_list.deinit(self.base.allocator);
554 self.text_block_free_list.deinit(self.allocator);634 self.text_block_free_list.deinit(self.base.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);635 self.dbg_line_fn_free_list.deinit(self.base.allocator);
556 self.offset_table.deinit(self.allocator);636 self.dbg_info_decl_free_list.deinit(self.base.allocator);
557 if (self.owns_file_handle) {637 self.offset_table.deinit(self.base.allocator);
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 });
578 }638 }
579639
580 fn getDebugLineProgramOff(self: Elf) u32 {640 fn getDebugLineProgramOff(self: Elf) u32 {
...@@ -662,7 +722,7 @@ pub const File = struct {...@@ -662,7 +722,7 @@ pub const File = struct {
662722
663 /// TODO Improve this to use a table.723 /// TODO Improve this to use a table.
664 fn makeString(self: *Elf, bytes: []const u8) !u32 {724 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);
666 const result = self.shstrtab.items.len;726 const result = self.shstrtab.items.len;
667 self.shstrtab.appendSliceAssumeCapacity(bytes);727 self.shstrtab.appendSliceAssumeCapacity(bytes);
668 self.shstrtab.appendAssumeCapacity(0);728 self.shstrtab.appendAssumeCapacity(0);
...@@ -671,7 +731,7 @@ pub const File = struct {...@@ -671,7 +731,7 @@ pub const File = struct {
671731
672 /// TODO Improve this to use a table.732 /// TODO Improve this to use a table.
673 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {733 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);
675 const result = self.debug_strtab.items.len;735 const result = self.debug_strtab.items.len;
676 self.debug_strtab.appendSliceAssumeCapacity(bytes);736 self.debug_strtab.appendSliceAssumeCapacity(bytes);
677 self.debug_strtab.appendAssumeCapacity(0);737 self.debug_strtab.appendAssumeCapacity(0);
...@@ -702,8 +762,8 @@ pub const File = struct {...@@ -702,8 +762,8 @@ pub const File = struct {
702 const file_size = self.base.options.program_code_size_hint;762 const file_size = self.base.options.program_code_size_hint;
703 const p_align = 0x1000;763 const p_align = 0x1000;
704 const off = self.findFreeSpace(file_size, p_align);764 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 });765 log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
706 try self.program_headers.append(self.allocator, .{766 try self.program_headers.append(self.base.allocator, .{
707 .p_type = elf.PT_LOAD,767 .p_type = elf.PT_LOAD,
708 .p_offset = off,768 .p_offset = off,
709 .p_filesz = file_size,769 .p_filesz = file_size,
...@@ -721,14 +781,14 @@ pub const File = struct {...@@ -721,14 +781,14 @@ pub const File = struct {
721 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;781 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
722 // We really only need ptr alignment but since we are using PROGBITS, linux requires782 // We really only need ptr alignment but since we are using PROGBITS, linux requires
723 // page align.783 // 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);
725 const off = self.findFreeSpace(file_size, p_align);785 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 });
727 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.787 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
728 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something788 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
729 // else in virtual memory.789 // else in virtual memory.
730 const default_got_addr = 0x4000000;790 const default_got_addr = if (ptr_size == 2) @as(u32, 0x8000) else 0x4000000;
731 try self.program_headers.append(self.allocator, .{791 try self.program_headers.append(self.base.allocator, .{
732 .p_type = elf.PT_LOAD,792 .p_type = elf.PT_LOAD,
733 .p_offset = off,793 .p_offset = off,
734 .p_filesz = file_size,794 .p_filesz = file_size,
...@@ -743,10 +803,10 @@ pub const File = struct {...@@ -743,10 +803,10 @@ pub const File = struct {
743 if (self.shstrtab_index == null) {803 if (self.shstrtab_index == null) {
744 self.shstrtab_index = @intCast(u16, self.sections.items.len);804 self.shstrtab_index = @intCast(u16, self.sections.items.len);
745 assert(self.shstrtab.items.len == 0);805 assert(self.shstrtab.items.len == 0);
746 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0806 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
747 const off = self.findFreeSpace(self.shstrtab.items.len, 1);807 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 });808 log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
749 try self.sections.append(self.allocator, .{809 try self.sections.append(self.base.allocator, .{
750 .sh_name = try self.makeString(".shstrtab"),810 .sh_name = try self.makeString(".shstrtab"),
751 .sh_type = elf.SHT_STRTAB,811 .sh_type = elf.SHT_STRTAB,
752 .sh_flags = 0,812 .sh_flags = 0,
...@@ -765,7 +825,7 @@ pub const File = struct {...@@ -765,7 +825,7 @@ pub const File = struct {
765 self.text_section_index = @intCast(u16, self.sections.items.len);825 self.text_section_index = @intCast(u16, self.sections.items.len);
766 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];826 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, .{
769 .sh_name = try self.makeString(".text"),829 .sh_name = try self.makeString(".text"),
770 .sh_type = elf.SHT_PROGBITS,830 .sh_type = elf.SHT_PROGBITS,
771 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,831 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
...@@ -783,7 +843,7 @@ pub const File = struct {...@@ -783,7 +843,7 @@ pub const File = struct {
783 self.got_section_index = @intCast(u16, self.sections.items.len);843 self.got_section_index = @intCast(u16, self.sections.items.len);
784 const phdr = &self.program_headers.items[self.phdr_got_index.?];844 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, .{
787 .sh_name = try self.makeString(".got"),847 .sh_name = try self.makeString(".got"),
788 .sh_type = elf.SHT_PROGBITS,848 .sh_type = elf.SHT_PROGBITS,
789 .sh_flags = elf.SHF_ALLOC,849 .sh_flags = elf.SHF_ALLOC,
...@@ -803,9 +863,9 @@ pub const File = struct {...@@ -803,9 +863,9 @@ pub const File = struct {
803 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);863 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
804 const file_size = self.base.options.symbol_count_hint * each_size;864 const file_size = self.base.options.symbol_count_hint * each_size;
805 const off = self.findFreeSpace(file_size, min_align);865 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, .{
809 .sh_name = try self.makeString(".symtab"),869 .sh_name = try self.makeString(".symtab"),
810 .sh_type = elf.SHT_SYMTAB,870 .sh_type = elf.SHT_SYMTAB,
811 .sh_flags = 0,871 .sh_flags = 0,
...@@ -824,7 +884,7 @@ pub const File = struct {...@@ -824,7 +884,7 @@ pub const File = struct {
824 if (self.debug_str_section_index == null) {884 if (self.debug_str_section_index == null) {
825 self.debug_str_section_index = @intCast(u16, self.sections.items.len);885 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
826 assert(self.debug_strtab.items.len == 0);886 assert(self.debug_strtab.items.len == 0);
827 try self.sections.append(self.allocator, .{887 try self.sections.append(self.base.allocator, .{
828 .sh_name = try self.makeString(".debug_str"),888 .sh_name = try self.makeString(".debug_str"),
829 .sh_type = elf.SHT_PROGBITS,889 .sh_type = elf.SHT_PROGBITS,
830 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,890 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
...@@ -845,11 +905,11 @@ pub const File = struct {...@@ -845,11 +905,11 @@ pub const File = struct {
845 const file_size_hint = 200;905 const file_size_hint = 200;
846 const p_align = 1;906 const p_align = 1;
847 const off = self.findFreeSpace(file_size_hint, p_align);907 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", .{
849 off,909 off,
850 off + file_size_hint,910 off + file_size_hint,
851 });911 });
852 try self.sections.append(self.allocator, .{912 try self.sections.append(self.base.allocator, .{
853 .sh_name = try self.makeString(".debug_info"),913 .sh_name = try self.makeString(".debug_info"),
854 .sh_type = elf.SHT_PROGBITS,914 .sh_type = elf.SHT_PROGBITS,
855 .sh_flags = 0,915 .sh_flags = 0,
...@@ -862,7 +922,7 @@ pub const File = struct {...@@ -862,7 +922,7 @@ pub const File = struct {
862 .sh_entsize = 0,922 .sh_entsize = 0,
863 });923 });
864 self.shdr_table_dirty = true;924 self.shdr_table_dirty = true;
865 self.debug_info_section_dirty = true;925 self.debug_info_header_dirty = true;
866 }926 }
867 if (self.debug_abbrev_section_index == null) {927 if (self.debug_abbrev_section_index == null) {
868 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);928 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
...@@ -870,11 +930,11 @@ pub const File = struct {...@@ -870,11 +930,11 @@ pub const File = struct {
870 const file_size_hint = 128;930 const file_size_hint = 128;
871 const p_align = 1;931 const p_align = 1;
872 const off = self.findFreeSpace(file_size_hint, p_align);932 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", .{
874 off,934 off,
875 off + file_size_hint,935 off + file_size_hint,
876 });936 });
877 try self.sections.append(self.allocator, .{937 try self.sections.append(self.base.allocator, .{
878 .sh_name = try self.makeString(".debug_abbrev"),938 .sh_name = try self.makeString(".debug_abbrev"),
879 .sh_type = elf.SHT_PROGBITS,939 .sh_type = elf.SHT_PROGBITS,
880 .sh_flags = 0,940 .sh_flags = 0,
...@@ -895,11 +955,11 @@ pub const File = struct {...@@ -895,11 +955,11 @@ pub const File = struct {
895 const file_size_hint = 160;955 const file_size_hint = 160;
896 const p_align = 16;956 const p_align = 16;
897 const off = self.findFreeSpace(file_size_hint, p_align);957 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", .{
899 off,959 off,
900 off + file_size_hint,960 off + file_size_hint,
901 });961 });
902 try self.sections.append(self.allocator, .{962 try self.sections.append(self.base.allocator, .{
903 .sh_name = try self.makeString(".debug_aranges"),963 .sh_name = try self.makeString(".debug_aranges"),
904 .sh_type = elf.SHT_PROGBITS,964 .sh_type = elf.SHT_PROGBITS,
905 .sh_flags = 0,965 .sh_flags = 0,
...@@ -920,11 +980,11 @@ pub const File = struct {...@@ -920,11 +980,11 @@ pub const File = struct {
920 const file_size_hint = 250;980 const file_size_hint = 250;
921 const p_align = 1;981 const p_align = 1;
922 const off = self.findFreeSpace(file_size_hint, p_align);982 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", .{
924 off,984 off,
925 off + file_size_hint,985 off + file_size_hint,
926 });986 });
927 try self.sections.append(self.allocator, .{987 try self.sections.append(self.base.allocator, .{
928 .sh_name = try self.makeString(".debug_line"),988 .sh_name = try self.makeString(".debug_line"),
929 .sh_type = elf.SHT_PROGBITS,989 .sh_type = elf.SHT_PROGBITS,
930 .sh_flags = 0,990 .sh_flags = 0,
...@@ -972,8 +1032,15 @@ pub const File = struct {...@@ -972,8 +1032,15 @@ pub const File = struct {
972 }1032 }
973 }1033 }
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
975 /// Commit pending changes and write headers.1042 /// Commit pending changes and write headers.
976 pub fn flush(self: *Elf) !void {1043 pub fn flush(self: *Elf, module: *Module) !void {
977 const target_endian = self.base.options.target.cpu.arch.endian();1044 const target_endian = self.base.options.target.cpu.arch.endian();
978 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();1045 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
979 const ptr_width_bytes: u8 = self.ptrWidthBytes();1046 const ptr_width_bytes: u8 = self.ptrWidthBytes();
...@@ -992,7 +1059,7 @@ pub const File = struct {...@@ -992,7 +1059,7 @@ pub const File = struct {
992 // These are LEB encoded but since the values are all less than 1271059 // These are LEB encoded but since the values are all less than 127
993 // we can simply append these bytes.1060 // we can simply append these bytes.
994 const abbrev_buf = [_]u8{1061 const abbrev_buf = [_]u8{
995 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header1062 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
996 DW.AT_stmt_list, DW.FORM_sec_offset,1063 DW.AT_stmt_list, DW.FORM_sec_offset,
997 DW.AT_low_pc , DW.FORM_addr,1064 DW.AT_low_pc , DW.FORM_addr,
998 DW.AT_high_pc , DW.FORM_addr,1065 DW.AT_high_pc , DW.FORM_addr,
...@@ -1002,6 +1069,34 @@ pub const File = struct {...@@ -1002,6 +1069,34 @@ pub const File = struct {
1002 DW.AT_language , DW.FORM_data2,1069 DW.AT_language , DW.FORM_data2,
1003 0, 0, // table sentinel1070 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
1005 0, 0, 0, // section sentinel1100 0, 0, 0, // section sentinel
1006 };1101 };
10071102
...@@ -1012,14 +1107,14 @@ pub const File = struct {...@@ -1012,14 +1107,14 @@ pub const File = struct {
1012 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);1107 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1013 }1108 }
1014 debug_abbrev_sect.sh_size = needed_size;1109 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", .{
1016 debug_abbrev_sect.sh_offset,1111 debug_abbrev_sect.sh_offset,
1017 debug_abbrev_sect.sh_offset + needed_size,1112 debug_abbrev_sect.sh_offset + needed_size,
1018 });1113 });
10191114
1020 const abbrev_offset = 0;1115 const abbrev_offset = 0;
1021 self.debug_abbrev_table_offset = abbrev_offset;1116 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);
1023 if (!self.shdr_table_dirty) {1118 if (!self.shdr_table_dirty) {
1024 // Then it won't get written with the others and we need to do it.1119 // Then it won't get written with the others and we need to do it.
1025 try self.writeSectHeader(self.debug_abbrev_section_index.?);1120 try self.writeSectHeader(self.debug_abbrev_section_index.?);
...@@ -1027,21 +1122,37 @@ pub const File = struct {...@@ -1027,21 +1122,37 @@ pub const File = struct {
10271122
1028 self.debug_abbrev_section_dirty = false;1123 self.debug_abbrev_section_dirty = false;
1029 }1124 }
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.?;
1031 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];1131 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);
1034 defer di_buf.deinit();1134 defer di_buf.deinit();
10351135
1036 // Enough for a 64-bit header and main compilation unit without resizing.1136 // We have a function to compute the upper bound size, because it's needed
1037 try di_buf.ensureCapacity(100);1137 // for determining where to put the offset of the first `LinkBlock`.
1138 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
10381139
1039 // initial length - length of the .debug_info contribution for this compilation unit,1140 // initial length - length of the .debug_info contribution for this compilation unit,
1040 // not including the initial length itself.1141 // not including the initial length itself.
1041 // We have to come back and write it later after we know the size.1142 // We have to come back and write it later after we know the size.
1042 const init_len_index = di_buf.items.len;1143 const after_init_len = di_buf.items.len + init_len_size;
1043 di_buf.items.len += init_len_size;1144 // +1 for the final 0 that ends the compilation unit children.
1044 const after_init_len = di_buf.items.len;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 }
1045 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version1156 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
1046 const abbrev_offset = self.debug_abbrev_table_offset.?;1157 const abbrev_offset = self.debug_abbrev_table_offset.?;
1047 switch (self.ptr_width) {1158 switch (self.ptr_width) {
...@@ -1057,14 +1168,14 @@ pub const File = struct {...@@ -1057,14 +1168,14 @@ pub const File = struct {
1057 // Write the form for the compile unit, which must match the abbrev table above.1168 // Write the form for the compile unit, which must match the abbrev table above.
1058 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);1169 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
1059 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);1170 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);
1061 // Currently only one compilation unit is supported, so the address range is simply1172 // Currently only one compilation unit is supported, so the address range is simply
1062 // identical to the main program header virtual address and memory size.1173 // identical to the main program header virtual address and memory size.
1063 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];1174 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1064 const low_pc = text_phdr.p_vaddr;1175 const low_pc = text_phdr.p_vaddr;
1065 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;1176 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 header1178 di_buf.appendAssumeCapacity(abbrev_compile_unit);
1068 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset1179 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
1069 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);1180 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
1070 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);1181 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
...@@ -1076,43 +1187,19 @@ pub const File = struct {...@@ -1076,43 +1187,19 @@ pub const File = struct {
1076 // Until then we say it is C99.1187 // Until then we say it is C99.
1077 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);1188 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
10781189
1079 const init_len = di_buf.items.len - after_init_len;1190 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
1080 switch (self.ptr_width) {1191 // Move the first N decls to the end to make more padding for the header.
1081 .p32 => {1192 @panic("TODO: handle .debug_info header exceeding its padding");
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.?);
1108 }1193 }
11091194 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
1110 self.debug_info_section_dirty = false;1195 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
1196 self.debug_info_header_dirty = false;
1111 }1197 }
1198
1112 if (self.debug_aranges_section_dirty) {1199 if (self.debug_aranges_section_dirty) {
1113 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];1200 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);
1116 defer di_buf.deinit();1203 defer di_buf.deinit();
11171204
1118 // Enough for all the data without resizing. When support for more compilation units1205 // Enough for all the data without resizing. When support for more compilation units
...@@ -1167,12 +1254,12 @@ pub const File = struct {...@@ -1167,12 +1254,12 @@ pub const File = struct {
1167 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);1254 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
1168 }1255 }
1169 debug_aranges_sect.sh_size = needed_size;1256 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", .{
1171 debug_aranges_sect.sh_offset,1258 debug_aranges_sect.sh_offset,
1172 debug_aranges_sect.sh_offset + needed_size,1259 debug_aranges_sect.sh_offset + needed_size,
1173 });1260 });
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);
1176 if (!self.shdr_table_dirty) {1263 if (!self.shdr_table_dirty) {
1177 // Then it won't get written with the others and we need to do it.1264 // Then it won't get written with the others and we need to do it.
1178 try self.writeSectHeader(self.debug_aranges_section_index.?);1265 try self.writeSectHeader(self.debug_aranges_section_index.?);
...@@ -1180,14 +1267,17 @@ pub const File = struct {...@@ -1180,14 +1267,17 @@ pub const File = struct {
11801267
1181 self.debug_aranges_section_dirty = false;1268 self.debug_aranges_section_dirty = false;
1182 }1269 }
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 }
1184 const dbg_line_prg_off = self.getDebugLineProgramOff();1274 const dbg_line_prg_off = self.getDebugLineProgramOff();
1185 const dbg_line_prg_end = self.getDebugLineProgramEnd();1275 const dbg_line_prg_end = self.getDebugLineProgramEnd();
1186 assert(dbg_line_prg_end != 0);1276 assert(dbg_line_prg_end != 0);
11871277
1188 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];1278 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);
1191 defer di_buf.deinit();1281 defer di_buf.deinit();
11921282
1193 // The size of this header is variable, depending on the number of directories,1283 // The size of this header is variable, depending on the number of directories,
...@@ -1271,7 +1361,7 @@ pub const File = struct {...@@ -1271,7 +1361,7 @@ pub const File = struct {
1271 @panic("TODO: handle .debug_line header exceeding its padding");1361 @panic("TODO: handle .debug_line header exceeding its padding");
1272 }1362 }
1273 const jmp_amt = dbg_line_prg_off - di_buf.items.len;1363 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);
1275 self.debug_line_header_dirty = false;1365 self.debug_line_header_dirty = false;
1276 }1366 }
12771367
...@@ -1294,8 +1384,8 @@ pub const File = struct {...@@ -1294,8 +1384,8 @@ pub const File = struct {
12941384
1295 switch (self.ptr_width) {1385 switch (self.ptr_width) {
1296 .p32 => {1386 .p32 => {
1297 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);1387 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1298 defer self.allocator.free(buf);1388 defer self.base.allocator.free(buf);
12991389
1300 for (buf) |*phdr, i| {1390 for (buf) |*phdr, i| {
1301 phdr.* = progHeaderTo32(self.program_headers.items[i]);1391 phdr.* = progHeaderTo32(self.program_headers.items[i]);
...@@ -1303,11 +1393,11 @@ pub const File = struct {...@@ -1303,11 +1393,11 @@ pub const File = struct {
1303 bswapAllFields(elf.Elf32_Phdr, phdr);1393 bswapAllFields(elf.Elf32_Phdr, phdr);
1304 }1394 }
1305 }1395 }
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.?);
1307 },1397 },
1308 .p64 => {1398 .p64 => {
1309 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);1399 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1310 defer self.allocator.free(buf);1400 defer self.base.allocator.free(buf);
13111401
1312 for (buf) |*phdr, i| {1402 for (buf) |*phdr, i| {
1313 phdr.* = self.program_headers.items[i];1403 phdr.* = self.program_headers.items[i];
...@@ -1315,7 +1405,7 @@ pub const File = struct {...@@ -1315,7 +1405,7 @@ pub const File = struct {
1315 bswapAllFields(elf.Elf64_Phdr, phdr);1405 bswapAllFields(elf.Elf64_Phdr, phdr);
1316 }1406 }
1317 }1407 }
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.?);
1319 },1409 },
1320 }1410 }
1321 self.phdr_table_dirty = false;1411 self.phdr_table_dirty = false;
...@@ -1332,9 +1422,9 @@ pub const File = struct {...@@ -1332,9 +1422,9 @@ pub const File = struct {
1332 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);1422 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1333 }1423 }
1334 shstrtab_sect.sh_size = needed_size;1424 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);
1338 if (!self.shdr_table_dirty) {1428 if (!self.shdr_table_dirty) {
1339 // Then it won't get written with the others and we need to do it.1429 // Then it won't get written with the others and we need to do it.
1340 try self.writeSectHeader(self.shstrtab_index.?);1430 try self.writeSectHeader(self.shstrtab_index.?);
...@@ -1353,9 +1443,9 @@ pub const File = struct {...@@ -1353,9 +1443,9 @@ pub const File = struct {
1353 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);1443 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1354 }1444 }
1355 debug_strtab_sect.sh_size = needed_size;1445 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);
1359 if (!self.shdr_table_dirty) {1449 if (!self.shdr_table_dirty) {
1360 // Then it won't get written with the others and we need to do it.1450 // Then it won't get written with the others and we need to do it.
1361 try self.writeSectHeader(self.debug_str_section_index.?);1451 try self.writeSectHeader(self.debug_str_section_index.?);
...@@ -1382,53 +1472,53 @@ pub const File = struct {...@@ -1382,53 +1472,53 @@ pub const File = struct {
13821472
1383 switch (self.ptr_width) {1473 switch (self.ptr_width) {
1384 .p32 => {1474 .p32 => {
1385 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);1475 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1386 defer self.allocator.free(buf);1476 defer self.base.allocator.free(buf);
13871477
1388 for (buf) |*shdr, i| {1478 for (buf) |*shdr, i| {
1389 shdr.* = sectHeaderTo32(self.sections.items[i]);1479 shdr.* = sectHeaderTo32(self.sections.items[i]);
1480 log.debug("writing section {}\n", .{shdr.*});
1390 if (foreign_endian) {1481 if (foreign_endian) {
1391 bswapAllFields(elf.Elf32_Shdr, shdr);1482 bswapAllFields(elf.Elf32_Shdr, shdr);
1392 }1483 }
1393 }1484 }
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.?);
1395 },1486 },
1396 .p64 => {1487 .p64 => {
1397 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);1488 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1398 defer self.allocator.free(buf);1489 defer self.base.allocator.free(buf);
13991490
1400 for (buf) |*shdr, i| {1491 for (buf) |*shdr, i| {
1401 shdr.* = self.sections.items[i];1492 shdr.* = self.sections.items[i];
1402 log.debug(.link, "writing section {}\n", .{shdr.*});1493 log.debug("writing section {}\n", .{shdr.*});
1403 if (foreign_endian) {1494 if (foreign_endian) {
1404 bswapAllFields(elf.Elf64_Shdr, shdr);1495 bswapAllFields(elf.Elf64_Shdr, shdr);
1405 }1496 }
1406 }1497 }
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.?);
1408 },1499 },
1409 }1500 }
1410 self.shdr_table_dirty = false;1501 self.shdr_table_dirty = false;
1411 }1502 }
1412 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {1503 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", .{});
1414 self.error_flags.no_entry_point_found = true;1505 self.error_flags.no_entry_point_found = true;
1415 } else {1506 } else {
1507 log.debug("flushing. no_entry_point_found = false\n", .{});
1416 self.error_flags.no_entry_point_found = false;1508 self.error_flags.no_entry_point_found = false;
1417 try self.writeElfHeader();1509 try self.writeElfHeader();
1418 }1510 }
14191511
1420 // The point of flush() is to commit changes, so nothing should be dirty after this.1512 // The point of flush() is to commit changes, so in theory, nothing should
1421 assert(!self.debug_info_section_dirty);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.
1422 assert(!self.debug_abbrev_section_dirty);1516 assert(!self.debug_abbrev_section_dirty);
1423 assert(!self.debug_aranges_section_dirty);1517 assert(!self.debug_aranges_section_dirty);
1424 assert(!self.debug_line_header_dirty);
1425 assert(!self.phdr_table_dirty);1518 assert(!self.phdr_table_dirty);
1426 assert(!self.shdr_table_dirty);1519 assert(!self.shdr_table_dirty);
1427 assert(!self.shstrtab_dirty);1520 assert(!self.shstrtab_dirty);
1428 assert(!self.debug_strtab_dirty);1521 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);
1432 }1522 }
14331523
1434 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {1524 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
...@@ -1557,7 +1647,7 @@ pub const File = struct {...@@ -1557,7 +1647,7 @@ pub const File = struct {
15571647
1558 assert(index == e_ehsize);1648 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);
1561 }1651 }
15621652
1563 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {1653 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
...@@ -1587,7 +1677,7 @@ pub const File = struct {...@@ -1587,7 +1677,7 @@ pub const File = struct {
1587 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {1677 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
1588 // The free list is heuristics, it doesn't have to be perfect, so we can1678 // The free list is heuristics, it doesn't have to be perfect, so we can
1589 // ignore the OOM here.1679 // 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 {};
1591 }1681 }
1592 } else {1682 } else {
1593 text_block.prev = null;1683 text_block.prev = null;
...@@ -1688,7 +1778,7 @@ pub const File = struct {...@@ -1688,7 +1778,7 @@ pub const File = struct {
1688 const sym = self.local_symbols.items[last.local_sym_index];1778 const sym = self.local_symbols.items[last.local_sym_index];
1689 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;1779 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1690 } else 0;1780 } 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);
1692 if (amt != text_size) return error.InputOutput;1782 if (amt != text_size) return error.InputOutput;
1693 shdr.sh_offset = new_offset;1783 shdr.sh_offset = new_offset;
1694 phdr.p_offset = new_offset;1784 phdr.p_offset = new_offset;
...@@ -1701,8 +1791,8 @@ pub const File = struct {...@@ -1701,8 +1791,8 @@ pub const File = struct {
17011791
1702 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address1792 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
1703 // range of the compilation unit. When we expand the text section, this range changes,1793 // range of the compilation unit. When we expand the text section, this range changes,
1704 // so the .debug_info section becomes dirty.1794 // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
1705 self.debug_info_section_dirty = true;1795 self.debug_info_header_dirty = true;
1706 // This becomes dirty for the same reason. We could potentially make this more1796 // This becomes dirty for the same reason. We could potentially make this more
1707 // fine-grained with the addition of support for more compilation units. It is planned to1797 // fine-grained with the addition of support for more compilation units. It is planned to
1708 // model each package as a different compilation unit.1798 // model each package as a different compilation unit.
...@@ -1737,31 +1827,31 @@ pub const File = struct {...@@ -1737,31 +1827,31 @@ pub const File = struct {
1737 }1827 }
17381828
1739 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {1829 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);1832 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
1743 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);1833 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
17441834
1745 if (self.local_symbol_free_list.popOrNull()) |i| {1835 if (self.local_symbol_free_list.popOrNull()) |i| {
1746 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });1836 log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
1747 decl.link.local_sym_index = i;1837 decl.link.elf.local_sym_index = i;
1748 } else {1838 } else {
1749 log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });1839 log.debug("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);1840 decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1751 _ = self.local_symbols.addOneAssumeCapacity();1841 _ = self.local_symbols.addOneAssumeCapacity();
1752 }1842 }
17531843
1754 if (self.offset_table_free_list.popOrNull()) |i| {1844 if (self.offset_table_free_list.popOrNull()) |i| {
1755 decl.link.offset_table_index = i;1845 decl.link.elf.offset_table_index = i;
1756 } else {1846 } 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);
1758 _ = self.offset_table.addOneAssumeCapacity();1848 _ = self.offset_table.addOneAssumeCapacity();
1759 self.offset_table_count_dirty = true;1849 self.offset_table_count_dirty = true;
1760 }1850 }
17611851
1762 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];1852 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] = .{
1765 .st_name = 0,1855 .st_name = 0,
1766 .st_info = 0,1856 .st_info = 0,
1767 .st_other = 0,1857 .st_other = 0,
...@@ -1769,39 +1859,39 @@ pub const File = struct {...@@ -1769,39 +1859,39 @@ pub const File = struct {
1769 .st_value = phdr.p_vaddr,1859 .st_value = phdr.p_vaddr,
1770 .st_size = 0,1860 .st_size = 0,
1771 };1861 };
1772 self.offset_table.items[decl.link.offset_table_index] = 0;1862 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
1773 }1863 }
17741864
1775 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {1865 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1776 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1866 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1777 self.freeTextBlock(&decl.link);1867 self.freeTextBlock(&decl.link.elf);
1778 if (decl.link.local_sym_index != 0) {1868 if (decl.link.elf.local_sym_index != 0) {
1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};1869 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_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;
1785 }1875 }
1786 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing1876 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1787 // is desired for both.1877 // is desired for both.
1788 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link);1878 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
1789 if (decl.fn_link.prev) |prev| {1879 if (decl.fn_link.elf.prev) |prev| {
1790 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};1880 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1791 prev.next = decl.fn_link.next;1881 prev.next = decl.fn_link.elf.next;
1792 if (decl.fn_link.next) |next| {1882 if (decl.fn_link.elf.next) |next| {
1793 next.prev = prev;1883 next.prev = prev;
1794 } else {1884 } else {
1795 self.dbg_line_fn_last = prev;1885 self.dbg_line_fn_last = prev;
1796 }1886 }
1797 } else if (decl.fn_link.next) |next| {1887 } else if (decl.fn_link.elf.next) |next| {
1798 self.dbg_line_fn_first = next;1888 self.dbg_line_fn_first = next;
1799 next.prev = null;1889 next.prev = null;
1800 }1890 }
1801 if (self.dbg_line_fn_first == &decl.fn_link) {1891 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
1802 self.dbg_line_fn_first = null;1892 self.dbg_line_fn_first = null;
1803 }1893 }
1804 if (self.dbg_line_fn_last == &decl.fn_link) {1894 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
1805 self.dbg_line_fn_last = null;1895 self.dbg_line_fn_last = null;
1806 }1896 }
1807 }1897 }
...@@ -1810,18 +1900,33 @@ pub const File = struct {...@@ -1810,18 +1900,33 @@ pub const File = struct {
1810 const tracy = trace(@src());1900 const tracy = trace(@src());
1811 defer tracy.end();1901 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);
1814 defer code_buffer.deinit();1904 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);
1817 defer dbg_line_buffer.deinit();1907 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
1819 const typed_value = decl.typed_value.most_recent.typed_value;1920 const typed_value = decl.typed_value.most_recent.typed_value;
1820 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {1921 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1821 .Fn => true,1922 .Fn => true,
1822 else => false,1923 else => false,
1823 };1924 };
1824 if (is_fn) {1925 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
1825 // For functions we need to add a prologue to the debug line program.1930 // For functions we need to add a prologue to the debug line program.
1826 try dbg_line_buffer.ensureCapacity(26);1931 try dbg_line_buffer.ensureCapacity(26);
18271932
...@@ -1871,8 +1976,41 @@ pub const File = struct {...@@ -1871,8 +1976,41 @@ pub const File = struct {
1871 // Emit a line for the begin curly with prologue_end=false. The codegen will1976 // Emit a line for the begin curly with prologue_end=false. The codegen will
1872 // do the work of setting prologue_end=true and epilogue_begin=true.1977 // do the work of setting prologue_end=true and epilogue_begin=true.
1873 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);1978 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
1874 }2012 }
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);
1876 const code = switch (res) {2014 const code = switch (res) {
1877 .externally_managed => |x| x,2015 .externally_managed => |x| x,
1878 .appended => code_buffer.items,2016 .appended => code_buffer.items,
...@@ -1887,24 +2025,24 @@ pub const File = struct {...@@ -1887,24 +2025,24 @@ pub const File = struct {
18872025
1888 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;2026 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()2028 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];2029 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
1892 if (local_sym.st_size != 0) {2030 if (local_sym.st_size != 0) {
1893 const capacity = decl.link.capacity(self.*);2031 const capacity = decl.link.elf.capacity(self.*);
1894 const need_realloc = code.len > capacity or2032 const need_realloc = code.len > capacity or
1895 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);2033 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1896 if (need_realloc) {2034 if (need_realloc) {
1897 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);2035 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
1898 log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });2036 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1899 if (vaddr != local_sym.st_value) {2037 if (vaddr != local_sym.st_value) {
1900 local_sym.st_value = vaddr;2038 local_sym.st_value = vaddr;
19012039
1902 log.debug(.link, " (writing new offset table entry)\n", .{});2040 log.debug(" (writing new offset table entry)\n", .{});
1903 self.offset_table.items[decl.link.offset_table_index] = vaddr;2041 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
1904 try self.writeOffsetTableEntry(decl.link.offset_table_index);2042 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
1905 }2043 }
1906 } else if (code.len < local_sym.st_size) {2044 } else if (code.len < local_sym.st_size) {
1907 self.shrinkTextBlock(&decl.link, code.len);2045 self.shrinkTextBlock(&decl.link.elf, code.len);
1908 }2046 }
1909 local_sym.st_size = code.len;2047 local_sym.st_size = code.len;
1910 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));2048 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
...@@ -1912,13 +2050,13 @@ pub const File = struct {...@@ -1912,13 +2050,13 @@ pub const File = struct {
1912 local_sym.st_other = 0;2050 local_sym.st_other = 0;
1913 local_sym.st_shndx = self.text_section_index.?;2051 local_sym.st_shndx = self.text_section_index.?;
1914 // TODO this write could be avoided if no fields of the symbol were changed.2052 // 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);
1916 } else {2054 } else {
1917 const decl_name = mem.spanZ(decl.name);2055 const decl_name = mem.spanZ(decl.name);
1918 const name_str_index = try self.makeString(decl_name);2056 const name_str_index = try self.makeString(decl_name);
1919 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);2057 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
1920 log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });2058 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1921 errdefer self.freeTextBlock(&decl.link);2059 errdefer self.freeTextBlock(&decl.link.elf);
19222060
1923 local_sym.* = .{2061 local_sym.* = .{
1924 .st_name = name_str_index,2062 .st_name = name_str_index,
...@@ -1928,37 +2066,60 @@ pub const File = struct {...@@ -1928,37 +2066,60 @@ pub const File = struct {
1928 .st_value = vaddr,2066 .st_value = vaddr,
1929 .st_size = code.len,2067 .st_size = code.len,
1930 };2068 };
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);2071 try self.writeSymbol(decl.link.elf.local_sym_index);
1934 try self.writeOffsetTableEntry(decl.link.offset_table_index);2072 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
1935 }2073 }
19362074
1937 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;2075 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1938 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;2076 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
1941 // If the Decl is a function, we need to update the .debug_line program.2083 // If the Decl is a function, we need to update the .debug_line program.
1942 if (is_fn) {2084 if (is_fn) {
1943 // Perform the relocation based on vaddr.2085 // Perform the relocations based on vaddr.
1944 const target_endian = self.base.options.target.cpu.arch.endian();
1945 switch (self.ptr_width) {2086 switch (self.ptr_width) {
1946 .p32 => {2087 .p32 => {
1947 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];2088 {
1948 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);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 }
1949 },2096 },
1950 .p64 => {2097 .p64 => {
1951 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];2098 {
1952 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);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 }
1953 },2106 },
1954 }2107 }
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
1956 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });2113 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
19572114
1958 // Now we have the full contents and may allocate a region to store it.2115 // 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
1960 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];2121 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;
1962 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);2123 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1963 if (self.dbg_line_fn_last) |last| {2124 if (self.dbg_line_fn_last) |last| {
1964 if (src_fn.next) |next| {2125 if (src_fn.next) |next| {
...@@ -1966,14 +2127,14 @@ pub const File = struct {...@@ -1966,14 +2127,14 @@ pub const File = struct {
1966 if (src_fn.off + src_fn.len + min_nop_size > next.off) {2127 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1967 // It grew too big, so we move it to a new location.2128 // It grew too big, so we move it to a new location.
1968 if (src_fn.prev) |prev| {2129 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 {};
1970 prev.next = src_fn.next;2131 prev.next = src_fn.next;
1971 }2132 }
1972 next.prev = src_fn.prev;2133 next.prev = src_fn.prev;
1973 src_fn.next = null;2134 src_fn.next = null;
1974 // Populate where it used to be with NOPs.2135 // Populate where it used to be with NOPs.
1975 const file_pos = debug_line_sect.sh_offset + src_fn.off;2136 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);
1977 // TODO Look at the free list before appending at the end.2138 // TODO Look at the free list before appending at the end.
1978 src_fn.prev = last;2139 src_fn.prev = last;
1979 last.next = src_fn;2140 last.next = src_fn;
...@@ -2004,12 +2165,12 @@ pub const File = struct {...@@ -2004,12 +2165,12 @@ pub const File = struct {
2004 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {2165 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2005 const new_offset = self.findFreeSpace(needed_size, 1);2166 const new_offset = self.findFreeSpace(needed_size, 1);
2006 const existing_size = last_src_fn.off;2167 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", .{
2008 existing_size,2169 existing_size,
2009 debug_line_sect.sh_offset,2170 debug_line_sect.sh_offset,
2010 new_offset,2171 new_offset,
2011 });2172 });
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);
2013 if (amt != existing_size) return error.InputOutput;2174 if (amt != existing_size) return error.InputOutput;
2014 debug_line_sect.sh_offset = new_offset;2175 debug_line_sect.sh_offset = new_offset;
2015 }2176 }
...@@ -2023,15 +2184,169 @@ pub const File = struct {...@@ -2023,15 +2184,169 @@ pub const File = struct {
2023 // We only have support for one compilation unit so far, so the offsets are directly2184 // We only have support for one compilation unit so far, so the offsets are directly
2024 // from the .debug_line section.2185 // from the .debug_line section.
2025 const file_pos = debug_line_sect.sh_offset + src_fn.off;2186 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);
2027 }2199 }
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
2029 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2217 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
2030 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};2218 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
2031 return self.updateDeclExports(module, decl, decl_exports);2219 return self.updateDeclExports(module, decl, decl_exports);
2032 }2220 }
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
2035 pub fn updateDeclExports(2350 pub fn updateDeclExports(
2036 self: *Elf,2351 self: *Elf,
2037 module: *Module,2352 module: *Module,
...@@ -2041,10 +2356,10 @@ pub const File = struct {...@@ -2041,10 +2356,10 @@ pub const File = struct {
2041 const tracy = trace(@src());2356 const tracy = trace(@src());
2042 defer tracy.end();2357 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);
2045 const typed_value = decl.typed_value.most_recent.typed_value;2360 const typed_value = decl.typed_value.most_recent.typed_value;
2046 if (decl.link.local_sym_index == 0) return;2361 if (decl.link.elf.local_sym_index == 0) return;
2047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];2362 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
20482363
2049 for (exports) |exp| {2364 for (exports) |exp| {
2050 if (exp.options.section) |section_name| {2365 if (exp.options.section) |section_name| {
...@@ -2052,7 +2367,7 @@ pub const File = struct {...@@ -2052,7 +2367,7 @@ pub const File = struct {
2052 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);2367 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2053 module.failed_exports.putAssumeCapacityNoClobber(2368 module.failed_exports.putAssumeCapacityNoClobber(
2054 exp,2369 exp,
2055 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),2370 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
2056 );2371 );
2057 continue;2372 continue;
2058 }2373 }
...@@ -2070,7 +2385,7 @@ pub const File = struct {...@@ -2070,7 +2385,7 @@ pub const File = struct {
2070 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);2385 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
2071 module.failed_exports.putAssumeCapacityNoClobber(2386 module.failed_exports.putAssumeCapacityNoClobber(
2072 exp,2387 exp,
2073 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),2388 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2074 );2389 );
2075 continue;2390 continue;
2076 },2391 },
...@@ -2122,15 +2437,15 @@ pub const File = struct {...@@ -2122,15 +2437,15 @@ pub const File = struct {
2122 const casted_line_off = @intCast(u28, line_delta);2437 const casted_line_off = @intCast(u28, line_delta);
21232438
2124 const shdr = &self.sections.items[self.debug_line_section_index.?];2439 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();
2126 var data: [4]u8 = undefined;2441 var data: [4]u8 = undefined;
2127 leb128.writeUnsignedFixed(4, &data, casted_line_off);2442 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2128 try self.file.?.pwriteAll(&data, file_pos);2443 try self.base.file.?.pwriteAll(&data, file_pos);
2129 }2444 }
21302445
2131 pub fn deleteExport(self: *Elf, exp: Export) void {2446 pub fn deleteExport(self: *Elf, exp: Export) void {
2132 const sym_index = exp.sym_index orelse return;2447 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 {};
2134 self.global_symbols.items[sym_index].st_info = 0;2449 self.global_symbols.items[sym_index].st_info = 0;
2135 }2450 }
21362451
...@@ -2143,14 +2458,14 @@ pub const File = struct {...@@ -2143,14 +2458,14 @@ pub const File = struct {
2143 if (foreign_endian) {2458 if (foreign_endian) {
2144 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);2459 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
2145 }2460 }
2146 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2461 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2147 },2462 },
2148 64 => {2463 64 => {
2149 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};2464 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
2150 if (foreign_endian) {2465 if (foreign_endian) {
2151 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);2466 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
2152 }2467 }
2153 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2468 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
2154 },2469 },
2155 else => return error.UnsupportedArchitecture,2470 else => return error.UnsupportedArchitecture,
2156 }2471 }
...@@ -2166,7 +2481,7 @@ pub const File = struct {...@@ -2166,7 +2481,7 @@ pub const File = struct {
2166 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);2481 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
2167 }2482 }
2168 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);2483 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);
2170 },2485 },
2171 64 => {2486 64 => {
2172 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};2487 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
...@@ -2174,7 +2489,7 @@ pub const File = struct {...@@ -2174,7 +2489,7 @@ pub const File = struct {
2174 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);2489 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
2175 }2490 }
2176 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);2491 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);
2178 },2493 },
2179 else => return error.UnsupportedArchitecture,2494 else => return error.UnsupportedArchitecture,
2180 }2495 }
...@@ -2191,7 +2506,7 @@ pub const File = struct {...@@ -2191,7 +2506,7 @@ pub const File = struct {
2191 if (needed_size > allocated_size) {2506 if (needed_size > allocated_size) {
2192 // Must move the entire got section.2507 // Must move the entire got section.
2193 const new_offset = self.findFreeSpace(needed_size, entry_size);2508 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);
2195 if (amt != shdr.sh_size) return error.InputOutput;2510 if (amt != shdr.sh_size) return error.InputOutput;
2196 shdr.sh_offset = new_offset;2511 shdr.sh_offset = new_offset;
2197 phdr.p_offset = new_offset;2512 phdr.p_offset = new_offset;
...@@ -2211,17 +2526,20 @@ pub const File = struct {...@@ -2211,17 +2526,20 @@ pub const File = struct {
2211 .p32 => {2526 .p32 => {
2212 var buf: [4]u8 = undefined;2527 var buf: [4]u8 = undefined;
2213 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);2528 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);
2215 },2530 },
2216 .p64 => {2531 .p64 => {
2217 var buf: [8]u8 = undefined;2532 var buf: [8]u8 = undefined;
2218 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);2533 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);
2220 },2535 },
2221 }2536 }
2222 }2537 }
22232538
2224 fn writeSymbol(self: *Elf, index: usize) !void {2539 fn writeSymbol(self: *Elf, index: usize) !void {
2540 const tracy = trace(@src());
2541 defer tracy.end();
2542
2225 const syms_sect = &self.sections.items[self.symtab_section_index.?];2543 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2226 // Make sure we are not pointlessly writing symbol data that will have to get relocated2544 // Make sure we are not pointlessly writing symbol data that will have to get relocated
2227 // due to running out of space.2545 // due to running out of space.
...@@ -2239,7 +2557,7 @@ pub const File = struct {...@@ -2239,7 +2557,7 @@ pub const File = struct {
2239 // Move all the symbols to a new file location.2557 // Move all the symbols to a new file location.
2240 const new_offset = self.findFreeSpace(needed_size, sym_align);2558 const new_offset = self.findFreeSpace(needed_size, sym_align);
2241 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;2559 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);
2243 if (amt != existing_size) return error.InputOutput;2561 if (amt != existing_size) return error.InputOutput;
2244 syms_sect.sh_offset = new_offset;2562 syms_sect.sh_offset = new_offset;
2245 }2563 }
...@@ -2264,7 +2582,7 @@ pub const File = struct {...@@ -2264,7 +2582,7 @@ pub const File = struct {
2264 bswapAllFields(elf.Elf32_Sym, &sym[0]);2582 bswapAllFields(elf.Elf32_Sym, &sym[0]);
2265 }2583 }
2266 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;2584 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);
2268 },2586 },
2269 .p64 => {2587 .p64 => {
2270 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};2588 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
...@@ -2272,7 +2590,7 @@ pub const File = struct {...@@ -2272,7 +2590,7 @@ pub const File = struct {
2272 bswapAllFields(elf.Elf64_Sym, &sym[0]);2590 bswapAllFields(elf.Elf64_Sym, &sym[0]);
2273 }2591 }
2274 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;2592 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);
2276 },2594 },
2277 }2595 }
2278 }2596 }
...@@ -2287,8 +2605,8 @@ pub const File = struct {...@@ -2287,8 +2605,8 @@ pub const File = struct {
2287 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;2605 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
2288 switch (self.ptr_width) {2606 switch (self.ptr_width) {
2289 .p32 => {2607 .p32 => {
2290 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);2608 const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
2291 defer self.allocator.free(buf);2609 defer self.base.allocator.free(buf);
22922610
2293 for (buf) |*sym, i| {2611 for (buf) |*sym, i| {
2294 sym.* = .{2612 sym.* = .{
...@@ -2303,11 +2621,11 @@ pub const File = struct {...@@ -2303,11 +2621,11 @@ pub const File = struct {
2303 bswapAllFields(elf.Elf32_Sym, sym);2621 bswapAllFields(elf.Elf32_Sym, sym);
2304 }2622 }
2305 }2623 }
2306 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);2624 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2307 },2625 },
2308 .p64 => {2626 .p64 => {
2309 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);2627 const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
2310 defer self.allocator.free(buf);2628 defer self.base.allocator.free(buf);
23112629
2312 for (buf) |*sym, i| {2630 for (buf) |*sym, i| {
2313 sym.* = .{2631 sym.* = .{
...@@ -2322,7 +2640,7 @@ pub const File = struct {...@@ -2322,7 +2640,7 @@ pub const File = struct {
2322 bswapAllFields(elf.Elf64_Sym, sym);2640 bswapAllFields(elf.Elf64_Sym, sym);
2323 }2641 }
2324 }2642 }
2325 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);2643 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
2326 },2644 },
2327 }2645 }
2328 }2646 }
...@@ -2337,6 +2655,9 @@ pub const File = struct {...@@ -2337,6 +2655,9 @@ pub const File = struct {
2337 /// The reloc offset for the virtual address of a function in its Line Number Program.2655 /// The reloc offset for the virtual address of a function in its Line Number Program.
2338 /// Size is a virtual address integer.2656 /// Size is a virtual address integer.
2339 const dbg_line_vaddr_reloc_index = 3;2657 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
2341 /// The reloc offset for the line offset of a function from the previous function's line.2662 /// The reloc offset for the line offset of a function from the previous function's line.
2342 /// It's a fixed-size 4-byte ULEB128.2663 /// It's a fixed-size 4-byte ULEB128.
...@@ -2348,6 +2669,10 @@ pub const File = struct {...@@ -2348,6 +2669,10 @@ pub const File = struct {
2348 return self.getRelocDbgLineOff() + 5;2669 return self.getRelocDbgLineOff() + 5;
2349 }2670 }
23502671
2672 fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
2673 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
2674 }
2675
2351 fn dbgLineNeededHeaderBytes(self: Elf) u32 {2676 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2352 const directory_entry_format_count = 1;2677 const directory_entry_format_count = 1;
2353 const file_name_entry_format_count = 1;2678 const file_name_entry_format_count = 1;
...@@ -2362,18 +2687,27 @@ pub const File = struct {...@@ -2362,18 +2687,27 @@ pub const File = struct {
23622687
2363 }2688 }
23642689
2690 fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
2691 return 120;
2692 }
2693
2694 const min_nop_size = 2;
2695
2365 /// Writes to the file a buffer, prefixed and suffixed by the specified number of2696 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
2366 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes2697 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
2367 /// are less than 126,976 bytes (if this limit is ever reached, this function can be2698 /// are less than 126,976 bytes (if this limit is ever reached, this function can be
2368 /// improved to make more than one pwritev call, or the limit can be raised by a fixed2699 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
2369 /// amount by increasing the length of `vecs`).2700 /// amount by increasing the length of `vecs`).
2370 fn pwriteWithNops(2701 fn pwriteDbgLineNops(
2371 self: *Elf,2702 self: *Elf,
2372 prev_padding_size: usize,2703 prev_padding_size: usize,
2373 buf: []const u8,2704 buf: []const u8,
2374 next_padding_size: usize,2705 next_padding_size: usize,
2375 offset: usize,2706 offset: usize,
2376 ) !void {2707 ) !void {
2708 const tracy = trace(@src());
2709 defer tracy.end();
2710
2377 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;2711 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
2378 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};2712 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};
2379 var vecs: [32]std.os.iovec_const = undefined;2713 var vecs: [32]std.os.iovec_const = undefined;
...@@ -2437,12 +2771,85 @@ pub const File = struct {...@@ -2437,12 +2771,85 @@ pub const File = struct {
2437 vec_index += 1;2771 vec_index += 1;
2438 }2772 }
2439 }2773 }
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);
2441 }2775 }
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
2445 };2849 };
2850
2851 pub const MachO = @import("link/MachO.zig");
2852 const Wasm = @import("link/Wasm.zig");
2446};2853};
24472854
2448/// Saturating multiplication2855/// Saturating multiplication
...@@ -2483,7 +2890,7 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {...@@ -2483,7 +2890,7 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
2483 };2890 };
2484}2891}
24852892
2486fn determineMode(options: Options) fs.File.Mode {2893pub fn determineMode(options: Options) fs.File.Mode {
2487 // On common systems with a 0o022 umask, 0o777 will still result in a file created2894 // On common systems with a 0o022 umask, 0o777 will still result in a file created
2488 // with 0o755 permissions, but it works appropriately if the system is configured2895 // with 0o755 permissions, but it works appropriately if the system is configured
2489 // more leniently. As another data point, C's fopen seems to open files with the2896 // 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(...@@ -16,20 +16,42 @@ pub fn analyze(
16 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);16 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
17 defer table.deinit();17 defer table.deinit();
18 try table.ensureCapacity(body.instructions.len);18 try table.ensureCapacity(body.instructions.len);
19 try analyzeWithTable(arena, &table, body);19 try analyzeWithTable(arena, &table, null, body);
20}20}
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 {
23 var i: usize = body.instructions.len;28 var i: usize = body.instructions.len;
2429
25 while (i != 0) {30 if (new_set) |ns| {
26 i -= 1;31 // We are only interested in doing this for instructions which are born
27 const base = body.instructions[i];32 // before a conditional branch, so after obtaining the new set for
28 try analyzeInst(arena, table, base);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 }
29 }46 }
30}47}
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 {
33 if (table.contains(base)) {55 if (table.contains(base)) {
34 base.deaths = 0;56 base.deaths = 0;
35 } else {57 } else {
...@@ -42,56 +64,70 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void...@@ -42,56 +64,70 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
42 .constant => return,64 .constant => return,
43 .block => {65 .block => {
44 const inst = base.castTag(.block).?;66 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);67 try analyzeWithTable(arena, table, new_set, inst.body);
46 // We let this continue so that it can possibly mark the block as68 // We let this continue so that it can possibly mark the block as
47 // unreferenced below.69 // unreferenced below.
48 },70 },
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 },
49 .condbr => {76 .condbr => {
50 const inst = base.castTag(.condbr).?;77 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
61 // Each death that occurs inside one branch, but not the other, needs79 // Each death that occurs inside one branch, but not the other, needs
62 // to be added as a death immediately upon entering the other branch.80 // to be added as a death immediately upon entering the other branch.
63 // During the iteration of the table, we additionally propagate the81
64 // deaths to the parent table.82 var then_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
65 var true_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);83 defer then_table.deinit();
66 defer true_entry_deaths.deinit();84 try analyzeWithTable(arena, table, &then_table, inst.then_body);
67 var false_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);85
68 defer false_entry_deaths.deinit();86 // Reset the table back to its state from before the branch.
69 {87 for (then_table.items()) |entry| {
70 var it = false_table.iterator();88 table.removeAssertDiscard(entry.key);
71 while (it.next()) |entry| {89 }
72 const false_death = entry.key;90
73 if (!true_table.contains(false_death)) {91 var else_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
74 try true_entry_deaths.append(false_death);92 defer else_table.deinit();
75 // Here we are only adding to the parent table if the following iteration93 try analyzeWithTable(arena, table, &else_table, inst.else_body);
76 // would miss it.94
77 try table.putNoClobber(false_death, {});95 var then_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
78 }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);
79 }112 }
113 _ = try table.put(then_death, {});
80 }114 }
81 {115 // Now we have to correctly populate new_set.
82 var it = true_table.iterator();116 if (new_set) |ns| {
83 while (it.next()) |entry| {117 try ns.ensureCapacity(ns.items().len + then_table.items().len + else_table.items().len);
84 const true_death = entry.key;118 for (then_table.items()) |entry| {
85 try table.putNoClobber(true_death, {});119 _ = ns.putAssumeCapacity(entry.key, {});
86 if (!false_table.contains(true_death)) {120 }
87 try false_entry_deaths.append(true_death);121 for (else_table.items()) |entry| {
88 }122 _ = ns.putAssumeCapacity(entry.key, {});
89 }123 }
90 }124 }
91 inst.true_death_count = std.math.cast(@TypeOf(inst.true_death_count), true_entry_deaths.items.len) catch return error.OutOfMemory;125 inst.then_death_count = std.math.cast(@TypeOf(inst.then_death_count), then_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;126 inst.else_death_count = std.math.cast(@TypeOf(inst.else_death_count), else_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);127 const allocated_slice = try arena.alloc(*ir.Inst, then_entry_deaths.items.len + else_entry_deaths.items.len);
94 inst.deaths = allocated_slice.ptr;128 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
96 // Continue on with the instruction analysis. The following code will find the condition132 // Continue on with the instruction analysis. The following code will find the condition
97 // instruction, and the deaths flag for the CondBr instruction will indicate whether the133 // 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...@@ -108,11 +144,12 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
108 if (prev == null) {144 if (prev == null) {
109 // Death.145 // Death.
110 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;146 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
147 if (new_set) |ns| try ns.putNoClobber(operand, {});
111 }148 }
112 }149 }
113 } else {150 } else {
114 @panic("Handle liveness analysis for instructions with many parameters");151 @panic("Handle liveness analysis for instructions with many parameters");
115 }152 }
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 });
118}155}
src-self-hosted/main.zig+33-27
...@@ -30,6 +30,7 @@ const usage =...@@ -30,6 +30,7 @@ const usage =
30 \\ build-obj [source] Create object from source or assembly30 \\ build-obj [source] Create object from source or assembly
31 \\ fmt [source] Parse file and render in canonical zig format31 \\ fmt [source] Parse file and render in canonical zig format
32 \\ targets List available compilation targets32 \\ targets List available compilation targets
33 \\ env Print lib path, std path, compiler id and version
33 \\ version Print version number and exit34 \\ version Print version number and exit
34 \\ zen Print zen of zig and exit35 \\ zen Print zen of zig and exit
35 \\36 \\
...@@ -42,27 +43,33 @@ pub fn log(...@@ -42,27 +43,33 @@ pub fn log(
42 comptime format: []const u8,43 comptime format: []const u8,
43 args: anytype,44 args: anytype,
44) void {45) void {
45 if (@enumToInt(level) > @enumToInt(std.log.level))46 // Hide anything more verbose than warn unless it was added with `-Dlog=foo`.
46 return;47 if (@enumToInt(level) > @enumToInt(std.log.level) or
4748 @enumToInt(level) > @enumToInt(std.log.Level.warn))
48 const scope_name = @tagName(scope);49 {
49 const ok = comptime for (build_options.log_scopes) |log_scope| {50 const scope_name = @tagName(scope);
50 if (mem.eql(u8, log_scope, scope_name))51 const ok = comptime for (build_options.log_scopes) |log_scope| {
51 break true;52 if (mem.eql(u8, log_scope, scope_name))
52 } else false;53 break true;
54 } else false;
5355
54 if (!ok)56 if (!ok)
55 return;57 return;
58 }
5659
57 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";60 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
5861
59 // Print the message to stderr, silently ignoring any errors62 // Print the message to stderr, silently ignoring any errors
60 std.debug.print(prefix ++ format, args);63 std.debug.print(prefix ++ format ++ "\n", args);
61}64}
6265
66var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
67
63pub fn main() !void {68pub fn main() !void {
64 // TODO general purpose allocator in the zig std lib69 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator;
65 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;70 defer if (!std.builtin.link_libc) {
71 _ = general_purpose_allocator.deinit();
72 };
66 var arena_instance = std.heap.ArenaAllocator.init(gpa);73 var arena_instance = std.heap.ArenaAllocator.init(gpa);
67 defer arena_instance.deinit();74 defer arena_instance.deinit();
68 const arena = &arena_instance.allocator;75 const arena = &arena_instance.allocator;
...@@ -89,11 +96,9 @@ pub fn main() !void {...@@ -89,11 +96,9 @@ pub fn main() !void {
89 const stdout = io.getStdOut().outStream();96 const stdout = io.getStdOut().outStream();
90 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);97 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
91 } else if (mem.eql(u8, cmd, "version")) {98 } else if (mem.eql(u8, cmd, "version")) {
92 // Need to set up the build script to give the version as a comptime value.99 try std.io.getStdOut().writeAll(build_options.version ++ "\n");
93 // TODO when you solve this, also take a look at link.zig, there is a placeholder100 } else if (mem.eql(u8, cmd, "env")) {
94 // that says "TODO version here".101 try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream());
95 std.debug.print("TODO version command not implemented yet\n", .{});
96 return error.Unimplemented;
97 } else if (mem.eql(u8, cmd, "zen")) {102 } else if (mem.eql(u8, cmd, "zen")) {
98 try io.getStdOut().writeAll(info_zen);103 try io.getStdOut().writeAll(info_zen);
99 } else if (mem.eql(u8, cmd, "help")) {104 } else if (mem.eql(u8, cmd, "help")) {
...@@ -147,6 +152,7 @@ const usage_build_generic =...@@ -147,6 +152,7 @@ const usage_build_generic =
147 \\ -ofmt=[mode] Override target object format152 \\ -ofmt=[mode] Override target object format
148 \\ elf Executable and Linking Format153 \\ elf Executable and Linking Format
149 \\ c Compile to C source code154 \\ c Compile to C source code
155 \\ wasm WebAssembly
150 \\ coff (planned) Common Object File Format (Windows)156 \\ coff (planned) Common Object File Format (Windows)
151 \\ pe (planned) Portable Executable (Windows)157 \\ pe (planned) Portable Executable (Windows)
152 \\ macho (planned) macOS relocatables158 \\ macho (planned) macOS relocatables
...@@ -336,39 +342,39 @@ fn buildOutputType(...@@ -336,39 +342,39 @@ fn buildOutputType(
336 } else if (mem.startsWith(u8, arg, "-l")) {342 } else if (mem.startsWith(u8, arg, "-l")) {
337 try system_libs.append(arg[2..]);343 try system_libs.append(arg[2..]);
338 } else {344 } else {
339 std.debug.print("unrecognized parameter: '{}'", .{arg});345 std.debug.print("unrecognized parameter: '{}'\n", .{arg});
340 process.exit(1);346 process.exit(1);
341 }347 }
342 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {348 } 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", .{});
344 process.exit(1);350 process.exit(1);
345 } else if (mem.endsWith(u8, arg, ".o") or351 } else if (mem.endsWith(u8, arg, ".o") or
346 mem.endsWith(u8, arg, ".obj") or352 mem.endsWith(u8, arg, ".obj") or
347 mem.endsWith(u8, arg, ".a") or353 mem.endsWith(u8, arg, ".a") or
348 mem.endsWith(u8, arg, ".lib"))354 mem.endsWith(u8, arg, ".lib"))
349 {355 {
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", .{});
351 process.exit(1);357 process.exit(1);
352 } else if (mem.endsWith(u8, arg, ".c") or358 } else if (mem.endsWith(u8, arg, ".c") or
353 mem.endsWith(u8, arg, ".cpp"))359 mem.endsWith(u8, arg, ".cpp"))
354 {360 {
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", .{});
356 process.exit(1);362 process.exit(1);
357 } else if (mem.endsWith(u8, arg, ".so") or363 } else if (mem.endsWith(u8, arg, ".so") or
358 mem.endsWith(u8, arg, ".dylib") or364 mem.endsWith(u8, arg, ".dylib") or
359 mem.endsWith(u8, arg, ".dll"))365 mem.endsWith(u8, arg, ".dll"))
360 {366 {
361 std.debug.print("linking against dynamic libraries not yet supported", .{});367 std.debug.print("linking against dynamic libraries not yet supported\n", .{});
362 process.exit(1);368 process.exit(1);
363 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {369 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
364 if (root_src_file) |other| {370 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 });
366 process.exit(1);372 process.exit(1);
367 } else {373 } else {
368 root_src_file = arg;374 root_src_file = arg;
369 }375 }
370 } else {376 } else {
371 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});377 std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg});
372 }378 }
373 }379 }
374 }380 }
...@@ -385,7 +391,7 @@ fn buildOutputType(...@@ -385,7 +391,7 @@ fn buildOutputType(
385 };391 };
386392
387 if (system_libs.items.len != 0) {393 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", .{});
389 process.exit(1);395 process.exit(1);
390 }396 }
391397
...@@ -554,7 +560,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo...@@ -554,7 +560,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
554 });560 });
555 }561 }
556 } else {562 } 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});
558 }564 }
559565
560 if (zir_out_path) |zop| {566 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(...@@ -67,7 +67,7 @@ pub fn cmdTargets(
67) !void {67) !void {
68 const available_glibcs = blk: {68 const available_glibcs = blk: {
69 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| {69 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)});
71 std.process.exit(1);71 std.process.exit(1);
72 };72 };
73 defer allocator.free(zig_lib_dir);73 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 {...@@ -179,8 +179,7 @@ export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
179 return 0;179 return 0;
180}180}
181181
182fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {182fn argvToArrayList(allocator: *Allocator, argc: c_int, argv: [*]const [*:0]const u8) !ArrayList([]const u8) {
183 const allocator = std.heap.c_allocator;
184 var args_list = std.ArrayList([]const u8).init(allocator);183 var args_list = std.ArrayList([]const u8).init(allocator);
185 const argc_usize = @intCast(usize, argc);184 const argc_usize = @intCast(usize, argc);
186 var arg_i: usize = 0;185 var arg_i: usize = 0;
...@@ -188,8 +187,16 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {...@@ -188,8 +187,16 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
188 try args_list.append(mem.spanZ(argv[arg_i]));187 try args_list.append(mem.spanZ(argv[arg_i]));
189 }188 }
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..];
193 return self_hosted_main.cmdFmt(allocator, args);200 return self_hosted_main.cmdFmt(allocator, args);
194}201}
195202
...@@ -387,6 +394,25 @@ fn detectNativeCpuWithLLVM(...@@ -387,6 +394,25 @@ fn detectNativeCpuWithLLVM(
387 return result;394 return result;
388}395}
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
390// ABI warning416// ABI warning
391export fn stage2_cmd_targets(417export fn stage2_cmd_targets(
392 zig_triple: ?[*:0]const u8,418 zig_triple: ?[*:0]const u8,
src-self-hosted/test.zig+5-4
...@@ -407,8 +407,6 @@ pub const TestContext = struct {...@@ -407,8 +407,6 @@ pub const TestContext = struct {
407 defer root_node.end();407 defer root_node.end();
408408
409 for (self.cases.items) |case| {409 for (self.cases.items) |case| {
410 std.testing.base_allocator_instance.reset();
411
412 var prg_node = root_node.start(case.name, case.updates.items.len);410 var prg_node = root_node.start(case.name, case.updates.items.len);
413 prg_node.activate();411 prg_node.activate();
414 defer prg_node.end();412 defer prg_node.end();
...@@ -419,12 +417,11 @@ pub const TestContext = struct {...@@ -419,12 +417,11 @@ pub const TestContext = struct {
419 progress.refresh_rate_ns = 0;417 progress.refresh_rate_ns = 0;
420418
421 try self.runOneCase(std.testing.allocator, &prg_node, case);419 try self.runOneCase(std.testing.allocator, &prg_node, case);
422 try std.testing.allocator_instance.validate();
423 }420 }
424 }421 }
425422
426 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {423 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);
428 const target = target_info.target;425 const target = target_info.target;
429426
430 var arena_allocator = std.heap.ArenaAllocator.init(allocator);427 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
...@@ -481,6 +478,10 @@ pub const TestContext = struct {...@@ -481,6 +478,10 @@ pub const TestContext = struct {
481 for (all_errors.list) |err| {478 for (all_errors.list) |err| {
482 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });479 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
483 }480 }
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 }
484 std.debug.warn("Test failed.\n", .{});485 std.debug.warn("Test failed.\n", .{});
485 std.process.exit(1);486 std.process.exit(1);
486 }487 }
src-self-hosted/translate_c.zig+290-303
...@@ -61,7 +61,8 @@ const Scope = struct {...@@ -61,7 +61,8 @@ const Scope = struct {
61 pending_block: Block,61 pending_block: Block,
62 cases: []*ast.Node,62 cases: []*ast.Node,
63 case_index: usize,63 case_index: usize,
64 has_default: bool = false,64 switch_label: ?[]const u8,
65 default_label: ?[]const u8,
65 };66 };
6667
67 /// Used for the scope of condition expressions, for example `if (cond)`.68 /// Used for the scope of condition expressions, for example `if (cond)`.
...@@ -73,7 +74,7 @@ const Scope = struct {...@@ -73,7 +74,7 @@ const Scope = struct {
7374
74 fn getBlockScope(self: *Condition, c: *Context) !*Block {75 fn getBlockScope(self: *Condition, c: *Context) !*Block {
75 if (self.block) |*b| return b;76 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);
77 return &self.block.?;78 return &self.block.?;
78 }79 }
7980
...@@ -93,21 +94,22 @@ const Scope = struct {...@@ -93,21 +94,22 @@ const Scope = struct {
93 mangle_count: u32 = 0,94 mangle_count: u32 = 0,
94 lbrace: ast.TokenIndex,95 lbrace: ast.TokenIndex,
9596
96 fn init(c: *Context, parent: *Scope, label: ?[]const u8) !Block {97 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
97 return Block{98 var blk = Block{
98 .base = .{99 .base = .{
99 .id = .Block,100 .id = .Block,
100 .parent = parent,101 .parent = parent,
101 },102 },
102 .statements = std.ArrayList(*ast.Node).init(c.gpa),103 .statements = std.ArrayList(*ast.Node).init(c.gpa),
103 .variables = AliasList.init(c.gpa),104 .variables = AliasList.init(c.gpa),
104 .label = if (label) |l| blk: {105 .label = null,
105 const ll = try appendIdentifier(c, l);
106 _ = try appendToken(c, .Colon, ":");
107 break :blk ll;
108 } else null,
109 .lbrace = try appendToken(c, .LBrace, "{"),106 .lbrace = try appendToken(c, .LBrace, "{"),
110 };107 };
108 if (labeled) {
109 blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
110 _ = try appendToken(c, .Colon, ":");
111 }
112 return blk;
111 }113 }
112114
113 fn deinit(self: *Block) void {115 fn deinit(self: *Block) void {
...@@ -116,19 +118,31 @@ const Scope = struct {...@@ -116,19 +118,31 @@ const Scope = struct {
116 self.* = undefined;118 self.* = undefined;
117 }119 }
118120
119 fn complete(self: *Block, c: *Context) !*ast.Node.Block {121 fn complete(self: *Block, c: *Context) !*ast.Node {
120 // We reserve 1 extra statement if the parent is a Loop. This is in case of122 // We reserve 1 extra statement if the parent is a Loop. This is in case of
121 // do while, we want to put `if (cond) break;` at the end.123 // do while, we want to put `if (cond) break;` at the end.
122 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);124 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);125 const rbrace = try appendToken(c, .RBrace, "}");
124 node.* = .{126 if (self.label) |label| {
125 .statements_len = self.statements.items.len,127 const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);
126 .lbrace = self.lbrace,128 node.* = .{
127 .rbrace = try appendToken(c, .RBrace, "}"),129 .statements_len = self.statements.items.len,
128 .label = self.label,130 .lbrace = self.lbrace,
129 };131 .rbrace = rbrace,
130 mem.copy(*ast.Node, node.statements(), self.statements.items);132 .label = label,
131 return node;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 }
132 }146 }
133147
134 /// Given the desired name, return a name that does not shadow anything from outer scopes.148 /// Given the desired name, return a name that does not shadow anything from outer scopes.
...@@ -318,15 +332,9 @@ pub const Context = struct {...@@ -318,15 +332,9 @@ pub const Context = struct {
318 return node;332 return node;
319 }333 }
320334
321 fn createBlock(c: *Context, label: ?[]const u8, statements_len: ast.NodeIndex) !*ast.Node.Block {335 fn createBlock(c: *Context, 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;
327 const block_node = try ast.Node.Block.alloc(c.arena, statements_len);336 const block_node = try ast.Node.Block.alloc(c.arena, statements_len);
328 block_node.* = .{337 block_node.* = .{
329 .label = label_node,
330 .lbrace = try appendToken(c, .LBrace, "{"),338 .lbrace = try appendToken(c, .LBrace, "{"),
331 .statements_len = statements_len,339 .statements_len = statements_len,
332 .rbrace = undefined,340 .rbrace = undefined,
...@@ -577,7 +585,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -577,7 +585,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
577585
578 // actual function definition with body586 // actual function definition with body
579 const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);587 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);
581 defer block_scope.deinit();589 defer block_scope.deinit();
582 var scope = &block_scope.base;590 var scope = &block_scope.base;
583591
...@@ -626,8 +634,48 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -626,8 +634,48 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
626 error.UnsupportedType,634 error.UnsupportedType,
627 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),635 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
628 };636 };
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
629 const body_node = try block_scope.complete(rp.c);677 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);
631 return addTopLevelDecl(c, fn_name, &proto_node.base);679 return addTopLevelDecl(c, fn_name, &proto_node.base);
632}680}
633681
...@@ -931,7 +979,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*...@@ -931,7 +979,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
931 else => |e| return e,979 else => |e| return e,
932 };980 };
933981
934 const align_expr = blk: {982 const align_expr = blk_2: {
935 const alignment = ZigClangFieldDecl_getAlignedAttribute(field_decl, rp.c.clang_context);983 const alignment = ZigClangFieldDecl_getAlignedAttribute(field_decl, rp.c.clang_context);
936 if (alignment != 0) {984 if (alignment != 0) {
937 _ = try appendToken(rp.c, .Keyword_align, "align");985 _ = try appendToken(rp.c, .Keyword_align, "align");
...@@ -940,9 +988,9 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*...@@ -940,9 +988,9 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
940 const expr = try transCreateNodeInt(rp.c, alignment / 8);988 const expr = try transCreateNodeInt(rp.c, alignment / 8);
941 _ = try appendToken(rp.c, .RParen, ")");989 _ = try appendToken(rp.c, .RParen, ")");
942990
943 break :blk expr;991 break :blk_2 expr;
944 }992 }
945 break :blk null;993 break :blk_2 null;
946 };994 };
947995
948 const field_node = try c.arena.create(ast.Node.ContainerField);996 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...@@ -1073,9 +1121,9 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
10731121
1074 const field_name_tok = try appendIdentifier(c, field_name);1122 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: {
1077 _ = try appendToken(c, .Colon, "=");1125 _ = 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));
1079 } else1127 } else
1080 null;1128 null;
10811129
...@@ -1233,7 +1281,7 @@ fn transStmt(...@@ -1233,7 +1281,7 @@ fn transStmt(
1233 .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),1281 .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
1234 .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),1282 .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
1235 .NullStmtClass => {1283 .NullStmtClass => {
1236 const block = try rp.c.createBlock(null, 0);1284 const block = try rp.c.createBlock(0);
1237 block.rbrace = try appendToken(rp.c, .RBrace, "}");1285 block.rbrace = try appendToken(rp.c, .RBrace, "}");
1238 return &block.base;1286 return &block.base;
1239 },1287 },
...@@ -1307,14 +1355,14 @@ fn transBinaryOperator(...@@ -1307,14 +1355,14 @@ fn transBinaryOperator(
1307 const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);1355 const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
1308 if (expr) {1356 if (expr) {
1309 _ = try appendToken(rp.c, .Semicolon, ";");1357 _ = 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);
1311 try block_scope.statements.append(&break_node.base);1359 try block_scope.statements.append(&break_node.base);
1312 const block_node = try block_scope.complete(rp.c);1360 const block_node = try block_scope.complete(rp.c);
1313 const rparen = try appendToken(rp.c, .RParen, ")");1361 const rparen = try appendToken(rp.c, .RParen, ")");
1314 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);1362 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
1315 grouped_expr.* = .{1363 grouped_expr.* = .{
1316 .lparen = lparen,1364 .lparen = lparen,
1317 .expr = &block_node.base,1365 .expr = block_node,
1318 .rparen = rparen,1366 .rparen = rparen,
1319 };1367 };
1320 return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);1368 return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
...@@ -1476,11 +1524,10 @@ fn transCompoundStmtInline(...@@ -1476,11 +1524,10 @@ fn transCompoundStmtInline(
1476}1524}
14771525
1478fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {1526fn 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);
1480 defer block_scope.deinit();1528 defer block_scope.deinit();
1481 try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);1529 try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
1482 const node = try block_scope.complete(rp.c);1530 return try block_scope.complete(rp.c);
1483 return &node.base;
1484}1531}
14851532
1486fn transCStyleCastExprClass(1533fn transCStyleCastExprClass(
...@@ -1684,6 +1731,14 @@ fn transBoolExpr(...@@ -1684,6 +1731,14 @@ fn transBoolExpr(
1684 lrvalue: LRValue,1731 lrvalue: LRValue,
1685 grouped: bool,1732 grouped: bool,
1686) TransError!*ast.Node {1733) 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
1687 const lparen = if (grouped)1742 const lparen = if (grouped)
1688 try appendToken(rp.c, .LParen, "(")1743 try appendToken(rp.c, .LParen, "(")
1689 else1744 else
...@@ -2380,7 +2435,7 @@ fn transZeroInitExpr(...@@ -2380,7 +2435,7 @@ fn transZeroInitExpr(
2380 ty: *const ZigClangType,2435 ty: *const ZigClangType,
2381) TransError!*ast.Node {2436) TransError!*ast.Node {
2382 switch (ZigClangType_getTypeClass(ty)) {2437 switch (ZigClangType_getTypeClass(ty)) {
2383 .Builtin => blk: {2438 .Builtin => {
2384 const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);2439 const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
2385 switch (ZigClangBuiltinType_getKind(builtin_ty)) {2440 switch (ZigClangBuiltinType_getKind(builtin_ty)) {
2386 .Bool => return try transCreateNodeBoolLiteral(rp.c, false),2441 .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
...@@ -2539,7 +2594,7 @@ fn transDoWhileLoop(...@@ -2539,7 +2594,7 @@ fn transDoWhileLoop(
2539 // zig: if (!cond) break;2594 // zig: if (!cond) break;
2540 // zig: }2595 // zig: }
2541 const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);2596 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).?;
2543 } else blk: {2598 } else blk: {
2544 // the C statement is without a block, so we need to create a block to contain it.2599 // the C statement is without a block, so we need to create a block to contain it.
2545 // c: do2600 // c: do
...@@ -2550,7 +2605,7 @@ fn transDoWhileLoop(...@@ -2550,7 +2605,7 @@ fn transDoWhileLoop(
2550 // zig: if (!cond) break;2605 // zig: if (!cond) break;
2551 // zig: }2606 // zig: }
2552 new = true;2607 new = true;
2553 const block = try rp.c.createBlock(null, 2);2608 const block = try rp.c.createBlock(2);
2554 block.statements_len = 1; // over-allocated so we can add another below2609 block.statements_len = 1; // over-allocated so we can add another below
2555 block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);2610 block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
2556 break :blk block;2611 break :blk block;
...@@ -2579,7 +2634,7 @@ fn transForLoop(...@@ -2579,7 +2634,7 @@ fn transForLoop(
2579 defer if (block_scope) |*bs| bs.deinit();2634 defer if (block_scope) |*bs| bs.deinit();
25802635
2581 if (ZigClangForStmt_getInit(stmt)) |init| {2636 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);
2583 loop_scope.parent = &block_scope.?.base;2638 loop_scope.parent = &block_scope.?.base;
2584 const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);2639 const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
2585 try block_scope.?.statements.append(init_node);2640 try block_scope.?.statements.append(init_node);
...@@ -2609,8 +2664,7 @@ fn transForLoop(...@@ -2609,8 +2664,7 @@ fn transForLoop(
2609 while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);2664 while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
2610 if (block_scope) |*bs| {2665 if (block_scope) |*bs| {
2611 try bs.statements.append(&while_node.base);2666 try bs.statements.append(&while_node.base);
2612 const node = try bs.complete(rp.c);2667 return try bs.complete(rp.c);
2613 return &node.base;
2614 } else {2668 } else {
2615 _ = try appendToken(rp.c, .Semicolon, ";");2669 _ = try appendToken(rp.c, .Semicolon, ";");
2616 return &while_node.base;2670 return &while_node.base;
...@@ -2665,17 +2719,19 @@ fn transSwitch(...@@ -2665,17 +2719,19 @@ fn transSwitch(
2665 .cases = switch_node.cases(),2719 .cases = switch_node.cases(),
2666 .case_index = 0,2720 .case_index = 0,
2667 .pending_block = undefined,2721 .pending_block = undefined,
2722 .default_label = null,
2723 .switch_label = null,
2668 };2724 };
26692725
2670 // tmp block that all statements will go before being picked up by a case or default2726 // 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);
2672 defer block_scope.deinit();2728 defer block_scope.deinit();
26732729
2674 // Note that we do not defer a deinit here; the switch_scope.pending_block field2730 // Note that we do not defer a deinit here; the switch_scope.pending_block field
2675 // has its own memory management. This resource is freed inside `transCase` and2731 // has its own memory management. This resource is freed inside `transCase` and
2676 // then the final pending_block is freed at the bottom of this function with2732 // then the final pending_block is freed at the bottom of this function with
2677 // pending_block.deinit().2733 // 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);
2679 try switch_scope.pending_block.statements.append(&switch_node.base);2735 try switch_scope.pending_block.statements.append(&switch_node.base);
26802736
2681 const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);2737 const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
...@@ -2690,11 +2746,19 @@ fn transSwitch(...@@ -2690,11 +2746,19 @@ fn transSwitch(
2690 switch_scope.pending_block.statements.appendAssumeCapacity(n);2746 switch_scope.pending_block.statements.appendAssumeCapacity(n);
2691 }2747 }
26922748
2693 switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch");2749 if (switch_scope.default_label == null) {
2694 _ = try appendToken(rp.c, .Colon, ":");2750 switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
2695 if (!switch_scope.has_default) {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) {
2696 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));2757 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 };
2698 _ = try appendToken(rp.c, .Comma, ",");2762 _ = try appendToken(rp.c, .Comma, ",");
26992763
2700 if (switch_scope.case_index >= switch_scope.cases.len)2764 if (switch_scope.case_index >= switch_scope.cases.len)
...@@ -2708,7 +2772,7 @@ fn transSwitch(...@@ -2708,7 +2772,7 @@ fn transSwitch(
27082772
2709 const result_node = try switch_scope.pending_block.complete(rp.c);2773 const result_node = try switch_scope.pending_block.complete(rp.c);
2710 switch_scope.pending_block.deinit();2774 switch_scope.pending_block.deinit();
2711 return &result_node.base;2775 return result_node;
2712}2776}
27132777
2714fn transCase(2778fn transCase(
...@@ -2718,7 +2782,7 @@ fn transCase(...@@ -2718,7 +2782,7 @@ fn transCase(
2718) TransError!*ast.Node {2782) TransError!*ast.Node {
2719 const block_scope = scope.findBlockScope(rp.c) catch unreachable;2783 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
2720 const switch_scope = scope.getSwitch();2784 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");
2722 _ = try appendToken(rp.c, .Semicolon, ";");2786 _ = try appendToken(rp.c, .Semicolon, ";");
27232787
2724 const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {2788 const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
...@@ -2738,7 +2802,10 @@ fn transCase(...@@ -2738,7 +2802,10 @@ fn transCase(
2738 try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);2802 try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
27392803
2740 const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);2804 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 };
2742 _ = try appendToken(rp.c, .Comma, ",");2809 _ = try appendToken(rp.c, .Comma, ",");
27432810
2744 if (switch_scope.case_index >= switch_scope.cases.len)2811 if (switch_scope.case_index >= switch_scope.cases.len)
...@@ -2755,9 +2822,9 @@ fn transCase(...@@ -2755,9 +2822,9 @@ fn transCase(
27552822
2756 const pending_node = try switch_scope.pending_block.complete(rp.c);2823 const pending_node = try switch_scope.pending_block.complete(rp.c);
2757 switch_scope.pending_block.deinit();2824 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
2762 return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);2829 return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
2763}2830}
...@@ -2769,12 +2836,14 @@ fn transDefault(...@@ -2769,12 +2836,14 @@ fn transDefault(
2769) TransError!*ast.Node {2836) TransError!*ast.Node {
2770 const block_scope = scope.findBlockScope(rp.c) catch unreachable;2837 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
2771 const switch_scope = scope.getSwitch();2838 const switch_scope = scope.getSwitch();
2772 const label = "__default";2839 switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
2773 switch_scope.has_default = true;
2774 _ = try appendToken(rp.c, .Semicolon, ";");2840 _ = try appendToken(rp.c, .Semicolon, ";");
27752841
2776 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));2842 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 };
2778 _ = try appendToken(rp.c, .Comma, ",");2847 _ = try appendToken(rp.c, .Comma, ",");
27792848
2780 if (switch_scope.case_index >= switch_scope.cases.len)2849 if (switch_scope.case_index >= switch_scope.cases.len)
...@@ -2782,7 +2851,7 @@ fn transDefault(...@@ -2782,7 +2851,7 @@ fn transDefault(
2782 switch_scope.cases[switch_scope.case_index] = &else_prong.base;2851 switch_scope.cases[switch_scope.case_index] = &else_prong.base;
2783 switch_scope.case_index += 1;2852 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.?);
2786 _ = try appendToken(rp.c, .Colon, ":");2855 _ = try appendToken(rp.c, .Colon, ":");
27872856
2788 // take all pending statements2857 // take all pending statements
...@@ -2791,8 +2860,8 @@ fn transDefault(...@@ -2791,8 +2860,8 @@ fn transDefault(
27912860
2792 const pending_node = try switch_scope.pending_block.complete(rp.c);2861 const pending_node = try switch_scope.pending_block.complete(rp.c);
2793 switch_scope.pending_block.deinit();2862 switch_scope.pending_block.deinit();
2794 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, null);2863 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
2795 try switch_scope.pending_block.statements.append(&pending_node.base);2864 try switch_scope.pending_block.statements.append(pending_node);
27962865
2797 return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);2866 return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
2798}2867}
...@@ -2886,7 +2955,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,...@@ -2886,7 +2955,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
2886 return transCompoundStmt(rp, scope, comp);2955 return transCompoundStmt(rp, scope, comp);
2887 }2956 }
2888 const lparen = try appendToken(rp.c, .LParen, "(");2957 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);
2890 defer block_scope.deinit();2959 defer block_scope.deinit();
28912960
2892 var it = ZigClangCompoundStmt_body_begin(comp);2961 var it = ZigClangCompoundStmt_body_begin(comp);
...@@ -2907,7 +2976,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,...@@ -2907,7 +2976,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
2907 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);2976 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
2908 grouped_expr.* = .{2977 grouped_expr.* = .{
2909 .lparen = lparen,2978 .lparen = lparen,
2910 .expr = &block_node.base,2979 .expr = block_node,
2911 .rparen = rparen,2980 .rparen = rparen,
2912 };2981 };
2913 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);2982 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
...@@ -3081,7 +3150,7 @@ fn transUnaryExprOrTypeTraitExpr(...@@ -3081,7 +3150,7 @@ fn transUnaryExprOrTypeTraitExpr(
3081 .AlignOf => "@alignOf",3150 .AlignOf => "@alignOf",
3082 .PreferredAlignOf,3151 .PreferredAlignOf,
3083 .VecStep,3152 .VecStep,
3084 .OpenMPRequiredSimdAlign, 3153 .OpenMPRequiredSimdAlign,
3085 => return revertAndWarn(3154 => return revertAndWarn(
3086 rp,3155 rp,
3087 error.UnsupportedTranslation,3156 error.UnsupportedTranslation,
...@@ -3201,7 +3270,7 @@ fn transCreatePreCrement(...@@ -3201,7 +3270,7 @@ fn transCreatePreCrement(
3201 // zig: _ref.* += 1;3270 // zig: _ref.* += 1;
3202 // zig: break :blk _ref.*3271 // zig: break :blk _ref.*
3203 // zig: })3272 // 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);
3205 defer block_scope.deinit();3274 defer block_scope.deinit();
3206 const ref = try block_scope.makeMangledName(rp.c, "ref");3275 const ref = try block_scope.makeMangledName(rp.c, "ref");
32073276
...@@ -3231,7 +3300,7 @@ fn transCreatePreCrement(...@@ -3231,7 +3300,7 @@ fn transCreatePreCrement(
3231 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);3300 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
3232 try block_scope.statements.append(assign);3301 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);
3235 try block_scope.statements.append(&break_node.base);3304 try block_scope.statements.append(&break_node.base);
3236 const block_node = try block_scope.complete(rp.c);3305 const block_node = try block_scope.complete(rp.c);
3237 // semicolon must immediately follow rbrace because it is the last token in a block3306 // semicolon must immediately follow rbrace because it is the last token in a block
...@@ -3239,7 +3308,7 @@ fn transCreatePreCrement(...@@ -3239,7 +3308,7 @@ fn transCreatePreCrement(
3239 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3308 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3240 grouped_expr.* = .{3309 grouped_expr.* = .{
3241 .lparen = try appendToken(rp.c, .LParen, "("),3310 .lparen = try appendToken(rp.c, .LParen, "("),
3242 .expr = &block_node.base,3311 .expr = block_node,
3243 .rparen = try appendToken(rp.c, .RParen, ")"),3312 .rparen = try appendToken(rp.c, .RParen, ")"),
3244 };3313 };
3245 return &grouped_expr.base;3314 return &grouped_expr.base;
...@@ -3275,7 +3344,7 @@ fn transCreatePostCrement(...@@ -3275,7 +3344,7 @@ fn transCreatePostCrement(
3275 // zig: _ref.* += 1;3344 // zig: _ref.* += 1;
3276 // zig: break :blk _tmp3345 // zig: break :blk _tmp
3277 // zig: })3346 // 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);
3279 defer block_scope.deinit();3348 defer block_scope.deinit();
3280 const ref = try block_scope.makeMangledName(rp.c, "ref");3349 const ref = try block_scope.makeMangledName(rp.c, "ref");
32813350
...@@ -3333,7 +3402,7 @@ fn transCreatePostCrement(...@@ -3333,7 +3402,7 @@ fn transCreatePostCrement(
3333 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3402 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3334 grouped_expr.* = .{3403 grouped_expr.* = .{
3335 .lparen = try appendToken(rp.c, .LParen, "("),3404 .lparen = try appendToken(rp.c, .LParen, "("),
3336 .expr = &block_node.base,3405 .expr = block_node,
3337 .rparen = try appendToken(rp.c, .RParen, ")"),3406 .rparen = try appendToken(rp.c, .RParen, ")"),
3338 };3407 };
3339 return &grouped_expr.base;3408 return &grouped_expr.base;
...@@ -3450,7 +3519,7 @@ fn transCreateCompoundAssign(...@@ -3450,7 +3519,7 @@ fn transCreateCompoundAssign(
3450 // zig: _ref.* = _ref.* + rhs;3519 // zig: _ref.* = _ref.* + rhs;
3451 // zig: break :blk _ref.*3520 // zig: break :blk _ref.*
3452 // zig: })3521 // 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);
3454 defer block_scope.deinit();3523 defer block_scope.deinit();
3455 const ref = try block_scope.makeMangledName(rp.c, "ref");3524 const ref = try block_scope.makeMangledName(rp.c, "ref");
34563525
...@@ -3518,13 +3587,13 @@ fn transCreateCompoundAssign(...@@ -3518,13 +3587,13 @@ fn transCreateCompoundAssign(
3518 try block_scope.statements.append(assign);3587 try block_scope.statements.append(assign);
3519 }3588 }
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);
3522 try block_scope.statements.append(&break_node.base);3591 try block_scope.statements.append(&break_node.base);
3523 const block_node = try block_scope.complete(rp.c);3592 const block_node = try block_scope.complete(rp.c);
3524 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3593 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3525 grouped_expr.* = .{3594 grouped_expr.* = .{
3526 .lparen = try appendToken(rp.c, .LParen, "("),3595 .lparen = try appendToken(rp.c, .LParen, "("),
3527 .expr = &block_node.base,3596 .expr = block_node,
3528 .rparen = try appendToken(rp.c, .RParen, ")"),3597 .rparen = try appendToken(rp.c, .RParen, ")"),
3529 };3598 };
3530 return &grouped_expr.base;3599 return &grouped_expr.base;
...@@ -3594,8 +3663,16 @@ fn transCPtrCast(...@@ -3594,8 +3663,16 @@ fn transCPtrCast(
35943663
3595fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {3664fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
3596 const break_scope = scope.getBreakableScope();3665 const break_scope = scope.getBreakableScope();
3597 const label_text: ?[]const u8 = if (break_scope.id == .Switch) "__switch" else null;3666 const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
3598 const br = try transCreateNodeBreak(rp.c, label_text, null);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);
3599 _ = try appendToken(rp.c, .Semicolon, ";");3676 _ = try appendToken(rp.c, .Semicolon, ";");
3600 return &br.base;3677 return &br.base;
3601}3678}
...@@ -3626,7 +3703,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const...@@ -3626,7 +3703,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
3626 // })3703 // })
3627 const lparen = try appendToken(rp.c, .LParen, "(");3704 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);
3630 defer block_scope.deinit();3707 defer block_scope.deinit();
36313708
3632 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");3709 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
...@@ -3675,7 +3752,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const...@@ -3675,7 +3752,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
3675 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3752 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3676 grouped_expr.* = .{3753 grouped_expr.* = .{
3677 .lparen = lparen,3754 .lparen = lparen,
3678 .expr = &block_node.base,3755 .expr = block_node,
3679 .rparen = try appendToken(rp.c, .RParen, ")"),3756 .rparen = try appendToken(rp.c, .RParen, ")"),
3680 };3757 };
3681 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);3758 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
...@@ -4074,8 +4151,7 @@ fn transCreateNodeAssign(...@@ -4074,8 +4151,7 @@ fn transCreateNodeAssign(
4074 // zig: lhs = _tmp;4151 // zig: lhs = _tmp;
4075 // zig: break :blk _tmp4152 // zig: break :blk _tmp
4076 // zig: })4153 // zig: })
4077 const label_name = "blk";4154 var block_scope = try Scope.Block.init(rp.c, scope, true);
4078 var block_scope = try Scope.Block.init(rp.c, scope, label_name);
4079 defer block_scope.deinit();4155 defer block_scope.deinit();
40804156
4081 const tmp = try block_scope.makeMangledName(rp.c, "tmp");4157 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
...@@ -4110,7 +4186,7 @@ fn transCreateNodeAssign(...@@ -4110,7 +4186,7 @@ fn transCreateNodeAssign(
4110 try block_scope.statements.append(assign);4186 try block_scope.statements.append(assign);
41114187
4112 const break_node = blk: {4188 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.?));
4114 const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);4190 const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
4115 break :blk try tmp_ctrl_flow.finish(rhs_expr);4191 break :blk try tmp_ctrl_flow.finish(rhs_expr);
4116 };4192 };
...@@ -4119,7 +4195,7 @@ fn transCreateNodeAssign(...@@ -4119,7 +4195,7 @@ fn transCreateNodeAssign(
4119 const block_node = try block_scope.complete(rp.c);4195 const block_node = try block_scope.complete(rp.c);
4120 // semicolon must immediately follow rbrace because it is the last token in a block4196 // semicolon must immediately follow rbrace because it is the last token in a block
4121 _ = try appendToken(rp.c, .Semicolon, ";");4197 _ = try appendToken(rp.c, .Semicolon, ";");
4122 return &block_node.base;4198 return block_node;
4123}4199}
41244200
4125fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {4201fn 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...@@ -4412,7 +4488,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
44124488
4413 const block = try ast.Node.Block.alloc(c.arena, 1);4489 const block = try ast.Node.Block.alloc(c.arena, 1);
4414 block.* = .{4490 block.* = .{
4415 .label = null,
4416 .lbrace = block_lbrace,4491 .lbrace = block_lbrace,
4417 .statements_len = 1,4492 .statements_len = 1,
4418 .rbrace = try appendToken(c, .RBrace, "}"),4493 .rbrace = try appendToken(c, .RBrace, "}"),
...@@ -4487,23 +4562,12 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else {...@@ -4487,23 +4562,12 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
4487 return node;4562 return node;
4488}4563}
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
4501fn transCreateNodeBreak(4565fn transCreateNodeBreak(
4502 c: *Context,4566 c: *Context,
4503 label: ?[]const u8,4567 label: ?ast.TokenIndex,
4504 rhs: ?*ast.Node,4568 rhs: ?*ast.Node,
4505) !*ast.Node.ControlFlowExpression {4569) !*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);
4507 return ctrl_flow.finish(rhs);4571 return ctrl_flow.finish(rhs);
4508}4572}
45094573
...@@ -4907,7 +4971,7 @@ fn finishTransFnProto(...@@ -4907,7 +4971,7 @@ fn finishTransFnProto(
4907 const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;4971 const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
4908 const extern_export_inline_tok = if (is_export)4972 const extern_export_inline_tok = if (is_export)
4909 try appendToken(rp.c, .Keyword_export, "export")4973 try appendToken(rp.c, .Keyword_export, "export")
4910 else if (cc == .C and is_extern)4974 else if (is_extern)
4911 try appendToken(rp.c, .Keyword_extern, "extern")4975 try appendToken(rp.c, .Keyword_extern, "extern")
4912 else4976 else
4913 null;4977 null;
...@@ -5212,26 +5276,32 @@ pub fn freeErrors(errors: []ClangErrMsg) void {...@@ -5212,26 +5276,32 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
5212 ZigClangErrorMsg_delete(errors.ptr, errors.len);5276 ZigClangErrorMsg_delete(errors.ptr, errors.len);
5213}5277}
52145278
5215const CTokIterator = struct {5279const MacroCtx = struct {
5216 source: []const u8,5280 source: []const u8,
5217 list: []const CToken,5281 list: []const CToken,
5218 i: usize = 0,5282 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 {
5221 if (self.i >= self.list.len) return null;5287 if (self.i >= self.list.len) return null;
5222 return self.list[self.i + 1].id;5288 return self.list[self.i + 1].id;
5223 }5289 }
52245290
5225 fn next(self: *CTokIterator) ?CToken.Id {5291 fn next(self: *MacroCtx) ?CToken.Id {
5226 if (self.i >= self.list.len) return null;5292 if (self.i >= self.list.len) return null;
5227 self.i += 1;5293 self.i += 1;
5228 return self.list[self.i].id;5294 return self.list[self.i].id;
5229 }5295 }
52305296
5231 fn slice(self: *CTokIterator, index: usize) []const u8 {5297 fn slice(self: *MacroCtx) []const u8 {
5232 const tok = self.list[index];5298 const tok = self.list[self.i];
5233 return self.source[tok.start..tok.end];5299 return self.source[tok.start..tok.end];
5234 }5300 }
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 }
5235};5305};
52365306
5237fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {5307fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
...@@ -5278,18 +5348,21 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5278,18 +5348,21 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5278 try tok_list.append(tok);5348 try tok_list.append(tok);
5279 }5349 }
52805350
5281 var tok_it = CTokIterator{5351 var macro_ctx = MacroCtx{
5282 .source = slice,5352 .source = slice,
5283 .list = tok_list.items,5353 .list = tok_list.items,
5354 .name = mangled_name,
5355 .loc = begin_loc,
5284 };5356 };
5285 assert(mem.eql(u8, tok_it.slice(0), name));5357 assert(mem.eql(u8, macro_ctx.slice(), name));
52865358
5287 var macro_fn = false;5359 var macro_fn = false;
5288 switch (tok_it.peek().?) {5360 switch (macro_ctx.peek().?) {
5289 .Identifier => {5361 .Identifier => {
5290 // if it equals itself, ignore. for example, from stdio.h:5362 // if it equals itself, ignore. for example, from stdio.h:
5291 // #define stdin stdin5363 // #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])) {
5293 continue;5366 continue;
5294 }5367 }
5295 },5368 },
...@@ -5300,15 +5373,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5300,15 +5373,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5300 },5373 },
5301 .LParen => {5374 .LParen => {
5302 // if the name is immediately followed by a '(' then it is a function5375 // 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;
5304 },5377 },
5305 else => {},5378 else => {},
5306 }5379 }
53075380
5308 (if (macro_fn)5381 (if (macro_fn)
5309 transMacroFnDefine(c, &tok_it, mangled_name, begin_loc)5382 transMacroFnDefine(c, &macro_ctx)
5310 else5383 else
5311 transMacroDefine(c, &tok_it, mangled_name, begin_loc)) catch |err| switch (err) {5384 transMacroDefine(c, &macro_ctx)) catch |err| switch (err) {
5312 error.ParseError => continue,5385 error.ParseError => continue,
5313 error.OutOfMemory => |e| return e,5386 error.OutOfMemory => |e| return e,
5314 };5387 };
...@@ -5318,24 +5391,18 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5318,24 +5391,18 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5318 }5391 }
5319}5392}
53205393
5321fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {5394fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
5322 const scope = &c.global_scope.base;5395 const scope = &c.global_scope.base;
53235396
5324 const visib_tok = try appendToken(c, .Keyword_pub, "pub");5397 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
5325 const mut_tok = try appendToken(c, .Keyword_const, "const");5398 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);
5327 const eq_token = try appendToken(c, .Equal, "=");5400 const eq_token = try appendToken(c, .Equal, "=");
53285401
5329 const init_node = try parseCExpr(c, it, source_loc, scope);5402 const init_node = try parseCExpr(c, m, scope);
5330 const last = it.next().?;5403 const last = m.next().?;
5331 if (last != .Eof and last != .Nl)5404 if (last != .Eof and last != .Nl)
5332 return failDecl(5405 return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
5333 c,
5334 source_loc,
5335 name,
5336 "unable to translate C expr: unexpected token .{}",
5337 .{@tagName(last)},
5338 );
53395406
5340 const semicolon_token = try appendToken(c, .Semicolon, ";");5407 const semicolon_token = try appendToken(c, .Semicolon, ";");
5341 const node = try ast.Node.VarDecl.create(c.arena, .{5408 const node = try ast.Node.VarDecl.create(c.arena, .{
...@@ -5347,45 +5414,33 @@ fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc...@@ -5347,45 +5414,33 @@ fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc
5347 .eq_token = eq_token,5414 .eq_token = eq_token,
5348 .init_node = init_node,5415 .init_node = init_node,
5349 });5416 });
5350 _ = try c.global_scope.macro_table.put(name, &node.base);5417 _ = try c.global_scope.macro_table.put(m.name, &node.base);
5351}5418}
53525419
5353fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {5420fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5354 var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);5421 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
5355 defer block_scope.deinit();5422 defer block_scope.deinit();
5356 const scope = &block_scope.base;5423 const scope = &block_scope.base;
53575424
5358 const pub_tok = try appendToken(c, .Keyword_pub, "pub");5425 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
5359 const inline_tok = try appendToken(c, .Keyword_inline, "inline");5426 const inline_tok = try appendToken(c, .Keyword_inline, "inline");
5360 const fn_tok = try appendToken(c, .Keyword_fn, "fn");5427 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);
5362 _ = try appendToken(c, .LParen, "(");5429 _ = try appendToken(c, .LParen, "(");
53635430
5364 if (it.next().? != .LParen) {5431 if (m.next().? != .LParen) {
5365 return failDecl(5432 return m.fail(c, "unable to translate C expr: expected '('", .{});
5366 c,
5367 source_loc,
5368 name,
5369 "unable to translate C expr: expected '('",
5370 .{},
5371 );
5372 }5433 }
53735434
5374 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);5435 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
5375 defer fn_params.deinit();5436 defer fn_params.deinit();
53765437
5377 while (true) {5438 while (true) {
5378 if (it.next().? != .Identifier) {5439 if (m.next().? != .Identifier) {
5379 return failDecl(5440 return m.fail(c, "unable to translate C expr: expected identifier", .{});
5380 c,
5381 source_loc,
5382 name,
5383 "unable to translate C expr: expected identifier",
5384 .{},
5385 );
5386 }5441 }
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());
5389 const param_name_tok = try appendIdentifier(c, mangled_name);5444 const param_name_tok = try appendIdentifier(c, mangled_name);
5390 _ = try appendToken(c, .Colon, ":");5445 _ = try appendToken(c, .Colon, ":");
53915446
...@@ -5403,20 +5458,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l...@@ -5403,20 +5458,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
5403 .param_type = .{ .any_type = &any_type.base },5458 .param_type = .{ .any_type = &any_type.base },
5404 };5459 };
54055460
5406 if (it.peek().? != .Comma)5461 if (m.peek().? != .Comma)
5407 break;5462 break;
5408 _ = it.next();5463 _ = m.next();
5409 _ = try appendToken(c, .Comma, ",");5464 _ = try appendToken(c, .Comma, ",");
5410 }5465 }
54115466
5412 if (it.next().? != .RParen) {5467 if (m.next().? != .RParen) {
5413 return failDecl(5468 return m.fail(c, "unable to translate C expr: expected ')'", .{});
5414 c,
5415 source_loc,
5416 name,
5417 "unable to translate C expr: expected ')'",
5418 .{},
5419 );
5420 }5469 }
54215470
5422 _ = try appendToken(c, .RParen, ")");5471 _ = try appendToken(c, .RParen, ")");
...@@ -5424,20 +5473,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l...@@ -5424,20 +5473,14 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
5424 const type_of = try c.createBuiltinCall("@TypeOf", 1);5473 const type_of = try c.createBuiltinCall("@TypeOf", 1);
54255474
5426 const return_kw = try appendToken(c, .Keyword_return, "return");5475 const return_kw = try appendToken(c, .Keyword_return, "return");
5427 const expr = try parseCExpr(c, it, source_loc, scope);5476 const expr = try parseCExpr(c, m, scope);
5428 const last = it.next().?;5477 const last = m.next().?;
5429 if (last != .Eof and last != .Nl)5478 if (last != .Eof and last != .Nl)
5430 return failDecl(5479 return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
5431 c,
5432 source_loc,
5433 name,
5434 "unable to translate C expr: unexpected token .{}",
5435 .{@tagName(last)},
5436 );
5437 _ = try appendToken(c, .Semicolon, ";");5480 _ = try appendToken(c, .Semicolon, ";");
5438 const type_of_arg = if (expr.tag != .Block) expr else blk: {5481 const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
5439 const blk = @fieldParentPtr(ast.Node.Block, "base", expr);5482 const stmts = expr.blockStatements();
5440 const blk_last = blk.statements()[blk.statements_len - 1];5483 const blk_last = stmts[stmts.len - 1];
5441 const br = blk_last.cast(ast.Node.ControlFlowExpression).?;5484 const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
5442 break :blk br.getRHS().?;5485 break :blk br.getRHS().?;
5443 };5486 };
...@@ -5460,42 +5503,35 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l...@@ -5460,42 +5503,35 @@ fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_l
5460 .visib_token = pub_tok,5503 .visib_token = pub_tok,
5461 .extern_export_inline_token = inline_tok,5504 .extern_export_inline_token = inline_tok,
5462 .name_token = name_tok,5505 .name_token = name_tok,
5463 .body_node = &block_node.base,5506 .body_node = block_node,
5464 });5507 });
5465 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);5508 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);
5468}5511}
54695512
5470const ParseError = Error || error{ParseError};5513const ParseError = Error || error{ParseError};
54715514
5472fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {5515fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
5473 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);5516 const node = try parseCPrefixOpExpr(c, m, scope);
5474 switch (it.next().?) {5517 switch (m.next().?) {
5475 .QuestionMark => {5518 .QuestionMark => {
5476 // must come immediately after expr5519 // must come immediately after expr
5477 _ = try appendToken(c, .RParen, ")");5520 _ = try appendToken(c, .RParen, ")");
5478 const if_node = try transCreateNodeIf(c);5521 const if_node = try transCreateNodeIf(c);
5479 if_node.condition = node;5522 if_node.condition = node;
5480 if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);5523 if_node.body = try parseCPrimaryExpr(c, m, scope);
5481 if (it.next().? != .Colon) {5524 if (m.next().? != .Colon) {
5482 try failDecl(5525 try m.fail(c, "unable to translate C expr: expected ':'", .{});
5483 c,
5484 source_loc,
5485 it.slice(0),
5486 "unable to translate C expr: expected ':'",
5487 .{},
5488 );
5489 return error.ParseError;5526 return error.ParseError;
5490 }5527 }
5491 if_node.@"else" = try transCreateNodeElse(c);5528 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);
5493 return &if_node.base;5530 return &if_node.base;
5494 },5531 },
5495 .Comma => {5532 .Comma => {
5496 _ = try appendToken(c, .Semicolon, ";");5533 _ = try appendToken(c, .Semicolon, ";");
5497 const label_name = "blk";5534 var block_scope = try Scope.Block.init(c, scope, true);
5498 var block_scope = try Scope.Block.init(c, scope, label_name);
5499 defer block_scope.deinit();5535 defer block_scope.deinit();
55005536
5501 var last = node;5537 var last = node;
...@@ -5512,30 +5548,29 @@ fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation...@@ -5512,30 +5548,29 @@ fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation
5512 };5548 };
5513 try block_scope.statements.append(&op_node.base);5549 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);
5516 _ = try appendToken(c, .Semicolon, ";");5552 _ = try appendToken(c, .Semicolon, ";");
5517 if (it.next().? != .Comma) {5553 if (m.next().? != .Comma) {
5518 it.i -= 1;5554 m.i -= 1;
5519 break;5555 break;
5520 }5556 }
5521 }5557 }
55225558
5523 const break_node = try transCreateNodeBreak(c, label_name, last);5559 const break_node = try transCreateNodeBreak(c, block_scope.label, last);
5524 try block_scope.statements.append(&break_node.base);5560 try block_scope.statements.append(&break_node.base);
5525 const block_node = try block_scope.complete(c);5561 return try block_scope.complete(c);
5526 return &block_node.base;
5527 },5562 },
5528 else => {5563 else => {
5529 it.i -= 1;5564 m.i -= 1;
5530 return node;5565 return node;
5531 },5566 },
5532 }5567 }
5533}5568}
55345569
5535fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {5570fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
5536 var lit_bytes = it.slice(it.i);5571 var lit_bytes = m.slice();
55375572
5538 switch (it.list[it.i].id) {5573 switch (m.list[m.i].id) {
5539 .IntegerLiteral => |suffix| {5574 .IntegerLiteral => |suffix| {
5540 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {5575 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
5541 switch (lit_bytes[1]) {5576 switch (lit_bytes[1]) {
...@@ -5596,8 +5631,8 @@ fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocati...@@ -5596,8 +5631,8 @@ fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocati
5596 }5631 }
5597}5632}
55985633
5599fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ![]const u8 {5634fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
5600 var source = source_bytes;5635 var source = m.slice();
5601 for (source) |c, i| {5636 for (source) |c, i| {
5602 if (c == '\"' or c == '\'') {5637 if (c == '\"' or c == '\'') {
5603 source = source[i..];5638 source = source[i..];
...@@ -5669,11 +5704,11 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const...@@ -5669,11 +5704,11 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
5669 bytes[i] = '?';5704 bytes[i] = '?';
5670 },5705 },
5671 'u', 'U' => {5706 '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", .{});
5673 return error.ParseError;5708 return error.ParseError;
5674 },5709 },
5675 else => {5710 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", .{});
5677 return error.ParseError;5712 return error.ParseError;
5678 },5713 },
5679 }5714 }
...@@ -5692,21 +5727,21 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const...@@ -5692,21 +5727,21 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
5692 switch (c) {5727 switch (c) {
5693 '0'...'9' => {5728 '0'...'9' => {
5694 num = std.math.mul(u8, num, 16) catch {5729 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", .{});
5696 return error.ParseError;5731 return error.ParseError;
5697 };5732 };
5698 num += c - '0';5733 num += c - '0';
5699 },5734 },
5700 'a'...'f' => {5735 'a'...'f' => {
5701 num = std.math.mul(u8, num, 16) catch {5736 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", .{});
5703 return error.ParseError;5738 return error.ParseError;
5704 };5739 };
5705 num += c - 'a' + 10;5740 num += c - 'a' + 10;
5706 },5741 },
5707 'A'...'F' => {5742 'A'...'F' => {
5708 num = std.math.mul(u8, num, 16) catch {5743 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", .{});
5710 return error.ParseError;5745 return error.ParseError;
5711 };5746 };
5712 num += c - 'A' + 10;5747 num += c - 'A' + 10;
...@@ -5733,7 +5768,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const...@@ -5733,7 +5768,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
5733 if (accept_digit) {5768 if (accept_digit) {
5734 count += 1;5769 count += 1;
5735 num = std.math.mul(u8, num, 8) catch {5770 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", .{});
5737 return error.ParseError;5772 return error.ParseError;
5738 };5773 };
5739 num += c - '0';5774 num += c - '0';
...@@ -5756,13 +5791,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const...@@ -5756,13 +5791,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
5756 return bytes[0..i];5791 return bytes[0..i];
5757}5792}
57585793
5759fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {5794fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
5760 const tok = it.next().?;5795 const tok = m.next().?;
5761 const slice = it.slice(it.i);5796 const slice = m.slice();
5762 switch (tok) {5797 switch (tok) {
5763 .CharLiteral => {5798 .CharLiteral => {
5764 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {5799 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));
5766 const node = try c.arena.create(ast.Node.OneToken);5801 const node = try c.arena.create(ast.Node.OneToken);
5767 node.* = .{5802 node.* = .{
5768 .base = .{ .tag = .CharLiteral },5803 .base = .{ .tag = .CharLiteral },
...@@ -5780,7 +5815,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL...@@ -5780,7 +5815,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
5780 }5815 }
5781 },5816 },
5782 .StringLiteral => {5817 .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));
5784 const node = try c.arena.create(ast.Node.OneToken);5819 const node = try c.arena.create(ast.Node.OneToken);
5785 node.* = .{5820 node.* = .{
5786 .base = .{ .tag = .StringLiteral },5821 .base = .{ .tag = .StringLiteral },
...@@ -5789,7 +5824,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL...@@ -5789,7 +5824,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
5789 return &node.base;5824 return &node.base;
5790 },5825 },
5791 .IntegerLiteral, .FloatLiteral => {5826 .IntegerLiteral, .FloatLiteral => {
5792 return parseCNumLit(c, it, source_loc);5827 return parseCNumLit(c, m);
5793 },5828 },
5794 // eventually this will be replaced by std.c.parse which will handle these correctly5829 // eventually this will be replaced by std.c.parse which will handle these correctly
5795 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),5830 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
...@@ -5800,61 +5835,55 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL...@@ -5800,61 +5835,55 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
5800 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),5835 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
5801 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),5836 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
5802 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),5837 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
5803 .Keyword_unsigned => if (it.next()) |t| switch (t) {5838 .Keyword_unsigned => if (m.next()) |t| switch (t) {
5804 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),5839 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
5805 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),5840 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
5806 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),5841 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
5807 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {5842 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
5808 _ = it.next();5843 _ = m.next();
5809 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");5844 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
5810 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),5845 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
5811 else => {5846 else => {
5812 it.i -= 1;5847 m.i -= 1;
5813 return transCreateNodeIdentifierUnchecked(c, "c_uint");5848 return transCreateNodeIdentifierUnchecked(c, "c_uint");
5814 },5849 },
5815 } else {5850 } else {
5816 return transCreateNodeIdentifierUnchecked(c, "c_uint");5851 return transCreateNodeIdentifierUnchecked(c, "c_uint");
5817 },5852 },
5818 .Keyword_signed => if (it.next()) |t| switch (t) {5853 .Keyword_signed => if (m.next()) |t| switch (t) {
5819 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),5854 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
5820 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),5855 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
5821 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),5856 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
5822 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {5857 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
5823 _ = it.next();5858 _ = m.next();
5824 return transCreateNodeIdentifierUnchecked(c, "c_longlong");5859 return transCreateNodeIdentifierUnchecked(c, "c_longlong");
5825 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),5860 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
5826 else => {5861 else => {
5827 it.i -= 1;5862 m.i -= 1;
5828 return transCreateNodeIdentifierUnchecked(c, "c_int");5863 return transCreateNodeIdentifierUnchecked(c, "c_int");
5829 },5864 },
5830 } else {5865 } else {
5831 return transCreateNodeIdentifierUnchecked(c, "c_int");5866 return transCreateNodeIdentifierUnchecked(c, "c_int");
5832 },5867 },
5833 .Identifier => {5868 .Identifier => {
5834 const mangled_name = scope.getAlias(it.slice(it.i));5869 const mangled_name = scope.getAlias(slice);
5835 return transCreateNodeIdentifier(c, mangled_name);5870 return transCreateNodeIdentifier(c, mangled_name);
5836 },5871 },
5837 .LParen => {5872 .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().?;
5841 if (next_id != .RParen) {5876 if (next_id != .RParen) {
5842 try failDecl(5877 try m.fail(c, "unable to translate C expr: expected ')'' instead got: {}", .{@tagName(next_id)});
5843 c,
5844 source_loc,
5845 it.slice(0),
5846 "unable to translate C expr: expected ')'' instead got: {}",
5847 .{@tagName(next_id)},
5848 );
5849 return error.ParseError;5878 return error.ParseError;
5850 }5879 }
5851 var saw_l_paren = false;5880 var saw_l_paren = false;
5852 var saw_integer_literal = false;5881 var saw_integer_literal = false;
5853 switch (it.peek().?) {5882 switch (m.peek().?) {
5854 // (type)(to_cast)5883 // (type)(to_cast)
5855 .LParen => {5884 .LParen => {
5856 saw_l_paren = true;5885 saw_l_paren = true;
5857 _ = it.next();5886 _ = m.next();
5858 },5887 },
5859 // (type)identifier5888 // (type)identifier
5860 .Identifier => {},5889 .Identifier => {},
...@@ -5868,16 +5897,10 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL...@@ -5868,16 +5897,10 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
5868 // hack to get zig fmt to render a comma in builtin calls5897 // hack to get zig fmt to render a comma in builtin calls
5869 _ = try appendToken(c, .Comma, ",");5898 _ = 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) {5902 if (saw_l_paren and m.next().? != .RParen) {
5874 try failDecl(5903 try m.fail(c, "unable to translate C expr: expected ')''", .{});
5875 c,
5876 source_loc,
5877 it.slice(0),
5878 "unable to translate C expr: expected ')''",
5879 .{},
5880 );
5881 return error.ParseError;5904 return error.ParseError;
5882 }5905 }
58835906
...@@ -5905,13 +5928,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL...@@ -5905,13 +5928,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceL
5905 return &group_node.base;5928 return &group_node.base;
5906 },5929 },
5907 else => {5930 else => {
5908 try failDecl(5931 try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)});
5909 c,
5910 source_loc,
5911 it.slice(0),
5912 "unable to translate C expr: unexpected token .{}",
5913 .{@tagName(tok)},
5914 );
5915 return error.ParseError;5932 return error.ParseError;
5916 },5933 },
5917 }5934 }
...@@ -6018,52 +6035,40 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {...@@ -6018,52 +6035,40 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
6018 return &group_node.base;6035 return &group_node.base;
6019}6036}
60206037
6021fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {6038fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
6022 var node = try parseCPrimaryExpr(c, it, source_loc, scope);6039 var node = try parseCPrimaryExpr(c, m, scope);
6023 while (true) {6040 while (true) {
6024 var op_token: ast.TokenIndex = undefined;6041 var op_token: ast.TokenIndex = undefined;
6025 var op_id: ast.Node.Tag = undefined;6042 var op_id: ast.Node.Tag = undefined;
6026 var bool_op = false;6043 var bool_op = false;
6027 switch (it.next().?) {6044 switch (m.next().?) {
6028 .Period => {6045 .Period => {
6029 if (it.next().? != .Identifier) {6046 if (m.next().? != .Identifier) {
6030 try failDecl(6047 try m.fail(c, "unable to translate C expr: expected identifier", .{});
6031 c,
6032 source_loc,
6033 it.slice(0),
6034 "unable to translate C expr: expected identifier",
6035 .{},
6036 );
6037 return error.ParseError;6048 return error.ParseError;
6038 }6049 }
60396050
6040 node = try transCreateNodeFieldAccess(c, node, it.slice(it.i));6051 node = try transCreateNodeFieldAccess(c, node, m.slice());
6041 continue;6052 continue;
6042 },6053 },
6043 .Arrow => {6054 .Arrow => {
6044 if (it.next().? != .Identifier) {6055 if (m.next().? != .Identifier) {
6045 try failDecl(6056 try m.fail(c, "unable to translate C expr: expected identifier", .{});
6046 c,
6047 source_loc,
6048 it.slice(0),
6049 "unable to translate C expr: expected identifier",
6050 .{},
6051 );
6052 return error.ParseError;6057 return error.ParseError;
6053 }6058 }
6054 const deref = try transCreateNodePtrDeref(c, node);6059 const deref = try transCreateNodePtrDeref(c, node);
6055 node = try transCreateNodeFieldAccess(c, deref, it.slice(it.i));6060 node = try transCreateNodeFieldAccess(c, deref, m.slice());
6056 continue;6061 continue;
6057 },6062 },
6058 .Asterisk => {6063 .Asterisk => {
6059 if (it.peek().? == .RParen) {6064 if (m.peek().? == .RParen) {
6060 // type *)6065 // type *)
60616066
6062 // hack to get zig fmt to render a comma in builtin calls6067 // hack to get zig fmt to render a comma in builtin calls
6063 _ = try appendToken(c, .Comma, ",");6068 _ = try appendToken(c, .Comma, ",");
60646069
6065 // last token of `node`6070 // 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
6068 if (prev_id == .Keyword_void) {6073 if (prev_id == .Keyword_void) {
6069 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);6074 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
...@@ -6134,17 +6139,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource...@@ -6134,17 +6139,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
6134 },6139 },
6135 .LBracket => {6140 .LBracket => {
6136 const arr_node = try transCreateNodeArrayAccess(c, node);6141 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);
6138 arr_node.rtoken = try appendToken(c, .RBracket, "]");6143 arr_node.rtoken = try appendToken(c, .RBracket, "]");
6139 node = &arr_node.base;6144 node = &arr_node.base;
6140 if (it.next().? != .RBracket) {6145 if (m.next().? != .RBracket) {
6141 try failDecl(6146 try m.fail(c, "unable to translate C expr: expected ']'", .{});
6142 c,
6143 source_loc,
6144 it.slice(0),
6145 "unable to translate C expr: expected ']'",
6146 .{},
6147 );
6148 return error.ParseError;6147 return error.ParseError;
6149 }6148 }
6150 continue;6149 continue;
...@@ -6154,19 +6153,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource...@@ -6154,19 +6153,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
6154 var call_params = std.ArrayList(*ast.Node).init(c.gpa);6153 var call_params = std.ArrayList(*ast.Node).init(c.gpa);
6155 defer call_params.deinit();6154 defer call_params.deinit();
6156 while (true) {6155 while (true) {
6157 const arg = try parseCPrefixOpExpr(c, it, source_loc, scope);6156 const arg = try parseCPrefixOpExpr(c, m, scope);
6158 try call_params.append(arg);6157 try call_params.append(arg);
6159 switch (it.next().?) {6158 switch (m.next().?) {
6160 .Comma => _ = try appendToken(c, .Comma, ","),6159 .Comma => _ = try appendToken(c, .Comma, ","),
6161 .RParen => break,6160 .RParen => break,
6162 else => {6161 else => {
6163 try failDecl(6162 try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
6164 c,
6165 source_loc,
6166 it.slice(0),
6167 "unable to translate C expr: expected ',' or ')'",
6168 .{},
6169 );
6170 return error.ParseError;6163 return error.ParseError;
6171 },6164 },
6172 }6165 }
...@@ -6193,19 +6186,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource...@@ -6193,19 +6186,13 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
6193 defer init_vals.deinit();6186 defer init_vals.deinit();
61946187
6195 while (true) {6188 while (true) {
6196 const val = try parseCPrefixOpExpr(c, it, source_loc, scope);6189 const val = try parseCPrefixOpExpr(c, m, scope);
6197 try init_vals.append(val);6190 try init_vals.append(val);
6198 switch (it.next().?) {6191 switch (m.next().?) {
6199 .Comma => _ = try appendToken(c, .Comma, ","),6192 .Comma => _ = try appendToken(c, .Comma, ","),
6200 .RBrace => break,6193 .RBrace => break,
6201 else => {6194 else => {
6202 try failDecl(6195 try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
6203 c,
6204 source_loc,
6205 it.slice(0),
6206 "unable to translate C expr: expected ',' or '}}'",
6207 .{},
6208 );
6209 return error.ParseError;6196 return error.ParseError;
6210 },6197 },
6211 }6198 }
...@@ -6254,22 +6241,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource...@@ -6254,22 +6241,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
6254 op_id = .ArrayCat;6241 op_id = .ArrayCat;
6255 op_token = try appendToken(c, .PlusPlus, "++");6242 op_token = try appendToken(c, .PlusPlus, "++");
62566243
6257 it.i -= 1;6244 m.i -= 1;
6258 },6245 },
6259 .Identifier => {6246 .Identifier => {
6260 op_id = .ArrayCat;6247 op_id = .ArrayCat;
6261 op_token = try appendToken(c, .PlusPlus, "++");6248 op_token = try appendToken(c, .PlusPlus, "++");
62626249
6263 it.i -= 1;6250 m.i -= 1;
6264 },6251 },
6265 else => {6252 else => {
6266 it.i -= 1;6253 m.i -= 1;
6267 return node;6254 return node;
6268 },6255 },
6269 }6256 }
6270 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;6257 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
6271 const lhs_node = try cast_fn(c, node);6258 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);
6273 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);6260 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6274 op_node.* = .{6261 op_node.* = .{
6275 .base = .{ .tag = op_id },6262 .base = .{ .tag = op_id },
...@@ -6281,36 +6268,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource...@@ -6281,36 +6268,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSource
6281 }6268 }
6282}6269}
62836270
6284fn parseCPrefixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {6271fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
6285 switch (it.next().?) {6272 switch (m.next().?) {
6286 .Bang => {6273 .Bang => {
6287 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");6274 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);
6289 return &node.base;6276 return &node.base;
6290 },6277 },
6291 .Minus => {6278 .Minus => {
6292 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");6279 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);
6294 return &node.base;6281 return &node.base;
6295 },6282 },
6296 .Plus => return try parseCPrefixOpExpr(c, it, source_loc, scope),6283 .Plus => return try parseCPrefixOpExpr(c, m, scope),
6297 .Tilde => {6284 .Tilde => {
6298 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");6285 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);
6300 return &node.base;6287 return &node.base;
6301 },6288 },
6302 .Asterisk => {6289 .Asterisk => {
6303 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);6290 const node = try parseCPrefixOpExpr(c, m, scope);
6304 return try transCreateNodePtrDeref(c, node);6291 return try transCreateNodePtrDeref(c, node);
6305 },6292 },
6306 .Ampersand => {6293 .Ampersand => {
6307 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");6294 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);
6309 return &node.base;6296 return &node.base;
6310 },6297 },
6311 else => {6298 else => {
6312 it.i -= 1;6299 m.i -= 1;
6313 return try parseCSuffixOpExpr(c, it, source_loc, scope);6300 return try parseCSuffixOpExpr(c, m, scope);
6314 },6301 },
6315 }6302 }
6316}6303}
src-self-hosted/type.zig+299-46
...@@ -70,6 +70,11 @@ pub const Type = extern union {...@@ -70,6 +70,11 @@ pub const Type = extern union {
70 .single_mut_pointer => return .Pointer,70 .single_mut_pointer => return .Pointer,
71 .single_const_pointer_to_comptime_int => return .Pointer,71 .single_const_pointer_to_comptime_int => return .Pointer,
72 .const_slice_u8 => return .Pointer,72 .const_slice_u8 => return .Pointer,
73
74 .optional,
75 .optional_single_const_pointer,
76 .optional_single_mut_pointer,
77 => return .Optional,
73 }78 }
74 }79 }
7580
...@@ -102,8 +107,18 @@ pub const Type = extern union {...@@ -102,8 +107,18 @@ pub const Type = extern union {
102 return @fieldParentPtr(T, "base", self.ptr_otherwise);107 return @fieldParentPtr(T, "base", self.ptr_otherwise);
103 }108 }
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
105 pub fn eql(a: Type, b: Type) bool {121 pub fn eql(a: Type, b: Type) bool {
106 //std.debug.warn("test {} == {}\n", .{ a, b });
107 // As a shortcut, if the small tags / addresses match, we're done.122 // As a shortcut, if the small tags / addresses match, we're done.
108 if (a.tag_if_small_enough == b.tag_if_small_enough)123 if (a.tag_if_small_enough == b.tag_if_small_enough)
109 return true;124 return true;
...@@ -122,8 +137,8 @@ pub const Type = extern union {...@@ -122,8 +137,8 @@ pub const Type = extern union {
122 .Null => return true,137 .Null => return true,
123 .Pointer => {138 .Pointer => {
124 // Hot path for common case:139 // Hot path for common case:
125 if (a.cast(Payload.SingleConstPointer)) |a_payload| {140 if (a.castPointer()) |a_payload| {
126 if (b.cast(Payload.SingleConstPointer)) |b_payload| {141 if (b.castPointer()) |b_payload| {
127 return eql(a_payload.pointee_type, b_payload.pointee_type);142 return eql(a_payload.pointee_type, b_payload.pointee_type);
128 }143 }
129 }144 }
...@@ -180,9 +195,13 @@ pub const Type = extern union {...@@ -180,9 +195,13 @@ pub const Type = extern union {
180 }195 }
181 return true;196 return true;
182 },197 },
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 },
183 .Float,203 .Float,
184 .Struct,204 .Struct,
185 .Optional,
186 .ErrorUnion,205 .ErrorUnion,
187 .ErrorSet,206 .ErrorSet,
188 .Enum,207 .Enum,
...@@ -197,6 +216,74 @@ pub const Type = extern union {...@@ -197,6 +216,74 @@ pub const Type = extern union {
197 }216 }
198 }217 }
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
200 pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {287 pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
201 if (self.tag_if_small_enough < Tag.no_payload_count) {288 if (self.tag_if_small_enough < Tag.no_payload_count) {
202 return Type{ .tag_if_small_enough = self.tag_if_small_enough };289 return Type{ .tag_if_small_enough = self.tag_if_small_enough };
...@@ -253,24 +340,6 @@ pub const Type = extern union {...@@ -253,24 +340,6 @@ pub const Type = extern union {
253 };340 };
254 return Type{ .ptr_otherwise = &new_payload.base };341 return Type{ .ptr_otherwise = &new_payload.base };
255 },342 },
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 },
274 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),343 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
275 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),344 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
276 .function => {345 .function => {
...@@ -288,6 +357,12 @@ pub const Type = extern union {...@@ -288,6 +357,12 @@ pub const Type = extern union {
288 };357 };
289 return Type{ .ptr_otherwise = &new_payload.base };358 return Type{ .ptr_otherwise = &new_payload.base };
290 },359 },
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"),
291 }366 }
292 }367 }
293368
...@@ -298,6 +373,14 @@ pub const Type = extern union {...@@ -298,6 +373,14 @@ pub const Type = extern union {
298 return Type{ .ptr_otherwise = &new_payload.base };373 return Type{ .ptr_otherwise = &new_payload.base };
299 }374 }
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
301 pub fn format(384 pub fn format(
302 self: Type,385 self: Type,
303 comptime fmt: []const u8,386 comptime fmt: []const u8,
...@@ -373,13 +456,13 @@ pub const Type = extern union {...@@ -373,13 +456,13 @@ pub const Type = extern union {
373 continue;456 continue;
374 },457 },
375 .single_const_pointer => {458 .single_const_pointer => {
376 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);459 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
377 try out_stream.writeAll("*const ");460 try out_stream.writeAll("*const ");
378 ty = payload.pointee_type;461 ty = payload.pointee_type;
379 continue;462 continue;
380 },463 },
381 .single_mut_pointer => {464 .single_mut_pointer => {
382 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);465 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
383 try out_stream.writeAll("*");466 try out_stream.writeAll("*");
384 ty = payload.pointee_type;467 ty = payload.pointee_type;
385 continue;468 continue;
...@@ -392,6 +475,24 @@ pub const Type = extern union {...@@ -392,6 +475,24 @@ pub const Type = extern union {
392 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);475 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
393 return out_stream.print("u{}", .{payload.bits});476 return out_stream.print("u{}", .{payload.bits});
394 },477 },
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 },
395 }496 }
396 unreachable;497 unreachable;
397 }498 }
...@@ -481,12 +582,16 @@ pub const Type = extern union {...@@ -481,12 +582,16 @@ pub const Type = extern union {
481 .single_const_pointer_to_comptime_int,582 .single_const_pointer_to_comptime_int,
482 .const_slice_u8,583 .const_slice_u8,
483 .array_u8_sentinel_0,584 .array_u8_sentinel_0,
484 .array, // TODO check for zero bits585 .optional,
485 .single_const_pointer,586 .optional_single_mut_pointer,
486 .single_mut_pointer,587 .optional_single_const_pointer,
487 .int_signed, // TODO check for zero bits
488 .int_unsigned, // TODO check for zero bits
489 => true,588 => 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
491 .c_void,596 .c_void,
492 .void,597 .void,
...@@ -533,6 +638,8 @@ pub const Type = extern union {...@@ -533,6 +638,8 @@ pub const Type = extern union {
533 .const_slice_u8,638 .const_slice_u8,
534 .single_const_pointer,639 .single_const_pointer,
535 .single_mut_pointer,640 .single_mut_pointer,
641 .optional_single_const_pointer,
642 .optional_single_mut_pointer,
536 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),643 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
537644
538 .c_short => return @divExact(CType.short.sizeInBits(target), 8),645 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -565,6 +672,17 @@ pub const Type = extern union {...@@ -565,6 +672,17 @@ pub const Type = extern union {
565 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);672 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
566 },673 },
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
568 .c_void,686 .c_void,
569 .void,687 .void,
570 .type,688 .type,
...@@ -615,6 +733,8 @@ pub const Type = extern union {...@@ -615,6 +733,8 @@ pub const Type = extern union {
615 .const_slice_u8,733 .const_slice_u8,
616 .single_const_pointer,734 .single_const_pointer,
617 .single_mut_pointer,735 .single_mut_pointer,
736 .optional_single_const_pointer,
737 .optional_single_mut_pointer,
618 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),738 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
619739
620 .c_short => return @divExact(CType.short.sizeInBits(target), 8),740 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -644,6 +764,21 @@ pub const Type = extern union {...@@ -644,6 +764,21 @@ pub const Type = extern union {
644764
645 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);765 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
646 },766 },
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 },
647 };782 };
648 }783 }
649784
...@@ -692,6 +827,9 @@ pub const Type = extern union {...@@ -692,6 +827,9 @@ pub const Type = extern union {
692 .function,827 .function,
693 .int_unsigned,828 .int_unsigned,
694 .int_signed,829 .int_signed,
830 .optional,
831 .optional_single_mut_pointer,
832 .optional_single_const_pointer,
695 => false,833 => false,
696834
697 .single_const_pointer,835 .single_const_pointer,
...@@ -748,6 +886,9 @@ pub const Type = extern union {...@@ -748,6 +886,9 @@ pub const Type = extern union {
748 .function,886 .function,
749 .int_unsigned,887 .int_unsigned,
750 .int_signed,888 .int_signed,
889 .optional,
890 .optional_single_mut_pointer,
891 .optional_single_const_pointer,
751 => false,892 => false,
752893
753 .const_slice_u8 => true,894 .const_slice_u8 => true,
...@@ -799,6 +940,9 @@ pub const Type = extern union {...@@ -799,6 +940,9 @@ pub const Type = extern union {
799 .int_unsigned,940 .int_unsigned,
800 .int_signed,941 .int_signed,
801 .single_mut_pointer,942 .single_mut_pointer,
943 .optional,
944 .optional_single_mut_pointer,
945 .optional_single_const_pointer,
802 => false,946 => false,
803947
804 .single_const_pointer,948 .single_const_pointer,
...@@ -856,10 +1000,29 @@ pub const Type = extern union {...@@ -856,10 +1000,29 @@ pub const Type = extern union {
856 .single_const_pointer,1000 .single_const_pointer,
857 .single_const_pointer_to_comptime_int,1001 .single_const_pointer_to_comptime_int,
858 .const_slice_u8,1002 .const_slice_u8,
1003 .optional,
1004 .optional_single_mut_pointer,
1005 .optional_single_const_pointer,
859 => false,1006 => false,
860 };1007 };
861 }1008 }
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
863 /// Asserts the type is a pointer or array type.1026 /// Asserts the type is a pointer or array type.
864 pub fn elemType(self: Type) Type {1027 pub fn elemType(self: Type) Type {
865 return switch (self.tag()) {1028 return switch (self.tag()) {
...@@ -903,16 +1066,63 @@ pub const Type = extern union {...@@ -903,16 +1066,63 @@ pub const Type = extern union {
903 .function,1066 .function,
904 .int_unsigned,1067 .int_unsigned,
905 .int_signed,1068 .int_signed,
1069 .optional,
1070 .optional_single_const_pointer,
1071 .optional_single_mut_pointer,
906 => unreachable,1072 => unreachable,
9071073
908 .array => self.cast(Payload.Array).?.elem_type,1074 .array => self.cast(Payload.Array).?.elem_type,
909 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,1075 .single_const_pointer => self.castPointer().?.pointee_type,
910 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,1076 .single_mut_pointer => self.castPointer().?.pointee_type,
911 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),1077 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
912 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),1078 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
913 };1079 };
914 }1080 }
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
916 /// Asserts the type is an array or vector.1126 /// Asserts the type is an array or vector.
917 pub fn arrayLen(self: Type) u64 {1127 pub fn arrayLen(self: Type) u64 {
918 return switch (self.tag()) {1128 return switch (self.tag()) {
...@@ -960,6 +1170,9 @@ pub const Type = extern union {...@@ -960,6 +1170,9 @@ pub const Type = extern union {
960 .const_slice_u8,1170 .const_slice_u8,
961 .int_unsigned,1171 .int_unsigned,
962 .int_signed,1172 .int_signed,
1173 .optional,
1174 .optional_single_mut_pointer,
1175 .optional_single_const_pointer,
963 => unreachable,1176 => unreachable,
9641177
965 .array => self.cast(Payload.Array).?.len,1178 .array => self.cast(Payload.Array).?.len,
...@@ -1014,6 +1227,9 @@ pub const Type = extern union {...@@ -1014,6 +1227,9 @@ pub const Type = extern union {
1014 .const_slice_u8,1227 .const_slice_u8,
1015 .int_unsigned,1228 .int_unsigned,
1016 .int_signed,1229 .int_signed,
1230 .optional,
1231 .optional_single_mut_pointer,
1232 .optional_single_const_pointer,
1017 => unreachable,1233 => unreachable,
10181234
1019 .array => return null,1235 .array => return null,
...@@ -1065,6 +1281,9 @@ pub const Type = extern union {...@@ -1065,6 +1281,9 @@ pub const Type = extern union {
1065 .u16,1281 .u16,
1066 .u32,1282 .u32,
1067 .u64,1283 .u64,
1284 .optional,
1285 .optional_single_mut_pointer,
1286 .optional_single_const_pointer,
1068 => false,1287 => false,
10691288
1070 .int_signed,1289 .int_signed,
...@@ -1120,6 +1339,9 @@ pub const Type = extern union {...@@ -1120,6 +1339,9 @@ pub const Type = extern union {
1120 .i16,1339 .i16,
1121 .i32,1340 .i32,
1122 .i64,1341 .i64,
1342 .optional,
1343 .optional_single_mut_pointer,
1344 .optional_single_const_pointer,
1123 => false,1345 => false,
11241346
1125 .int_unsigned,1347 .int_unsigned,
...@@ -1165,6 +1387,9 @@ pub const Type = extern union {...@@ -1165,6 +1387,9 @@ pub const Type = extern union {
1165 .single_const_pointer_to_comptime_int,1387 .single_const_pointer_to_comptime_int,
1166 .array_u8_sentinel_0,1388 .array_u8_sentinel_0,
1167 .const_slice_u8,1389 .const_slice_u8,
1390 .optional,
1391 .optional_single_mut_pointer,
1392 .optional_single_const_pointer,
1168 => unreachable,1393 => unreachable,
11691394
1170 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },1395 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
...@@ -1228,6 +1453,9 @@ pub const Type = extern union {...@@ -1228,6 +1453,9 @@ pub const Type = extern union {
1228 .i32,1453 .i32,
1229 .u64,1454 .u64,
1230 .i64,1455 .i64,
1456 .optional,
1457 .optional_single_mut_pointer,
1458 .optional_single_const_pointer,
1231 => false,1459 => false,
12321460
1233 .usize,1461 .usize,
...@@ -1320,6 +1548,9 @@ pub const Type = extern union {...@@ -1320,6 +1548,9 @@ pub const Type = extern union {
1320 .c_ulonglong,1548 .c_ulonglong,
1321 .int_unsigned,1549 .int_unsigned,
1322 .int_signed,1550 .int_signed,
1551 .optional,
1552 .optional_single_mut_pointer,
1553 .optional_single_const_pointer,
1323 => unreachable,1554 => unreachable,
1324 };1555 };
1325 }1556 }
...@@ -1378,6 +1609,9 @@ pub const Type = extern union {...@@ -1378,6 +1609,9 @@ pub const Type = extern union {
1378 .c_ulonglong,1609 .c_ulonglong,
1379 .int_unsigned,1610 .int_unsigned,
1380 .int_signed,1611 .int_signed,
1612 .optional,
1613 .optional_single_mut_pointer,
1614 .optional_single_const_pointer,
1381 => unreachable,1615 => unreachable,
1382 }1616 }
1383 }1617 }
...@@ -1435,6 +1669,9 @@ pub const Type = extern union {...@@ -1435,6 +1669,9 @@ pub const Type = extern union {
1435 .c_ulonglong,1669 .c_ulonglong,
1436 .int_unsigned,1670 .int_unsigned,
1437 .int_signed,1671 .int_signed,
1672 .optional,
1673 .optional_single_mut_pointer,
1674 .optional_single_const_pointer,
1438 => unreachable,1675 => unreachable,
1439 }1676 }
1440 }1677 }
...@@ -1492,6 +1729,9 @@ pub const Type = extern union {...@@ -1492,6 +1729,9 @@ pub const Type = extern union {
1492 .c_ulonglong,1729 .c_ulonglong,
1493 .int_unsigned,1730 .int_unsigned,
1494 .int_signed,1731 .int_signed,
1732 .optional,
1733 .optional_single_mut_pointer,
1734 .optional_single_const_pointer,
1495 => unreachable,1735 => unreachable,
1496 };1736 };
1497 }1737 }
...@@ -1546,6 +1786,9 @@ pub const Type = extern union {...@@ -1546,6 +1786,9 @@ pub const Type = extern union {
1546 .c_ulonglong,1786 .c_ulonglong,
1547 .int_unsigned,1787 .int_unsigned,
1548 .int_signed,1788 .int_signed,
1789 .optional,
1790 .optional_single_mut_pointer,
1791 .optional_single_const_pointer,
1549 => unreachable,1792 => unreachable,
1550 };1793 };
1551 }1794 }
...@@ -1600,6 +1843,9 @@ pub const Type = extern union {...@@ -1600,6 +1843,9 @@ pub const Type = extern union {
1600 .c_ulonglong,1843 .c_ulonglong,
1601 .int_unsigned,1844 .int_unsigned,
1602 .int_signed,1845 .int_signed,
1846 .optional,
1847 .optional_single_mut_pointer,
1848 .optional_single_const_pointer,
1603 => unreachable,1849 => unreachable,
1604 };1850 };
1605 }1851 }
...@@ -1654,6 +1900,9 @@ pub const Type = extern union {...@@ -1654,6 +1900,9 @@ pub const Type = extern union {
1654 .single_const_pointer_to_comptime_int,1900 .single_const_pointer_to_comptime_int,
1655 .array_u8_sentinel_0,1901 .array_u8_sentinel_0,
1656 .const_slice_u8,1902 .const_slice_u8,
1903 .optional,
1904 .optional_single_mut_pointer,
1905 .optional_single_const_pointer,
1657 => false,1906 => false,
1658 };1907 };
1659 }1908 }
...@@ -1698,6 +1947,9 @@ pub const Type = extern union {...@@ -1698,6 +1947,9 @@ pub const Type = extern union {
1698 .array_u8_sentinel_0,1947 .array_u8_sentinel_0,
1699 .const_slice_u8,1948 .const_slice_u8,
1700 .c_void,1949 .c_void,
1950 .optional,
1951 .optional_single_mut_pointer,
1952 .optional_single_const_pointer,
1701 => return null,1953 => return null,
17021954
1703 .void => return Value.initTag(.void_value),1955 .void => return Value.initTag(.void_value),
...@@ -1726,13 +1978,8 @@ pub const Type = extern union {...@@ -1726,13 +1978,8 @@ pub const Type = extern union {
1726 ty = array.elem_type;1978 ty = array.elem_type;
1727 continue;1979 continue;
1728 },1980 },
1729 .single_const_pointer => {1981 .single_const_pointer, .single_mut_pointer => {
1730 const ptr = ty.cast(Payload.SingleConstPointer).?;1982 const ptr = ty.castPointer().?;
1731 ty = ptr.pointee_type;
1732 continue;
1733 },
1734 .single_mut_pointer => {
1735 const ptr = ty.cast(Payload.SingleMutPointer).?;
1736 ty = ptr.pointee_type;1983 ty = ptr.pointee_type;
1737 continue;1984 continue;
1738 },1985 },
...@@ -1787,6 +2034,9 @@ pub const Type = extern union {...@@ -1787,6 +2034,9 @@ pub const Type = extern union {
1787 .array,2034 .array,
1788 .single_const_pointer,2035 .single_const_pointer,
1789 .single_mut_pointer,2036 .single_mut_pointer,
2037 .optional,
2038 .optional_single_mut_pointer,
2039 .optional_single_const_pointer,
1790 => return false,2040 => return false,
1791 };2041 };
1792 }2042 }
...@@ -1847,6 +2097,9 @@ pub const Type = extern union {...@@ -1847,6 +2097,9 @@ pub const Type = extern union {
1847 int_signed,2097 int_signed,
1848 int_unsigned,2098 int_unsigned,
1849 function,2099 function,
2100 optional,
2101 optional_single_mut_pointer,
2102 optional_single_const_pointer,
18502103
1851 pub const last_no_payload_tag = Tag.const_slice_u8;2104 pub const last_no_payload_tag = Tag.const_slice_u8;
1852 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;2105 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -1868,14 +2121,8 @@ pub const Type = extern union {...@@ -1868,14 +2121,8 @@ pub const Type = extern union {
1868 len: u64,2121 len: u64,
1869 };2122 };
18702123
1871 pub const SingleConstPointer = struct {2124 pub const Pointer = struct {
1872 base: Payload = Payload{ .tag = .single_const_pointer },2125 base: Payload,
1873
1874 pointee_type: Type,
1875 };
1876
1877 pub const SingleMutPointer = struct {
1878 base: Payload = Payload{ .tag = .single_mut_pointer },
18792126
1880 pointee_type: Type,2127 pointee_type: Type,
1881 };2128 };
...@@ -1899,6 +2146,12 @@ pub const Type = extern union {...@@ -1899,6 +2146,12 @@ pub const Type = extern union {
1899 return_type: Type,2146 return_type: Type,
1900 cc: std.builtin.CallingConvention,2147 cc: std.builtin.CallingConvention,
1901 };2148 };
2149
2150 pub const Optional = struct {
2151 base: Payload = Payload{ .tag = .optional },
2152
2153 child_type: Type,
2154 };
1902 };2155 };
1903};2156};
19042157
src-self-hosted/value.zig+76-1
...@@ -562,12 +562,87 @@ pub const Value = extern union {...@@ -562,12 +562,87 @@ pub const Value = extern union {
562 .bool_true => return 1,562 .bool_true => return 1,
563563
564 .int_u64 => return self.cast(Payload.Int_u64).?.int,564 .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),
566 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,566 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
567 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,567 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
568 }568 }
569 }569 }
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
571 pub fn toBool(self: Value) bool {646 pub fn toBool(self: Value) bool {
572 return switch (self.tag()) {647 return switch (self.tag()) {
573 .bool_true => true,648 .bool_true => true,
src-self-hosted/zir.zig+331-83
...@@ -151,6 +151,11 @@ pub const Inst = struct {...@@ -151,6 +151,11 @@ pub const Inst = struct {
151 isnonnull,151 isnonnull,
152 /// Return a boolean true if an optional is null. `x == null`152 /// Return a boolean true if an optional is null. `x == null`
153 isnull,153 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,
154 /// Ambiguously remainder division or modulus. If the computation would possibly have159 /// Ambiguously remainder division or modulus. If the computation would possibly have
155 /// a different value depending on whether the operation is remainder division or modulus,160 /// a different value depending on whether the operation is remainder division or modulus,
156 /// a compile error is emitted. Otherwise the computation is performed.161 /// a compile error is emitted. Otherwise the computation is performed.
...@@ -189,6 +194,8 @@ pub const Inst = struct {...@@ -189,6 +194,8 @@ pub const Inst = struct {
189 single_const_ptr_type,194 single_const_ptr_type,
190 /// Create a mutable pointer type based on the element type. `*T`195 /// Create a mutable pointer type based on the element type. `*T`
191 single_mut_ptr_type,196 single_mut_ptr_type,
197 /// Create a pointer type with attributes
198 ptr_type,
192 /// Write a value to a pointer. For loading, see `deref`.199 /// Write a value to a pointer. For loading, see `deref`.
193 store,200 store,
194 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.201 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
...@@ -208,10 +215,19 @@ pub const Inst = struct {...@@ -208,10 +215,19 @@ pub const Inst = struct {
208 @"unreachable",215 @"unreachable",
209 /// Bitwise XOR. `^`216 /// Bitwise XOR. `^`
210 xor,217 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
212 pub fn Type(tag: Tag) type {229 pub fn Type(tag: Tag) type {
213 return switch (tag) {230 return switch (tag) {
214 .arg,
215 .breakpoint,231 .breakpoint,
216 .dbg_stmt,232 .dbg_stmt,
217 .returnvoid,233 .returnvoid,
...@@ -227,6 +243,7 @@ pub const Inst = struct {...@@ -227,6 +243,7 @@ pub const Inst = struct {
227 .@"return",243 .@"return",
228 .isnull,244 .isnull,
229 .isnonnull,245 .isnonnull,
246 .iserr,
230 .ptrtoint,247 .ptrtoint,
231 .alloc,248 .alloc,
232 .ensure_result_used,249 .ensure_result_used,
...@@ -237,6 +254,11 @@ pub const Inst = struct {...@@ -237,6 +254,11 @@ pub const Inst = struct {
237 .typeof,254 .typeof,
238 .single_const_ptr_type,255 .single_const_ptr_type,
239 .single_mut_ptr_type,256 .single_mut_ptr_type,
257 .optional_type,
258 .unwrap_optional_safe,
259 .unwrap_optional_unsafe,
260 .unwrap_err_safe,
261 .unwrap_err_unsafe,
240 => UnOp,262 => UnOp,
241263
242 .add,264 .add,
...@@ -268,6 +290,7 @@ pub const Inst = struct {...@@ -268,6 +290,7 @@ pub const Inst = struct {
268 .xor,290 .xor,
269 => BinOp,291 => BinOp,
270292
293 .arg => Arg,
271 .block => Block,294 .block => Block,
272 .@"break" => Break,295 .@"break" => Break,
273 .breakvoid => BreakVoid,296 .breakvoid => BreakVoid,
...@@ -279,6 +302,7 @@ pub const Inst = struct {...@@ -279,6 +302,7 @@ pub const Inst = struct {
279 .declval_in_module => DeclValInModule,302 .declval_in_module => DeclValInModule,
280 .coerce_result_block_ptr => CoerceResultBlockPtr,303 .coerce_result_block_ptr => CoerceResultBlockPtr,
281 .compileerror => CompileError,304 .compileerror => CompileError,
305 .loop => Loop,
282 .@"const" => Const,306 .@"const" => Const,
283 .str => Str,307 .str => Str,
284 .int => Int,308 .int => Int,
...@@ -292,6 +316,7 @@ pub const Inst = struct {...@@ -292,6 +316,7 @@ pub const Inst = struct {
292 .fntype => FnType,316 .fntype => FnType,
293 .elemptr => ElemPtr,317 .elemptr => ElemPtr,
294 .condbr => CondBr,318 .condbr => CondBr,
319 .ptr_type => PtrType,
295 };320 };
296 }321 }
297322
...@@ -347,6 +372,7 @@ pub const Inst = struct {...@@ -347,6 +372,7 @@ pub const Inst = struct {
347 .inttype,372 .inttype,
348 .isnonnull,373 .isnonnull,
349 .isnull,374 .isnull,
375 .iserr,
350 .mod_rem,376 .mod_rem,
351 .mul,377 .mul,
352 .mulwrap,378 .mulwrap,
...@@ -366,6 +392,12 @@ pub const Inst = struct {...@@ -366,6 +392,12 @@ pub const Inst = struct {
366 .subwrap,392 .subwrap,
367 .typeof,393 .typeof,
368 .xor,394 .xor,
395 .optional_type,
396 .unwrap_optional_safe,
397 .unwrap_optional_unsafe,
398 .unwrap_err_safe,
399 .unwrap_err_unsafe,
400 .ptr_type,
369 => false,401 => false,
370402
371 .@"break",403 .@"break",
...@@ -376,6 +408,7 @@ pub const Inst = struct {...@@ -376,6 +408,7 @@ pub const Inst = struct {
376 .returnvoid,408 .returnvoid,
377 .unreach_nocheck,409 .unreach_nocheck,
378 .@"unreachable",410 .@"unreachable",
411 .loop,
379 => true,412 => true,
380 };413 };
381 }414 }
...@@ -431,6 +464,16 @@ pub const Inst = struct {...@@ -431,6 +464,16 @@ pub const Inst = struct {
431 kw_args: struct {},464 kw_args: struct {},
432 };465 };
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
434 pub const Block = struct {477 pub const Block = struct {
435 pub const base_tag = Tag.block;478 pub const base_tag = Tag.block;
436 base: Inst,479 base: Inst,
...@@ -577,6 +620,16 @@ pub const Inst = struct {...@@ -577,6 +620,16 @@ pub const Inst = struct {
577 kw_args: struct {},620 kw_args: struct {},
578 };621 };
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
580 pub const FieldPtr = struct {633 pub const FieldPtr = struct {
581 pub const base_tag = Tag.fieldptr;634 pub const base_tag = Tag.fieldptr;
582 base: Inst,635 base: Inst,
...@@ -774,6 +827,24 @@ pub const Inst = struct {...@@ -774,6 +827,24 @@ pub const Inst = struct {
774 },827 },
775 kw_args: struct {},828 kw_args: struct {},
776 };829 };
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 };
777};848};
778849
779pub const ErrorMsg = struct {850pub const ErrorMsg = struct {
...@@ -785,12 +856,24 @@ pub const Module = struct {...@@ -785,12 +856,24 @@ pub const Module = struct {
785 decls: []*Decl,856 decls: []*Decl,
786 arena: std.heap.ArenaAllocator,857 arena: std.heap.ArenaAllocator,
787 error_msg: ?ErrorMsg = null,858 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
789 pub const Body = struct {870 pub const Body = struct {
790 instructions: []*Inst,871 instructions: []*Inst,
791 };872 };
792873
793 pub fn deinit(self: *Module, allocator: *Allocator) void {874 pub fn deinit(self: *Module, allocator: *Allocator) void {
875 self.metadata.deinit();
876 self.body_metadata.deinit();
794 allocator.free(self.decls);877 allocator.free(self.decls);
795 self.arena.deinit();878 self.arena.deinit();
796 self.* = undefined;879 self.* = undefined;
...@@ -838,27 +921,25 @@ pub const Module = struct {...@@ -838,27 +921,25 @@ pub const Module = struct {
838 .module = &self,921 .module = &self,
839 .inst_table = InstPtrTable.init(allocator),922 .inst_table = InstPtrTable.init(allocator),
840 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),923 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
924 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
841 .arena = std.heap.ArenaAllocator.init(allocator),925 .arena = std.heap.ArenaAllocator.init(allocator),
842 .indent = 2,926 .indent = 2,
927 .next_instr_index = undefined,
843 };928 };
844 defer write.arena.deinit();929 defer write.arena.deinit();
845 defer write.inst_table.deinit();930 defer write.inst_table.deinit();
846 defer write.block_table.deinit();931 defer write.block_table.deinit();
932 defer write.loop_table.deinit();
847933
848 // First, build a map of *Inst to @ or % indexes934 // First, build a map of *Inst to @ or % indexes
849 try write.inst_table.ensureCapacity(self.decls.len);935 try write.inst_table.ensureCapacity(self.decls.len);
850936
851 for (self.decls) |decl, decl_i| {937 for (self.decls) |decl, decl_i| {
852 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });938 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 }
859 }939 }
860940
861 for (self.decls) |decl, i| {941 for (self.decls) |decl, i| {
942 write.next_instr_index = 0;
862 try stream.print("@{} ", .{decl.name});943 try stream.print("@{} ", .{decl.name});
863 try write.writeInstToStream(stream, decl.inst);944 try write.writeInstToStream(stream, decl.inst);
864 try stream.writeByte('\n');945 try stream.writeByte('\n');
...@@ -872,8 +953,10 @@ const Writer = struct {...@@ -872,8 +953,10 @@ const Writer = struct {
872 module: *const Module,953 module: *const Module,
873 inst_table: InstPtrTable,954 inst_table: InstPtrTable,
874 block_table: std.AutoHashMap(*Inst.Block, []const u8),955 block_table: std.AutoHashMap(*Inst.Block, []const u8),
956 loop_table: std.AutoHashMap(*Inst.Loop, []const u8),
875 arena: std.heap.ArenaAllocator,957 arena: std.heap.ArenaAllocator,
876 indent: usize,958 indent: usize,
959 next_instr_index: usize,
877960
878 fn writeInstToStream(961 fn writeInstToStream(
879 self: *Writer,962 self: *Writer,
...@@ -904,7 +987,7 @@ const Writer = struct {...@@ -904,7 +987,7 @@ const Writer = struct {
904 if (i != 0) {987 if (i != 0) {
905 try stream.writeAll(", ");988 try stream.writeAll(", ");
906 }989 }
907 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name));990 try self.writeParamToStream(stream, &@field(inst.positionals, arg_field.name));
908 }991 }
909992
910 comptime var need_comma = pos_fields.len != 0;993 comptime var need_comma = pos_fields.len != 0;
...@@ -914,13 +997,13 @@ const Writer = struct {...@@ -914,13 +997,13 @@ const Writer = struct {
914 if (@field(inst.kw_args, arg_field.name)) |non_optional| {997 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
915 if (need_comma) try stream.writeAll(", ");998 if (need_comma) try stream.writeAll(", ");
916 try stream.print("{}=", .{arg_field.name});999 try stream.print("{}=", .{arg_field.name});
917 try self.writeParamToStream(stream, non_optional);1000 try self.writeParamToStream(stream, &non_optional);
918 need_comma = true;1001 need_comma = true;
919 }1002 }
920 } else {1003 } else {
921 if (need_comma) try stream.writeAll(", ");1004 if (need_comma) try stream.writeAll(", ");
922 try stream.print("{}=", .{arg_field.name});1005 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));
924 need_comma = true;1007 need_comma = true;
925 }1008 }
926 }1009 }
...@@ -928,7 +1011,8 @@ const Writer = struct {...@@ -928,7 +1011,8 @@ const Writer = struct {
928 try stream.writeByte(')');1011 try stream.writeByte(')');
929 }1012 }
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.*;
932 if (@typeInfo(@TypeOf(param)) == .Enum) {1016 if (@typeInfo(@TypeOf(param)) == .Enum) {
933 return stream.writeAll(@tagName(param));1017 return stream.writeAll(@tagName(param));
934 }1018 }
...@@ -946,15 +1030,36 @@ const Writer = struct {...@@ -946,15 +1030,36 @@ const Writer = struct {
946 },1030 },
947 Module.Body => {1031 Module.Body => {
948 try stream.writeAll("{\n");1032 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 });
950 try stream.writeByteNTimes(' ', self.indent);1049 try stream.writeByteNTimes(' ', self.indent);
951 try stream.print("%{} ", .{i});1050 try stream.print("%{} ", .{my_i});
952 if (inst.cast(Inst.Block)) |block| {1051 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});
954 try self.block_table.put(block, name);1053 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);
955 }1057 }
956 self.indent += 2;1058 self.indent += 2;
957 try self.writeInstToStream(stream, inst);1059 try self.writeInstToStream(stream, inst);
1060 if (self.module.metadata.get(inst)) |metadata| {
1061 try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
1062 }
958 self.indent -= 2;1063 self.indent -= 2;
959 try stream.writeByte('\n');1064 try stream.writeByte('\n');
960 }1065 }
...@@ -970,6 +1075,10 @@ const Writer = struct {...@@ -970,6 +1075,10 @@ const Writer = struct {
970 const name = self.block_table.get(param).?;1075 const name = self.block_table.get(param).?;
971 return std.zig.renderStringLiteral(name, stream);1076 return std.zig.renderStringLiteral(name, stream);
972 },1077 },
1078 *Inst.Loop => {
1079 const name = self.loop_table.get(param).?;
1080 return std.zig.renderStringLiteral(name, stream);
1081 },
973 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),1082 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
974 }1083 }
975 }1084 }
...@@ -1006,8 +1115,10 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module...@@ -1006,8 +1115,10 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
1006 .decls = .{},1115 .decls = .{},
1007 .unnamed_index = 0,1116 .unnamed_index = 0,
1008 .block_table = std.StringHashMap(*Inst.Block).init(allocator),1117 .block_table = std.StringHashMap(*Inst.Block).init(allocator),
1118 .loop_table = std.StringHashMap(*Inst.Loop).init(allocator),
1009 };1119 };
1010 defer parser.block_table.deinit();1120 defer parser.block_table.deinit();
1121 defer parser.loop_table.deinit();
1011 errdefer parser.arena.deinit();1122 errdefer parser.arena.deinit();
10121123
1013 parser.parseRoot() catch |err| switch (err) {1124 parser.parseRoot() catch |err| switch (err) {
...@@ -1021,6 +1132,8 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module...@@ -1021,6 +1132,8 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
1021 .decls = parser.decls.toOwnedSlice(allocator),1132 .decls = parser.decls.toOwnedSlice(allocator),
1022 .arena = parser.arena,1133 .arena = parser.arena,
1023 .error_msg = parser.error_msg,1134 .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),
1024 };1137 };
1025}1138}
10261139
...@@ -1034,6 +1147,7 @@ const Parser = struct {...@@ -1034,6 +1147,7 @@ const Parser = struct {
1034 error_msg: ?ErrorMsg = null,1147 error_msg: ?ErrorMsg = null,
1035 unnamed_index: usize,1148 unnamed_index: usize,
1036 block_table: std.StringHashMap(*Inst.Block),1149 block_table: std.StringHashMap(*Inst.Block),
1150 loop_table: std.StringHashMap(*Inst.Loop),
10371151
1038 const Body = struct {1152 const Body = struct {
1039 instructions: std.ArrayList(*Inst),1153 instructions: std.ArrayList(*Inst),
...@@ -1245,6 +1359,8 @@ const Parser = struct {...@@ -1245,6 +1359,8 @@ const Parser = struct {
12451359
1246 if (InstType == Inst.Block) {1360 if (InstType == Inst.Block) {
1247 try self.block_table.put(inst_name, inst_specific);1361 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);
1248 }1364 }
12491365
1250 if (@hasField(InstType, "ty")) {1366 if (@hasField(InstType, "ty")) {
...@@ -1356,6 +1472,10 @@ const Parser = struct {...@@ -1356,6 +1472,10 @@ const Parser = struct {
1356 const name = try self.parseStringLiteral();1472 const name = try self.parseStringLiteral();
1357 return self.block_table.get(name).?;1473 return self.block_table.get(name).?;
1358 },1474 },
1475 *Inst.Loop => {
1476 const name = try self.parseStringLiteral();
1477 return self.loop_table.get(name).?;
1478 },
1359 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),1479 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
1360 }1480 }
1361 return self.fail("TODO parse parameter {}", .{@typeName(T)});1481 return self.fail("TODO parse parameter {}", .{@typeName(T)});
...@@ -1421,8 +1541,14 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {...@@ -1421,8 +1541,14 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
1421 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),1541 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1422 .indent = 0,1542 .indent = 0,
1423 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),1543 .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),
1424 };1547 };
1548 errdefer ctx.metadata.deinit();
1549 errdefer ctx.body_metadata.deinit();
1425 defer ctx.block_table.deinit();1550 defer ctx.block_table.deinit();
1551 defer ctx.loop_table.deinit();
1426 defer ctx.decls.deinit(allocator);1552 defer ctx.decls.deinit(allocator);
1427 defer ctx.names.deinit();1553 defer ctx.names.deinit();
1428 defer ctx.primitive_table.deinit();1554 defer ctx.primitive_table.deinit();
...@@ -1433,7 +1559,50 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {...@@ -1433,7 +1559,50 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
1433 return Module{1559 return Module{
1434 .decls = ctx.decls.toOwnedSlice(allocator),1560 .decls = ctx.decls.toOwnedSlice(allocator),
1435 .arena = ctx.arena,1561 .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,
1436 };1603 };
1604
1605 module.dump();
1437}1606}
14381607
1439const EmitZIR = struct {1608const EmitZIR = struct {
...@@ -1446,6 +1615,9 @@ const EmitZIR = struct {...@@ -1446,6 +1615,9 @@ const EmitZIR = struct {
1446 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),1615 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
1447 indent: usize,1616 indent: usize,
1448 block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),1617 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
1450 fn emit(self: *EmitZIR) !void {1622 fn emit(self: *EmitZIR) !void {
1451 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced1623 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
...@@ -1545,7 +1717,7 @@ const EmitZIR = struct {...@@ -1545,7 +1717,7 @@ const EmitZIR = struct {
1545 } else blk: {1717 } else blk: {
1546 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;1718 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
1547 };1719 };
1548 try new_body.inst_table.putNoClobber(inst, new_inst);1720 _ = try new_body.inst_table.put(inst, new_inst);
1549 return new_inst;1721 return new_inst;
1550 } else {1722 } else {
1551 return new_body.inst_table.get(inst).?;1723 return new_body.inst_table.get(inst).?;
...@@ -1596,6 +1768,70 @@ const EmitZIR = struct {...@@ -1596,6 +1768,70 @@ const EmitZIR = struct {
1596 return &declref_inst.base;1768 return &declref_inst.base;
1597 }1769 }
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
1599 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {1835 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
1600 const allocator = &self.arena.allocator;1836 const allocator = &self.arena.allocator;
1601 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {1837 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
...@@ -1659,68 +1895,7 @@ const EmitZIR = struct {...@@ -1659,68 +1895,7 @@ const EmitZIR = struct {
1659 },1895 },
1660 .Fn => {1896 .Fn => {
1661 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;1897 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
16621898 return self.emitFn(module_fn, src, typed_value.ty);
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);
1724 },1899 },
1725 .Array => {1900 .Array => {
1726 // TODO more checks to make sure this can be emitted as a string literal1901 // TODO more checks to make sure this can be emitted as a string literal
...@@ -1751,7 +1926,7 @@ const EmitZIR = struct {...@@ -1751,7 +1926,7 @@ const EmitZIR = struct {
1751 }1926 }
1752 }1927 }
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 {
1755 const new_inst = try self.arena.allocator.create(Inst.NoOp);1930 const new_inst = try self.arena.allocator.create(Inst.NoOp);
1756 new_inst.* = .{1931 new_inst.* = .{
1757 .base = .{1932 .base = .{
...@@ -1843,19 +2018,21 @@ const EmitZIR = struct {...@@ -1843,19 +2018,21 @@ const EmitZIR = struct {
1843 const new_inst = switch (inst.tag) {2018 const new_inst = switch (inst.tag) {
1844 .constant => unreachable, // excluded from function bodies2019 .constant => unreachable, // excluded from function bodies
18452020
1846 .arg => try self.emitNoOp(inst.src, .arg),2021 .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint),
1847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),2022 .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck),
1848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),2023 .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid),
1849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),2024 .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
18512025
1852 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),2026 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
1853 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),2027 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
1854 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),2028 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
1855 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),2029 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
1856 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),2030 .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),
1857 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),2032 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
1858 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),2033 .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
1860 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),2037 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1861 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),2038 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
...@@ -1886,6 +2063,22 @@ const EmitZIR = struct {...@@ -1886,6 +2063,22 @@ const EmitZIR = struct {
1886 break :blk &new_inst.base;2063 break :blk &new_inst.base;
1887 },2064 },
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
1889 .block => blk: {2082 .block => blk: {
1890 const old_inst = inst.castTag(.block).?;2083 const old_inst = inst.castTag(.block).?;
1891 const new_inst = try self.arena.allocator.create(Inst.Block);2084 const new_inst = try self.arena.allocator.create(Inst.Block);
...@@ -1911,6 +2104,31 @@ const EmitZIR = struct {...@@ -1911,6 +2104,31 @@ const EmitZIR = struct {
1911 break :blk &new_inst.base;2104 break :blk &new_inst.base;
1912 },2105 },
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
1914 .brvoid => blk: {2132 .brvoid => blk: {
1915 const old_inst = inst.cast(ir.Inst.BrVoid).?;2133 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1916 const new_block = self.block_table.get(old_inst.block).?;2134 const new_block = self.block_table.get(old_inst.block).?;
...@@ -2019,10 +2237,24 @@ const EmitZIR = struct {...@@ -2019,10 +2237,24 @@ const EmitZIR = struct {
2019 defer then_body.deinit();2237 defer then_body.deinit();
2020 defer else_body.deinit();2238 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
2022 try self.emitBody(old_inst.then_body, inst_table, &then_body);2250 try self.emitBody(old_inst.then_body, inst_table, &then_body);
2023 try self.emitBody(old_inst.else_body, inst_table, &else_body);2251 try self.emitBody(old_inst.else_body, inst_table, &else_body);
20242252
2025 const new_inst = try self.arena.allocator.create(Inst.CondBr);2253 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
2026 new_inst.* = .{2258 new_inst.* = .{
2027 .base = .{2259 .base = .{
2028 .src = inst.src,2260 .src = inst.src,
...@@ -2038,6 +2270,7 @@ const EmitZIR = struct {...@@ -2038,6 +2270,7 @@ const EmitZIR = struct {
2038 break :blk &new_inst.base;2270 break :blk &new_inst.base;
2039 },2271 },
2040 };2272 };
2273 try self.metadata.put(new_inst, .{ .deaths = inst.deaths });
2041 try instructions.append(new_inst);2274 try instructions.append(new_inst);
2042 try inst_table.put(inst, new_inst);2275 try inst_table.put(inst, new_inst);
2043 }2276 }
...@@ -2142,6 +2375,21 @@ const EmitZIR = struct {...@@ -2142,6 +2375,21 @@ const EmitZIR = struct {
2142 std.debug.panic("TODO implement emitType for {}", .{ty});2375 std.debug.panic("TODO implement emitType for {}", .{ty});
2143 }2376 }
2144 },2377 },
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 },
2145 else => std.debug.panic("TODO implement emitType for {}", .{ty}),2393 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
2146 },2394 },
2147 }2395 }
src-self-hosted/zir_sema.zig+172-25
...@@ -53,6 +53,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -53,6 +53,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
53 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),53 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
54 .single_const_ptr_type => return analyzeInstSingleConstPtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?),54 .single_const_ptr_type => return analyzeInstSingleConstPtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?),
55 .single_mut_ptr_type => return analyzeInstSingleMutPtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?),55 .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).?),
56 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),57 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
57 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),58 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
58 .int => {59 .int => {
...@@ -60,14 +61,15 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -60,14 +61,15 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
60 return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);61 return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
61 },62 },
62 .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),63 .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
64 .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
63 .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),65 .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
64 .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),66 .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
65 .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),67 .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
66 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),68 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
67 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),69 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
68 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),70 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
69 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?),71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
70 .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?),72 .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
71 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),73 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
72 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),74 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
73 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),75 .@"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!...@@ -102,14 +104,29 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
102 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),104 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
103 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),105 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
104 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),106 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
107 .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?, true),
105 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),108 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
106 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),109 .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),
107 }115 }
108}116}
109117
110pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {118pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
111 for (body.instructions) |src_inst| {119 for (body.instructions) |src_inst, i| {
112 src_inst.analyzed_inst = try analyzeInst(mod, scope, src_inst);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 }
113 }130 }
114}131}
115132
...@@ -303,8 +320,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr...@@ -303,8 +320,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
303320
304fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {321fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
305 const operand = try resolveInst(mod, scope, inst.positionals.operand);322 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
306 const b = try mod.requireRuntimeBlock(scope, inst.base.src);335 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
307 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);
308 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);336 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
309}337}
310338
...@@ -333,7 +361,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst....@@ -333,7 +361,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
333361
334fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {362fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
335 const var_type = try resolveType(mod, scope, inst.positionals.operand);363 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);
337 const b = try mod.requireRuntimeBlock(scope, inst.base.src);365 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
338 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);366 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
339}367}
...@@ -365,7 +393,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType)...@@ -365,7 +393,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType)
365 // TODO support C-style var args393 // TODO support C-style var args
366 const param_count = fn_ty.fnParamLen();394 const param_count = fn_ty.fnParamLen();
367 if (arg_index >= param_count) {395 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)", .{
369 arg_index,397 arg_index,
370 fn_ty,398 fn_ty,
371 param_count,399 param_count,
...@@ -408,7 +436,7 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileE...@@ -408,7 +436,7 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileE
408 return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});436 return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
409}437}
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 {
412 const b = try mod.requireRuntimeBlock(scope, inst.base.src);440 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
413 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;441 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
414 const param_index = b.instructions.items.len;442 const param_index = b.instructions.items.len;
...@@ -420,7 +448,42 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!...@@ -420,7 +448,42 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!
420 });448 });
421 }449 }
422 const param_type = fn_ty.fnParamType(param_index);450 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;
424}487}
425488
426fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {489fn 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...@@ -445,7 +508,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
445 .decl = parent_block.decl,508 .decl = parent_block.decl,
446 .instructions = .{},509 .instructions = .{},
447 .arena = parent_block.arena,510 .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 :(
449 .label = @as(?Scope.Block.Label, Scope.Block.Label{512 .label = @as(?Scope.Block.Label, Scope.Block.Label{
450 .zir_block = inst,513 .zir_block = inst,
451 .results = .{},514 .results = .{},
...@@ -537,7 +600,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -537,7 +600,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
537 return mod.fail(600 return mod.fail(
538 scope,601 scope,
539 inst.positionals.func.src,602 inst.positionals.func.src,
540 "expected at least {} arguments, found {}",603 "expected at least {} argument(s), found {}",
541 .{ fn_params_len, call_params_len },604 .{ fn_params_len, call_params_len },
542 );605 );
543 }606 }
...@@ -547,7 +610,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -547,7 +610,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
547 return mod.fail(610 return mod.fail(
548 scope,611 scope,
549 inst.positionals.func.src,612 inst.positionals.func.src,
550 "expected {} arguments, found {}",613 "expected {} argument(s), found {}",
551 .{ fn_params_len, call_params_len },614 .{ fn_params_len, call_params_len },
552 );615 );
553 }616 }
...@@ -609,6 +672,69 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I...@@ -609,6 +672,69 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I
609 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});672 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
610}673}
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
612fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {738fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
613 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);739 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...@@ -793,8 +919,11 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
793 // required a larger index.919 // required a larger index.
794 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));920 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);922 const type_payload = try scope.arena().create(Type.Payload.Pointer);
797 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };923 type_payload.* = .{
924 .base = .{ .tag = .single_const_pointer },
925 .pointee_type = array_ptr.ty.elemType().elemType(),
926 };
798927
799 return mod.constInst(scope, inst.base.src, .{928 return mod.constInst(scope, inst.base.src, .{
800 .ty = Type.initPayload(&type_payload.base),929 .ty = Type.initPayload(&type_payload.base),
...@@ -1046,6 +1175,10 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver...@@ -1046,6 +1175,10 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver
1046 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);1175 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
1047}1176}
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
1049fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {1182fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
1050 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);1183 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
1051 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);1184 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...@@ -1083,18 +1216,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1083 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);1216 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
1084}1217}
10851218
1086fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1219fn analyzeInstUnreachable(
1087 return mod.analyzeUnreach(scope, unreach.base.src);1220 mod: *Module,
1088}1221 scope: *Scope,
10891222 unreach: *zir.Inst.NoOp,
1090fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1223 safety_check: bool,
1224) InnerError!*Inst {
1091 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);1225 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
1092 // TODO Add compile error for @optimizeFor occurring too late in a scope.1226 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1093 if (mod.wantSafety(scope)) {1227 if (safety_check and mod.wantSafety(scope)) {
1094 // TODO Once we have a panic function to call, call it here instead of this.1228 return mod.safetyPanic(b, unreach.base.src, .unreach);
1095 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);1229 } else {
1230 return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
1096 }1231 }
1097 return mod.analyzeUnreach(scope, unreach.base.src);
1098}1232}
10991233
1100fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1234fn 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!...@@ -1105,6 +1239,15 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
11051239
1106fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {1240fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
1107 const b = try mod.requireRuntimeBlock(scope, inst.base.src);1241 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 }
1108 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);1251 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
1109}1252}
11101253
...@@ -1149,12 +1292,16 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr...@@ -1149,12 +1292,16 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr
11491292
1150fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1293fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1151 const elem_type = try resolveType(mod, scope, inst.positionals.operand);1294 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);
1153 return mod.constType(scope, inst.base.src, ty);1296 return mod.constType(scope, inst.base.src, ty);
1154}1297}
11551298
1156fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1299fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1157 const elem_type = try resolveType(mod, scope, inst.positionals.operand);1300 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);
1159 return mod.constType(scope, inst.base.src, ty);1302 return mod.constType(scope, inst.base.src, ty);
1160}1303}
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 {...@@ -2438,6 +2438,7 @@ struct ScopeBlock {
2438 LVal lval;2438 LVal lval;
2439 bool safety_off;2439 bool safety_off;
2440 bool fast_math_on;2440 bool fast_math_on;
2441 bool name_used;
2441};2442};
24422443
2443// This scope is created from every defer expression.2444// This scope is created from every defer expression.
...@@ -2488,6 +2489,8 @@ struct ScopeLoop {...@@ -2488,6 +2489,8 @@ struct ScopeLoop {
2488 ZigList<IrBasicBlockSrc *> *incoming_blocks;2489 ZigList<IrBasicBlockSrc *> *incoming_blocks;
2489 ResultLocPeerParent *peer_parent;2490 ResultLocPeerParent *peer_parent;
2490 ScopeExpr *spill_scope;2491 ScopeExpr *spill_scope;
2492
2493 bool name_used;
2491};2494};
24922495
2493// This scope blocks certain things from working such as comptime continue2496// 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) {...@@ -7303,7 +7303,14 @@ void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {
7303 case ZigTypeIdEnum:7303 case ZigTypeIdEnum:
7304 {7304 {
7305 TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);7305 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 }
7307 return;7314 return;
7308 }7315 }
7309 case ZigTypeIdErrorUnion:7316 case ZigTypeIdErrorUnion:
...@@ -8623,7 +8630,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu...@@ -8623,7 +8630,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
8623 enum_type->llvm_type = get_llvm_type(g, tag_int_type);8630 enum_type->llvm_type = get_llvm_type(g, tag_int_type);
86248631
8625 // create debug type for tag8632 // 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;
8627 uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;8634 uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
8628 ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,8635 ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
8629 ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),8636 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...@@ -3481,8 +3481,9 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutableGen *exec
3481static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToPtr *instruction) {3481static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToPtr *instruction) {
3482 ZigType *wanted_type = instruction->base.value->type;3482 ZigType *wanted_type = instruction->base.value->type;
3483 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);3483 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) {
3486 ZigType *usize = g->builtin_types.entry_usize;3487 ZigType *usize = g->builtin_types.entry_usize;
3487 LLVMValueRef zero = LLVMConstNull(usize->llvm_type);3488 LLVMValueRef zero = LLVMConstNull(usize->llvm_type);
34883489
...@@ -3499,7 +3500,6 @@ static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable...@@ -3499,7 +3500,6 @@ static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable
3499 }3500 }
35003501
3501 {3502 {
3502 const uint32_t align_bytes = get_ptr_align(g, wanted_type);
3503 LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false);3503 LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false);
3504 LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, "");3504 LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, "");
3505 LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, "");3505 LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, "");
...@@ -5887,6 +5887,12 @@ static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutableGen *executable...@@ -5887,6 +5887,12 @@ static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutableGen *executable
5887static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutableGen *executable,5887static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutableGen *executable,
5888 IrInstGenReturnAddress *instruction)5888 IrInstGenReturnAddress *instruction)
5889{5889{
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
5890 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type);5896 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type);
5891 LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");5897 LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");
5892 return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, "");5898 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,...@@ -7866,17 +7872,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val,
7866 // TODO ^^ make an actual global variable7872 // TODO ^^ make an actual global variable
7867}7873}
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
7880static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {7875static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
7881 bool is_extern = var->decl_node->data.variable_declaration.is_extern;7876 bool is_extern = var->decl_node->data.variable_declaration.is_extern;
7882 bool is_export = var->decl_node->data.variable_declaration.is_export;7877 bool is_export = var->decl_node->data.variable_declaration.is_export;
...@@ -8354,8 +8349,6 @@ static void zig_llvm_emit_output(CodeGen *g) {...@@ -8354,8 +8349,6 @@ static void zig_llvm_emit_output(CodeGen *g) {
8354 exit(1);8349 exit(1);
8355 }8350 }
83568351
8357 validate_inline_fns(g);
8358
8359 if (g->emit_bin) {8352 if (g->emit_bin) {
8360 g->link_objects.append(&g->o_file_output_path);8353 g->link_objects.append(&g->o_file_output_path);
8361 if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) {8354 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...@@ -10260,9 +10253,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
10260 gen_h->types_to_declare.append(type_entry);10253 gen_h->types_to_declare.append(type_entry);
10261 return;10254 return;
10262 case ZigTypeIdStruct:10255 case ZigTypeIdStruct:
10263 for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {10256 if(type_entry->data.structure.layout == ContainerLayoutExtern) {
10264 TypeStructField *field = type_entry->data.structure.fields[i];10257 for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
10265 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);10258 TypeStructField *field = type_entry->data.structure.fields[i];
10259 prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
10260 }
10266 }10261 }
10267 gen_h->types_to_declare.append(type_entry);10262 gen_h->types_to_declare.append(type_entry);
10268 return;10263 return;
...@@ -10695,21 +10690,19 @@ static void gen_h_file(CodeGen *g) {...@@ -10695,21 +10690,19 @@ static void gen_h_file(CodeGen *g) {
10695 fprintf(out_h, "\n");10690 fprintf(out_h, "\n");
10696 }10691 }
1069710692
10698 fprintf(out_h, "%s", buf_ptr(&types_buf));
10699
10700 fprintf(out_h, "#ifdef __cplusplus\n");10693 fprintf(out_h, "#ifdef __cplusplus\n");
10701 fprintf(out_h, "extern \"C\" {\n");10694 fprintf(out_h, "extern \"C\" {\n");
10702 fprintf(out_h, "#endif\n");10695 fprintf(out_h, "#endif\n");
10703 fprintf(out_h, "\n");10696 fprintf(out_h, "\n");
1070410697
10698 fprintf(out_h, "%s", buf_ptr(&types_buf));
10705 fprintf(out_h, "%s\n", buf_ptr(&fns_buf));10699 fprintf(out_h, "%s\n", buf_ptr(&fns_buf));
10700 fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
1070610701
10707 fprintf(out_h, "#ifdef __cplusplus\n");10702 fprintf(out_h, "#ifdef __cplusplus\n");
10708 fprintf(out_h, "} // extern \"C\"\n");10703 fprintf(out_h, "} // extern \"C\"\n");
10709 fprintf(out_h, "#endif\n\n");10704 fprintf(out_h, "#endif\n\n");
1071010705
10711 fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
10712
10713 fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));10706 fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));
1071410707
10715 if (fclose(out_h))10708 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) {...@@ -5477,6 +5477,25 @@ static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
5477 return result;5477 return result;
5478}5478}
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
5480static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,5499static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
5481 ResultLoc *result_loc)5500 ResultLoc *result_loc)
5482{5501{
...@@ -5485,6 +5504,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *...@@ -5485,6 +5504,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
5485 ZigList<IrInstSrc *> incoming_values = {0};5504 ZigList<IrInstSrc *> incoming_values = {0};
5486 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};5505 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
5488 ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);5510 ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
54895511
5490 Scope *outer_block_scope = &scope_block->base;5512 Scope *outer_block_scope = &scope_block->base;
...@@ -5496,6 +5518,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *...@@ -5496,6 +5518,9 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
5496 }5518 }
54975519
5498 if (block_node->data.block.statements.length == 0) {5520 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 }
5499 // {}5524 // {}
5500 return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);5525 return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
5501 }5526 }
...@@ -5553,6 +5578,10 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *...@@ -5553,6 +5578,10 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
5553 }5578 }
5554 }5579 }
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
5556 if (found_invalid_inst)5585 if (found_invalid_inst)
5557 return irb->codegen->invalid_inst_src;5586 return irb->codegen->invalid_inst_src;
55585587
...@@ -6321,9 +6350,9 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod...@@ -6321,9 +6350,9 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
6321 BuiltinFnEntry *builtin_fn = entry->value;6350 BuiltinFnEntry *builtin_fn = entry->value;
6322 size_t actual_param_count = node->data.fn_call_expr.params.length;6351 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) {
6325 add_node_error(irb->codegen, node,6354 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,
6327 builtin_fn->param_count, actual_param_count));6356 builtin_fn->param_count, actual_param_count));
6328 return irb->codegen->invalid_inst_src;6357 return irb->codegen->invalid_inst_src;
6329 }6358 }
...@@ -8153,6 +8182,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no...@@ -8153,6 +8182,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
8153 ZigList<IrInstSrc *> incoming_values = {0};8182 ZigList<IrInstSrc *> incoming_values = {0};
8154 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};8183 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
8156 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);8188 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
8157 loop_scope->break_block = end_block;8189 loop_scope->break_block = end_block;
8158 loop_scope->continue_block = continue_block;8190 loop_scope->continue_block = continue_block;
...@@ -8170,6 +8202,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no...@@ -8170,6 +8202,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
8170 if (body_result == irb->codegen->invalid_inst_src)8202 if (body_result == irb->codegen->invalid_inst_src)
8171 return body_result;8203 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
8173 if (!instr_is_unreachable(body_result)) {8209 if (!instr_is_unreachable(body_result)) {
8174 ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));8210 ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
8175 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));8211 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...@@ -8264,6 +8300,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
8264 ZigList<IrInstSrc *> incoming_values = {0};8300 ZigList<IrInstSrc *> incoming_values = {0};
8265 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};8301 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
8267 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);8306 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
8268 loop_scope->break_block = end_block;8307 loop_scope->break_block = end_block;
8269 loop_scope->continue_block = continue_block;8308 loop_scope->continue_block = continue_block;
...@@ -8281,6 +8320,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no...@@ -8281,6 +8320,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
8281 if (body_result == irb->codegen->invalid_inst_src)8320 if (body_result == irb->codegen->invalid_inst_src)
8282 return body_result;8321 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
8284 if (!instr_is_unreachable(body_result)) {8327 if (!instr_is_unreachable(body_result)) {
8285 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));8328 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
8286 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));8329 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...@@ -8354,6 +8397,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
83548397
8355 Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);8398 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
8357 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);8403 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
8358 loop_scope->break_block = end_block;8404 loop_scope->break_block = end_block;
8359 loop_scope->continue_block = continue_block;8405 loop_scope->continue_block = continue_block;
...@@ -8370,6 +8416,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no...@@ -8370,6 +8416,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
8370 if (body_result == irb->codegen->invalid_inst_src)8416 if (body_result == irb->codegen->invalid_inst_src)
8371 return body_result;8417 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
8373 if (!instr_is_unreachable(body_result)) {8423 if (!instr_is_unreachable(body_result)) {
8374 ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));8424 ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
8375 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));8425 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...@@ -8502,6 +8552,9 @@ static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNod
8502 elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);8552 elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);
8503 build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime);8553 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
8505 ZigList<IrInstSrc *> incoming_values = {0};8558 ZigList<IrInstSrc *> incoming_values = {0};
8506 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};8559 ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
8507 ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);8560 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...@@ -8521,6 +8574,10 @@ static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNod
8521 if (body_result == irb->codegen->invalid_inst_src)8574 if (body_result == irb->codegen->invalid_inst_src)
8522 return irb->codegen->invalid_inst_src;8575 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
8524 if (!instr_is_unreachable(body_result)) {8581 if (!instr_is_unreachable(body_result)) {
8525 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));8582 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
8526 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));8583 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...@@ -9465,6 +9522,7 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n
9465 if (node->data.break_expr.name == nullptr ||9522 if (node->data.break_expr.name == nullptr ||
9466 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))9523 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
9467 {9524 {
9525 this_loop_scope->name_used = true;
9468 loop_scope = this_loop_scope;9526 loop_scope = this_loop_scope;
9469 break;9527 break;
9470 }9528 }
...@@ -9474,6 +9532,7 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n...@@ -9474,6 +9532,7 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n
9474 (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))9532 (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
9475 {9533 {
9476 assert(this_block_scope->end_block != nullptr);9534 assert(this_block_scope->end_block != nullptr);
9535 this_block_scope->name_used = true;
9477 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);9536 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
9478 }9537 }
9479 } else if (search_scope->id == ScopeIdSuspend) {9538 } else if (search_scope->id == ScopeIdSuspend) {
...@@ -9541,6 +9600,7 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN...@@ -9541,6 +9600,7 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN
9541 if (node->data.continue_expr.name == nullptr ||9600 if (node->data.continue_expr.name == nullptr ||
9542 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))9601 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
9543 {9602 {
9603 this_loop_scope->name_used = true;
9544 loop_scope = this_loop_scope;9604 loop_scope = this_loop_scope;
9545 break;9605 break;
9546 }9606 }
...@@ -14037,7 +14097,8 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I...@@ -14037,7 +14097,8 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I
1403714097
14038 // If there is only one possible tag, then we know at comptime what it is.14098 // If there is only one possible tag, then we know at comptime what it is.
14039 if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&14099 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)
14041 {14102 {
14042 IrInstGen *result = ir_const(ira, source_instr, tag_type);14103 IrInstGen *result = ir_const(ira, source_instr, tag_type);
14043 init_const_bigint(result->value, tag_type,14104 init_const_bigint(result->value, tag_type,
...@@ -14077,7 +14138,8 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,...@@ -14077,7 +14138,8 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
1407714138
14078 // If there is only 1 possible tag, then we know at comptime what it is.14139 // If there is only 1 possible tag, then we know at comptime what it is.
14079 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&14140 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)
14081 {14143 {
14082 IrInstGen *result = ir_const(ira, source_instr, wanted_type);14144 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
14083 result->value->special = ConstValSpecialStatic;14145 result->value->special = ConstValSpecialStatic;
...@@ -14116,7 +14178,14 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,...@@ -14116,7 +14178,14 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
14116 if (!val)14178 if (!val)
14117 return ira->codegen->invalid_inst_gen;14179 return ira->codegen->invalid_inst_gen;
14118 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);14180 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 }
14120 ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);14189 ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);
14121 if (field_type == nullptr)14190 if (field_type == nullptr)
14122 return ira->codegen->invalid_inst_gen;14191 return ira->codegen->invalid_inst_gen;
...@@ -14152,6 +14221,13 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,...@@ -14152,6 +14221,13 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
14152 return result;14221 return result;
14153 }14222 }
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
14155 // if the union has all fields 0 bits, we can do it14231 // if the union has all fields 0 bits, we can do it
14156 // and in fact it's a noop cast because the union value is just the enum value14232 // and in fact it's a noop cast because the union value is just the enum value
14157 if (wanted_type->data.unionation.gen_field_count == 0) {14233 if (wanted_type->data.unionation.gen_field_count == 0) {
...@@ -20127,7 +20203,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20127,7 +20203,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20127 if (fn_type_id->is_var_args) {20203 if (fn_type_id->is_var_args) {
20128 if (call_param_count < src_param_count) {20204 if (call_param_count < src_param_count) {
20129 ErrorMsg *msg = ir_add_error_node(ira, source_node,20205 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));
20131 if (fn_proto_node) {20208 if (fn_proto_node) {
20132 add_error_note(ira->codegen, msg, fn_proto_node,20209 add_error_note(ira->codegen, msg, fn_proto_node,
20133 buf_sprintf("declared here"));20210 buf_sprintf("declared here"));
...@@ -20136,7 +20213,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20136,7 +20213,8 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20136 }20213 }
20137 } else if (src_param_count != call_param_count) {20214 } else if (src_param_count != call_param_count) {
20138 ErrorMsg *msg = ir_add_error_node(ira, source_node,20215 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));
20140 if (fn_proto_node) {20218 if (fn_proto_node) {
20141 add_error_note(ira->codegen, msg, fn_proto_node,20219 add_error_note(ira->codegen, msg, fn_proto_node,
20142 buf_sprintf("declared here"));20220 buf_sprintf("declared here"));
...@@ -23755,7 +23833,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23755,7 +23833,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23755 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);23833 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
23756 return result;23834 return result;
23757 }23835 }
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) {
23759 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);23837 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
23760 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];23838 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
23761 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23839 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,...@@ -23770,7 +23848,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23770 case ZigTypeIdEnum: {23848 case ZigTypeIdEnum: {
23771 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))23849 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
23772 return ira->codegen->invalid_inst_gen;23850 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) {
23774 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];23852 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
23775 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);23853 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);
23776 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23854 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) {...@@ -25068,12 +25146,12 @@ static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) {
25068 zig_unreachable();25146 zig_unreachable();
25069}25147}
2507025148
25071static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {25149static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, ZigType *ptr_type_entry) {
25072 Error err;
25073 ZigType *attrs_type;25150 ZigType *attrs_type;
25074 BuiltinPtrSize size_enum_index;25151 BuiltinPtrSize size_enum_index;
25075 if (is_slice(ptr_type_entry)) {25152 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);
25077 size_enum_index = BuiltinPtrSizeSlice;25155 size_enum_index = BuiltinPtrSizeSlice;
25078 } else if (ptr_type_entry->id == ZigTypeIdPointer) {25156 } else if (ptr_type_entry->id == ZigTypeIdPointer) {
25079 attrs_type = ptr_type_entry;25157 attrs_type = ptr_type_entry;
...@@ -25082,9 +25160,6 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent...@@ -25082,9 +25160,6 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
25082 zig_unreachable();25160 zig_unreachable();
25083 }25161 }
2508425162
25085 if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusSizeKnown)))
25086 return nullptr;
25087
25088 ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);25163 ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
25089 assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown));25164 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...@@ -25115,9 +25190,18 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
25115 fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile;25190 fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile;
25116 // alignment: u3225191 // alignment: u32
25117 ensure_field_index(result->type, "alignment", 3);25192 ensure_field_index(result->type, "alignment", 3);
25118 fields[3]->special = ConstValSpecialStatic;
25119 fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int;25193 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 }
25121 // child: type25205 // child: type
25122 ensure_field_index(result->type, "child", 4);25206 ensure_field_index(result->type, "child", 4);
25123 fields[4]->special = ConstValSpecialStatic;25207 fields[4]->special = ConstValSpecialStatic;
...@@ -25131,7 +25215,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent...@@ -25131,7 +25215,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
25131 // sentinel: anytype25215 // sentinel: anytype
25132 ensure_field_index(result->type, "sentinel", 6);25216 ensure_field_index(result->type, "sentinel", 6);
25133 fields[6]->special = ConstValSpecialStatic;25217 fields[6]->special = ConstValSpecialStatic;
25134 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {25218 if (attrs_type->data.pointer.sentinel != nullptr) {
25135 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);25219 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
25136 set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);25220 set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
25137 } else {25221 } else {
...@@ -25166,9 +25250,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25166,9 +25250,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25166 assert(type_entry != nullptr);25250 assert(type_entry != nullptr);
25167 assert(!type_is_invalid(type_entry));25251 assert(!type_is_invalid(type_entry));
2516825252
25169 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25170 return err;
25171
25172 auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);25253 auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
25173 if (entry != nullptr) {25254 if (entry != nullptr) {
25174 *out = entry->value;25255 *out = entry->value;
...@@ -25232,7 +25313,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25232,7 +25313,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25232 }25313 }
25233 case ZigTypeIdPointer:25314 case ZigTypeIdPointer:
25234 {25315 {
25235 result = create_ptr_like_type_info(ira, type_entry);25316 result = create_ptr_like_type_info(ira, source_instr, type_entry);
25236 if (result == nullptr)25317 if (result == nullptr)
25237 return ErrorSemanticAnalyzeFail;25318 return ErrorSemanticAnalyzeFail;
25238 break;25319 break;
...@@ -25318,6 +25399,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25318,6 +25399,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25318 }25399 }
25319 case ZigTypeIdEnum:25400 case ZigTypeIdEnum:
25320 {25401 {
25402 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25403 return err;
25404
25321 result = ira->codegen->pass1_arena->create<ZigValue>();25405 result = ira->codegen->pass1_arena->create<ZigValue>();
25322 result->special = ConstValSpecialStatic;25406 result->special = ConstValSpecialStatic;
25323 result->type = ir_type_info_get_type(ira, "Enum", nullptr);25407 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...@@ -25456,6 +25540,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25456 }25540 }
25457 case ZigTypeIdUnion:25541 case ZigTypeIdUnion:
25458 {25542 {
25543 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25544 return err;
25545
25459 result = ira->codegen->pass1_arena->create<ZigValue>();25546 result = ira->codegen->pass1_arena->create<ZigValue>();
25460 result->special = ConstValSpecialStatic;25547 result->special = ConstValSpecialStatic;
25461 result->type = ir_type_info_get_type(ira, "Union", nullptr);25548 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...@@ -25546,12 +25633,15 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25546 case ZigTypeIdStruct:25633 case ZigTypeIdStruct:
25547 {25634 {
25548 if (type_entry->data.structure.special == StructSpecialSlice) {25635 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);
25550 if (result == nullptr)25637 if (result == nullptr)
25551 return ErrorSemanticAnalyzeFail;25638 return ErrorSemanticAnalyzeFail;
25552 break;25639 break;
25553 }25640 }
2555425641
25642 if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
25643 return err;
25644
25555 result = ira->codegen->pass1_arena->create<ZigValue>();25645 result = ira->codegen->pass1_arena->create<ZigValue>();
25556 result->special = ConstValSpecialStatic;25646 result->special = ConstValSpecialStatic;
25557 result->type = ir_type_info_get_type(ira, "Struct", nullptr);25647 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
...@@ -28765,6 +28855,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28765,6 +28855,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28765 if (type_is_invalid(switch_type))28855 if (type_is_invalid(switch_type))
28766 return ira->codegen->invalid_inst_gen;28856 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
28768 if (switch_type->id == ZigTypeIdEnum) {28862 if (switch_type->id == ZigTypeIdEnum) {
28769 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};28863 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
28770 field_prev_uses.init(switch_type->data.enumeration.src_field_count);28864 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,...@@ -28820,9 +28914,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28820 }28914 }
28821 }28915 }
28822 if (instruction->have_underscore_prong) {28916 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) {
28824 ir_add_error(ira, &instruction->base.base,28921 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"));
28826 }28923 }
28827 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {28924 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
28828 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];28925 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
...@@ -28837,7 +28934,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28837,7 +28934,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28837 }28934 }
28838 }28935 }
28839 } else if (instruction->else_prong == nullptr) {28936 } 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) {
28841 ir_add_error(ira, &instruction->base.base,28938 ir_add_error(ira, &instruction->base.base,
28842 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));28939 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
28843 }28940 }
...@@ -30056,7 +30153,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -30056,7 +30153,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
30056 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);30153 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
30057 }30154 }
30058 ir_add_error(ira, &arg_index_inst->base,30155 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)",
30060 arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));30157 arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));
30061 return ira->codegen->invalid_inst_gen;30158 return ira->codegen->invalid_inst_gen;
30062 }30159 }
src/main.cpp+3
...@@ -38,6 +38,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {...@@ -38,6 +38,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
38 " builtin show the source code of @import(\"builtin\")\n"38 " builtin show the source code of @import(\"builtin\")\n"
39 " cc use Zig as a drop-in C compiler\n"39 " cc use Zig as a drop-in C compiler\n"
40 " c++ use Zig as a drop-in C++ compiler\n"40 " c++ use Zig as a drop-in C++ compiler\n"
41 " env print lib path, std path, compiler id and version\n"
41 " fmt parse files and render in canonical zig format\n"42 " fmt parse files and render in canonical zig format\n"
42 " id print the base64-encoded compiler id\n"43 " id print the base64-encoded compiler id\n"
43 " init-exe initialize a `zig build` application in the cwd\n"44 " init-exe initialize a `zig build` application in the cwd\n"
...@@ -582,6 +583,8 @@ static int main0(int argc, char **argv) {...@@ -582,6 +583,8 @@ static int main0(int argc, char **argv) {
582 return (term.how == TerminationIdClean) ? term.code : -1;583 return (term.how == TerminationIdClean) ? term.code : -1;
583 } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {584 } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
584 return stage2_fmt(argc, argv);585 return stage2_fmt(argc, argv);
586 } else if (argc >= 2 && strcmp(argv[1], "env") == 0) {
587 return stage2_env(argc, argv);
585 } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) {588 } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) {
586 emit_h = false;589 emit_h = false;
587 strip = true;590 strip = true;
src/stage2.cpp+5
...@@ -27,6 +27,11 @@ void stage2_zen(const char **ptr, size_t *len) {...@@ -27,6 +27,11 @@ void stage2_zen(const char **ptr, size_t *len) {
27 stage2_panic(msg, strlen(msg));27 stage2_panic(msg, strlen(msg));
28}28}
2929
30int stage2_env(int argc, char** argv) {
31 const char *msg = "stage0 called stage2_env";
32 stage2_panic(msg, strlen(msg));
33}
34
30void stage2_attach_segfault_handler(void) { }35void stage2_attach_segfault_handler(void) { }
3136
32void stage2_panic(const char *ptr, size_t len) {37void 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);...@@ -141,6 +141,9 @@ ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
141// ABI warning141// ABI warning
142ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);142ZIG_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
144// ABI warning147// ABI warning
145ZIG_EXTERN_C void stage2_attach_segfault_handler(void);148ZIG_EXTERN_C void stage2_attach_segfault_handler(void);
146149
src/tokenizer.cpp+1
...@@ -17,6 +17,7 @@...@@ -17,6 +17,7 @@
1717
18#define WHITESPACE \18#define WHITESPACE \
19 ' ': \19 ' ': \
20 case '\r': \
20 case '\n'21 case '\n'
2122
22#define DIGIT_NON_ZERO \23#define DIGIT_NON_ZERO \
src/zig_clang.cpp+13
...@@ -2619,6 +2619,19 @@ struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct Zi...@@ -2619,6 +2619,19 @@ struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct Zi
2619 return bitcast(casted->getBeginLoc());2619 return bitcast(casted->getBeginLoc());
2620}2620}
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
2622const struct ZigClangExpr *ZigClangReturnStmt_getRetValue(const struct ZigClangReturnStmt *self) {2635const struct ZigClangExpr *ZigClangReturnStmt_getRetValue(const struct ZigClangReturnStmt *self) {
2623 auto casted = reinterpret_cast<const clang::ReturnStmt *>(self);2636 auto casted = reinterpret_cast<const clang::ReturnStmt *>(self);
2624 return reinterpret_cast<const struct ZigClangExpr *>(casted->getRetValue());2637 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...@@ -1142,6 +1142,7 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangCStyleCastExpr_getType(const struct
11421142
1143ZIG_EXTERN_C bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *, struct ZigClangExprEvalResult *, const struct ZigClangASTContext *);1143ZIG_EXTERN_C bool ZigClangIntegerLiteral_EvaluateAsInt(const struct ZigClangIntegerLiteral *, struct ZigClangExprEvalResult *, const struct ZigClangASTContext *);
1144ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct ZigClangIntegerLiteral *);1144ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangIntegerLiteral_getBeginLoc(const struct ZigClangIntegerLiteral *);
1145ZIG_EXTERN_C bool ZigClangIntegerLiteral_isZero(const struct ZigClangIntegerLiteral *, bool *, const struct ZigClangASTContext *);
11451146
1146ZIG_EXTERN_C const struct ZigClangExpr *ZigClangReturnStmt_getRetValue(const struct ZigClangReturnStmt *);1147ZIG_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");...@@ -2,6 +2,29 @@ const tests = @import("tests.zig");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.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
5 cases.addTest("@alignCast of zero sized types",28 cases.addTest("@alignCast of zero sized types",
6 \\export fn foo() void {29 \\export fn foo() void {
7 \\ const a: *void = undefined;30 \\ const a: *void = undefined;
...@@ -28,6 +51,46 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -28,6 +51,46 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
28 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",51 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
29 });52 });
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
31 cases.addTest("invalid pointer with @Type",94 cases.addTest("invalid pointer with @Type",
32 \\export fn entry() void {95 \\export fn entry() void {
33 \\ _ = @Type(.{ .Pointer = .{96 \\ _ = @Type(.{ .Pointer = .{
...@@ -541,6 +604,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -541,6 +604,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
541 \\ b,604 \\ b,
542 \\ _,605 \\ _,
543 \\};606 \\};
607 \\const U = union(E) {
608 \\ a: i32,
609 \\ b: u32,
610 \\};
544 \\pub export fn entry() void {611 \\pub export fn entry() void {
545 \\ var e: E = .b;612 \\ var e: E = .b;
546 \\ switch (e) { // error: switch not handling the tag `b`613 \\ switch (e) { // error: switch not handling the tag `b`
...@@ -551,10 +618,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -551,10 +618,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
551 \\ .a => {},618 \\ .a => {},
552 \\ .b => {},619 \\ .b => {},
553 \\ }620 \\ }
621 \\ var u = U{.a = 2};
622 \\ switch (u) { // error: `_` prong not allowed when switching on tagged union
623 \\ .a => {},
624 \\ .b => {},
625 \\ _ => {},
626 \\ }
554 \\}627 \\}
555 , &[_][]const u8{628 , &[_][]const u8{
556 "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",629 "tmp.zig:12: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",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",
558 });632 });
559633
560 cases.add("switch expression - unreachable else prong (bool)",634 cases.add("switch expression - unreachable else prong (bool)",
...@@ -682,7 +756,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -682,7 +756,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
682 \\ for (arr) |bits| _ = @popCount(bits);756 \\ for (arr) |bits| _ = @popCount(bits);
683 \\}757 \\}
684 , &[_][]const u8{758 , &[_][]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",
686 });760 });
687761
688 cases.addTest("@call rejects non comptime-known fn - always_inline",762 cases.addTest("@call rejects non comptime-known fn - always_inline",
...@@ -4080,7 +4154,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -4080,7 +4154,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
4080 \\}4154 \\}
4081 \\fn b(a: i32, b: i32, c: i32) void { }4155 \\fn b(a: i32, b: i32, c: i32) void { }
4082 , &[_][]const u8{4156 , &[_][]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",
4084 });4158 });
40854159
4086 cases.add("invalid type",4160 cases.add("invalid type",
...@@ -4693,7 +4767,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -4693,7 +4767,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
4693 \\4767 \\
4694 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }4768 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }
4695 , &[_][]const u8{4769 , &[_][]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",
4697 });4771 });
46984772
4699 cases.add("missing function name",4773 cases.add("missing function name",
...@@ -5475,7 +5549,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5475,7 +5549,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5475 \\}5549 \\}
5476 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }5550 \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }
5477 , &[_][]const u8{5551 , &[_][]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",
5479 });5553 });
54805554
5481 cases.add("assign through constant pointer",5555 cases.add("assign through constant pointer",
...@@ -6128,32 +6202,33 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -6128,32 +6202,33 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
6128 "tmp.zig:2:15: error: expected error union type, found '?i32'",6202 "tmp.zig:2:15: error: expected error union type, found '?i32'",
6129 });6203 });
61306204
6131 cases.add("inline fn calls itself indirectly",6205 // TODO test this in stage2, but we won't even try in stage1
6132 \\export fn foo() void {6206 //cases.add("inline fn calls itself indirectly",
6133 \\ bar();6207 // \\export fn foo() void {
6134 \\}6208 // \\ bar();
6135 \\inline fn bar() void {6209 // \\}
6136 \\ baz();6210 // \\inline fn bar() void {
6137 \\ quux();6211 // \\ baz();
6138 \\}6212 // \\ quux();
6139 \\inline fn baz() void {6213 // \\}
6140 \\ bar();6214 // \\inline fn baz() void {
6141 \\ quux();6215 // \\ bar();
6142 \\}6216 // \\ quux();
6143 \\extern fn quux() void;6217 // \\}
6144 , &[_][]const u8{6218 // \\extern fn quux() void;
6145 "tmp.zig:4:1: error: unable to inline function",6219 //, &[_][]const u8{
6146 });6220 // "tmp.zig:4:1: error: unable to inline function",
61476221 //});
6148 cases.add("save reference to inline function",6222
6149 \\export fn foo() void {6223 //cases.add("save reference to inline function",
6150 \\ quux(@ptrToInt(bar));6224 // \\export fn foo() void {
6151 \\}6225 // \\ quux(@ptrToInt(bar));
6152 \\inline fn bar() void { }6226 // \\}
6153 \\extern fn quux(usize) void;6227 // \\inline fn bar() void { }
6154 , &[_][]const u8{6228 // \\extern fn quux(usize) void;
6155 "tmp.zig:4:1: error: unable to inline function",6229 //, &[_][]const u8{
6156 });6230 // "tmp.zig:4:1: error: unable to inline function",
6231 //});
61576232
6158 cases.add("signed integer division",6233 cases.add("signed integer division",
6159 \\export fn foo(a: i32, b: i32) i32 {6234 \\export fn foo(a: i32, b: i32) i32 {
...@@ -6641,12 +6716,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -6641,12 +6716,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
6641 "tmp.zig:9:13: error: type '*MyType' does not support field access",6716 "tmp.zig:9:13: error: type '*MyType' does not support field access",
6642 });6717 });
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
6650 cases.add("invalid legacy unicode escape",6719 cases.add("invalid legacy unicode escape",
6651 \\export fn entry() void {6720 \\export fn entry() void {
6652 \\ const a = '\U1234';6721 \\ const a = '\U1234';
test/run_translated_c.zig-1
...@@ -15,7 +15,6 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -15,7 +15,6 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
15 \\ }15 \\ }
16 \\ if (s0 != 1) abort();16 \\ if (s0 != 1) abort();
17 \\ if (s1 != 10) abort();17 \\ if (s1 != 10) abort();
18 \\ return 0;
19 \\}18 \\}
20 , "");19 , "");
2120
test/stage1/behavior/enum.zig+37
...@@ -85,6 +85,43 @@ test "empty non-exhaustive enum" {...@@ -85,6 +85,43 @@ test "empty non-exhaustive enum" {
85 comptime S.doTheTest(42);85 comptime S.doTheTest(42);
86}86}
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
88test "enum type" {125test "enum type" {
89 const foo1 = Foo{ .One = 13 };126 const foo1 = Foo{ .One = 13 };
90 const foo2 = Foo{127 const foo2 = Foo{
test/stage1/behavior/union.zig+23
...@@ -690,3 +690,26 @@ test "method call on an empty union" {...@@ -690,3 +690,26 @@ test "method call on an empty union" {
690 S.doTheTest();690 S.doTheTest();
691 comptime S.doTheTest();691 comptime S.doTheTest();
692}692}
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{...@@ -10,36 +10,45 @@ const linux_x64 = std.zig.CrossTarget{
1010
11pub fn addCases(ctx: *TestContext) !void {11pub fn addCases(ctx: *TestContext) !void {
12 ctx.c("empty start function", linux_x64,12 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {}13 \\export fn _start() noreturn {
14 \\ unreachable;
15 \\}
14 ,16 ,
15 \\noreturn void _start(void) {}17 \\zig_noreturn void _start(void) {
18 \\ zig_unreachable();
19 \\}
16 \\20 \\
17 );21 );
18 ctx.c("less empty start function", linux_x64,22 ctx.c("less empty start function", linux_x64,
19 \\fn main() noreturn {}23 \\fn main() noreturn {
24 \\ unreachable;
25 \\}
20 \\26 \\
21 \\export fn _start() noreturn {27 \\export fn _start() noreturn {
22 \\ main();28 \\ main();
23 \\}29 \\}
24 ,30 ,
25 \\noreturn void main(void);31 \\zig_noreturn void main(void);
26 \\32 \\
27 \\noreturn void _start(void) {33 \\zig_noreturn void _start(void) {
28 \\ main();34 \\ main();
29 \\}35 \\}
30 \\36 \\
31 \\noreturn void main(void) {}37 \\zig_noreturn void main(void) {
38 \\ zig_unreachable();
39 \\}
32 \\40 \\
33 );41 );
34 // TODO: implement return values42 // TODO: implement return values
35 // TODO: figure out a way to prevent asm constants from being generated43 // TODO: figure out a way to prevent asm constants from being generated
36 ctx.c("inline asm", linux_x64,44 ctx.c("inline asm", linux_x64,
37 \\fn exitGood() void {45 \\fn exitGood() noreturn {
38 \\ asm volatile ("syscall"46 \\ asm volatile ("syscall"
39 \\ :47 \\ :
40 \\ : [number] "{rax}" (231),48 \\ : [number] "{rax}" (231),
41 \\ [arg1] "{rdi}" (0)49 \\ [arg1] "{rdi}" (0)
42 \\ );50 \\ );
51 \\ unreachable;
43 \\}52 \\}
44 \\53 \\
45 \\export fn _start() noreturn {54 \\export fn _start() noreturn {
...@@ -48,21 +57,93 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -48,21 +57,93 @@ pub fn addCases(ctx: *TestContext) !void {
48 ,57 ,
49 \\#include <stddef.h>58 \\#include <stddef.h>
50 \\59 \\
51 \\void exitGood(void);60 \\zig_noreturn void exitGood(void);
52 \\61 \\
53 \\const char *const exitGood__anon_0 = "{rax}";62 \\const char *const exitGood__anon_0 = "{rax}";
54 \\const char *const exitGood__anon_1 = "{rdi}";63 \\const char *const exitGood__anon_1 = "{rdi}";
55 \\const char *const exitGood__anon_2 = "syscall";64 \\const char *const exitGood__anon_2 = "syscall";
56 \\65 \\
57 \\noreturn void _start(void) {66 \\zig_noreturn void _start(void) {
58 \\ exitGood();67 \\ exitGood();
59 \\}68 \\}
60 \\69 \\
61 \\void exitGood(void) {70 \\zig_noreturn void exitGood(void) {
62 \\ register size_t rax_constant __asm__("rax") = 231;71 \\ register size_t rax_constant __asm__("rax") = 231;
63 \\ register size_t rdi_constant __asm__("rdi") = 0;72 \\ register size_t rdi_constant __asm__("rdi") = 0;
64 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));73 \\ __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();
66 \\}147 \\}
67 \\148 \\
68 );149 );
test/stage2/compare_output.zig+145-9
...@@ -12,17 +12,22 @@ const linux_riscv64 = std.zig.CrossTarget{...@@ -12,17 +12,22 @@ const linux_riscv64 = std.zig.CrossTarget{
12 .os_tag = .linux,12 .os_tag = .linux,
13};13};
1414
15pub fn addCases(ctx: *TestContext) !void {15const wasi = std.zig.CrossTarget{
16 if (std.Target.current.os.tag != .linux or16 .cpu_arch = .wasm32,
17 std.Target.current.cpu.arch != .x86_64)17 .os_tag = .wasi,
18 {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 }
2319
20pub fn addCases(ctx: *TestContext) !void {
24 {21 {
25 var case = ctx.exe("hello world with updates", linux_x64);22 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
26 // Regular old hello world31 // Regular old hello world
27 case.addCompareOutput(32 case.addCompareOutput(
28 \\export fn _start() noreturn {33 \\export fn _start() noreturn {
...@@ -123,7 +128,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -123,7 +128,7 @@ pub fn addCases(ctx: *TestContext) !void {
123 \\128 \\
124 );129 );
125 }130 }
126 131
127 {132 {
128 var case = ctx.exe("hello world", linux_riscv64);133 var case = ctx.exe("hello world", linux_riscv64);
129 // Regular old hello world134 // Regular old hello world
...@@ -438,5 +443,136 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -438,5 +443,136 @@ pub fn addCases(ctx: *TestContext) !void {
438 ,443 ,
439 "",444 "",
440 );445 );
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 );
441 }577 }
442}578}
test/stage2/zir.zig+9-9
...@@ -28,7 +28,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -28,7 +28,7 @@ pub fn addCases(ctx: *TestContext) !void {
28 \\@unnamed$5 = export(@unnamed$4, "entry")28 \\@unnamed$5 = export(@unnamed$4, "entry")
29 \\@unnamed$6 = fntype([], @void, cc=C)29 \\@unnamed$6 = fntype([], @void, cc=C)
30 \\@entry = fn(@unnamed$6, {30 \\@entry = fn(@unnamed$6, {
31 \\ %0 = returnvoid()31 \\ %0 = returnvoid() ; deaths=0b1000000000000000
32 \\})32 \\})
33 \\33 \\
34 );34 );
...@@ -75,7 +75,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -75,7 +75,7 @@ pub fn addCases(ctx: *TestContext) !void {
75 \\@3 = int(3)75 \\@3 = int(3)
76 \\@unnamed$6 = fntype([], @void, cc=C)76 \\@unnamed$6 = fntype([], @void, cc=C)
77 \\@entry = fn(@unnamed$6, {77 \\@entry = fn(@unnamed$6, {
78 \\ %0 = returnvoid()78 \\ %0 = returnvoid() ; deaths=0b1000000000000000
79 \\})79 \\})
80 \\@entry__anon_1 = str("2\x08\x01\n")80 \\@entry__anon_1 = str("2\x08\x01\n")
81 \\@9 = declref("9__anon_0")81 \\@9 = declref("9__anon_0")
...@@ -117,18 +117,18 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -117,18 +117,18 @@ pub fn addCases(ctx: *TestContext) !void {
117 \\@unnamed$5 = export(@unnamed$4, "entry")117 \\@unnamed$5 = export(@unnamed$4, "entry")
118 \\@unnamed$6 = fntype([], @void, cc=C)118 \\@unnamed$6 = fntype([], @void, cc=C)
119 \\@entry = fn(@unnamed$6, {119 \\@entry = fn(@unnamed$6, {
120 \\ %0 = call(@a, [], modifier=auto)120 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
121 \\ %1 = returnvoid()121 \\ %1 = returnvoid() ; deaths=0b1000000000000000
122 \\})122 \\})
123 \\@unnamed$8 = fntype([], @void, cc=C)123 \\@unnamed$8 = fntype([], @void, cc=C)
124 \\@a = fn(@unnamed$8, {124 \\@a = fn(@unnamed$8, {
125 \\ %0 = call(@b, [], modifier=auto)125 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
126 \\ %1 = returnvoid()126 \\ %1 = returnvoid() ; deaths=0b1000000000000000
127 \\})127 \\})
128 \\@unnamed$10 = fntype([], @void, cc=C)128 \\@unnamed$10 = fntype([], @void, cc=C)
129 \\@b = fn(@unnamed$10, {129 \\@b = fn(@unnamed$10, {
130 \\ %0 = call(@a, [], modifier=auto)130 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
131 \\ %1 = returnvoid()131 \\ %1 = returnvoid() ; deaths=0b1000000000000000
132 \\})132 \\})
133 \\133 \\
134 );134 );
...@@ -193,7 +193,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -193,7 +193,7 @@ pub fn addCases(ctx: *TestContext) !void {
193 \\@unnamed$5 = export(@unnamed$4, "entry")193 \\@unnamed$5 = export(@unnamed$4, "entry")
194 \\@unnamed$6 = fntype([], @void, cc=C)194 \\@unnamed$6 = fntype([], @void, cc=C)
195 \\@entry = fn(@unnamed$6, {195 \\@entry = fn(@unnamed$6, {
196 \\ %0 = returnvoid()196 \\ %0 = returnvoid() ; deaths=0b1000000000000000
197 \\})197 \\})
198 \\198 \\
199 );199 );
test/translate_c.zig+79-50
...@@ -3,12 +3,33 @@ const std = @import("std");...@@ -3,12 +3,33 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub 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
6 cases.add("alignof",27 cases.add("alignof",
7 \\int main() {28 \\void main() {
8 \\ int a = _Alignof(int);29 \\ int a = _Alignof(int);
9 \\}30 \\}
10 , &[_][]const u8{31 , &[_][]const u8{
11 \\pub export fn main() c_int {32 \\pub export fn main() void {
12 \\ var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));33 \\ var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));
13 \\}34 \\}
14 });35 });
...@@ -99,10 +120,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -99,10 +120,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
99 \\}120 \\}
100 , &[_][]const u8{121 , &[_][]const u8{
101 \\pub export fn foo() void {122 \\pub export fn foo() void {
102 \\ while (@as(c_int, 0) != 0) while (@as(c_int, 0) != 0) {};123 \\ while (false) while (false) {};
103 \\ while (true) while (@as(c_int, 0) != 0) {};124 \\ while (true) while (false) {};
104 \\ while (true) while (true) {125 \\ while (true) while (true) {
105 \\ if (!(@as(c_int, 0) != 0)) break;126 \\ if (!false) break;
106 \\ };127 \\ };
107 \\}128 \\}
108 });129 });
...@@ -539,6 +560,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -539,6 +560,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
539 \\ c = (a * b);560 \\ c = (a * b);
540 \\ c = @divTrunc(a, b);561 \\ c = @divTrunc(a, b);
541 \\ c = @rem(a, b);562 \\ c = @rem(a, b);
563 \\ return 0;
542 \\}564 \\}
543 \\pub export fn u() c_uint {565 \\pub export fn u() c_uint {
544 \\ var a: c_uint = undefined;566 \\ var a: c_uint = undefined;
...@@ -549,6 +571,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -549,6 +571,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
549 \\ c = (a *% b);571 \\ c = (a *% b);
550 \\ c = (a / b);572 \\ c = (a / b);
551 \\ c = (a % b);573 \\ c = (a % b);
574 \\ return 0;
552 \\}575 \\}
553 });576 });
554577
...@@ -1260,11 +1283,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1260,11 +1283,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1260 \\void __attribute__((cdecl)) foo4(float *a);1283 \\void __attribute__((cdecl)) foo4(float *a);
1261 \\void __attribute__((thiscall)) foo5(float *a);1284 \\void __attribute__((thiscall)) foo5(float *a);
1262 , &[_][]const u8{1285 , &[_][]const u8{
1263 \\pub fn foo1(a: [*c]f32) callconv(.Fastcall) void;1286 \\pub extern fn foo1(a: [*c]f32) callconv(.Fastcall) void;
1264 \\pub fn foo2(a: [*c]f32) callconv(.Stdcall) void;1287 \\pub extern fn foo2(a: [*c]f32) callconv(.Stdcall) void;
1265 \\pub fn foo3(a: [*c]f32) callconv(.Vectorcall) void;1288 \\pub extern fn foo3(a: [*c]f32) callconv(.Vectorcall) void;
1266 \\pub extern fn foo4(a: [*c]f32) void;1289 \\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;
1268 });1291 });
12691292
1270 cases.addWithTarget("Calling convention", CrossTarget.parse(.{1293 cases.addWithTarget("Calling convention", CrossTarget.parse(.{
...@@ -1274,8 +1297,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1274,8 +1297,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1274 \\void __attribute__((pcs("aapcs"))) foo1(float *a);1297 \\void __attribute__((pcs("aapcs"))) foo1(float *a);
1275 \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);1298 \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);
1276 , &[_][]const u8{1299 , &[_][]const u8{
1277 \\pub fn foo1(a: [*c]f32) callconv(.AAPCS) void;1300 \\pub extern fn foo1(a: [*c]f32) callconv(.AAPCS) void;
1278 \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;1301 \\pub extern fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
1279 });1302 });
12801303
1281 cases.addWithTarget("Calling convention", CrossTarget.parse(.{1304 cases.addWithTarget("Calling convention", CrossTarget.parse(.{
...@@ -1284,7 +1307,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1284,7 +1307,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1284 }) catch unreachable,1307 }) catch unreachable,
1285 \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);1308 \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);
1286 , &[_][]const u8{1309 , &[_][]const u8{
1287 \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void;1310 \\pub extern fn foo1(a: [*c]f32) callconv(.Vectorcall) void;
1288 });1311 });
12891312
1290 cases.add("Parameterless function prototypes",1313 cases.add("Parameterless function prototypes",
...@@ -1596,13 +1619,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1596,13 +1619,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1596 });1619 });
15971620
1598 cases.add("worst-case assign",1621 cases.add("worst-case assign",
1599 \\int foo() {1622 \\void foo() {
1600 \\ int a;1623 \\ int a;
1601 \\ int b;1624 \\ int b;
1602 \\ a = b = 2;1625 \\ a = b = 2;
1603 \\}1626 \\}
1604 , &[_][]const u8{1627 , &[_][]const u8{
1605 \\pub export fn foo() c_int {1628 \\pub export fn foo() void {
1606 \\ var a: c_int = undefined;1629 \\ var a: c_int = undefined;
1607 \\ var b: c_int = undefined;1630 \\ var b: c_int = undefined;
1608 \\ a = blk: {1631 \\ a = blk: {
...@@ -1634,8 +1657,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1634,8 +1657,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1634 , &[_][]const u8{1657 , &[_][]const u8{
1635 \\pub export fn foo() c_int {1658 \\pub export fn foo() c_int {
1636 \\ var a: c_int = 5;1659 \\ var a: c_int = 5;
1637 \\ while (@as(c_int, 2) != 0) a = 2;1660 \\ while (true) a = 2;
1638 \\ while (@as(c_int, 4) != 0) {1661 \\ while (true) {
1639 \\ var a_1: c_int = 4;1662 \\ var a_1: c_int = 4;
1640 \\ a_1 = 9;1663 \\ a_1 = 9;
1641 \\ _ = @as(c_int, 6);1664 \\ _ = @as(c_int, 6);
...@@ -1644,17 +1667,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1644,17 +1667,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1644 \\ while (true) {1667 \\ while (true) {
1645 \\ var a_1: c_int = 2;1668 \\ var a_1: c_int = 2;
1646 \\ a_1 = 12;1669 \\ a_1 = 12;
1647 \\ if (!(@as(c_int, 4) != 0)) break;1670 \\ if (!true) break;
1648 \\ }1671 \\ }
1649 \\ while (true) {1672 \\ while (true) {
1650 \\ a = 7;1673 \\ a = 7;
1651 \\ if (!(@as(c_int, 4) != 0)) break;1674 \\ if (!true) break;
1652 \\ }1675 \\ }
1676 \\ return 0;
1653 \\}1677 \\}
1654 });1678 });
16551679
1656 cases.add("for loops",1680 cases.add("for loops",
1657 \\int foo() {1681 \\void foo() {
1658 \\ for (int i = 2, b = 4; i + 2; i = 2) {1682 \\ for (int i = 2, b = 4; i + 2; i = 2) {
1659 \\ int a = 2;1683 \\ int a = 2;
1660 \\ a = 6, 5, 7;1684 \\ a = 6, 5, 7;
...@@ -1662,7 +1686,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1662,7 +1686,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1662 \\ char i = 2;1686 \\ char i = 2;
1663 \\}1687 \\}
1664 , &[_][]const u8{1688 , &[_][]const u8{
1665 \\pub export fn foo() c_int {1689 \\pub export fn foo() void {
1666 \\ {1690 \\ {
1667 \\ var i: c_int = 2;1691 \\ var i: c_int = 2;
1668 \\ var b: c_int = 4;1692 \\ var b: c_int = 4;
...@@ -1679,8 +1703,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1679,8 +1703,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16791703
1680 cases.add("shadowing primitive types",1704 cases.add("shadowing primitive types",
1681 \\unsigned anyerror = 2;1705 \\unsigned anyerror = 2;
1706 \\#define noreturn _Noreturn
1682 , &[_][]const u8{1707 , &[_][]const u8{
1683 \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2));1708 \\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");
1684 });1712 });
16851713
1686 cases.add("floats",1714 cases.add("floats",
...@@ -1702,13 +1730,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1702,13 +1730,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1702 \\}1730 \\}
1703 , &[_][]const u8{1731 , &[_][]const u8{
1704 \\pub export fn bar() c_int {1732 \\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);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);
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);1734 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
1707 \\}1735 \\}
1708 });1736 });
17091737
1710 cases.add("switch on int",1738 cases.add("switch on int",
1711 \\int switch_fn(int i) {1739 \\void switch_fn(int i) {
1712 \\ int res = 0;1740 \\ int res = 0;
1713 \\ switch (i) {1741 \\ switch (i) {
1714 \\ case 0:1742 \\ case 0:
...@@ -1723,19 +1751,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1723,19 +1751,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1723 \\ }1751 \\ }
1724 \\}1752 \\}
1725 , &[_][]const u8{1753 , &[_][]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 {
1727 \\ var i = arg_i;1755 \\ var i = arg_i;
1728 \\ var res: c_int = 0;1756 \\ var res: c_int = 0;
1729 \\ __switch: {1757 \\ @"switch": {
1730 \\ __case_2: {1758 \\ case_2: {
1731 \\ __default: {1759 \\ default: {
1732 \\ __case_1: {1760 \\ case_1: {
1733 \\ __case_0: {1761 \\ case: {
1734 \\ switch (i) {1762 \\ switch (i) {
1735 \\ @as(c_int, 0) => break :__case_0,1763 \\ @as(c_int, 0) => break :case,
1736 \\ @as(c_int, 1)...@as(c_int, 3) => break :__case_1,1764 \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1,
1737 \\ else => break :__default,1765 \\ else => break :default,
1738 \\ @as(c_int, 4) => break :__case_2,1766 \\ @as(c_int, 4) => break :case_2,
1739 \\ }1767 \\ }
1740 \\ }1768 \\ }
1741 \\ res = 1;1769 \\ res = 1;
...@@ -1743,7 +1771,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1743,7 +1771,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1743 \\ res = 2;1771 \\ res = 2;
1744 \\ }1772 \\ }
1745 \\ res = (@as(c_int, 3) * i);1773 \\ res = (@as(c_int, 3) * i);
1746 \\ break :__switch;1774 \\ break :@"switch";
1747 \\ }1775 \\ }
1748 \\ res = 5;1776 \\ res = 5;
1749 \\ }1777 \\ }
...@@ -1783,13 +1811,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1783,13 +1811,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1783 });1811 });
17841812
1785 cases.add("assign",1813 cases.add("assign",
1786 \\int max(int a) {1814 \\void max(int a) {
1787 \\ int tmp;1815 \\ int tmp;
1788 \\ tmp = a;1816 \\ tmp = a;
1789 \\ a = tmp;1817 \\ a = tmp;
1790 \\}1818 \\}
1791 , &[_][]const u8{1819 , &[_][]const u8{
1792 \\pub export fn max(arg_a: c_int) c_int {1820 \\pub export fn max(arg_a: c_int) void {
1793 \\ var a = arg_a;1821 \\ var a = arg_a;
1794 \\ var tmp: c_int = undefined;1822 \\ var tmp: c_int = undefined;
1795 \\ tmp = a;1823 \\ tmp = a;
...@@ -2078,7 +2106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2078,7 +2106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2078 \\ int b;2106 \\ int b;
2079 \\}a;2107 \\}a;
2080 \\float b = 2.0f;2108 \\float b = 2.0f;
2081 \\int foo(void) {2109 \\void foo(void) {
2082 \\ struct Foo *c;2110 \\ struct Foo *c;
2083 \\ a.b;2111 \\ a.b;
2084 \\ c->b;2112 \\ c->b;
...@@ -2089,7 +2117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2089,7 +2117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2089 \\};2117 \\};
2090 \\pub extern var a: struct_Foo;2118 \\pub extern var a: struct_Foo;
2091 \\pub export var b: f32 = 2;2119 \\pub export var b: f32 = 2;
2092 \\pub export fn foo() c_int {2120 \\pub export fn foo() void {
2093 \\ var c: [*c]struct_Foo = undefined;2121 \\ var c: [*c]struct_Foo = undefined;
2094 \\ _ = a.b;2122 \\ _ = a.b;
2095 \\ _ = c.*.b;2123 \\ _ = c.*.b;
...@@ -2200,11 +2228,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2200,11 +2228,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2200 \\ if (a < b) return b;2228 \\ if (a < b) return b;
2201 \\ if (a < b) return b else return a;2229 \\ if (a < b) return b else return a;
2202 \\ if (a < b) {} else {}2230 \\ if (a < b) {} else {}
2231 \\ return 0;
2203 \\}2232 \\}
2204 });2233 });
22052234
2206 cases.add("if statements",2235 cases.add("if statements",
2207 \\int foo() {2236 \\void foo() {
2208 \\ if (2) {2237 \\ if (2) {
2209 \\ int a = 2;2238 \\ int a = 2;
2210 \\ }2239 \\ }
...@@ -2213,8 +2242,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2213,8 +2242,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2213 \\ }2242 \\ }
2214 \\}2243 \\}
2215 , &[_][]const u8{2244 , &[_][]const u8{
2216 \\pub export fn foo() c_int {2245 \\pub export fn foo() void {
2217 \\ if (@as(c_int, 2) != 0) {2246 \\ if (true) {
2218 \\ var a: c_int = 2;2247 \\ var a: c_int = 2;
2219 \\ }2248 \\ }
2220 \\ if ((blk: {2249 \\ if ((blk: {
...@@ -2748,8 +2777,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2748,8 +2777,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2748 \\}2777 \\}
2749 , &[_][]const u8{2778 , &[_][]const u8{
2750 \\pub fn foo() callconv(.C) void {2779 \\pub fn foo() callconv(.C) void {
2751 \\ if (@as(c_int, 1) != 0) while (true) {2780 \\ if (true) while (true) {
2752 \\ if (!(@as(c_int, 0) != 0)) break;2781 \\ if (!false) break;
2753 \\ };2782 \\ };
2754 \\}2783 \\}
2755 });2784 });
...@@ -2778,11 +2807,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2778,11 +2807,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2778 \\ var x = arg_x;2807 \\ var x = arg_x;
2779 \\ return blk: {2808 \\ return blk: {
2780 \\ const tmp = x;2809 \\ const tmp = x;
2781 \\ (blk: {2810 \\ (blk_1: {
2782 \\ const ref = &p;2811 \\ const ref = &p;
2783 \\ const tmp_1 = ref.*;2812 \\ const tmp_2 = ref.*;
2784 \\ ref.* += 1;2813 \\ ref.* += 1;
2785 \\ break :blk tmp_1;2814 \\ break :blk_1 tmp_2;
2786 \\ }).?.* = tmp;2815 \\ }).?.* = tmp;
2787 \\ break :blk tmp;2816 \\ break :blk tmp;
2788 \\ };2817 \\ };
...@@ -2807,12 +2836,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2807,12 +2836,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2807 });2836 });
28082837
2809 cases.add("arg name aliasing decl which comes after",2838 cases.add("arg name aliasing decl which comes after",
2810 \\int foo(int bar) {2839 \\void foo(int bar) {
2811 \\ bar = 2;2840 \\ bar = 2;
2812 \\}2841 \\}
2813 \\int bar = 4;2842 \\int bar = 4;
2814 , &[_][]const u8{2843 , &[_][]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 {
2816 \\ var bar_1 = arg_bar_1;2845 \\ var bar_1 = arg_bar_1;
2817 \\ bar_1 = 2;2846 \\ bar_1 = 2;
2818 \\}2847 \\}
...@@ -2820,12 +2849,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2820,12 +2849,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2820 });2849 });
28212850
2822 cases.add("arg name aliasing macro which comes after",2851 cases.add("arg name aliasing macro which comes after",
2823 \\int foo(int bar) {2852 \\void foo(int bar) {
2824 \\ bar = 2;2853 \\ bar = 2;
2825 \\}2854 \\}
2826 \\#define bar 42855 \\#define bar 4
2827 , &[_][]const u8{2856 , &[_][]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 {
2829 \\ var bar_1 = arg_bar_1;2858 \\ var bar_1 = arg_bar_1;
2830 \\ bar_1 = 2;2859 \\ bar_1 = 2;
2831 \\}2860 \\}
tools/process_headers.zig+11-10
...@@ -248,7 +248,7 @@ const Contents = struct {...@@ -248,7 +248,7 @@ const Contents = struct {
248};248};
249249
250const HashToContents = std.StringHashMap(Contents);250const 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);
252const PathTable = std.StringHashMap(*TargetToHash);252const PathTable = std.StringHashMap(*TargetToHash);
253253
254const LibCVendor = enum {254const LibCVendor = enum {
...@@ -339,7 +339,7 @@ pub fn main() !void {...@@ -339,7 +339,7 @@ pub fn main() !void {
339 try dir_stack.append(target_include_dir);339 try dir_stack.append(target_include_dir);
340340
341 while (dir_stack.popOrNull()) |full_dir_name| {341 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) {
343 error.FileNotFound => continue :search,343 error.FileNotFound => continue :search,
344 error.AccessDenied => continue :search,344 error.AccessDenied => continue :search,
345 else => return err,345 else => return err,
...@@ -354,7 +354,8 @@ pub fn main() !void {...@@ -354,7 +354,8 @@ pub fn main() !void {
354 .Directory => try dir_stack.append(full_path),354 .Directory => try dir_stack.append(full_path),
355 .File => {355 .File => {
356 const rel_path = try std.fs.path.relative(allocator, target_include_dir, full_path);356 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);
358 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");359 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
359 total_bytes += raw_bytes.len;360 total_bytes += raw_bytes.len;
360 const hash = try allocator.alloc(u8, 32);361 const hash = try allocator.alloc(u8, 32);
...@@ -365,14 +366,14 @@ pub fn main() !void {...@@ -365,14 +366,14 @@ pub fn main() !void {
365 const gop = try hash_to_contents.getOrPut(hash);366 const gop = try hash_to_contents.getOrPut(hash);
366 if (gop.found_existing) {367 if (gop.found_existing) {
367 max_bytes_saved += raw_bytes.len;368 max_bytes_saved += raw_bytes.len;
368 gop.kv.value.hit_count += 1;369 gop.entry.value.hit_count += 1;
369 std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{370 std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{
370 libc_target.name,371 libc_target.name,
371 rel_path,372 rel_path,
372 raw_bytes.len,373 raw_bytes.len,
373 });374 });
374 } else {375 } else {
375 gop.kv.value = Contents{376 gop.entry.value = Contents{
376 .bytes = trimmed,377 .bytes = trimmed,
377 .hit_count = 1,378 .hit_count = 1,
378 .hash = hash,379 .hash = hash,
...@@ -380,13 +381,13 @@ pub fn main() !void {...@@ -380,13 +381,13 @@ pub fn main() !void {
380 };381 };
381 }382 }
382 const path_gop = try path_table.getOrPut(rel_path);383 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: {
384 const ptr = try allocator.create(TargetToHash);385 const ptr = try allocator.create(TargetToHash);
385 ptr.* = TargetToHash.init(allocator);386 ptr.* = TargetToHash.init(allocator);
386 path_gop.kv.value = ptr;387 path_gop.entry.value = ptr;
387 break :blk ptr;388 break :blk ptr;
388 };389 };
389 assert((try target_to_hash.put(dest_target, hash)) == null);390 try target_to_hash.putNoClobber(dest_target, hash);
390 },391 },
391 else => std.debug.warn("warning: weird file: {}\n", .{full_path}),392 else => std.debug.warn("warning: weird file: {}\n", .{full_path}),
392 }393 }
...@@ -410,7 +411,7 @@ pub fn main() !void {...@@ -410,7 +411,7 @@ pub fn main() !void {
410 {411 {
411 var hash_it = path_kv.value.iterator();412 var hash_it = path_kv.value.iterator();
412 while (hash_it.next()) |hash_kv| {413 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).?;
414 try contents_list.append(contents);415 try contents_list.append(contents);
415 }416 }
416 }417 }
...@@ -432,7 +433,7 @@ pub fn main() !void {...@@ -432,7 +433,7 @@ pub fn main() !void {
432 }433 }
433 var hash_it = path_kv.value.iterator();434 var hash_it = path_kv.value.iterator();
434 while (hash_it.next()) |hash_kv| {435 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).?;
436 if (contents.is_generic) continue;437 if (contents.is_generic) continue;
437438
438 const dest_target = hash_kv.key;439 const dest_target = hash_kv.key;