authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 17:09:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 17:09:40-07:00
logc6e0df6213af1ecf5734bc6bcb4d7c29a4b41351
tree42d358704ab86a506d1c53c0fc6da73738560fa7
parent41a8b6f57b3bd50b2ed6fdced74fba9130eac3d3
parentd61a9e37ae8f140407d1369500d21efbe2b198ab

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


69 files changed, 6721 insertions(+), 3221 deletions(-)

build.zig+9
...@@ -77,6 +77,9 @@ pub fn build(b: *Builder) !void {...@@ -77,6 +77,9 @@ pub fn build(b: *Builder) !void {
77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
78 if (link_libc) exe.linkLibC();78 if (link_libc) exe.linkLibC();
7979
80 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
81
82 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
80 exe.addBuildOption(bool, "enable_tracy", tracy != null);83 exe.addBuildOption(bool, "enable_tracy", tracy != null);
81 if (tracy) |tracy_path| {84 if (tracy) |tracy_path| {
82 const client_cpp = fs.path.join(85 const client_cpp = fs.path.join(
...@@ -104,6 +107,12 @@ pub fn build(b: *Builder) !void {...@@ -104,6 +107,12 @@ pub fn build(b: *Builder) !void {
104 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;107 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
105 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");108 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
106109
110
111 test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
112 test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
113 test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
114 test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
115
107 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");116 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
108 test_stage2_step.dependOn(&test_stage2.step);117 test_stage2_step.dependOn(&test_stage2.step);
109 test_step.dependOn(test_stage2_step);118 test_step.dependOn(test_stage2_step);
ci/azure/pipelines.yml+1-1
...@@ -41,7 +41,7 @@ jobs:...@@ -41,7 +41,7 @@ jobs:
4141
42 steps:42 steps:
43 - powershell: |43 - powershell: |
44 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-06-02/msys2-base-x86_64-20200602.sfx.exe", "sfx.exe")44 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-07-20/msys2-base-x86_64-20200720.sfx.exe", "sfx.exe")
45 .\sfx.exe -y -o\45 .\sfx.exe -y -o\
46 del sfx.exe46 del sfx.exe
47 displayName: Download/Extract/Install MSYS247 displayName: Download/Extract/Install MSYS2
doc/langref.html.in+2-1
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
2<html lang="en">2<html lang="en">
3 <head>3 <head>
4 <meta charset="utf-8">4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 <title>Documentation - The Zig Programming Language</title>6 <title>Documentation - The Zig Programming Language</title>
6 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/>7 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/>
7 <style>8 <style>
...@@ -7473,7 +7474,7 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -7473,7 +7474,7 @@ export fn @"A function name that is a complete sentence."() void {}
7473 <p>7474 <p>
7474 When looking at the resulting object, you can see the symbol is used verbatim:7475 When looking at the resulting object, you can see the symbol is used verbatim:
7475 </p>7476 </p>
7476 <pre>00000000000001f0 T A function name that is a complete sentence.</pre>7477 <pre><code>00000000000001f0 T A function name that is a complete sentence.</code></pre>
7477 {#see_also|Exporting a C Library#}7478 {#see_also|Exporting a C Library#}
7478 {#header_close#}7479 {#header_close#}
74797480
lib/std/array_list.zig+86
...@@ -108,6 +108,33 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -108,6 +108,33 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
108 mem.copy(T, self.items[i .. i + items.len], items);108 mem.copy(T, self.items[i .. i + items.len], items);
109 }109 }
110110
111 /// Replace range of elements `list[start..start+len]` with `new_items`
112 /// grows list if `len < new_items.len`. may allocate
113 /// shrinks list if `len > new_items.len`
114 pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
115 const after_range = start + len;
116 const range = self.items[start..after_range];
117
118 if (range.len == new_items.len)
119 mem.copy(T, range, new_items)
120 else if (range.len < new_items.len) {
121 const first = new_items[0..range.len];
122 const rest = new_items[range.len..];
123
124 mem.copy(T, range, first);
125 try self.insertSlice(after_range, rest);
126 } else {
127 mem.copy(T, range, new_items);
128 const after_subrange = start + new_items.len;
129
130 for (self.items[after_range..]) |item, i| {
131 self.items[after_subrange..][i] = item;
132 }
133
134 self.items.len -= len - new_items.len;
135 }
136 }
137
111 /// Extend the list by 1 element. Allocates more memory as necessary.138 /// Extend the list by 1 element. Allocates more memory as necessary.
112 pub fn append(self: *Self, item: T) !void {139 pub fn append(self: *Self, item: T) !void {
113 const new_item_ptr = try self.addOne();140 const new_item_ptr = try self.addOne();
...@@ -189,6 +216,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -189,6 +216,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
189 mem.set(T, self.items[old_len..self.items.len], value);216 mem.set(T, self.items[old_len..self.items.len], value);
190 }217 }
191218
219 /// Append a value to the list `n` times.
220 /// Asserts the capacity is enough.
221 pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
222 const new_len = self.items.len + n;
223 assert(new_len <= self.capacity);
224 mem.set(T, self.items.ptr[self.items.len..new_len], value);
225 self.items.len = new_len;
226 }
227
192 /// Adjust the list's length to `new_len`.228 /// Adjust the list's length to `new_len`.
193 /// Does not initialize added items if any.229 /// Does not initialize added items if any.
194 pub fn resize(self: *Self, new_len: usize) !void {230 pub fn resize(self: *Self, new_len: usize) !void {
...@@ -366,6 +402,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -366,6 +402,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
366 mem.copy(T, self.items[i .. i + items.len], items);402 mem.copy(T, self.items[i .. i + items.len], items);
367 }403 }
368404
405 /// Replace range of elements `list[start..start+len]` with `new_items`
406 /// grows list if `len < new_items.len`. may allocate
407 /// shrinks list if `len > new_items.len`
408 pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
409 var managed = self.toManaged(allocator);
410 try managed.replaceRange(start, len, new_items);
411 self.* = managed.toUnmanaged();
412 }
413
369 /// Extend the list by 1 element. Allocates more memory as necessary.414 /// Extend the list by 1 element. Allocates more memory as necessary.
370 pub fn append(self: *Self, allocator: *Allocator, item: T) !void {415 pub fn append(self: *Self, allocator: *Allocator, item: T) !void {
371 const new_item_ptr = try self.addOne(allocator);416 const new_item_ptr = try self.addOne(allocator);
...@@ -437,6 +482,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -437,6 +482,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
437 mem.set(T, self.items[old_len..self.items.len], value);482 mem.set(T, self.items[old_len..self.items.len], value);
438 }483 }
439484
485 /// Append a value to the list `n` times.
486 /// Asserts the capacity is enough.
487 pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
488 const new_len = self.items.len + n;
489 assert(new_len <= self.capacity);
490 mem.set(T, self.items.ptr[self.items.len..new_len], value);
491 self.items.len = new_len;
492 }
493
440 /// Adjust the list's length to `new_len`.494 /// Adjust the list's length to `new_len`.
441 /// Does not initialize added items if any.495 /// Does not initialize added items if any.
442 pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {496 pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
...@@ -714,6 +768,38 @@ test "std.ArrayList.insertSlice" {...@@ -714,6 +768,38 @@ test "std.ArrayList.insertSlice" {
714 testing.expect(list.items[0] == 1);768 testing.expect(list.items[0] == 1);
715}769}
716770
771test "std.ArrayList.replaceRange" {
772 var arena = std.heap.ArenaAllocator.init(testing.allocator);
773 defer arena.deinit();
774
775 const alloc = &arena.allocator;
776 const init = [_]i32{ 1, 2, 3, 4, 5 };
777 const new = [_]i32{ 0, 0, 0 };
778
779 var list_zero = ArrayList(i32).init(alloc);
780 var list_eq = ArrayList(i32).init(alloc);
781 var list_lt = ArrayList(i32).init(alloc);
782 var list_gt = ArrayList(i32).init(alloc);
783
784 try list_zero.appendSlice(&init);
785 try list_eq.appendSlice(&init);
786 try list_lt.appendSlice(&init);
787 try list_gt.appendSlice(&init);
788
789 try list_zero.replaceRange(1, 0, &new);
790 try list_eq.replaceRange(1, 3, &new);
791 try list_lt.replaceRange(1, 2, &new);
792
793 // after_range > new_items.len in function body
794 testing.expect(1 + 4 > new.len);
795 try list_gt.replaceRange(1, 4, &new);
796
797 testing.expectEqualSlices(i32, list_zero.items, &[_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 });
798 testing.expectEqualSlices(i32, list_eq.items, &[_]i32{ 1, 0, 0, 0, 5 });
799 testing.expectEqualSlices(i32, list_lt.items, &[_]i32{ 1, 0, 0, 0, 4, 5 });
800 testing.expectEqualSlices(i32, list_gt.items, &[_]i32{ 1, 0, 0, 0 });
801}
802
717const Item = struct {803const Item = struct {
718 integer: i32,804 integer: i32,
719 sub_items: ArrayList(Item),805 sub_items: ArrayList(Item),
lib/std/build.zig+41-24
...@@ -430,9 +430,9 @@ pub const Builder = struct {...@@ -430,9 +430,9 @@ pub const Builder = struct {
430 const entry = self.user_input_options.getEntry(name) orelse return null;430 const entry = self.user_input_options.getEntry(name) orelse return null;
431 entry.value.used = true;431 entry.value.used = true;
432 switch (type_id) {432 switch (type_id) {
433 TypeId.Bool => switch (entry.value.value) {433 .Bool => switch (entry.value.value) {
434 UserValue.Flag => return true,434 .Flag => return true,
435 UserValue.Scalar => |s| {435 .Scalar => |s| {
436 if (mem.eql(u8, s, "true")) {436 if (mem.eql(u8, s, "true")) {
437 return true;437 return true;
438 } else if (mem.eql(u8, s, "false")) {438 } else if (mem.eql(u8, s, "false")) {
...@@ -443,21 +443,21 @@ pub const Builder = struct {...@@ -443,21 +443,21 @@ pub const Builder = struct {
443 return null;443 return null;
444 }444 }
445 },445 },
446 UserValue.List => {446 .List => {
447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
448 self.markInvalidUserInput();448 self.markInvalidUserInput();
449 return null;449 return null;
450 },450 },
451 },451 },
452 TypeId.Int => panic("TODO integer options to build script", .{}),452 .Int => panic("TODO integer options to build script", .{}),
453 TypeId.Float => panic("TODO float options to build script", .{}),453 .Float => panic("TODO float options to build script", .{}),
454 TypeId.Enum => switch (entry.value.value) {454 .Enum => switch (entry.value.value) {
455 UserValue.Flag => {455 .Flag => {
456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
457 self.markInvalidUserInput();457 self.markInvalidUserInput();
458 return null;458 return null;
459 },459 },
460 UserValue.Scalar => |s| {460 .Scalar => |s| {
461 if (std.meta.stringToEnum(T, s)) |enum_lit| {461 if (std.meta.stringToEnum(T, s)) |enum_lit| {
462 return enum_lit;462 return enum_lit;
463 } else {463 } else {
...@@ -466,33 +466,35 @@ pub const Builder = struct {...@@ -466,33 +466,35 @@ pub const Builder = struct {
466 return null;466 return null;
467 }467 }
468 },468 },
469 UserValue.List => {469 .List => {
470 warn("Expected -D{} to be a string, but received a list.\n", .{name});470 warn("Expected -D{} to be a string, but received a list.\n", .{name});
471 self.markInvalidUserInput();471 self.markInvalidUserInput();
472 return null;472 return null;
473 },473 },
474 },474 },
475 TypeId.String => switch (entry.value.value) {475 .String => switch (entry.value.value) {
476 UserValue.Flag => {476 .Flag => {
477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
478 self.markInvalidUserInput();478 self.markInvalidUserInput();
479 return null;479 return null;
480 },480 },
481 UserValue.List => {481 .List => {
482 warn("Expected -D{} to be a string, but received a list.\n", .{name});482 warn("Expected -D{} to be a string, but received a list.\n", .{name});
483 self.markInvalidUserInput();483 self.markInvalidUserInput();
484 return null;484 return null;
485 },485 },
486 UserValue.Scalar => |s| return s,486 .Scalar => |s| return s,
487 },487 },
488 TypeId.List => switch (entry.value.value) {488 .List => switch (entry.value.value) {
489 UserValue.Flag => {489 .Flag => {
490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
491 self.markInvalidUserInput();491 self.markInvalidUserInput();
492 return null;492 return null;
493 },493 },
494 UserValue.Scalar => |s| return &[_][]const u8{s},494 .Scalar => |s| {
495 UserValue.List => |lst| return lst.span(),495 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
496 },
497 .List => |lst| return lst.span(),
496 },498 },
497 }499 }
498 }500 }
...@@ -1151,6 +1153,7 @@ pub const LibExeObjStep = struct {...@@ -1151,6 +1153,7 @@ pub const LibExeObjStep = struct {
1151 bundle_compiler_rt: bool,1153 bundle_compiler_rt: bool,
1152 disable_stack_probing: bool,1154 disable_stack_probing: bool,
1153 disable_sanitize_c: bool,1155 disable_sanitize_c: bool,
1156 rdynamic: bool,
1154 c_std: Builder.CStd,1157 c_std: Builder.CStd,
1155 override_lib_dir: ?[]const u8,1158 override_lib_dir: ?[]const u8,
1156 main_pkg_path: ?[]const u8,1159 main_pkg_path: ?[]const u8,
...@@ -1311,6 +1314,7 @@ pub const LibExeObjStep = struct {...@@ -1311,6 +1314,7 @@ pub const LibExeObjStep = struct {
1311 .bundle_compiler_rt = false,1314 .bundle_compiler_rt = false,
1312 .disable_stack_probing = false,1315 .disable_stack_probing = false,
1313 .disable_sanitize_c = false,1316 .disable_sanitize_c = false,
1317 .rdynamic = false,
1314 .output_dir = null,1318 .output_dir = null,
1315 .single_threaded = false,1319 .single_threaded = false,
1316 .installed_path = null,1320 .installed_path = null,
...@@ -1704,13 +1708,23 @@ pub const LibExeObjStep = struct {...@@ -1704,13 +1708,23 @@ pub const LibExeObjStep = struct {
17041708
1705 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {1709 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
1706 const out = self.build_options_contents.outStream();1710 const out = self.build_options_contents.outStream();
1711 if (T == []const []const u8) {
1712 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable;
1713 for (value) |slice| {
1714 out.writeAll(" ") catch unreachable;
1715 std.zig.renderStringLiteral(slice, out) catch unreachable;
1716 out.writeAll(",\n") catch unreachable;
1717 }
1718 out.writeAll("};\n") catch unreachable;
1719 return;
1720 }
1707 switch (@typeInfo(T)) {1721 switch (@typeInfo(T)) {
1708 .Enum => |enum_info| {1722 .Enum => |enum_info| {
1709 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;1723 out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
1710 inline for (enum_info.fields) |field| {1724 inline for (enum_info.fields) |field| {
1711 out.print(" {},\n", .{field.name}) catch unreachable;1725 out.print(" {},\n", .{field.name}) catch unreachable;
1712 }1726 }
1713 out.print("}};\n", .{}) catch unreachable;1727 out.writeAll("};\n") catch unreachable;
1714 },1728 },
1715 else => {},1729 else => {},
1716 }1730 }
...@@ -1843,10 +1857,10 @@ pub const LibExeObjStep = struct {...@@ -1843,10 +1857,10 @@ pub const LibExeObjStep = struct {
1843 zig_args.append(builder.zig_exe) catch unreachable;1857 zig_args.append(builder.zig_exe) catch unreachable;
18441858
1845 const cmd = switch (self.kind) {1859 const cmd = switch (self.kind) {
1846 Kind.Lib => "build-lib",1860 .Lib => "build-lib",
1847 Kind.Exe => "build-exe",1861 .Exe => "build-exe",
1848 Kind.Obj => "build-obj",1862 .Obj => "build-obj",
1849 Kind.Test => "test",1863 .Test => "test",
1850 };1864 };
1851 zig_args.append(cmd) catch unreachable;1865 zig_args.append(cmd) catch unreachable;
18521866
...@@ -1994,6 +2008,9 @@ pub const LibExeObjStep = struct {...@@ -1994,6 +2008,9 @@ pub const LibExeObjStep = struct {
1994 if (self.disable_sanitize_c) {2008 if (self.disable_sanitize_c) {
1995 try zig_args.append("-fno-sanitize-c");2009 try zig_args.append("-fno-sanitize-c");
1996 }2010 }
2011 if (self.rdynamic) {
2012 try zig_args.append("-rdynamic");
2013 }
19972014
1998 if (self.code_model != .default) {2015 if (self.code_model != .default) {
1999 try zig_args.append("-code-model");2016 try zig_args.append("-code-model");
lib/std/build/emit_raw.zig+5-3
...@@ -46,9 +46,10 @@ const BinaryElfOutput = struct {...@@ -46,9 +46,10 @@ const BinaryElfOutput = struct {
46 .segments = ArrayList(*BinaryElfSegment).init(allocator),46 .segments = ArrayList(*BinaryElfSegment).init(allocator),
47 .sections = ArrayList(*BinaryElfSection).init(allocator),47 .sections = ArrayList(*BinaryElfSection).init(allocator),
48 };48 };
49 const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file);49 const elf_hdr = try std.elf.readHeader(elf_file);
5050
51 for (elf_hdrs.section_headers) |section, i| {51 var section_headers = elf_hdr.section_header_iterator(elf_file);
52 while (try section_headers.next()) |section| {
52 if (sectionValidForOutput(section)) {53 if (sectionValidForOutput(section)) {
53 const newSection = try allocator.create(BinaryElfSection);54 const newSection = try allocator.create(BinaryElfSection);
5455
...@@ -61,7 +62,8 @@ const BinaryElfOutput = struct {...@@ -61,7 +62,8 @@ const BinaryElfOutput = struct {
61 }62 }
62 }63 }
6364
64 for (elf_hdrs.program_headers) |phdr, i| {65 var program_headers = elf_hdr.program_header_iterator(elf_file);
66 while (try program_headers.next()) |phdr| {
65 if (phdr.p_type == elf.PT_LOAD) {67 if (phdr.p_type == elf.PT_LOAD) {
66 const newSegment = try allocator.create(BinaryElfSegment);68 const newSegment = try allocator.create(BinaryElfSegment);
6769
lib/std/build/run.zig+18
...@@ -4,6 +4,7 @@ const build = std.build;...@@ -4,6 +4,7 @@ const build = std.build;
4const Step = build.Step;4const Step = build.Step;
5const Builder = build.Builder;5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;6const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
7const fs = std.fs;8const fs = std.fs;
8const mem = std.mem;9const mem = std.mem;
9const process = std.process;10const process = std.process;
...@@ -42,6 +43,10 @@ pub const RunStep = struct {...@@ -42,6 +43,10 @@ pub const RunStep = struct {
4243
43 pub const Arg = union(enum) {44 pub const Arg = union(enum) {
44 Artifact: *LibExeObjStep,45 Artifact: *LibExeObjStep,
46 WriteFile: struct {
47 step: *WriteFileStep,
48 file_name: []const u8,
49 },
45 Bytes: []u8,50 Bytes: []u8,
46 };51 };
4752
...@@ -62,6 +67,16 @@ pub const RunStep = struct {...@@ -62,6 +67,16 @@ pub const RunStep = struct {
62 self.step.dependOn(&artifact.step);67 self.step.dependOn(&artifact.step);
63 }68 }
6469
70 pub fn addWriteFileArg(self: *RunStep, write_file: *WriteFileStep, file_name: []const u8) void {
71 self.argv.append(Arg{
72 .WriteFile = .{
73 .step = write_file,
74 .file_name = file_name,
75 },
76 }) catch unreachable;
77 self.step.dependOn(&write_file.step);
78 }
79
65 pub fn addArg(self: *RunStep, arg: []const u8) void {80 pub fn addArg(self: *RunStep, arg: []const u8) void {
66 self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable;81 self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable;
67 }82 }
...@@ -142,6 +157,9 @@ pub const RunStep = struct {...@@ -142,6 +157,9 @@ pub const RunStep = struct {
142 for (self.argv.span()) |arg| {157 for (self.argv.span()) |arg| {
143 switch (arg) {158 switch (arg) {
144 Arg.Bytes => |bytes| try argv_list.append(bytes),159 Arg.Bytes => |bytes| try argv_list.append(bytes),
160 Arg.WriteFile => |file| {
161 try argv_list.append(file.step.getOutputPath(file.file_name));
162 },
145 Arg.Artifact => |artifact| {163 Arg.Artifact => |artifact| {
146 if (artifact.target.isWindows()) {164 if (artifact.target.isWindows()) {
147 // On Windows we don't have rpaths so we have to add .dll search paths to PATH165 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
lib/std/c.zig+4
...@@ -8,6 +8,10 @@ pub const Tokenizer = tokenizer.Tokenizer;...@@ -8,6 +8,10 @@ pub const Tokenizer = tokenizer.Tokenizer;
8pub const parse = @import("c/parse.zig").parse;8pub const parse = @import("c/parse.zig").parse;
9pub const ast = @import("c/ast.zig");9pub const ast = @import("c/ast.zig");
1010
11test "" {
12 _ = tokenizer;
13}
14
11pub usingnamespace @import("os/bits.zig");15pub usingnamespace @import("os/bits.zig");
1216
13pub usingnamespace switch (std.Target.current.os.tag) {17pub usingnamespace switch (std.Target.current.os.tag) {
lib/std/c/tokenizer.zig+76-115
...@@ -1,19 +1,10 @@...@@ -1,19 +1,10 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
33
4pub const Source = struct {
5 buffer: []const u8,
6 file_name: []const u8,
7 tokens: TokenList,
8
9 pub const TokenList = std.SegmentedList(Token, 64);
10};
11
12pub const Token = struct {4pub const Token = struct {
13 id: Id,5 id: Id,
14 start: usize,6 start: usize,
15 end: usize,7 end: usize,
16 source: *Source,
178
18 pub const Id = union(enum) {9 pub const Id = union(enum) {
19 Invalid,10 Invalid,
...@@ -251,31 +242,6 @@ pub const Token = struct {...@@ -251,31 +242,6 @@ pub const Token = struct {
251 }242 }
252 };243 };
253244
254 pub fn eql(a: Token, b: Token) bool {
255 // do we really need this cast here
256 if (@as(@TagType(Id), a.id) != b.id) return false;
257 return mem.eql(u8, a.slice(), b.slice());
258 }
259
260 pub fn slice(tok: Token) []const u8 {
261 return tok.source.buffer[tok.start..tok.end];
262 }
263
264 pub const Keyword = struct {
265 bytes: []const u8,
266 id: Id,
267 hash: u32,
268
269 fn init(bytes: []const u8, id: Id) Keyword {
270 @setEvalBranchQuota(2000);
271 return .{
272 .bytes = bytes,
273 .id = id,
274 .hash = std.hash_map.hashString(bytes),
275 };
276 }
277 };
278
279 // TODO extensions245 // TODO extensions
280 pub const keywords = std.ComptimeStringMap(Id, .{246 pub const keywords = std.ComptimeStringMap(Id, .{
281 .{ "auto", .Keyword_auto },247 .{ "auto", .Keyword_auto },
...@@ -355,26 +321,26 @@ pub const Token = struct {...@@ -355,26 +321,26 @@ pub const Token = struct {
355 }321 }
356322
357 pub const NumSuffix = enum {323 pub const NumSuffix = enum {
358 None,324 none,
359 F,325 f,
360 L,326 l,
361 U,327 u,
362 LU,328 lu,
363 LL,329 ll,
364 LLU,330 llu,
365 };331 };
366332
367 pub const StrKind = enum {333 pub const StrKind = enum {
368 None,334 none,
369 Wide,335 wide,
370 Utf8,336 utf_8,
371 Utf16,337 utf_16,
372 Utf32,338 utf_32,
373 };339 };
374};340};
375341
376pub const Tokenizer = struct {342pub const Tokenizer = struct {
377 source: *Source,343 buffer: []const u8,
378 index: usize = 0,344 index: usize = 0,
379 prev_tok_id: @TagType(Token.Id) = .Invalid,345 prev_tok_id: @TagType(Token.Id) = .Invalid,
380 pp_directive: bool = false,346 pp_directive: bool = false,
...@@ -385,7 +351,6 @@ pub const Tokenizer = struct {...@@ -385,7 +351,6 @@ pub const Tokenizer = struct {
385 .id = .Eof,351 .id = .Eof,
386 .start = self.index,352 .start = self.index,
387 .end = undefined,353 .end = undefined,
388 .source = self.source,
389 };354 };
390 var state: enum {355 var state: enum {
391 Start,356 Start,
...@@ -446,8 +411,8 @@ pub const Tokenizer = struct {...@@ -446,8 +411,8 @@ pub const Tokenizer = struct {
446 } = .Start;411 } = .Start;
447 var string = false;412 var string = false;
448 var counter: u32 = 0;413 var counter: u32 = 0;
449 while (self.index < self.source.buffer.len) : (self.index += 1) {414 while (self.index < self.buffer.len) : (self.index += 1) {
450 const c = self.source.buffer[self.index];415 const c = self.buffer[self.index];
451 switch (state) {416 switch (state) {
452 .Start => switch (c) {417 .Start => switch (c) {
453 '\n' => {418 '\n' => {
...@@ -460,11 +425,11 @@ pub const Tokenizer = struct {...@@ -460,11 +425,11 @@ pub const Tokenizer = struct {
460 state = .Cr;425 state = .Cr;
461 },426 },
462 '"' => {427 '"' => {
463 result.id = .{ .StringLiteral = .None };428 result.id = .{ .StringLiteral = .none };
464 state = .StringLiteral;429 state = .StringLiteral;
465 },430 },
466 '\'' => {431 '\'' => {
467 result.id = .{ .CharLiteral = .None };432 result.id = .{ .CharLiteral = .none };
468 state = .CharLiteralStart;433 state = .CharLiteralStart;
469 },434 },
470 'u' => {435 'u' => {
...@@ -641,11 +606,11 @@ pub const Tokenizer = struct {...@@ -641,11 +606,11 @@ pub const Tokenizer = struct {
641 state = .u8;606 state = .u8;
642 },607 },
643 '\'' => {608 '\'' => {
644 result.id = .{ .CharLiteral = .Utf16 };609 result.id = .{ .CharLiteral = .utf_16 };
645 state = .CharLiteralStart;610 state = .CharLiteralStart;
646 },611 },
647 '\"' => {612 '\"' => {
648 result.id = .{ .StringLiteral = .Utf16 };613 result.id = .{ .StringLiteral = .utf_16 };
649 state = .StringLiteral;614 state = .StringLiteral;
650 },615 },
651 else => {616 else => {
...@@ -655,7 +620,7 @@ pub const Tokenizer = struct {...@@ -655,7 +620,7 @@ pub const Tokenizer = struct {
655 },620 },
656 .u8 => switch (c) {621 .u8 => switch (c) {
657 '\"' => {622 '\"' => {
658 result.id = .{ .StringLiteral = .Utf8 };623 result.id = .{ .StringLiteral = .utf_8 };
659 state = .StringLiteral;624 state = .StringLiteral;
660 },625 },
661 else => {626 else => {
...@@ -665,11 +630,11 @@ pub const Tokenizer = struct {...@@ -665,11 +630,11 @@ pub const Tokenizer = struct {
665 },630 },
666 .U => switch (c) {631 .U => switch (c) {
667 '\'' => {632 '\'' => {
668 result.id = .{ .CharLiteral = .Utf32 };633 result.id = .{ .CharLiteral = .utf_32 };
669 state = .CharLiteralStart;634 state = .CharLiteralStart;
670 },635 },
671 '\"' => {636 '\"' => {
672 result.id = .{ .StringLiteral = .Utf32 };637 result.id = .{ .StringLiteral = .utf_32 };
673 state = .StringLiteral;638 state = .StringLiteral;
674 },639 },
675 else => {640 else => {
...@@ -679,11 +644,11 @@ pub const Tokenizer = struct {...@@ -679,11 +644,11 @@ pub const Tokenizer = struct {
679 },644 },
680 .L => switch (c) {645 .L => switch (c) {
681 '\'' => {646 '\'' => {
682 result.id = .{ .CharLiteral = .Wide };647 result.id = .{ .CharLiteral = .wide };
683 state = .CharLiteralStart;648 state = .CharLiteralStart;
684 },649 },
685 '\"' => {650 '\"' => {
686 result.id = .{ .StringLiteral = .Wide };651 result.id = .{ .StringLiteral = .wide };
687 state = .StringLiteral;652 state = .StringLiteral;
688 },653 },
689 else => {654 else => {
...@@ -808,7 +773,7 @@ pub const Tokenizer = struct {...@@ -808,7 +773,7 @@ pub const Tokenizer = struct {
808 .Identifier => switch (c) {773 .Identifier => switch (c) {
809 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},774 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
810 else => {775 else => {
811 result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;776 result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
812 if (self.prev_tok_id == .Hash)777 if (self.prev_tok_id == .Hash)
813 self.pp_directive = true;778 self.pp_directive = true;
814 break;779 break;
...@@ -1137,7 +1102,7 @@ pub const Tokenizer = struct {...@@ -1137,7 +1102,7 @@ pub const Tokenizer = struct {
1137 state = .IntegerSuffixL;1102 state = .IntegerSuffixL;
1138 },1103 },
1139 else => {1104 else => {
1140 result.id = .{ .IntegerLiteral = .None };1105 result.id = .{ .IntegerLiteral = .none };
1141 break;1106 break;
1142 },1107 },
1143 },1108 },
...@@ -1146,7 +1111,7 @@ pub const Tokenizer = struct {...@@ -1146,7 +1111,7 @@ pub const Tokenizer = struct {
1146 state = .IntegerSuffixUL;1111 state = .IntegerSuffixUL;
1147 },1112 },
1148 else => {1113 else => {
1149 result.id = .{ .IntegerLiteral = .U };1114 result.id = .{ .IntegerLiteral = .u };
1150 break;1115 break;
1151 },1116 },
1152 },1117 },
...@@ -1155,34 +1120,34 @@ pub const Tokenizer = struct {...@@ -1155,34 +1120,34 @@ pub const Tokenizer = struct {
1155 state = .IntegerSuffixLL;1120 state = .IntegerSuffixLL;
1156 },1121 },
1157 'u', 'U' => {1122 'u', 'U' => {
1158 result.id = .{ .IntegerLiteral = .LU };1123 result.id = .{ .IntegerLiteral = .lu };
1159 self.index += 1;1124 self.index += 1;
1160 break;1125 break;
1161 },1126 },
1162 else => {1127 else => {
1163 result.id = .{ .IntegerLiteral = .L };1128 result.id = .{ .IntegerLiteral = .l };
1164 break;1129 break;
1165 },1130 },
1166 },1131 },
1167 .IntegerSuffixLL => switch (c) {1132 .IntegerSuffixLL => switch (c) {
1168 'u', 'U' => {1133 'u', 'U' => {
1169 result.id = .{ .IntegerLiteral = .LLU };1134 result.id = .{ .IntegerLiteral = .llu };
1170 self.index += 1;1135 self.index += 1;
1171 break;1136 break;
1172 },1137 },
1173 else => {1138 else => {
1174 result.id = .{ .IntegerLiteral = .LL };1139 result.id = .{ .IntegerLiteral = .ll };
1175 break;1140 break;
1176 },1141 },
1177 },1142 },
1178 .IntegerSuffixUL => switch (c) {1143 .IntegerSuffixUL => switch (c) {
1179 'l', 'L' => {1144 'l', 'L' => {
1180 result.id = .{ .IntegerLiteral = .LLU };1145 result.id = .{ .IntegerLiteral = .llu };
1181 self.index += 1;1146 self.index += 1;
1182 break;1147 break;
1183 },1148 },
1184 else => {1149 else => {
1185 result.id = .{ .IntegerLiteral = .LU };1150 result.id = .{ .IntegerLiteral = .lu };
1186 break;1151 break;
1187 },1152 },
1188 },1153 },
...@@ -1230,26 +1195,26 @@ pub const Tokenizer = struct {...@@ -1230,26 +1195,26 @@ pub const Tokenizer = struct {
1230 },1195 },
1231 .FloatSuffix => switch (c) {1196 .FloatSuffix => switch (c) {
1232 'l', 'L' => {1197 'l', 'L' => {
1233 result.id = .{ .FloatLiteral = .L };1198 result.id = .{ .FloatLiteral = .l };
1234 self.index += 1;1199 self.index += 1;
1235 break;1200 break;
1236 },1201 },
1237 'f', 'F' => {1202 'f', 'F' => {
1238 result.id = .{ .FloatLiteral = .F };1203 result.id = .{ .FloatLiteral = .f };
1239 self.index += 1;1204 self.index += 1;
1240 break;1205 break;
1241 },1206 },
1242 else => {1207 else => {
1243 result.id = .{ .FloatLiteral = .None };1208 result.id = .{ .FloatLiteral = .none };
1244 break;1209 break;
1245 },1210 },
1246 },1211 },
1247 }1212 }
1248 } else if (self.index == self.source.buffer.len) {1213 } else if (self.index == self.buffer.len) {
1249 switch (state) {1214 switch (state) {
1250 .Start => {},1215 .Start => {},
1251 .u, .u8, .U, .L, .Identifier => {1216 .u, .u8, .U, .L, .Identifier => {
1252 result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;1217 result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
1253 },1218 },
12541219
1255 .Cr,1220 .Cr,
...@@ -1270,11 +1235,11 @@ pub const Tokenizer = struct {...@@ -1270,11 +1235,11 @@ pub const Tokenizer = struct {
1270 .MacroString,1235 .MacroString,
1271 => result.id = .Invalid,1236 => result.id = .Invalid,
12721237
1273 .FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .None },1238 .FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none },
12741239
1275 .FloatFraction,1240 .FloatFraction,
1276 .FloatFractionHex,1241 .FloatFractionHex,
1277 => result.id = .{ .FloatLiteral = .None },1242 => result.id = .{ .FloatLiteral = .none },
12781243
1279 .IntegerLiteralOct,1244 .IntegerLiteralOct,
1280 .IntegerLiteralBinary,1245 .IntegerLiteralBinary,
...@@ -1282,13 +1247,13 @@ pub const Tokenizer = struct {...@@ -1282,13 +1247,13 @@ pub const Tokenizer = struct {
1282 .IntegerLiteral,1247 .IntegerLiteral,
1283 .IntegerSuffix,1248 .IntegerSuffix,
1284 .Zero,1249 .Zero,
1285 => result.id = .{ .IntegerLiteral = .None },1250 => result.id = .{ .IntegerLiteral = .none },
1286 .IntegerSuffixU => result.id = .{ .IntegerLiteral = .U },1251 .IntegerSuffixU => result.id = .{ .IntegerLiteral = .u },
1287 .IntegerSuffixL => result.id = .{ .IntegerLiteral = .L },1252 .IntegerSuffixL => result.id = .{ .IntegerLiteral = .l },
1288 .IntegerSuffixLL => result.id = .{ .IntegerLiteral = .LL },1253 .IntegerSuffixLL => result.id = .{ .IntegerLiteral = .ll },
1289 .IntegerSuffixUL => result.id = .{ .IntegerLiteral = .LU },1254 .IntegerSuffixUL => result.id = .{ .IntegerLiteral = .lu },
12901255
1291 .FloatSuffix => result.id = .{ .FloatLiteral = .None },1256 .FloatSuffix => result.id = .{ .FloatLiteral = .none },
1292 .Equal => result.id = .Equal,1257 .Equal => result.id = .Equal,
1293 .Bang => result.id = .Bang,1258 .Bang => result.id = .Bang,
1294 .Minus => result.id = .Minus,1259 .Minus => result.id = .Minus,
...@@ -1466,7 +1431,7 @@ test "preprocessor keywords" {...@@ -1466,7 +1431,7 @@ test "preprocessor keywords" {
1466 .Hash,1431 .Hash,
1467 .Identifier,1432 .Identifier,
1468 .AngleBracketLeft,1433 .AngleBracketLeft,
1469 .{ .IntegerLiteral = .None },1434 .{ .IntegerLiteral = .none },
1470 .Nl,1435 .Nl,
1471 .Hash,1436 .Hash,
1472 .Keyword_ifdef,1437 .Keyword_ifdef,
...@@ -1499,18 +1464,18 @@ test "line continuation" {...@@ -1499,18 +1464,18 @@ test "line continuation" {
1499 .Identifier,1464 .Identifier,
1500 .Identifier,1465 .Identifier,
1501 .Nl,1466 .Nl,
1502 .{ .StringLiteral = .None },1467 .{ .StringLiteral = .none },
1503 .Nl,1468 .Nl,
1504 .Hash,1469 .Hash,
1505 .Keyword_define,1470 .Keyword_define,
1506 .{ .StringLiteral = .None },1471 .{ .StringLiteral = .none },
1507 .Nl,1472 .Nl,
1508 .{ .StringLiteral = .None },1473 .{ .StringLiteral = .none },
1509 .Nl,1474 .Nl,
1510 .Hash,1475 .Hash,
1511 .Keyword_define,1476 .Keyword_define,
1512 .{ .StringLiteral = .None },1477 .{ .StringLiteral = .none },
1513 .{ .StringLiteral = .None },1478 .{ .StringLiteral = .none },
1514 });1479 });
1515}1480}
15161481
...@@ -1527,23 +1492,23 @@ test "string prefix" {...@@ -1527,23 +1492,23 @@ test "string prefix" {
1527 \\L'foo'1492 \\L'foo'
1528 \\1493 \\
1529 , &[_]Token.Id{1494 , &[_]Token.Id{
1530 .{ .StringLiteral = .None },1495 .{ .StringLiteral = .none },
1531 .Nl,1496 .Nl,
1532 .{ .StringLiteral = .Utf16 },1497 .{ .StringLiteral = .utf_16 },
1533 .Nl,1498 .Nl,
1534 .{ .StringLiteral = .Utf8 },1499 .{ .StringLiteral = .utf_8 },
1535 .Nl,1500 .Nl,
1536 .{ .StringLiteral = .Utf32 },1501 .{ .StringLiteral = .utf_32 },
1537 .Nl,1502 .Nl,
1538 .{ .StringLiteral = .Wide },1503 .{ .StringLiteral = .wide },
1539 .Nl,1504 .Nl,
1540 .{ .CharLiteral = .None },1505 .{ .CharLiteral = .none },
1541 .Nl,1506 .Nl,
1542 .{ .CharLiteral = .Utf16 },1507 .{ .CharLiteral = .utf_16 },
1543 .Nl,1508 .Nl,
1544 .{ .CharLiteral = .Utf32 },1509 .{ .CharLiteral = .utf_32 },
1545 .Nl,1510 .Nl,
1546 .{ .CharLiteral = .Wide },1511 .{ .CharLiteral = .wide },
1547 .Nl,1512 .Nl,
1548 });1513 });
1549}1514}
...@@ -1555,33 +1520,29 @@ test "num suffixes" {...@@ -1555,33 +1520,29 @@ test "num suffixes" {
1555 \\ 1u 1ul 1ull 11520 \\ 1u 1ul 1ull 1
1556 \\1521 \\
1557 , &[_]Token.Id{1522 , &[_]Token.Id{
1558 .{ .FloatLiteral = .F },1523 .{ .FloatLiteral = .f },
1559 .{ .FloatLiteral = .L },1524 .{ .FloatLiteral = .l },
1560 .{ .FloatLiteral = .None },1525 .{ .FloatLiteral = .none },
1561 .{ .FloatLiteral = .None },1526 .{ .FloatLiteral = .none },
1562 .{ .FloatLiteral = .None },1527 .{ .FloatLiteral = .none },
1563 .Nl,1528 .Nl,
1564 .{ .IntegerLiteral = .L },1529 .{ .IntegerLiteral = .l },
1565 .{ .IntegerLiteral = .LU },1530 .{ .IntegerLiteral = .lu },
1566 .{ .IntegerLiteral = .LL },1531 .{ .IntegerLiteral = .ll },
1567 .{ .IntegerLiteral = .LLU },1532 .{ .IntegerLiteral = .llu },
1568 .{ .IntegerLiteral = .None },1533 .{ .IntegerLiteral = .none },
1569 .Nl,1534 .Nl,
1570 .{ .IntegerLiteral = .U },1535 .{ .IntegerLiteral = .u },
1571 .{ .IntegerLiteral = .LU },1536 .{ .IntegerLiteral = .lu },
1572 .{ .IntegerLiteral = .LLU },1537 .{ .IntegerLiteral = .llu },
1573 .{ .IntegerLiteral = .None },1538 .{ .IntegerLiteral = .none },
1574 .Nl,1539 .Nl,
1575 });1540 });
1576}1541}
15771542
1578fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void {1543fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void {
1579 var tokenizer = Tokenizer{1544 var tokenizer = Tokenizer{
1580 .source = &Source{1545 .buffer = source,
1581 .buffer = source,
1582 .file_name = undefined,
1583 .tokens = undefined,
1584 },
1585 };1546 };
1586 for (expected_tokens) |expected_token_id| {1547 for (expected_tokens) |expected_token_id| {
1587 const token = tokenizer.next();1548 const token = tokenizer.next();
lib/std/child_process.zig+9-13
...@@ -364,6 +364,7 @@ pub const ChildProcess = struct {...@@ -364,6 +364,7 @@ pub const ChildProcess = struct {
364 error.FileTooBig => unreachable,364 error.FileTooBig => unreachable,
365 error.DeviceBusy => unreachable,365 error.DeviceBusy => unreachable,
366 error.FileLocksNotSupported => unreachable,366 error.FileLocksNotSupported => unreachable,
367 error.BadPathName => unreachable, // Windows-only
367 else => |e| return e,368 else => |e| return e,
368 }369 }
369 else370 else
...@@ -480,25 +481,20 @@ pub const ChildProcess = struct {...@@ -480,25 +481,20 @@ pub const ChildProcess = struct {
480481
481 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);482 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
482483
483 // TODO use CreateFileW here since we are using a string literal for the path
484 const nul_handle = if (any_ignore)484 const nul_handle = if (any_ignore)
485 windows.CreateFile(485 windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
486 "NUL",486 .dir = std.fs.cwd().fd,
487 windows.GENERIC_READ,487 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
488 windows.FILE_SHARE_READ,488 .share_access = windows.FILE_SHARE_READ,
489 null,489 .creation = windows.OPEN_EXISTING,
490 windows.OPEN_EXISTING,490 .io_mode = .blocking,
491 windows.FILE_ATTRIBUTE_NORMAL,491 }) catch |err| switch (err) {
492 null,
493 ) catch |err| switch (err) {
494 error.SharingViolation => unreachable, // not possible for "NUL"
495 error.PathAlreadyExists => unreachable, // not possible for "NUL"492 error.PathAlreadyExists => unreachable, // not possible for "NUL"
496 error.PipeBusy => unreachable, // not possible for "NUL"493 error.PipeBusy => unreachable, // not possible for "NUL"
497 error.InvalidUtf8 => unreachable, // not possible for "NUL"
498 error.BadPathName => unreachable, // not possible for "NUL"
499 error.FileNotFound => unreachable, // not possible for "NUL"494 error.FileNotFound => unreachable, // not possible for "NUL"
500 error.AccessDenied => unreachable, // not possible for "NUL"495 error.AccessDenied => unreachable, // not possible for "NUL"
501 error.NameTooLong => unreachable, // not possible for "NUL"496 error.NameTooLong => unreachable, // not possible for "NUL"
497 error.WouldBlock => unreachable, // not possible for "NUL"
502 else => |e| return e,498 else => |e| return e,
503 }499 }
504 else500 else
lib/std/crypto/blake2.zig+224-27
...@@ -1,8 +1,7 @@...@@ -1,8 +1,7 @@
1const mem = @import("../mem.zig");1const mem = @import("../mem.zig");
2const math = @import("../math.zig");
3const endian = @import("../endian.zig");
4const debug = @import("../debug.zig");
5const builtin = @import("builtin");2const builtin = @import("builtin");
3const debug = @import("../debug.zig");
4const math = @import("../math.zig");
6const htest = @import("test.zig");5const htest = @import("test.zig");
76
8const RoundParam = struct {7const RoundParam = struct {
...@@ -31,7 +30,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {...@@ -31,7 +30,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
31pub const Blake2s224 = Blake2s(224);30pub const Blake2s224 = Blake2s(224);
32pub const Blake2s256 = Blake2s(256);31pub const Blake2s256 = Blake2s(256);
3332
34fn Blake2s(comptime out_len: usize) type {33pub fn Blake2s(comptime out_len: usize) type {
35 return struct {34 return struct {
36 const Self = @This();35 const Self = @This();
37 pub const block_length = 64;36 pub const block_length = 64;
...@@ -67,10 +66,17 @@ fn Blake2s(comptime out_len: usize) type {...@@ -67,10 +66,17 @@ fn Blake2s(comptime out_len: usize) type {
67 buf: [64]u8,66 buf: [64]u8,
68 buf_len: u8,67 buf_len: u8,
6968
69 key: []const u8,
70
70 pub fn init() Self {71 pub fn init() Self {
72 return init_keyed("");
73 }
74
75 pub fn init_keyed(key: []const u8) Self {
71 debug.assert(8 <= out_len and out_len <= 512);76 debug.assert(8 <= out_len and out_len <= 512);
7277
73 var s: Self = undefined;78 var s: Self = undefined;
79 s.key = key;
74 s.reset();80 s.reset();
75 return s;81 return s;
76 }82 }
...@@ -78,14 +84,24 @@ fn Blake2s(comptime out_len: usize) type {...@@ -78,14 +84,24 @@ fn Blake2s(comptime out_len: usize) type {
78 pub fn reset(d: *Self) void {84 pub fn reset(d: *Self) void {
79 mem.copy(u32, d.h[0..], iv[0..]);85 mem.copy(u32, d.h[0..], iv[0..]);
8086
81 // No key plus default parameters87 // default parameters
82 d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3);88 d.h[0] ^= 0x01010000 ^ @truncate(u32, d.key.len << 8) ^ @intCast(u32, out_len >> 3);
83 d.t = 0;89 d.t = 0;
84 d.buf_len = 0;90 d.buf_len = 0;
91
92 if (d.key.len > 0) {
93 mem.set(u8, d.buf[d.key.len..], 0);
94 d.update(d.key);
95 d.buf_len = 64;
96 }
85 }97 }
8698
87 pub fn hash(b: []const u8, out: []u8) void {99 pub fn hash(b: []const u8, out: []u8) void {
88 var d = Self.init();100 Self.hash_keyed("", b, out);
101 }
102
103 pub fn hash_keyed(key: []const u8, b: []const u8, out: []u8) void {
104 var d = Self.init_keyed(key);
89 d.update(b);105 d.update(b);
90 d.final(out);106 d.final(out);
91 }107 }
...@@ -94,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type {...@@ -94,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type {
94 var off: usize = 0;110 var off: usize = 0;
95111
96 // Partial buffer exists from previous update. Copy into buffer then hash.112 // Partial buffer exists from previous update. Copy into buffer then hash.
97 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {113 if (d.buf_len != 0 and d.buf_len + b.len > 64) {
98 off += 64 - d.buf_len;114 off += 64 - d.buf_len;
99 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);115 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
100 d.t += 64;116 d.t += 64;
...@@ -103,7 +119,7 @@ fn Blake2s(comptime out_len: usize) type {...@@ -103,7 +119,7 @@ fn Blake2s(comptime out_len: usize) type {
103 }119 }
104120
105 // Full middle blocks.121 // Full middle blocks.
106 while (off + 64 <= b.len) : (off += 64) {122 while (off + 64 < b.len) : (off += 64) {
107 d.t += 64;123 d.t += 64;
108 d.round(b[off .. off + 64], false);124 d.round(b[off .. off + 64], false);
109 }125 }
...@@ -123,7 +139,7 @@ fn Blake2s(comptime out_len: usize) type {...@@ -123,7 +139,7 @@ fn Blake2s(comptime out_len: usize) type {
123 const rr = d.h[0 .. out_len / 32];139 const rr = d.h[0 .. out_len / 32];
124140
125 for (rr) |s, j| {141 for (rr) |s, j| {
126 mem.writeIntLittle(u32, out[4 * j ..][0..4], s);142 mem.writeIntSliceLittle(u32, out[4 * j ..], s);
127 }143 }
128 }144 }
129145
...@@ -188,6 +204,9 @@ test "blake2s224 single" {...@@ -188,6 +204,9 @@ test "blake2s224 single" {
188204
189 const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";205 const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";
190 htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog");206 htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog");
207
208 const h4 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
209 htest.assertEqualHash(Blake2s224, h4, "a" ** 32 ++ "b" ** 32);
191}210}
192211
193test "blake2s224 streaming" {212test "blake2s224 streaming" {
...@@ -212,6 +231,37 @@ test "blake2s224 streaming" {...@@ -212,6 +231,37 @@ test "blake2s224 streaming" {
212 h.update("c");231 h.update("c");
213 h.final(out[0..]);232 h.final(out[0..]);
214 htest.assertEqual(h2, out[0..]);233 htest.assertEqual(h2, out[0..]);
234
235 const h3 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
236
237 h.reset();
238 h.update("a" ** 32);
239 h.update("b" ** 32);
240 h.final(out[0..]);
241 htest.assertEqual(h3, out[0..]);
242
243 h.reset();
244 h.update("a" ** 32 ++ "b" ** 32);
245 h.final(out[0..]);
246 htest.assertEqual(h3, out[0..]);
247}
248
249test "comptime blake2s224" {
250 comptime {
251 @setEvalBranchQuota(6000);
252 var block = [_]u8{0} ** Blake2s224.block_length;
253 var out: [Blake2s224.digest_length]u8 = undefined;
254
255 const h1 = "86b7611563293f8c73627df7a6d6ba25ca0548c2a6481f7d116ee576";
256
257 htest.assertEqualHash(Blake2s224, h1, block[0..]);
258
259 var h = Blake2s224.init();
260 h.update(&block);
261 h.final(out[0..]);
262
263 htest.assertEqual(h1, out[0..]);
264 }
215}265}
216266
217test "blake2s256 single" {267test "blake2s256 single" {
...@@ -223,6 +273,9 @@ test "blake2s256 single" {...@@ -223,6 +273,9 @@ test "blake2s256 single" {
223273
224 const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";274 const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";
225 htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog");275 htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog");
276
277 const h4 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
278 htest.assertEqualHash(Blake2s256, h4, "a" ** 32 ++ "b" ** 32);
226}279}
227280
228test "blake2s256 streaming" {281test "blake2s256 streaming" {
...@@ -247,15 +300,60 @@ test "blake2s256 streaming" {...@@ -247,15 +300,60 @@ test "blake2s256 streaming" {
247 h.update("c");300 h.update("c");
248 h.final(out[0..]);301 h.final(out[0..]);
249 htest.assertEqual(h2, out[0..]);302 htest.assertEqual(h2, out[0..]);
303
304 const h3 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
305
306 h.reset();
307 h.update("a" ** 32);
308 h.update("b" ** 32);
309 h.final(out[0..]);
310 htest.assertEqual(h3, out[0..]);
311
312 h.reset();
313 h.update("a" ** 32 ++ "b" ** 32);
314 h.final(out[0..]);
315 htest.assertEqual(h3, out[0..]);
250}316}
251317
252test "blake2s256 aligned final" {318test "blake2s256 keyed" {
253 var block = [_]u8{0} ** Blake2s256.block_length;319 var out: [32]u8 = undefined;
254 var out: [Blake2s256.digest_length]u8 = undefined;320
321 const h1 = "10f918da4d74fab3302e48a5d67d03804b1ec95372a62a0f33b7c9fa28ba1ae6";
322 const key = "secret_key";
255323
256 var h = Blake2s256.init();324 Blake2s256.hash_keyed(key, "a" ** 64 ++ "b" ** 64, &out);
257 h.update(&block);325 htest.assertEqual(h1, out[0..]);
326
327 var h = Blake2s256.init_keyed(key);
328 h.update("a" ** 64 ++ "b" ** 64);
329 h.final(out[0..]);
330
331 htest.assertEqual(h1, out[0..]);
332
333 h.reset();
334 h.update("a" ** 64);
335 h.update("b" ** 64);
258 h.final(out[0..]);336 h.final(out[0..]);
337
338 htest.assertEqual(h1, out[0..]);
339}
340
341test "comptime blake2s256" {
342 comptime {
343 @setEvalBranchQuota(6000);
344 var block = [_]u8{0} ** Blake2s256.block_length;
345 var out: [Blake2s256.digest_length]u8 = undefined;
346
347 const h1 = "ae09db7cd54f42b490ef09b6bc541af688e4959bb8c53f359a6f56e38ab454a3";
348
349 htest.assertEqualHash(Blake2s256, h1, block[0..]);
350
351 var h = Blake2s256.init();
352 h.update(&block);
353 h.final(out[0..]);
354
355 htest.assertEqual(h1, out[0..]);
356 }
259}357}
260358
261/////////////////////359/////////////////////
...@@ -264,7 +362,7 @@ test "blake2s256 aligned final" {...@@ -264,7 +362,7 @@ test "blake2s256 aligned final" {
264pub const Blake2b384 = Blake2b(384);362pub const Blake2b384 = Blake2b(384);
265pub const Blake2b512 = Blake2b(512);363pub const Blake2b512 = Blake2b(512);
266364
267fn Blake2b(comptime out_len: usize) type {365pub fn Blake2b(comptime out_len: usize) type {
268 return struct {366 return struct {
269 const Self = @This();367 const Self = @This();
270 pub const block_length = 128;368 pub const block_length = 128;
...@@ -302,10 +400,17 @@ fn Blake2b(comptime out_len: usize) type {...@@ -302,10 +400,17 @@ fn Blake2b(comptime out_len: usize) type {
302 buf: [128]u8,400 buf: [128]u8,
303 buf_len: u8,401 buf_len: u8,
304402
403 key: []const u8,
404
305 pub fn init() Self {405 pub fn init() Self {
406 return init_keyed("");
407 }
408
409 pub fn init_keyed(key: []const u8) Self {
306 debug.assert(8 <= out_len and out_len <= 512);410 debug.assert(8 <= out_len and out_len <= 512);
307411
308 var s: Self = undefined;412 var s: Self = undefined;
413 s.key = key;
309 s.reset();414 s.reset();
310 return s;415 return s;
311 }416 }
...@@ -313,14 +418,24 @@ fn Blake2b(comptime out_len: usize) type {...@@ -313,14 +418,24 @@ fn Blake2b(comptime out_len: usize) type {
313 pub fn reset(d: *Self) void {418 pub fn reset(d: *Self) void {
314 mem.copy(u64, d.h[0..], iv[0..]);419 mem.copy(u64, d.h[0..], iv[0..]);
315420
316 // No key plus default parameters421 // default parameters
317 d.h[0] ^= 0x01010000 ^ (out_len >> 3);422 d.h[0] ^= 0x01010000 ^ (d.key.len << 8) ^ (out_len >> 3);
318 d.t = 0;423 d.t = 0;
319 d.buf_len = 0;424 d.buf_len = 0;
425
426 if (d.key.len > 0) {
427 mem.set(u8, d.buf[d.key.len..], 0);
428 d.update(d.key);
429 d.buf_len = 128;
430 }
320 }431 }
321432
322 pub fn hash(b: []const u8, out: []u8) void {433 pub fn hash(b: []const u8, out: []u8) void {
323 var d = Self.init();434 Self.hash_keyed("", b, out);
435 }
436
437 pub fn hash_keyed(key: []const u8, b: []const u8, out: []u8) void {
438 var d = Self.init_keyed(key);
324 d.update(b);439 d.update(b);
325 d.final(out);440 d.final(out);
326 }441 }
...@@ -329,7 +444,7 @@ fn Blake2b(comptime out_len: usize) type {...@@ -329,7 +444,7 @@ fn Blake2b(comptime out_len: usize) type {
329 var off: usize = 0;444 var off: usize = 0;
330445
331 // Partial buffer exists from previous update. Copy into buffer then hash.446 // Partial buffer exists from previous update. Copy into buffer then hash.
332 if (d.buf_len != 0 and d.buf_len + b.len >= 128) {447 if (d.buf_len != 0 and d.buf_len + b.len > 128) {
333 off += 128 - d.buf_len;448 off += 128 - d.buf_len;
334 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);449 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
335 d.t += 128;450 d.t += 128;
...@@ -338,7 +453,7 @@ fn Blake2b(comptime out_len: usize) type {...@@ -338,7 +453,7 @@ fn Blake2b(comptime out_len: usize) type {
338 }453 }
339454
340 // Full middle blocks.455 // Full middle blocks.
341 while (off + 128 <= b.len) : (off += 128) {456 while (off + 128 < b.len) : (off += 128) {
342 d.t += 128;457 d.t += 128;
343 d.round(b[off .. off + 128], false);458 d.round(b[off .. off + 128], false);
344 }459 }
...@@ -356,7 +471,7 @@ fn Blake2b(comptime out_len: usize) type {...@@ -356,7 +471,7 @@ fn Blake2b(comptime out_len: usize) type {
356 const rr = d.h[0 .. out_len / 64];471 const rr = d.h[0 .. out_len / 64];
357472
358 for (rr) |s, j| {473 for (rr) |s, j| {
359 mem.writeIntLittle(u64, out[8 * j ..][0..8], s);474 mem.writeIntSliceLittle(u64, out[8 * j ..], s);
360 }475 }
361 }476 }
362477
...@@ -421,6 +536,9 @@ test "blake2b384 single" {...@@ -421,6 +536,9 @@ test "blake2b384 single" {
421536
422 const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";537 const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";
423 htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog");538 htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog");
539
540 const h4 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
541 htest.assertEqualHash(Blake2b384, h4, "a" ** 64 ++ "b" ** 64);
424}542}
425543
426test "blake2b384 streaming" {544test "blake2b384 streaming" {
...@@ -445,6 +563,37 @@ test "blake2b384 streaming" {...@@ -445,6 +563,37 @@ test "blake2b384 streaming" {
445 h.update("c");563 h.update("c");
446 h.final(out[0..]);564 h.final(out[0..]);
447 htest.assertEqual(h2, out[0..]);565 htest.assertEqual(h2, out[0..]);
566
567 const h3 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
568
569 h.reset();
570 h.update("a" ** 64 ++ "b" ** 64);
571 h.final(out[0..]);
572 htest.assertEqual(h3, out[0..]);
573
574 h.reset();
575 h.update("a" ** 64);
576 h.update("b" ** 64);
577 h.final(out[0..]);
578 htest.assertEqual(h3, out[0..]);
579}
580
581test "comptime blake2b384" {
582 comptime {
583 @setEvalBranchQuota(7000);
584 var block = [_]u8{0} ** Blake2b384.block_length;
585 var out: [Blake2b384.digest_length]u8 = undefined;
586
587 const h1 = "e8aa1931ea0422e4446fecdd25c16cf35c240b10cb4659dd5c776eddcaa4d922397a589404b46eb2e53d78132d05fd7d";
588
589 htest.assertEqualHash(Blake2b384, h1, block[0..]);
590
591 var h = Blake2b384.init();
592 h.update(&block);
593 h.final(out[0..]);
594
595 htest.assertEqual(h1, out[0..]);
596 }
448}597}
449598
450test "blake2b512 single" {599test "blake2b512 single" {
...@@ -456,6 +605,9 @@ test "blake2b512 single" {...@@ -456,6 +605,9 @@ test "blake2b512 single" {
456605
457 const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";606 const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";
458 htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog");607 htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog");
608
609 const h4 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
610 htest.assertEqualHash(Blake2b512, h4, "a" ** 64 ++ "b" ** 64);
459}611}
460612
461test "blake2b512 streaming" {613test "blake2b512 streaming" {
...@@ -480,13 +632,58 @@ test "blake2b512 streaming" {...@@ -480,13 +632,58 @@ test "blake2b512 streaming" {
480 h.update("c");632 h.update("c");
481 h.final(out[0..]);633 h.final(out[0..]);
482 htest.assertEqual(h2, out[0..]);634 htest.assertEqual(h2, out[0..]);
635
636 const h3 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
637
638 h.reset();
639 h.update("a" ** 64 ++ "b" ** 64);
640 h.final(out[0..]);
641 htest.assertEqual(h3, out[0..]);
642
643 h.reset();
644 h.update("a" ** 64);
645 h.update("b" ** 64);
646 h.final(out[0..]);
647 htest.assertEqual(h3, out[0..]);
483}648}
484649
485test "blake2b512 aligned final" {650test "blake2b512 keyed" {
486 var block = [_]u8{0} ** Blake2b512.block_length;651 var out: [64]u8 = undefined;
487 var out: [Blake2b512.digest_length]u8 = undefined;
488652
489 var h = Blake2b512.init();653 const h1 = "8a978060ccaf582f388f37454363071ac9a67e3a704585fd879fb8a419a447e389c7c6de790faa20a7a7dccf197de736bc5b40b98a930b36df5bee7555750c4d";
490 h.update(&block);654 const key = "secret_key";
655
656 Blake2b512.hash_keyed(key, "a" ** 64 ++ "b" ** 64, &out);
657 htest.assertEqual(h1, out[0..]);
658
659 var h = Blake2b512.init_keyed(key);
660 h.update("a" ** 64 ++ "b" ** 64);
661 h.final(out[0..]);
662
663 htest.assertEqual(h1, out[0..]);
664
665 h.reset();
666 h.update("a" ** 64);
667 h.update("b" ** 64);
491 h.final(out[0..]);668 h.final(out[0..]);
669
670 htest.assertEqual(h1, out[0..]);
671}
672
673test "comptime blake2b512" {
674 comptime {
675 @setEvalBranchQuota(8000);
676 var block = [_]u8{0} ** Blake2b512.block_length;
677 var out: [Blake2b512.digest_length]u8 = undefined;
678
679 const h1 = "865939e120e6805438478841afb739ae4250cf372653078a065cdcfffca4caf798e6d462b65d658fc165782640eded70963449ae1500fb0f24981d7727e22c41";
680
681 htest.assertEqualHash(Blake2b512, h1, block[0..]);
682
683 var h = Blake2b512.init();
684 h.update(&block);
685 h.final(out[0..]);
686
687 htest.assertEqual(h1, out[0..]);
688 }
492}689}
lib/std/debug/leb128.zig+44
...@@ -159,6 +159,50 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {...@@ -159,6 +159,50 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
159 return buf.pos;159 return buf.pos;
160}160}
161161
162/// This is an "advanced" function. It allows one to use a fixed amount of memory to store a
163/// ULEB128. This defeats the entire purpose of using this data encoding; it will no longer use
164/// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes
165/// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile.
166/// An example use case of this is in emitting DWARF info where one wants to make a ULEB128 field
167/// "relocatable", meaning that it becomes possible to later go back and patch the number to be a
168/// different value without shifting all the following code.
169pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(false, l * 7)) void {
170 const T = @TypeOf(int);
171 const U = if (T.bit_count < 8) u8 else T;
172 var value = @intCast(U, int);
173
174 comptime var i = 0;
175 inline while (i < (l - 1)) : (i += 1) {
176 const byte = @truncate(u8, value) | 0b1000_0000;
177 value >>= 7;
178 ptr[i] = byte;
179 }
180 ptr[i] = @truncate(u8, value);
181}
182
183test "writeUnsignedFixed" {
184 {
185 var buf: [4]u8 = undefined;
186 writeUnsignedFixed(4, &buf, 0);
187 testing.expect((try test_read_uleb128(u64, &buf)) == 0);
188 }
189 {
190 var buf: [4]u8 = undefined;
191 writeUnsignedFixed(4, &buf, 1);
192 testing.expect((try test_read_uleb128(u64, &buf)) == 1);
193 }
194 {
195 var buf: [4]u8 = undefined;
196 writeUnsignedFixed(4, &buf, 1000);
197 testing.expect((try test_read_uleb128(u64, &buf)) == 1000);
198 }
199 {
200 var buf: [4]u8 = undefined;
201 writeUnsignedFixed(4, &buf, 10000000);
202 testing.expect((try test_read_uleb128(u64, &buf)) == 10000000);
203 }
204}
205
162// tests206// tests
163fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {207fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
164 var reader = std.io.fixedBufferStream(encoded);208 var reader = std.io.fixedBufferStream(encoded);
lib/std/dwarf.zig+1-1
...@@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");...@@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");
99
10const ArrayList = std.ArrayList;10const ArrayList = std.ArrayList;
1111
12usingnamespace @import("dwarf_bits.zig");12pub usingnamespace @import("dwarf_bits.zig");
1313
14const PcRange = struct {14const PcRange = struct {
15 start: u64,15 start: u64,
lib/std/dwarf_bits.zig+17
...@@ -680,3 +680,20 @@ pub const LANG_HP_Basic91 = 0x8004;...@@ -680,3 +680,20 @@ pub const LANG_HP_Basic91 = 0x8004;
680pub const LANG_HP_Pascal91 = 0x8005;680pub const LANG_HP_Pascal91 = 0x8005;
681pub const LANG_HP_IMacro = 0x8006;681pub const LANG_HP_IMacro = 0x8006;
682pub const LANG_HP_Assembler = 0x8007;682pub const LANG_HP_Assembler = 0x8007;
683
684pub const UT_compile = 0x01;
685pub const UT_type = 0x02;
686pub const UT_partial = 0x03;
687pub const UT_skeleton = 0x04;
688pub const UT_split_compile = 0x05;
689pub const UT_split_type = 0x06;
690pub const UT_lo_user = 0x80;
691pub const UT_hi_user = 0xff;
692
693pub const LNCT_path = 0x1;
694pub const LNCT_directory_index = 0x2;
695pub const LNCT_timestamp = 0x3;
696pub const LNCT_size = 0x4;
697pub const LNCT_MD5 = 0x5;
698pub const LNCT_lo_user = 0x2000;
699pub const LNCT_hi_user = 0x3fff;
lib/std/elf.zig+136-129
...@@ -341,6 +341,20 @@ const Header = struct {...@@ -341,6 +341,20 @@ const Header = struct {
341 shentsize: u16,341 shentsize: u16,
342 shnum: u16,342 shnum: u16,
343 shstrndx: u16,343 shstrndx: u16,
344
345 pub fn program_header_iterator(self: Header, file: File) ProgramHeaderIterator {
346 return .{
347 .elf_header = self,
348 .file = file,
349 };
350 }
351
352 pub fn section_header_iterator(self: Header, file: File) SectionHeaderIterator {
353 return .{
354 .elf_header = self,
355 .file = file,
356 };
357 }
344};358};
345359
346pub fn readHeader(file: File) !Header {360pub fn readHeader(file: File) !Header {
...@@ -378,144 +392,137 @@ pub fn readHeader(file: File) !Header {...@@ -378,144 +392,137 @@ pub fn readHeader(file: File) !Header {
378 });392 });
379}393}
380394
381/// All integers are native endian.395pub const ProgramHeaderIterator = struct {
382pub const AllHeaders = struct {396 elf_header: Header,
383 header: Header,397 file: File,
384 section_headers: []Elf64_Shdr,398 index: usize = 0,
385 program_headers: []Elf64_Phdr,399
386 allocator: *mem.Allocator,400 pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr {
387};401 if (self.index >= self.elf_header.phnum) return null;
388402 defer self.index += 1;
389pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {403
390 var hdrs: AllHeaders = .{404 if (self.elf_header.is_64) {
391 .allocator = allocator,405 var phdr: Elf64_Phdr = undefined;
392 .header = try readHeader(file),406 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
393 .section_headers = undefined,407 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
394 .program_headers = undefined,408
395 };409 // ELF endianness matches native endianness.
396 const is_64 = hdrs.header.is_64;410 if (self.elf_header.endian == std.builtin.endian) return phdr;
397 const need_bswap = hdrs.header.endian != std.builtin.endian;411
398412 // Convert fields to native endianness.
399 hdrs.section_headers = try allocator.alloc(Elf64_Shdr, hdrs.header.shnum);413 return Elf64_Phdr{
400 errdefer allocator.free(hdrs.section_headers);414 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
401415 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
402 hdrs.program_headers = try allocator.alloc(Elf64_Phdr, hdrs.header.phnum);416 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
403 errdefer allocator.free(hdrs.program_headers);417 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
404418 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
405 // If the ELF file is 64-bit and same-endianness, then all we have to do is419 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
406 // yeet the bytes into memory.420 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
407 // If only the endianness is different, they can be simply byte swapped.421 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
408 if (is_64) {422 };
409 const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers);423 }
410 const phdr_buf = std.mem.sliceAsBytes(hdrs.program_headers);
411 try preadNoEof(file, shdr_buf, hdrs.header.shoff);
412 try preadNoEof(file, phdr_buf, hdrs.header.phoff);
413424
414 if (need_bswap) {425 var phdr: Elf32_Phdr = undefined;
415 for (hdrs.section_headers) |*shdr| {426 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
416 shdr.* = .{427 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
417 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),428
418 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),429 // ELF endianness does NOT match native endianness.
419 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),430 if (self.elf_header.endian != std.builtin.endian) {
420 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),431 // Convert fields to native endianness.
421 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),432 phdr = .{
422 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),433 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
423 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),434 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
424 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),435 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
425 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),436 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
426 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),437 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
427 };438 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
428 }439 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
429 for (hdrs.program_headers) |*phdr| {440 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
430 phdr.* = .{441 };
431 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
432 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
433 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
434 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
435 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
436 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
437 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
438 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
439 };
440 }
441 }442 }
442443
443 return hdrs;444 // Convert 32-bit header to 64-bit.
445 return Elf64_Phdr{
446 .p_type = phdr.p_type,
447 .p_offset = phdr.p_offset,
448 .p_vaddr = phdr.p_vaddr,
449 .p_paddr = phdr.p_paddr,
450 .p_filesz = phdr.p_filesz,
451 .p_memsz = phdr.p_memsz,
452 .p_flags = phdr.p_flags,
453 .p_align = phdr.p_align,
454 };
444 }455 }
456};
445457
446 const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);458pub const SectionHeaderIterator = struct {
447 defer allocator.free(shdrs_32);459 elf_header: Header,
448460 file: File,
449 const phdrs_32 = try allocator.alloc(Elf32_Phdr, hdrs.header.phnum);461 index: usize = 0,
450 defer allocator.free(phdrs_32);462
451463 pub fn next(self: *SectionHeaderIterator) !?Elf64_Shdr {
452 const shdr_buf = std.mem.sliceAsBytes(shdrs_32);464 if (self.index >= self.elf_header.shnum) return null;
453 const phdr_buf = std.mem.sliceAsBytes(phdrs_32);465 defer self.index += 1;
454 try preadNoEof(file, shdr_buf, hdrs.header.shoff);466
455 try preadNoEof(file, phdr_buf, hdrs.header.phoff);467 if (self.elf_header.is_64) {
456468 var shdr: Elf64_Shdr = undefined;
457 if (need_bswap) {469 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(shdr)) * self.index;
458 for (hdrs.section_headers) |*shdr, i| {470 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
459 const o = shdrs_32[i];471
460 shdr.* = .{472 // ELF endianness matches native endianness.
461 .sh_name = @byteSwap(@TypeOf(o.sh_name), o.sh_name),473 if (self.elf_header.endian == std.builtin.endian) return shdr;
462 .sh_type = @byteSwap(@TypeOf(o.sh_type), o.sh_type),474
463 .sh_flags = @byteSwap(@TypeOf(o.sh_flags), o.sh_flags),475 // Convert fields to native endianness.
464 .sh_addr = @byteSwap(@TypeOf(o.sh_addr), o.sh_addr),476 return Elf64_Shdr{
465 .sh_offset = @byteSwap(@TypeOf(o.sh_offset), o.sh_offset),477 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
466 .sh_size = @byteSwap(@TypeOf(o.sh_size), o.sh_size),478 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
467 .sh_link = @byteSwap(@TypeOf(o.sh_link), o.sh_link),479 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
468 .sh_info = @byteSwap(@TypeOf(o.sh_info), o.sh_info),480 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
469 .sh_addralign = @byteSwap(@TypeOf(o.sh_addralign), o.sh_addralign),481 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
470 .sh_entsize = @byteSwap(@TypeOf(o.sh_entsize), o.sh_entsize),482 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
471 };483 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
472 }484 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
473 for (hdrs.program_headers) |*phdr, i| {485 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
474 const o = phdrs_32[i];486 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
475 phdr.* = .{
476 .p_type = @byteSwap(@TypeOf(o.p_type), o.p_type),
477 .p_offset = @byteSwap(@TypeOf(o.p_offset), o.p_offset),
478 .p_vaddr = @byteSwap(@TypeOf(o.p_vaddr), o.p_vaddr),
479 .p_paddr = @byteSwap(@TypeOf(o.p_paddr), o.p_paddr),
480 .p_filesz = @byteSwap(@TypeOf(o.p_filesz), o.p_filesz),
481 .p_memsz = @byteSwap(@TypeOf(o.p_memsz), o.p_memsz),
482 .p_flags = @byteSwap(@TypeOf(o.p_flags), o.p_flags),
483 .p_align = @byteSwap(@TypeOf(o.p_align), o.p_align),
484 };487 };
485 }488 }
486 } else {489
487 for (hdrs.section_headers) |*shdr, i| {490 var shdr: Elf32_Shdr = undefined;
488 const o = shdrs_32[i];491 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
489 shdr.* = .{492 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
490 .sh_name = o.sh_name,493
491 .sh_type = o.sh_type,494 // ELF endianness does NOT match native endianness.
492 .sh_flags = o.sh_flags,495 if (self.elf_header.endian != std.builtin.endian) {
493 .sh_addr = o.sh_addr,496 // Convert fields to native endianness.
494 .sh_offset = o.sh_offset,497 shdr = .{
495 .sh_size = o.sh_size,498 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
496 .sh_link = o.sh_link,499 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
497 .sh_info = o.sh_info,500 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
498 .sh_addralign = o.sh_addralign,501 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
499 .sh_entsize = o.sh_entsize,502 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
500 };503 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
501 }504 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
502 for (hdrs.program_headers) |*phdr, i| {505 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
503 const o = phdrs_32[i];506 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
504 phdr.* = .{507 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
505 .p_type = o.p_type,
506 .p_offset = o.p_offset,
507 .p_vaddr = o.p_vaddr,
508 .p_paddr = o.p_paddr,
509 .p_filesz = o.p_filesz,
510 .p_memsz = o.p_memsz,
511 .p_flags = o.p_flags,
512 .p_align = o.p_align,
513 };508 };
514 }509 }
515 }
516510
517 return hdrs;511 // Convert 32-bit header to 64-bit.
518}512 return Elf64_Shdr{
513 .sh_name = shdr.sh_name,
514 .sh_type = shdr.sh_type,
515 .sh_flags = shdr.sh_flags,
516 .sh_addr = shdr.sh_addr,
517 .sh_offset = shdr.sh_offset,
518 .sh_size = shdr.sh_size,
519 .sh_link = shdr.sh_link,
520 .sh_info = shdr.sh_info,
521 .sh_addralign = shdr.sh_addralign,
522 .sh_entsize = shdr.sh_entsize,
523 };
524 }
525};
519526
520pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {527pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
521 if (is_64) {528 if (is_64) {
...@@ -538,7 +545,7 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {...@@ -538,7 +545,7 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
538}545}
539546
540fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {547fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
541 var i: u64 = 0;548 var i: usize = 0;
542 while (i < buf.len) {549 while (i < buf.len) {
543 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {550 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
544 error.SystemResources => return error.SystemResources,551 error.SystemResources => return error.SystemResources,
lib/std/fifo.zig+8-2
...@@ -224,6 +224,7 @@ pub fn LinearFifo(...@@ -224,6 +224,7 @@ pub fn LinearFifo(
224 pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {224 pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {
225 return .{ .context = self };225 return .{ .context = self };
226 }226 }
227
227 /// Deprecated: `use reader`228 /// Deprecated: `use reader`
228 pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) {229 pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) {
229 return .{ .context = self };230 return .{ .context = self };
...@@ -315,6 +316,11 @@ pub fn LinearFifo(...@@ -315,6 +316,11 @@ pub fn LinearFifo(
315 return bytes.len;316 return bytes.len;
316 }317 }
317318
319 pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) {
320 return .{ .context = self };
321 }
322
323 /// Deprecated: `use writer`
318 pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {324 pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
319 return .{ .context = self };325 return .{ .context = self };
320 }326 }
...@@ -426,14 +432,14 @@ test "LinearFifo(u8, .Dynamic)" {...@@ -426,14 +432,14 @@ test "LinearFifo(u8, .Dynamic)" {
426 fifo.shrink(0);432 fifo.shrink(0);
427433
428 {434 {
429 try fifo.outStream().print("{}, {}!", .{ "Hello", "World" });435 try fifo.writer().print("{}, {}!", .{ "Hello", "World" });
430 var result: [30]u8 = undefined;436 var result: [30]u8 = undefined;
431 testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);437 testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
432 testing.expectEqual(@as(usize, 0), fifo.readableLength());438 testing.expectEqual(@as(usize, 0), fifo.readableLength());
433 }439 }
434440
435 {441 {
436 try fifo.outStream().writeAll("This is a test");442 try fifo.writer().writeAll("This is a test");
437 var result: [30]u8 = undefined;443 var result: [30]u8 = undefined;
438 testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);444 testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
439 testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);445 testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
lib/std/fmt.zig+2
...@@ -88,6 +88,8 @@ pub fn format(...@@ -88,6 +88,8 @@ 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);
9193
92 const State = enum {94 const State = enum {
93 Start,95 Start,
lib/std/fs.zig+37-23
...@@ -84,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -84,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
84 try crypto.randomBytes(rand_buf[0..]);84 try crypto.randomBytes(rand_buf[0..]);
85 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);85 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
8686
87 if (cwd().symLink(existing_path, new_path, .{})) {87 if (cwd().symLink(existing_path, tmp_path, .{})) {
88 return rename(tmp_path, new_path);88 return rename(tmp_path, new_path);
89 } else |err| switch (err) {89 } else |err| switch (err) {
90 error.PathAlreadyExists => continue,90 error.PathAlreadyExists => continue,
...@@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {...@@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
225/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.225/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
226pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {226pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
227 assert(path.isAbsoluteWindowsW(absolute_path_w));227 assert(path.isAbsoluteWindowsW(absolute_path_w));
228 const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);228 return os.mkdirW(absolute_path_w, default_new_dir_mode);
229 os.windows.CloseHandle(handle);
230}229}
231230
232pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");231pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");
...@@ -881,8 +880,7 @@ pub const Dir = struct {...@@ -881,8 +880,7 @@ pub const Dir = struct {
881 }880 }
882881
883 pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {882 pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
884 const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);883 try os.mkdiratW(self.fd, sub_path, default_new_dir_mode);
885 os.windows.CloseHandle(handle);
886 }884 }
887885
888 /// Calls makeDir recursively to make an entire path. Returns success if the path886 /// Calls makeDir recursively to make an entire path. Returns success if the path
...@@ -1119,7 +1117,7 @@ pub const Dir = struct {...@@ -1119,7 +1117,7 @@ pub const Dir = struct {
1119 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {1117 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1120 if (builtin.os.tag == .windows) {1118 if (builtin.os.tag == .windows) {
1121 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);1119 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1122 return self.deleteFileW(sub_path_w.span().ptr);1120 return self.deleteFileW(sub_path_w.span());
1123 } else if (builtin.os.tag == .wasi) {1121 } else if (builtin.os.tag == .wasi) {
1124 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {1122 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
1125 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
...@@ -1153,7 +1151,7 @@ pub const Dir = struct {...@@ -1153,7 +1151,7 @@ pub const Dir = struct {
1153 }1151 }
11541152
1155 /// Same as `deleteFile` except the parameter is WTF-16 encoded.1153 /// Same as `deleteFile` except the parameter is WTF-16 encoded.
1156 pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void {1154 pub fn deleteFileW(self: Dir, sub_path_w: []const u16) DeleteFileError!void {
1157 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {1155 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
1158 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1156 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1159 else => |e| return e,1157 else => |e| return e,
...@@ -1182,7 +1180,7 @@ pub const Dir = struct {...@@ -1182,7 +1180,7 @@ pub const Dir = struct {
1182 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {1180 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
1183 if (builtin.os.tag == .windows) {1181 if (builtin.os.tag == .windows) {
1184 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);1182 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1185 return self.deleteDirW(sub_path_w.span().ptr);1183 return self.deleteDirW(sub_path_w.span());
1186 } else if (builtin.os.tag == .wasi) {1184 } else if (builtin.os.tag == .wasi) {
1187 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {1185 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
1188 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR1186 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
...@@ -1204,7 +1202,7 @@ pub const Dir = struct {...@@ -1204,7 +1202,7 @@ pub const Dir = struct {
12041202
1205 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.1203 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
1206 /// This function is Windows-only.1204 /// This function is Windows-only.
1207 pub fn deleteDirW(self: Dir, sub_path_w: [*:0]const u16) DeleteDirError!void {1205 pub fn deleteDirW(self: Dir, sub_path_w: []const u16) DeleteDirError!void {
1208 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {1206 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
1209 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR1207 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
1210 else => |e| return e,1208 else => |e| return e,
...@@ -1263,11 +1261,11 @@ pub const Dir = struct {...@@ -1263,11 +1261,11 @@ pub const Dir = struct {
1263 /// are null-terminated, WTF16 encoded.1261 /// are null-terminated, WTF16 encoded.
1264 pub fn symLinkW(1262 pub fn symLinkW(
1265 self: Dir,1263 self: Dir,
1266 target_path_w: [:0]const u16,1264 target_path_w: []const u16,
1267 sym_link_path_w: [:0]const u16,1265 sym_link_path_w: []const u16,
1268 flags: SymLinkFlags,1266 flags: SymLinkFlags,
1269 ) !void {1267 ) !void {
1270 return os.windows.CreateSymbolicLinkW(self.fd, sym_link_path_w, target_path_w, flags.is_directory);1268 return os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
1271 }1269 }
12721270
1273 /// Read value of a symbolic link.1271 /// Read value of a symbolic link.
...@@ -1278,7 +1276,8 @@ pub const Dir = struct {...@@ -1278,7 +1276,8 @@ pub const Dir = struct {
1278 return self.readLinkWasi(sub_path, buffer);1276 return self.readLinkWasi(sub_path, buffer);
1279 }1277 }
1280 if (builtin.os.tag == .windows) {1278 if (builtin.os.tag == .windows) {
1281 return os.windows.ReadLink(self.fd, sub_path, buffer);1279 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1280 return self.readLinkW(sub_path_w.span(), buffer);
1282 }1281 }
1283 const sub_path_c = try os.toPosixPath(sub_path);1282 const sub_path_c = try os.toPosixPath(sub_path);
1284 return self.readLinkZ(&sub_path_c, buffer);1283 return self.readLinkZ(&sub_path_c, buffer);
...@@ -1295,15 +1294,15 @@ pub const Dir = struct {...@@ -1295,15 +1294,15 @@ pub const Dir = struct {
1295 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {1294 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1296 if (builtin.os.tag == .windows) {1295 if (builtin.os.tag == .windows) {
1297 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);1296 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
1298 return self.readLinkW(sub_path_w, buffer);1297 return self.readLinkW(sub_path_w.span(), buffer);
1299 }1298 }
1300 return os.readlinkatZ(self.fd, sub_path_c, buffer);1299 return os.readlinkatZ(self.fd, sub_path_c, buffer);
1301 }1300 }
13021301
1303 /// Windows-only. Same as `readLink` except the pathname parameter1302 /// Windows-only. Same as `readLink` except the pathname parameter
1304 /// is null-terminated, WTF16 encoded.1303 /// is null-terminated, WTF16 encoded.
1305 pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {1304 pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
1306 return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);1305 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
1307 }1306 }
13081307
1309 /// On success, caller owns returned buffer.1308 /// On success, caller owns returned buffer.
...@@ -1813,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags...@@ -1813,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
1813 assert(path.isAbsolute(target_path));1812 assert(path.isAbsolute(target_path));
1814 assert(path.isAbsolute(sym_link_path));1813 assert(path.isAbsolute(sym_link_path));
1815 if (builtin.os.tag == .windows) {1814 if (builtin.os.tag == .windows) {
1816 return os.windows.CreateSymbolicLink(null, sym_link_path, target_path, flags.is_directory);1815 const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
1816 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
1817 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
1817 }1818 }
1818 return os.symlink(target_path, sym_link_path);1819 return os.symlink(target_path, sym_link_path);
1819}1820}
...@@ -1822,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags...@@ -1822,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
1822/// Note that this function will by default try creating a symbolic link to a file. If you would1823/// Note that this function will by default try creating a symbolic link to a file. If you would
1823/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.1824/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
1824/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.1825/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
1825pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {1826pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
1826 assert(path.isAbsoluteWindowsW(target_path_w));1827 assert(path.isAbsoluteWindowsWTF16(target_path_w));
1827 assert(path.isAbsoluteWindowsW(sym_link_path_w));1828 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
1828 return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);1829 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
1829}1830}
18301831
1831/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.1832/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
...@@ -1836,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons...@@ -1836,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons
1836 if (builtin.os.tag == .windows) {1837 if (builtin.os.tag == .windows) {
1837 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);1838 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
1838 const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);1839 const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);
1839 return os.windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags.is_directory);1840 return os.windows.CreateSymbolicLink(sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
1840 }1841 }
1841 return os.symlinkZ(target_path_c, sym_link_path_c);1842 return os.symlinkZ(target_path_c, sym_link_path_c);
1842}1843}
...@@ -1938,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -1938,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
1938 return walker;1939 return walker;
1939}1940}
19401941
1941pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;1942pub const OpenSelfExeError = error{
1943 SharingViolation,
1944 PathAlreadyExists,
1945 FileNotFound,
1946 AccessDenied,
1947 PipeBusy,
1948 NameTooLong,
1949 /// On Windows, file paths must be valid Unicode.
1950 InvalidUtf8,
1951 /// On Windows, file paths cannot contain these characters:
1952 /// '/', '*', '?', '"', '<', '>', '|'
1953 BadPathName,
1954 Unexpected,
1955} || os.OpenError || SelfExePathError || os.FlockError;
19421956
1943pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {1957pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
1944 if (builtin.os.tag == .linux) {1958 if (builtin.os.tag == .linux) {
lib/std/fs/file.zig+14-1
...@@ -47,7 +47,20 @@ pub const File = struct {...@@ -47,7 +47,20 @@ pub const File = struct {
47 else => 0o666,47 else => 0o666,
48 };48 };
4949
50 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;50 pub const OpenError = error{
51 SharingViolation,
52 PathAlreadyExists,
53 FileNotFound,
54 AccessDenied,
55 PipeBusy,
56 NameTooLong,
57 /// On Windows, file paths must be valid Unicode.
58 InvalidUtf8,
59 /// On Windows, file paths cannot contain these characters:
60 /// '/', '*', '?', '"', '<', '>', '|'
61 BadPathName,
62 Unexpected,
63 } || os.OpenError || os.FlockError;
5164
52 pub const Lock = enum { None, Shared, Exclusive };65 pub const Lock = enum { None, Shared, Exclusive };
5366
lib/std/fs/watch.zig+7-9
...@@ -374,15 +374,13 @@ pub fn Watch(comptime V: type) type {...@@ -374,15 +374,13 @@ pub fn Watch(comptime V: type) type {
374 defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);374 defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
375 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];375 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
376376
377 const dir_handle = try windows.CreateFileW(377 const dir_handle = try windows.OpenFile(dirname_utf16le, .{
378 dirname_utf16le.ptr,378 .dir = std.fs.cwd().fd,
379 windows.FILE_LIST_DIRECTORY,379 .access_mask = windows.FILE_LIST_DIRECTORY,
380 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,380 .creation = windows.FILE_OPEN,
381 null,381 .io_mode = .blocking,
382 windows.OPEN_EXISTING,382 .open_dir = true,
383 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,383 });
384 null,
385 );
386 var dir_handle_consumed = false;384 var dir_handle_consumed = false;
387 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);385 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
388386
lib/std/hash/auto_hash.zig+15-17
...@@ -56,9 +56,6 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy)...@@ -56,9 +56,6 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy)
56pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {56pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
57 switch (strat) {57 switch (strat) {
58 .Shallow => {58 .Shallow => {
59 // TODO detect via a trait when Key has no padding bits to
60 // hash it as an array of bytes.
61 // Otherwise, hash every element.
62 for (key) |element| {59 for (key) |element| {
63 hash(hasher, element, .Shallow);60 hash(hasher, element, .Shallow);
64 }61 }
...@@ -75,30 +72,34 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo...@@ -75,30 +72,34 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo
75/// Strategy is provided to determine if pointers should be followed or not.72/// Strategy is provided to determine if pointers should be followed or not.
76pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {73pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
77 const Key = @TypeOf(key);74 const Key = @TypeOf(key);
75
76 if (strat == .Shallow and comptime meta.trait.hasUniqueRepresentation(Key)) {
77 @call(.{ .modifier = .always_inline }, hasher.update, .{mem.asBytes(&key)});
78 return;
79 }
80
78 switch (@typeInfo(Key)) {81 switch (@typeInfo(Key)) {
79 .NoReturn,82 .NoReturn,
80 .Opaque,83 .Opaque,
81 .Undefined,84 .Undefined,
82 .Void,85 .Void,
83 .Null,86 .Null,
84 .BoundFn,
85 .ComptimeFloat,87 .ComptimeFloat,
86 .ComptimeInt,88 .ComptimeInt,
87 .Type,89 .Type,
88 .EnumLiteral,90 .EnumLiteral,
89 .Frame,91 .Frame,
92 .Float,
90 => @compileError("cannot hash this type"),93 => @compileError("cannot hash this type"),
9194
92 // Help the optimizer see that hashing an int is easy by inlining!95 // Help the optimizer see that hashing an int is easy by inlining!
93 // TODO Check if the situation is better after #561 is resolved.96 // TODO Check if the situation is better after #561 is resolved.
94 .Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}),97 .Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}),
9598
96 .Float => |info| hash(hasher, @bitCast(std.meta.Int(false, info.bits), key), strat),
97
98 .Bool => hash(hasher, @boolToInt(key), strat),99 .Bool => hash(hasher, @boolToInt(key), strat),
99 .Enum => hash(hasher, @enumToInt(key), strat),100 .Enum => hash(hasher, @enumToInt(key), strat),
100 .ErrorSet => hash(hasher, @errorToInt(key), strat),101 .ErrorSet => hash(hasher, @errorToInt(key), strat),
101 .AnyFrame, .Fn => hash(hasher, @ptrToInt(key), strat),102 .AnyFrame, .BoundFn, .Fn => hash(hasher, @ptrToInt(key), strat),
102103
103 .Pointer => @call(.{ .modifier = .always_inline }, hashPointer, .{ hasher, key, strat }),104 .Pointer => @call(.{ .modifier = .always_inline }, hashPointer, .{ hasher, key, strat }),
104105
...@@ -121,9 +122,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -121,9 +122,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
121 },122 },
122123
123 .Struct => |info| {124 .Struct => |info| {
124 // TODO detect via a trait when Key has no padding bits to
125 // hash it as an array of bytes.
126 // Otherwise, hash every field.
127 inline for (info.fields) |field| {125 inline for (info.fields) |field| {
128 // We reuse the hash of the previous field as the seed for the126 // We reuse the hash of the previous field as the seed for the
129 // next one so that they're dependant.127 // next one so that they're dependant.
...@@ -266,12 +264,12 @@ test "hash slice deep" {...@@ -266,12 +264,12 @@ test "hash slice deep" {
266test "hash struct deep" {264test "hash struct deep" {
267 const Foo = struct {265 const Foo = struct {
268 a: u32,266 a: u32,
269 b: f64,267 b: u16,
270 c: *bool,268 c: *bool,
271269
272 const Self = @This();270 const Self = @This();
273271
274 pub fn init(allocator: *mem.Allocator, a_: u32, b_: f64, c_: bool) !Self {272 pub fn init(allocator: *mem.Allocator, a_: u32, b_: u16, c_: bool) !Self {
275 const ptr = try allocator.create(bool);273 const ptr = try allocator.create(bool);
276 ptr.* = c_;274 ptr.* = c_;
277 return Self{ .a = a_, .b = b_, .c = ptr };275 return Self{ .a = a_, .b = b_, .c = ptr };
...@@ -279,9 +277,9 @@ test "hash struct deep" {...@@ -279,9 +277,9 @@ test "hash struct deep" {
279 };277 };
280278
281 const allocator = std.testing.allocator;279 const allocator = std.testing.allocator;
282 const foo = try Foo.init(allocator, 123, 1.0, true);280 const foo = try Foo.init(allocator, 123, 10, true);
283 const bar = try Foo.init(allocator, 123, 1.0, true);281 const bar = try Foo.init(allocator, 123, 10, true);
284 const baz = try Foo.init(allocator, 123, 1.0, false);282 const baz = try Foo.init(allocator, 123, 10, false);
285 defer allocator.destroy(foo.c);283 defer allocator.destroy(foo.c);
286 defer allocator.destroy(bar.c);284 defer allocator.destroy(bar.c);
287 defer allocator.destroy(baz.c);285 defer allocator.destroy(baz.c);
...@@ -338,12 +336,12 @@ test "testHash struct" {...@@ -338,12 +336,12 @@ test "testHash struct" {
338test "testHash union" {336test "testHash union" {
339 const Foo = union(enum) {337 const Foo = union(enum) {
340 A: u32,338 A: u32,
341 B: f32,339 B: bool,
342 C: u32,340 C: u32,
343 };341 };
344342
345 const a = Foo{ .A = 18 };343 const a = Foo{ .A = 18 };
346 var b = Foo{ .B = 12.34 };344 var b = Foo{ .B = true };
347 const c = Foo{ .C = 18 };345 const c = Foo{ .C = 18 };
348 testing.expect(testHash(a) == testHash(a));346 testing.expect(testHash(a) == testHash(a));
349 testing.expect(testHash(a) != testHash(b));347 testing.expect(testHash(a) != testHash(b));
lib/std/hash_map.zig+21-8
...@@ -5,6 +5,7 @@ const testing = std.testing;...@@ -5,6 +5,7 @@ const testing = std.testing;
5const math = std.math;5const math = std.math;
6const mem = std.mem;6const mem = std.mem;
7const meta = std.meta;7const meta = std.meta;
8const trait = meta.trait;
8const autoHash = std.hash.autoHash;9const autoHash = std.hash.autoHash;
9const Wyhash = std.hash.Wyhash;10const Wyhash = std.hash.Wyhash;
10const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
...@@ -195,6 +196,10 @@ pub fn HashMap(...@@ -195,6 +196,10 @@ pub fn HashMap(
195 return self.unmanaged.getEntry(key);196 return self.unmanaged.getEntry(key);
196 }197 }
197198
199 pub fn getIndex(self: Self, key: K) ?usize {
200 return self.unmanaged.getIndex(key);
201 }
202
198 pub fn get(self: Self, key: K) ?V {203 pub fn get(self: Self, key: K) ?V {
199 return self.unmanaged.get(key);204 return self.unmanaged.get(key);
200 }205 }
...@@ -478,17 +483,21 @@ pub fn HashMapUnmanaged(...@@ -478,17 +483,21 @@ pub fn HashMapUnmanaged(
478 }483 }
479484
480 pub fn getEntry(self: Self, key: K) ?*Entry {485 pub fn getEntry(self: Self, key: K) ?*Entry {
486 const index = self.getIndex(key) orelse return null;
487 return &self.entries.items[index];
488 }
489
490 pub fn getIndex(self: Self, key: K) ?usize {
481 const header = self.index_header orelse {491 const header = self.index_header orelse {
482 // Linear scan.492 // Linear scan.
483 const h = if (store_hash) hash(key) else {};493 const h = if (store_hash) hash(key) else {};
484 for (self.entries.items) |*item| {494 for (self.entries.items) |*item, i| {
485 if (item.hash == h and eql(key, item.key)) {495 if (item.hash == h and eql(key, item.key)) {
486 return item;496 return i;
487 }497 }
488 }498 }
489 return null;499 return null;
490 };500 };
491
492 switch (header.capacityIndexType()) {501 switch (header.capacityIndexType()) {
493 .u8 => return self.getInternal(key, header, u8),502 .u8 => return self.getInternal(key, header, u8),
494 .u16 => return self.getInternal(key, header, u16),503 .u16 => return self.getInternal(key, header, u16),
...@@ -710,7 +719,7 @@ pub fn HashMapUnmanaged(...@@ -710,7 +719,7 @@ pub fn HashMapUnmanaged(
710 unreachable;719 unreachable;
711 }720 }
712721
713 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry {722 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?usize {
714 const indexes = header.indexes(I);723 const indexes = header.indexes(I);
715 const h = hash(key);724 const h = hash(key);
716 const start_index = header.constrainIndex(h);725 const start_index = header.constrainIndex(h);
...@@ -724,7 +733,7 @@ pub fn HashMapUnmanaged(...@@ -724,7 +733,7 @@ pub fn HashMapUnmanaged(
724 const entry = &self.entries.items[index.entry_index];733 const entry = &self.entries.items[index.entry_index];
725 const hash_match = if (store_hash) h == entry.hash else true;734 const hash_match = if (store_hash) h == entry.hash else true;
726 if (hash_match and eql(key, entry.key))735 if (hash_match and eql(key, entry.key))
727 return entry;736 return index.entry_index;
728 }737 }
729 return null;738 return null;
730 }739 }
...@@ -1023,9 +1032,13 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {...@@ -1023,9 +1032,13 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
1023pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {1032pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
1024 return struct {1033 return struct {
1025 fn hash(key: K) u32 {1034 fn hash(key: K) u32 {
1026 var hasher = Wyhash.init(0);1035 if (comptime trait.hasUniqueRepresentation(K)) {
1027 autoHash(&hasher, key);1036 return @truncate(u32, Wyhash.hash(0, std.mem.asBytes(&key)));
1028 return @truncate(u32, hasher.final());1037 } else {
1038 var hasher = Wyhash.init(0);
1039 autoHash(&hasher, key);
1040 return @truncate(u32, hasher.final());
1041 }
1029 }1042 }
1030 }.hash;1043 }.hash;
1031}1044}
lib/std/math/big/int.zig+1-1
...@@ -99,7 +99,7 @@ pub const Mutable = struct {...@@ -99,7 +99,7 @@ pub const Mutable = struct {
99 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {99 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
100 return .{100 return .{
101 .allocator = allocator,101 .allocator = allocator,
102 .limbs = limbs,102 .limbs = self.limbs,
103 .metadata = if (self.positive)103 .metadata = if (self.positive)
104 self.len & ~Managed.sign_bit104 self.len & ~Managed.sign_bit
105 else105 else
lib/std/math/big/int_test.zig+22
...@@ -2,6 +2,7 @@ const std = @import("../../std.zig");...@@ -2,6 +2,7 @@ const std = @import("../../std.zig");
2const mem = std.mem;2const mem = std.mem;
3const testing = std.testing;3const testing = std.testing;
4const Managed = std.math.big.int.Managed;4const Managed = std.math.big.int.Managed;
5const Mutable = std.math.big.int.Mutable;
5const Limb = std.math.big.Limb;6const Limb = std.math.big.Limb;
6const DoubleLimb = std.math.big.DoubleLimb;7const DoubleLimb = std.math.big.DoubleLimb;
7const maxInt = std.math.maxInt;8const maxInt = std.math.maxInt;
...@@ -1453,3 +1454,24 @@ test "big.int gcd one large" {...@@ -1453,3 +1454,24 @@ test "big.int gcd one large" {
14531454
1454 testing.expect((try r.to(u64)) == 1);1455 testing.expect((try r.to(u64)) == 1);
1455}1456}
1457
1458test "big.int mutable to managed" {
1459 const allocator = testing.allocator;
1460 var limbs_buf = try allocator.alloc(Limb, 8);
1461 defer allocator.free(limbs_buf);
1462
1463 var a = Mutable.init(limbs_buf, 0xdeadbeef);
1464 var a_managed = a.toManaged(allocator);
1465
1466 testing.expect(a.toConst().eq(a_managed.toConst()));
1467}
1468
1469test "big.int const to managed" {
1470 var a = try Managed.initSet(testing.allocator, 123423453456);
1471 defer a.deinit();
1472
1473 var b = try a.toConst().toManaged(testing.allocator);
1474 defer b.deinit();
1475
1476 testing.expect(a.toConst().eq(b.toConst()));
1477}
lib/std/mem.zig+73
...@@ -2030,6 +2030,79 @@ test "rotate" {...@@ -2030,6 +2030,79 @@ test "rotate" {
2030 testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 }));2030 testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 }));
2031}2031}
20322032
2033/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
2034/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
2035pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
2036 var i: usize = 0;
2037 var slide: usize = 0;
2038 var replacements: usize = 0;
2039 while (slide < input.len) {
2040 if (mem.indexOf(T, input[slide..], needle) == @as(usize, 0)) {
2041 mem.copy(T, output[i..i + replacement.len], replacement);
2042 i += replacement.len;
2043 slide += needle.len;
2044 replacements += 1;
2045 } else {
2046 output[i] = input[slide];
2047 i += 1;
2048 slide += 1;
2049 }
2050 }
2051
2052 return replacements;
2053}
2054
2055test "replace" {
2056 var output: [29]u8 = undefined;
2057 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
2058 testing.expect(replacements == 1);
2059 testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
2060
2061 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
2062 testing.expect(replacements == 2);
2063 testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
2064}
2065
2066/// Calculate the size needed in an output buffer to perform a replacement.
2067pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
2068 var i: usize = 0;
2069 var size: usize = input.len;
2070 while (i < input.len) : (i += 1) {
2071 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
2072 size = size - needle.len + replacement.len;
2073 i += needle.len;
2074 }
2075 }
2076
2077 return size;
2078}
2079
2080test "replacementSize" {
2081 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
2082 testing.expect(replacementSize(u8, "", "", "") == 0);
2083 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
2084 testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
2085}
2086
2087/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.
2088pub fn replaceOwned(comptime T: type, allocator: *Allocator, input: []const T, needle: []const T, replacement: []const T) Allocator.Error![]T {
2089 var output = try allocator.alloc(T, replacementSize(T, input, needle, replacement));
2090 _ = replace(T, input, needle, replacement, output);
2091 return output;
2092}
2093
2094test "replaceOwned" {
2095 const allocator = std.heap.page_allocator;
2096
2097 const base_replace = replaceOwned(u8, allocator, "All your base are belong to us", "base", "Zig") catch unreachable;
2098 defer allocator.free(base_replace);
2099 testing.expect(eql(u8, base_replace, "All your Zig are belong to us"));
2100
2101 const zen_replace = replaceOwned(u8, allocator, "Favor reading code over writing code.", " code", "") catch unreachable;
2102 defer allocator.free(zen_replace);
2103 testing.expect(eql(u8, zen_replace, "Favor reading over writing."));
2104}
2105
2033/// Converts a little-endian integer to host endianness.2106/// Converts a little-endian integer to host endianness.
2034pub fn littleToNative(comptime T: type, x: T) T {2107pub fn littleToNative(comptime T: type, x: T) T {
2035 return switch (builtin.endian) {2108 return switch (builtin.endian) {
lib/std/meta/trait.zig+68
...@@ -429,3 +429,71 @@ test "std.meta.trait.hasFunctions" {...@@ -429,3 +429,71 @@ test "std.meta.trait.hasFunctions" {
429 testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" }));429 testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" }));
430 testing.expect(!hasFunctions(TestStruct2, tuple));430 testing.expect(!hasFunctions(TestStruct2, tuple));
431}431}
432
433/// True if every value of the type `T` has a unique bit pattern representing it.
434/// In other words, `T` has no unused bits and no padding.
435pub fn hasUniqueRepresentation(comptime T: type) bool {
436 switch (@typeInfo(T)) {
437 else => return false, // TODO can we know if it's true for some of these types ?
438
439 .AnyFrame,
440 .Bool,
441 .BoundFn,
442 .Enum,
443 .ErrorSet,
444 .Fn,
445 .Int, // TODO check that it is still true
446 .Pointer,
447 => return true,
448
449 .Array => |info| return comptime hasUniqueRepresentation(info.child),
450
451 .Struct => |info| {
452 var sum_size = @as(usize, 0);
453
454 inline for (info.fields) |field| {
455 const FieldType = field.field_type;
456 if (comptime !hasUniqueRepresentation(FieldType)) return false;
457 sum_size += @sizeOf(FieldType);
458 }
459
460 return @sizeOf(T) == sum_size;
461 },
462
463 .Vector => |info| return comptime hasUniqueRepresentation(info.child),
464 }
465}
466
467test "std.meta.trait.hasUniqueRepresentation" {
468 const TestStruct1 = struct {
469 a: u32,
470 b: u32,
471 };
472
473 testing.expect(hasUniqueRepresentation(TestStruct1));
474
475 const TestStruct2 = struct {
476 a: u32,
477 b: u16,
478 };
479
480 testing.expect(!hasUniqueRepresentation(TestStruct2));
481
482 const TestStruct3 = struct {
483 a: u32,
484 b: u32,
485 };
486
487 testing.expect(hasUniqueRepresentation(TestStruct3));
488
489 testing.expect(hasUniqueRepresentation(i1));
490 testing.expect(hasUniqueRepresentation(u2));
491 testing.expect(hasUniqueRepresentation(i3));
492 testing.expect(hasUniqueRepresentation(u4));
493 testing.expect(hasUniqueRepresentation(i5));
494 testing.expect(hasUniqueRepresentation(u6));
495 testing.expect(hasUniqueRepresentation(i7));
496 testing.expect(hasUniqueRepresentation(u8));
497 testing.expect(hasUniqueRepresentation(i9));
498 testing.expect(hasUniqueRepresentation(u10));
499}
lib/std/net.zig+279-196
...@@ -14,8 +14,8 @@ const has_unix_sockets = @hasDecl(os, "sockaddr_un");...@@ -14,8 +14,8 @@ const has_unix_sockets = @hasDecl(os, "sockaddr_un");
1414
15pub const Address = extern union {15pub const Address = extern union {
16 any: os.sockaddr,16 any: os.sockaddr,
17 in: os.sockaddr_in,17 in: Ip4Address,
18 in6: os.sockaddr_in6,18 in6: Ip6Address,
19 un: if (has_unix_sockets) os.sockaddr_un else void,19 un: if (has_unix_sockets) os.sockaddr_un else void,
2020
21 // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/351221 // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512
...@@ -76,19 +76,227 @@ pub const Address = extern union {...@@ -76,19 +76,227 @@ pub const Address = extern union {
76 }76 }
77 }77 }
7878
79 pub fn parseIp6(buf: []const u8, port: u16) !Address {
80 return Address{.in6 = try Ip6Address.parse(buf, port) };
81 }
82
83 pub fn resolveIp6(buf: []const u8, port: u16) !Address {
84 return Address{.in6 = try Ip6Address.resolve(buf, port) };
85 }
86
87 pub fn parseIp4(buf: []const u8, port: u16) !Address {
88 return Address {.in = try Ip4Address.parse(buf, port) };
89 }
90
91 pub fn initIp4(addr: [4]u8, port: u16) Address {
92 return Address{.in = Ip4Address.init(addr, port) };
93 }
94
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) };
97 }
98
99 pub fn initUnix(path: []const u8) !Address {
100 var sock_addr = os.sockaddr_un{
101 .family = os.AF_UNIX,
102 .path = undefined,
103 };
104
105 // this enables us to have the proper length of the socket in getOsSockLen
106 mem.set(u8, &sock_addr.path, 0);
107
108 if (path.len > sock_addr.path.len) return error.NameTooLong;
109 mem.copy(u8, &sock_addr.path, path);
110
111 return Address{ .un = sock_addr };
112 }
113
114 /// Returns the port in native endian.
115 /// Asserts that the address is ip4 or ip6.
116 pub fn getPort(self: Address) u16 {
117 return switch (self.any.family) {
118 os.AF_INET => self.in.getPort(),
119 os.AF_INET6 => self.in6.getPort(),
120 else => unreachable,
121 };
122 }
123
124 /// `port` is native-endian.
125 /// Asserts that the address is ip4 or ip6.
126 pub fn setPort(self: *Address, port: u16) void {
127 switch (self.any.family) {
128 os.AF_INET => self.in.setPort(port),
129 os.AF_INET6 => self.in6.setPort(port),
130 else => unreachable,
131 }
132 }
133
134 /// Asserts that `addr` is an IP address.
135 /// This function will read past the end of the pointer, with a size depending
136 /// on the address family.
137 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
138 switch (addr.family) {
139 os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).*} },
140 os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).*} },
141 else => unreachable,
142 }
143 }
144
145 pub fn format(
146 self: Address,
147 comptime fmt: []const u8,
148 options: std.fmt.FormatOptions,
149 out_stream: anytype,
150 ) !void {
151 switch (self.any.family) {
152 os.AF_INET => try self.in.format(fmt, options, out_stream),
153 os.AF_INET6 => try self.in6.format(fmt, options, out_stream),
154 os.AF_UNIX => {
155 if (!has_unix_sockets) {
156 unreachable;
157 }
158
159 try std.fmt.format(out_stream, "{}", .{&self.un.path});
160 },
161 else => unreachable,
162 }
163 }
164
165 pub fn eql(a: Address, b: Address) bool {
166 const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
167 const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
168 return mem.eql(u8, a_bytes, b_bytes);
169 }
170
171 pub fn getOsSockLen(self: Address) os.socklen_t {
172 switch (self.any.family) {
173 os.AF_INET => return self.in.getOsSockLen(),
174 os.AF_INET6 => return self.in6.getOsSockLen(),
175 os.AF_UNIX => {
176 if (!has_unix_sockets) {
177 unreachable;
178 }
179
180 const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
181 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
182 },
183 else => unreachable,
184 }
185 }
186};
187
188pub const Ip4Address = extern struct {
189 sa: os.sockaddr_in,
190
191 pub fn parse(buf: []const u8, port: u16) !Ip4Address {
192 var result = Ip4Address{
193 .sa = .{
194 .port = mem.nativeToBig(u16, port),
195 .addr = undefined,
196 }
197 };
198 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);
199
200 var x: u8 = 0;
201 var index: u8 = 0;
202 var saw_any_digits = false;
203 for (buf) |c| {
204 if (c == '.') {
205 if (!saw_any_digits) {
206 return error.InvalidCharacter;
207 }
208 if (index == 3) {
209 return error.InvalidEnd;
210 }
211 out_ptr[index] = x;
212 index += 1;
213 x = 0;
214 saw_any_digits = false;
215 } else if (c >= '0' and c <= '9') {
216 saw_any_digits = true;
217 x = try std.math.mul(u8, x, 10);
218 x = try std.math.add(u8, x, c - '0');
219 } else {
220 return error.InvalidCharacter;
221 }
222 }
223 if (index == 3 and saw_any_digits) {
224 out_ptr[index] = x;
225 return result;
226 }
227
228 return error.Incomplete;
229 }
230
231 pub fn resolveIp(name: []const u8, port: u16) !Ip4Address {
232 if (parse(name, port)) |ip4| return ip4 else |err| switch (err) {
233 error.Overflow,
234 error.InvalidEnd,
235 error.InvalidCharacter,
236 error.Incomplete,
237 => {},
238 }
239 return error.InvalidIPAddressFormat;
240 }
241
242 pub fn init(addr: [4]u8, port: u16) Ip4Address {
243 return Ip4Address {
244 .sa = os.sockaddr_in{
245 .port = mem.nativeToBig(u16, port),
246 .addr = @ptrCast(*align(1) const u32, &addr).*,
247 },
248 };
249 }
250
251 /// Returns the port in native endian.
252 /// Asserts that the address is ip4 or ip6.
253 pub fn getPort(self: Ip4Address) u16 {
254 return mem.bigToNative(u16, self.sa.port);
255 }
256
257 /// `port` is native-endian.
258 /// Asserts that the address is ip4 or ip6.
259 pub fn setPort(self: *Ip4Address, port: u16) void {
260 self.sa.port = mem.nativeToBig(u16, port);
261 }
262
263 pub fn format(
264 self: Ip4Address,
265 comptime fmt: []const u8,
266 options: std.fmt.FormatOptions,
267 out_stream: anytype,
268 ) !void {
269 const bytes = @ptrCast(*const [4]u8, &self.sa.addr);
270 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
271 bytes[0],
272 bytes[1],
273 bytes[2],
274 bytes[3],
275 self.getPort(),
276 });
277 }
278
279 pub fn getOsSockLen(self: Ip4Address) os.socklen_t {
280 return @sizeOf(os.sockaddr_in);
281 }
282};
283
284pub const Ip6Address = extern struct {
285 sa: os.sockaddr_in6,
286
79 /// Parse a given IPv6 address string into an Address.287 /// Parse a given IPv6 address string into an Address.
80 /// Assumes the Scope ID of the address is fully numeric.288 /// Assumes the Scope ID of the address is fully numeric.
81 /// For non-numeric addresses, see `resolveIp6`.289 /// For non-numeric addresses, see `resolveIp6`.
82 pub fn parseIp6(buf: []const u8, port: u16) !Address {290 pub fn parse(buf: []const u8, port: u16) !Ip6Address {
83 var result = Address{291 var result = Ip6Address{
84 .in6 = os.sockaddr_in6{292 .sa = os.sockaddr_in6{
85 .scope_id = 0,293 .scope_id = 0,
86 .port = mem.nativeToBig(u16, port),294 .port = mem.nativeToBig(u16, port),
87 .flowinfo = 0,295 .flowinfo = 0,
88 .addr = undefined,296 .addr = undefined,
89 },297 },
90 };298 };
91 var ip_slice = result.in6.addr[0..];299 var ip_slice = result.sa.addr[0..];
92300
93 var tail: [16]u8 = undefined;301 var tail: [16]u8 = undefined;
94302
...@@ -101,10 +309,10 @@ pub const Address = extern union {...@@ -101,10 +309,10 @@ pub const Address = extern union {
101 if (scope_id) {309 if (scope_id) {
102 if (c >= '0' and c <= '9') {310 if (c >= '0' and c <= '9') {
103 const digit = c - '0';311 const digit = c - '0';
104 if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) {312 if (@mulWithOverflow(u32, result.sa.scope_id, 10, &result.sa.scope_id)) {
105 return error.Overflow;313 return error.Overflow;
106 }314 }
107 if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) {315 if (@addWithOverflow(u32, result.sa.scope_id, digit, &result.sa.scope_id)) {
108 return error.Overflow;316 return error.Overflow;
109 }317 }
110 } else {318 } else {
...@@ -141,10 +349,10 @@ pub const Address = extern union {...@@ -141,10 +349,10 @@ pub const Address = extern union {
141 return error.InvalidIpv4Mapping;349 return error.InvalidIpv4Mapping;
142 }350 }
143 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;351 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
144 const addr = (parseIp4(buf[start_index..], 0) catch {352 const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
145 return error.InvalidIpv4Mapping;353 return error.InvalidIpv4Mapping;
146 }).in.addr;354 }).sa.addr;
147 ip_slice = result.in6.addr[0..];355 ip_slice = result.sa.addr[0..];
148 ip_slice[10] = 0xff;356 ip_slice[10] = 0xff;
149 ip_slice[11] = 0xff;357 ip_slice[11] = 0xff;
150358
...@@ -180,22 +388,22 @@ pub const Address = extern union {...@@ -180,22 +388,22 @@ pub const Address = extern union {
180 index += 1;388 index += 1;
181 ip_slice[index] = @truncate(u8, x);389 ip_slice[index] = @truncate(u8, x);
182 index += 1;390 index += 1;
183 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);391 mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
184 return result;392 return result;
185 }393 }
186 }394 }
187395
188 pub fn resolveIp6(buf: []const u8, port: u16) !Address {396 pub fn resolve(buf: []const u8, port: u16) !Ip6Address {
189 // TODO: Unify the implementations of resolveIp6 and parseIp6.397 // TODO: Unify the implementations of resolveIp6 and parseIp6.
190 var result = Address{398 var result = Ip6Address{
191 .in6 = os.sockaddr_in6{399 .sa = os.sockaddr_in6{
192 .scope_id = 0,400 .scope_id = 0,
193 .port = mem.nativeToBig(u16, port),401 .port = mem.nativeToBig(u16, port),
194 .flowinfo = 0,402 .flowinfo = 0,
195 .addr = undefined,403 .addr = undefined,
196 },404 },
197 };405 };
198 var ip_slice = result.in6.addr[0..];406 var ip_slice = result.sa.addr[0..];
199407
200 var tail: [16]u8 = undefined;408 var tail: [16]u8 = undefined;
201409
...@@ -256,10 +464,10 @@ pub const Address = extern union {...@@ -256,10 +464,10 @@ pub const Address = extern union {
256 return error.InvalidIpv4Mapping;464 return error.InvalidIpv4Mapping;
257 }465 }
258 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;466 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
259 const addr = (parseIp4(buf[start_index..], 0) catch {467 const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
260 return error.InvalidIpv4Mapping;468 return error.InvalidIpv4Mapping;
261 }).in.addr;469 }).sa.addr;
262 ip_slice = result.in6.addr[0..];470 ip_slice = result.sa.addr[0..];
263 ip_slice[10] = 0xff;471 ip_slice[10] = 0xff;
264 ip_slice[11] = 0xff;472 ip_slice[11] = 0xff;
265473
...@@ -299,7 +507,7 @@ pub const Address = extern union {...@@ -299,7 +507,7 @@ pub const Address = extern union {
299 };507 };
300 }508 }
301509
302 result.in6.scope_id = resolved_scope_id;510 result.sa.scope_id = resolved_scope_id;
303511
304 if (index == 14) {512 if (index == 14) {
305 ip_slice[14] = @truncate(u8, x >> 8);513 ip_slice[14] = @truncate(u8, x >> 8);
...@@ -310,63 +518,14 @@ pub const Address = extern union {...@@ -310,63 +518,14 @@ pub const Address = extern union {
310 index += 1;518 index += 1;
311 ip_slice[index] = @truncate(u8, x);519 ip_slice[index] = @truncate(u8, x);
312 index += 1;520 index += 1;
313 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);521 mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
314 return result;
315 }
316 }
317
318 pub fn parseIp4(buf: []const u8, port: u16) !Address {
319 var result = Address{
320 .in = os.sockaddr_in{
321 .port = mem.nativeToBig(u16, port),
322 .addr = undefined,
323 },
324 };
325 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.in.addr)[0..]);
326
327 var x: u8 = 0;
328 var index: u8 = 0;
329 var saw_any_digits = false;
330 for (buf) |c| {
331 if (c == '.') {
332 if (!saw_any_digits) {
333 return error.InvalidCharacter;
334 }
335 if (index == 3) {
336 return error.InvalidEnd;
337 }
338 out_ptr[index] = x;
339 index += 1;
340 x = 0;
341 saw_any_digits = false;
342 } else if (c >= '0' and c <= '9') {
343 saw_any_digits = true;
344 x = try std.math.mul(u8, x, 10);
345 x = try std.math.add(u8, x, c - '0');
346 } else {
347 return error.InvalidCharacter;
348 }
349 }
350 if (index == 3 and saw_any_digits) {
351 out_ptr[index] = x;
352 return result;522 return result;
353 }523 }
354
355 return error.Incomplete;
356 }524 }
357525
358 pub fn initIp4(addr: [4]u8, port: u16) Address {526 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
359 return Address{527 return Ip6Address{
360 .in = os.sockaddr_in{528 .sa = os.sockaddr_in6{
361 .port = mem.nativeToBig(u16, port),
362 .addr = @ptrCast(*align(1) const u32, &addr).*,
363 },
364 };
365 }
366
367 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
368 return Address{
369 .in6 = os.sockaddr_in6{
370 .addr = addr,529 .addr = addr,
371 .port = mem.nativeToBig(u16, port),530 .port = mem.nativeToBig(u16, port),
372 .flowinfo = flowinfo,531 .flowinfo = flowinfo,
...@@ -375,147 +534,71 @@ pub const Address = extern union {...@@ -375,147 +534,71 @@ pub const Address = extern union {
375 };534 };
376 }535 }
377536
378 pub fn initUnix(path: []const u8) !Address {
379 var sock_addr = os.sockaddr_un{
380 .family = os.AF_UNIX,
381 .path = undefined,
382 };
383
384 // this enables us to have the proper length of the socket in getOsSockLen
385 mem.set(u8, &sock_addr.path, 0);
386
387 if (path.len > sock_addr.path.len) return error.NameTooLong;
388 mem.copy(u8, &sock_addr.path, path);
389
390 return Address{ .un = sock_addr };
391 }
392
393 /// Returns the port in native endian.537 /// Returns the port in native endian.
394 /// Asserts that the address is ip4 or ip6.538 /// Asserts that the address is ip4 or ip6.
395 pub fn getPort(self: Address) u16 {539 pub fn getPort(self: Ip6Address) u16 {
396 const big_endian_port = switch (self.any.family) {540 return mem.bigToNative(u16, self.sa.port);
397 os.AF_INET => self.in.port,
398 os.AF_INET6 => self.in6.port,
399 else => unreachable,
400 };
401 return mem.bigToNative(u16, big_endian_port);
402 }541 }
403542
404 /// `port` is native-endian.543 /// `port` is native-endian.
405 /// Asserts that the address is ip4 or ip6.544 /// Asserts that the address is ip4 or ip6.
406 pub fn setPort(self: *Address, port: u16) void {545 pub fn setPort(self: *Ip6Address, port: u16) void {
407 const ptr = switch (self.any.family) {546 self.sa.port = mem.nativeToBig(u16, port);
408 os.AF_INET => &self.in.port,
409 os.AF_INET6 => &self.in6.port,
410 else => unreachable,
411 };
412 ptr.* = mem.nativeToBig(u16, port);
413 }
414
415 /// Asserts that `addr` is an IP address.
416 /// This function will read past the end of the pointer, with a size depending
417 /// on the address family.
418 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
419 switch (addr.family) {
420 os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* },
421 os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* },
422 else => unreachable,
423 }
424 }547 }
425548
426 pub fn format(549 pub fn format(
427 self: Address,550 self: Ip6Address,
428 comptime fmt: []const u8,551 comptime fmt: []const u8,
429 options: std.fmt.FormatOptions,552 options: std.fmt.FormatOptions,
430 out_stream: anytype,553 out_stream: anytype,
431 ) !void {554 ) !void {
432 switch (self.any.family) {555 const port = mem.bigToNative(u16, self.sa.port);
433 os.AF_INET => {556 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
434 const port = mem.bigToNative(u16, self.in.port);557 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
435 const bytes = @ptrCast(*const [4]u8, &self.in.addr);558 self.sa.addr[12],
436 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{559 self.sa.addr[13],
437 bytes[0],560 self.sa.addr[14],
438 bytes[1],561 self.sa.addr[15],
439 bytes[2],562 port,
440 bytes[3],563 });
441 port,564 return;
442 });565 }
443 },566 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.sa.addr);
444 os.AF_INET6 => {567 const native_endian_parts = switch (builtin.endian) {
445 const port = mem.bigToNative(u16, self.in6.port);568 .Big => big_endian_parts.*,
446 if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {569 .Little => blk: {
447 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{570 var buf: [8]u16 = undefined;
448 self.in6.addr[12],571 for (big_endian_parts) |part, i| {
449 self.in6.addr[13],572 buf[i] = mem.bigToNative(u16, part);
450 self.in6.addr[14],
451 self.in6.addr[15],
452 port,
453 });
454 return;
455 }
456 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
457 const native_endian_parts = switch (builtin.endian) {
458 .Big => big_endian_parts.*,
459 .Little => blk: {
460 var buf: [8]u16 = undefined;
461 for (big_endian_parts) |part, i| {
462 buf[i] = mem.bigToNative(u16, part);
463 }
464 break :blk buf;
465 },
466 };
467 try out_stream.writeAll("[");
468 var i: usize = 0;
469 var abbrv = false;
470 while (i < native_endian_parts.len) : (i += 1) {
471 if (native_endian_parts[i] == 0) {
472 if (!abbrv) {
473 try out_stream.writeAll(if (i == 0) "::" else ":");
474 abbrv = true;
475 }
476 continue;
477 }
478 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
479 if (i != native_endian_parts.len - 1) {
480 try out_stream.writeAll(":");
481 }
482 }573 }
483 try std.fmt.format(out_stream, "]:{}", .{port});574 break :blk buf;
484 },575 },
485 os.AF_UNIX => {576 };
486 if (!has_unix_sockets) {577 try out_stream.writeAll("[");
487 unreachable;578 var i: usize = 0;
579 var abbrv = false;
580 while (i < native_endian_parts.len) : (i += 1) {
581 if (native_endian_parts[i] == 0) {
582 if (!abbrv) {
583 try out_stream.writeAll(if (i == 0) "::" else ":");
584 abbrv = true;
488 }585 }
489586 continue;
490 try std.fmt.format(out_stream, "{}", .{&self.un.path});587 }
491 },588 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
492 else => unreachable,589 if (i != native_endian_parts.len - 1) {
590 try out_stream.writeAll(":");
591 }
493 }592 }
593 try std.fmt.format(out_stream, "]:{}", .{port});
494 }594 }
495595
496 pub fn eql(a: Address, b: Address) bool {596 pub fn getOsSockLen(self: Ip6Address) os.socklen_t {
497 const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];597 return @sizeOf(os.sockaddr_in6);
498 const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
499 return mem.eql(u8, a_bytes, b_bytes);
500 }
501
502 pub fn getOsSockLen(self: Address) os.socklen_t {
503 switch (self.any.family) {
504 os.AF_INET => return @sizeOf(os.sockaddr_in),
505 os.AF_INET6 => return @sizeOf(os.sockaddr_in6),
506 os.AF_UNIX => {
507 if (!has_unix_sockets) {
508 unreachable;
509 }
510
511 const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
512 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
513 },
514 else => unreachable,
515 }
516 }598 }
517};599};
518600
601
519pub fn connectUnixSocket(path: []const u8) !fs.File {602pub fn connectUnixSocket(path: []const u8) !fs.File {
520 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;603 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
521 const sockfd = try os.socket(604 const sockfd = try os.socket(
...@@ -777,7 +860,7 @@ fn linuxLookupName(...@@ -777,7 +860,7 @@ fn linuxLookupName(
777 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));860 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));
778 var da6 = os.sockaddr_in6{861 var da6 = os.sockaddr_in6{
779 .family = os.AF_INET6,862 .family = os.AF_INET6,
780 .scope_id = addr.addr.in6.scope_id,863 .scope_id = addr.addr.in6.sa.scope_id,
781 .port = 65535,864 .port = 65535,
782 .flowinfo = 0,865 .flowinfo = 0,
783 .addr = [1]u8{0} ** 16,866 .addr = [1]u8{0} ** 16,
...@@ -795,7 +878,7 @@ fn linuxLookupName(...@@ -795,7 +878,7 @@ fn linuxLookupName(
795 var salen: os.socklen_t = undefined;878 var salen: os.socklen_t = undefined;
796 var dalen: os.socklen_t = undefined;879 var dalen: os.socklen_t = undefined;
797 if (addr.addr.any.family == os.AF_INET6) {880 if (addr.addr.any.family == os.AF_INET6) {
798 mem.copy(u8, &da6.addr, &addr.addr.in6.addr);881 mem.copy(u8, &da6.addr, &addr.addr.in6.sa.addr);
799 da = @ptrCast(*os.sockaddr, &da6);882 da = @ptrCast(*os.sockaddr, &da6);
800 dalen = @sizeOf(os.sockaddr_in6);883 dalen = @sizeOf(os.sockaddr_in6);
801 sa = @ptrCast(*os.sockaddr, &sa6);884 sa = @ptrCast(*os.sockaddr, &sa6);
...@@ -803,8 +886,8 @@ fn linuxLookupName(...@@ -803,8 +886,8 @@ fn linuxLookupName(
803 } else {886 } else {
804 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");887 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
805 mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");888 mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
806 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr);889 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.sa.addr);
807 da4.addr = addr.addr.in.addr;890 da4.addr = addr.addr.in.sa.addr;
808 da = @ptrCast(*os.sockaddr, &da4);891 da = @ptrCast(*os.sockaddr, &da4);
809 dalen = @sizeOf(os.sockaddr_in);892 dalen = @sizeOf(os.sockaddr_in);
810 sa = @ptrCast(*os.sockaddr, &sa4);893 sa = @ptrCast(*os.sockaddr, &sa4);
lib/std/os.zig+176-106
...@@ -1041,6 +1041,9 @@ pub const OpenError = error{...@@ -1041,6 +1041,9 @@ pub const OpenError = error{
10411041
1042 /// The underlying filesystem does not support file locks1042 /// The underlying filesystem does not support file locks
1043 FileLocksNotSupported,1043 FileLocksNotSupported,
1044
1045 BadPathName,
1046 InvalidUtf8,
1044} || UnexpectedError;1047} || UnexpectedError;
10451048
1046/// Open and possibly create a file. Keeps trying if it gets interrupted.1049/// Open and possibly create a file. Keeps trying if it gets interrupted.
...@@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t...@@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
1092 }1095 }
1093}1096}
10941097
1098fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
1099 const w = windows;
1100
1101 var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
1102 if (flags & O_RDWR != 0) {
1103 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
1104 } else if (flags & O_WRONLY != 0) {
1105 access_mask |= w.GENERIC_WRITE;
1106 } else {
1107 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
1108 }
1109
1110 const open_dir: bool = flags & O_DIRECTORY != 0;
1111 const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
1112
1113 const creation: w.ULONG = blk: {
1114 if (flags & O_CREAT != 0) {
1115 if (flags & O_EXCL != 0) {
1116 break :blk w.FILE_CREATE;
1117 }
1118 }
1119 break :blk w.FILE_OPEN;
1120 };
1121
1122 return .{
1123 .access_mask = access_mask,
1124 .io_mode = .blocking,
1125 .creation = creation,
1126 .open_dir = open_dir,
1127 .follow_symlinks = follow_symlinks,
1128 };
1129}
1130
1095/// Windows-only. The path parameter is1131/// Windows-only. The path parameter is
1096/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.1132/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
1097/// Translates the POSIX open API call to a Windows API call.1133/// Translates the POSIX open API call to a Windows API call.
1098pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {1134/// TODO currently, this function does not handle all flag combinations
1099 @compileError("TODO implement openW for windows");1135/// or makes use of perm argument.
1136pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
1137 var options = openOptionsFromFlags(flags);
1138 options.dir = std.fs.cwd().fd;
1139 return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
1140 error.WouldBlock => unreachable,
1141 error.PipeBusy => unreachable,
1142 else => |e| return e,
1143 };
1100}1144}
11011145
1102/// Open and possibly create a file. Keeps trying if it gets interrupted.1146/// Open and possibly create a file. Keeps trying if it gets interrupted.
1103/// `file_path` is relative to the open directory handle `dir_fd`.1147/// `file_path` is relative to the open directory handle `dir_fd`.
1104/// See also `openatC`.1148/// See also `openatC`.
1105/// TODO support windows
1106pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {1149pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
1150 if (builtin.os.tag == .wasi) {
1151 @compileError("use openatWasi instead");
1152 }
1153 if (builtin.os.tag == .windows) {
1154 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1155 return openatW(dir_fd, file_path_w.span(), flags, mode);
1156 }
1107 const file_path_c = try toPosixPath(file_path);1157 const file_path_c = try toPosixPath(file_path);
1108 return openatZ(dir_fd, &file_path_c, flags, mode);1158 return openatZ(dir_fd, &file_path_c, flags, mode);
1109}1159}
...@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");...@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
1145/// Open and possibly create a file. Keeps trying if it gets interrupted.1195/// Open and possibly create a file. Keeps trying if it gets interrupted.
1146/// `file_path` is relative to the open directory handle `dir_fd`.1196/// `file_path` is relative to the open directory handle `dir_fd`.
1147/// See also `openat`.1197/// See also `openat`.
1148/// TODO support windows
1149pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {1198pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
1199 if (builtin.os.tag == .windows) {
1200 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1201 return openatW(dir_fd, file_path_w.span(), flags, mode);
1202 }
1150 while (true) {1203 while (true) {
1151 const rc = system.openat(dir_fd, file_path, flags, mode);1204 const rc = system.openat(dir_fd, file_path, flags, mode);
1152 switch (errno(rc)) {1205 switch (errno(rc)) {
...@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)...@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
1177 }1230 }
1178}1231}
11791232
1233/// Windows-only. Similar to `openat` but with pathname argument null-terminated
1234/// WTF16 encoded.
1235/// TODO currently, this function does not handle all flag combinations
1236/// or makes use of perm argument.
1237pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
1238 var options = openOptionsFromFlags(flags);
1239 options.dir = dir_fd;
1240 return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
1241 error.WouldBlock => unreachable,
1242 error.PipeBusy => unreachable,
1243 else => |e| return e,
1244 };
1245}
1246
1180pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {1247pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
1181 while (true) {1248 while (true) {
1182 switch (errno(system.dup2(old_fd, new_fd))) {1249 switch (errno(system.dup2(old_fd, new_fd))) {
...@@ -1683,7 +1750,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {...@@ -1683,7 +1750,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
1683 @compileError("unlink is not supported in WASI; use unlinkat instead");1750 @compileError("unlink is not supported in WASI; use unlinkat instead");
1684 } else if (builtin.os.tag == .windows) {1751 } else if (builtin.os.tag == .windows) {
1685 const file_path_w = try windows.sliceToPrefixedFileW(file_path);1752 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1686 return windows.DeleteFileW(file_path_w.span().ptr);1753 return unlinkW(file_path_w.span());
1687 } else {1754 } else {
1688 const file_path_c = try toPosixPath(file_path);1755 const file_path_c = try toPosixPath(file_path);
1689 return unlinkZ(&file_path_c);1756 return unlinkZ(&file_path_c);
...@@ -1696,7 +1763,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");...@@ -1696,7 +1763,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");
1696pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {1763pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
1697 if (builtin.os.tag == .windows) {1764 if (builtin.os.tag == .windows) {
1698 const file_path_w = try windows.cStrToPrefixedFileW(file_path);1765 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1699 return windows.DeleteFileW(file_path_w.span().ptr);1766 return unlinkW(file_path_w.span());
1700 }1767 }
1701 switch (errno(system.unlink(file_path))) {1768 switch (errno(system.unlink(file_path))) {
1702 0 => return,1769 0 => return,
...@@ -1717,6 +1784,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {...@@ -1717,6 +1784,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
1717 }1784 }
1718}1785}
17191786
1787/// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded.
1788pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
1789 return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd });
1790}
1791
1720pub const UnlinkatError = UnlinkError || error{1792pub const UnlinkatError = UnlinkError || error{
1721 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.1793 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
1722 DirNotEmpty,1794 DirNotEmpty,
...@@ -1727,7 +1799,7 @@ pub const UnlinkatError = UnlinkError || error{...@@ -1727,7 +1799,7 @@ pub const UnlinkatError = UnlinkError || error{
1727pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {1799pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1728 if (builtin.os.tag == .windows) {1800 if (builtin.os.tag == .windows) {
1729 const file_path_w = try windows.sliceToPrefixedFileW(file_path);1801 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1730 return unlinkatW(dirfd, file_path_w.span().ptr, flags);1802 return unlinkatW(dirfd, file_path_w.span(), flags);
1731 } else if (builtin.os.tag == .wasi) {1803 } else if (builtin.os.tag == .wasi) {
1732 return unlinkatWasi(dirfd, file_path, flags);1804 return unlinkatWasi(dirfd, file_path, flags);
1733 } else {1805 } else {
...@@ -1774,7 +1846,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro...@@ -1774,7 +1846,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
1774pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {1846pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
1775 if (builtin.os.tag == .windows) {1847 if (builtin.os.tag == .windows) {
1776 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);1848 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
1777 return unlinkatW(dirfd, file_path_w.span().ptr, flags);1849 return unlinkatW(dirfd, file_path_w.span(), flags);
1778 }1850 }
1779 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {1851 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
1780 0 => return,1852 0 => return,
...@@ -1800,67 +1872,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr...@@ -1800,67 +1872,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
1800}1872}
18011873
1802/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.1874/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
1803pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatError!void {1875pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
1804 const w = windows;1876 const remove_dir = (flags & AT_REMOVEDIR) != 0;
18051877 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
1806 const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
1807 const create_options_flags = if (want_rmdir_behavior)
1808 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT)
1809 else
1810 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT); // would we ever want to delete the target instead?
1811
1812 const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2);
1813 var nt_name = w.UNICODE_STRING{
1814 .Length = path_len_bytes,
1815 .MaximumLength = path_len_bytes,
1816 // The Windows API makes this mutable, but it will not mutate here.
1817 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
1818 };
1819
1820 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
1821 // Windows does not recognize this, but it does work with empty string.
1822 nt_name.Length = 0;
1823 }
1824 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
1825 // Can't remove the parent directory with an open handle.
1826 return error.FileBusy;
1827 }
1828
1829 var attr = w.OBJECT_ATTRIBUTES{
1830 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
1831 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
1832 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1833 .ObjectName = &nt_name,
1834 .SecurityDescriptor = null,
1835 .SecurityQualityOfService = null,
1836 };
1837 var io: w.IO_STATUS_BLOCK = undefined;
1838 var tmp_handle: w.HANDLE = undefined;
1839 var rc = w.ntdll.NtCreateFile(
1840 &tmp_handle,
1841 w.SYNCHRONIZE | w.DELETE,
1842 &attr,
1843 &io,
1844 null,
1845 0,
1846 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
1847 w.FILE_OPEN,
1848 create_options_flags,
1849 null,
1850 0,
1851 );
1852 if (rc == .SUCCESS) {
1853 rc = w.ntdll.NtClose(tmp_handle);
1854 }
1855 switch (rc) {
1856 .SUCCESS => return,
1857 .OBJECT_NAME_INVALID => unreachable,
1858 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1859 .INVALID_PARAMETER => unreachable,
1860 .FILE_IS_A_DIRECTORY => return error.IsDir,
1861 .NOT_A_DIRECTORY => return error.NotDir,
1862 else => return w.unexpectedStatus(rc),
1863 }
1864}1878}
18651879
1866const RenameError = error{1880const RenameError = error{
...@@ -2087,7 +2101,7 @@ pub fn renameatW(...@@ -2087,7 +2101,7 @@ pub fn renameatW(
2087pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {2101pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2088 if (builtin.os.tag == .windows) {2102 if (builtin.os.tag == .windows) {
2089 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);2103 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
2090 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);2104 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
2091 } else if (builtin.os.tag == .wasi) {2105 } else if (builtin.os.tag == .wasi) {
2092 return mkdiratWasi(dir_fd, sub_dir_path, mode);2106 return mkdiratWasi(dir_fd, sub_dir_path, mode);
2093 } else {2107 } else {
...@@ -2145,8 +2159,19 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr...@@ -2145,8 +2159,19 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
2145 }2159 }
2146}2160}
21472161
2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {2162pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);2163 const sub_dir_handle = windows.OpenFile(sub_path_w, .{
2164 .dir = dir_fd,
2165 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2166 .creation = windows.FILE_CREATE,
2167 .io_mode = .blocking,
2168 .open_dir = true,
2169 }) catch |err| switch (err) {
2170 error.IsDir => unreachable,
2171 error.PipeBusy => unreachable,
2172 error.WouldBlock => unreachable,
2173 else => |e| return e,
2174 };
2150 windows.CloseHandle(sub_dir_handle);2175 windows.CloseHandle(sub_dir_handle);
2151}2176}
21522177
...@@ -2175,9 +2200,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {...@@ -2175,9 +2200,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2175 if (builtin.os.tag == .wasi) {2200 if (builtin.os.tag == .wasi) {
2176 @compileError("mkdir is not supported in WASI; use mkdirat instead");2201 @compileError("mkdir is not supported in WASI; use mkdirat instead");
2177 } else if (builtin.os.tag == .windows) {2202 } else if (builtin.os.tag == .windows) {
2178 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);2203 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2179 windows.CloseHandle(sub_dir_handle);2204 return mkdirW(dir_path_w.span(), mode);
2180 return;
2181 } else {2205 } else {
2182 const dir_path_c = try toPosixPath(dir_path);2206 const dir_path_c = try toPosixPath(dir_path);
2183 return mkdirZ(&dir_path_c, mode);2207 return mkdirZ(&dir_path_c, mode);
...@@ -2188,9 +2212,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {...@@ -2188,9 +2212,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2188pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {2212pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2189 if (builtin.os.tag == .windows) {2213 if (builtin.os.tag == .windows) {
2190 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);2214 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2191 const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null);2215 return mkdirW(dir_path_w.span(), mode);
2192 windows.CloseHandle(sub_dir_handle);
2193 return;
2194 }2216 }
2195 switch (errno(system.mkdir(dir_path, mode))) {2217 switch (errno(system.mkdir(dir_path, mode))) {
2196 0 => return,2218 0 => return,
...@@ -2211,6 +2233,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {...@@ -2211,6 +2233,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2211 }2233 }
2212}2234}
22132235
2236/// Windows-only. Same as `mkdir` but the parameters is WTF16 encoded.
2237pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
2238 const sub_dir_handle = windows.OpenFile(dir_path_w, .{
2239 .dir = std.fs.cwd().fd,
2240 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2241 .creation = windows.FILE_CREATE,
2242 .io_mode = .blocking,
2243 .open_dir = true,
2244 }) catch |err| switch (err) {
2245 error.IsDir => unreachable,
2246 error.PipeBusy => unreachable,
2247 error.WouldBlock => unreachable,
2248 else => |e| return e,
2249 };
2250 windows.CloseHandle(sub_dir_handle);
2251}
2252
2214pub const DeleteDirError = error{2253pub const DeleteDirError = error{
2215 AccessDenied,2254 AccessDenied,
2216 FileBusy,2255 FileBusy,
...@@ -2231,7 +2270,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {...@@ -2231,7 +2270,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
2231 @compileError("rmdir is not supported in WASI; use unlinkat instead");2270 @compileError("rmdir is not supported in WASI; use unlinkat instead");
2232 } else if (builtin.os.tag == .windows) {2271 } else if (builtin.os.tag == .windows) {
2233 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);2272 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2234 return windows.RemoveDirectoryW(dir_path_w.span().ptr);2273 return rmdirW(dir_path_w.span());
2235 } else {2274 } else {
2236 const dir_path_c = try toPosixPath(dir_path);2275 const dir_path_c = try toPosixPath(dir_path);
2237 return rmdirZ(&dir_path_c);2276 return rmdirZ(&dir_path_c);
...@@ -2244,7 +2283,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");...@@ -2244,7 +2283,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");
2244pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {2283pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
2245 if (builtin.os.tag == .windows) {2284 if (builtin.os.tag == .windows) {
2246 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);2285 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2247 return windows.RemoveDirectoryW(dir_path_w.span().ptr);2286 return rmdirW(dir_path_w.span());
2248 }2287 }
2249 switch (errno(system.rmdir(dir_path))) {2288 switch (errno(system.rmdir(dir_path))) {
2250 0 => return,2289 0 => return,
...@@ -2265,6 +2304,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {...@@ -2265,6 +2304,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
2265 }2304 }
2266}2305}
22672306
2307/// Windows-only. Same as `rmdir` except the parameter is WTF16 encoded.
2308pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
2309 return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
2310 error.IsDir => unreachable,
2311 else => |e| return e,
2312 };
2313}
2314
2268pub const ChangeCurDirError = error{2315pub const ChangeCurDirError = error{
2269 AccessDenied,2316 AccessDenied,
2270 FileSystem,2317 FileSystem,
...@@ -2354,7 +2401,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -2354,7 +2401,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2354 if (builtin.os.tag == .wasi) {2401 if (builtin.os.tag == .wasi) {
2355 @compileError("readlink is not supported in WASI; use readlinkat instead");2402 @compileError("readlink is not supported in WASI; use readlinkat instead");
2356 } else if (builtin.os.tag == .windows) {2403 } else if (builtin.os.tag == .windows) {
2357 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);2404 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2405 return readlinkW(file_path_w.span(), out_buffer);
2358 } else {2406 } else {
2359 const file_path_c = try toPosixPath(file_path);2407 const file_path_c = try toPosixPath(file_path);
2360 return readlinkZ(&file_path_c, out_buffer);2408 return readlinkZ(&file_path_c, out_buffer);
...@@ -2363,17 +2411,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -2363,17 +2411,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
23632411
2364pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");2412pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
23652413
2366/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.2414/// Windows-only. Same as `readlink` except `file_path` is WTF16 encoded.
2367/// See also `readlinkZ`.2415/// See also `readlinkZ`.
2368pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2416pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2369 return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);2417 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
2370}2418}
23712419
2372/// Same as `readlink` except `file_path` is null-terminated.2420/// Same as `readlink` except `file_path` is null-terminated.
2373pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2421pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2374 if (builtin.os.tag == .windows) {2422 if (builtin.os.tag == .windows) {
2375 const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);2423 const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
2376 return readlinkW(file_path_w.span().ptr, out_buffer);2424 return readlinkW(file_path_w.span(), out_buffer);
2377 }2425 }
2378 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);2426 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
2379 switch (errno(rc)) {2427 switch (errno(rc)) {
...@@ -2399,7 +2447,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink...@@ -2399,7 +2447,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
2399 return readlinkatWasi(dirfd, file_path, out_buffer);2447 return readlinkatWasi(dirfd, file_path, out_buffer);
2400 }2448 }
2401 if (builtin.os.tag == .windows) {2449 if (builtin.os.tag == .windows) {
2402 return windows.ReadLink(dirfd, file_path, out_buffer);2450 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2451 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
2403 }2452 }
2404 const file_path_c = try toPosixPath(file_path);2453 const file_path_c = try toPosixPath(file_path);
2405 return readlinkatZ(dirfd, &file_path_c, out_buffer);2454 return readlinkatZ(dirfd, &file_path_c, out_buffer);
...@@ -2429,8 +2478,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read...@@ -2429,8 +2478,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
24292478
2430/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.2479/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2431/// See also `readlinkat`.2480/// See also `readlinkat`.
2432pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2481pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2433 return windows.ReadLinkW(dirfd, file_path, out_buffer);2482 return windows.ReadLink(dirfd, file_path, out_buffer);
2434}2483}
24352484
2436/// Same as `readlinkat` except `file_path` is null-terminated.2485/// Same as `readlinkat` except `file_path` is null-terminated.
...@@ -2438,7 +2487,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea...@@ -2438,7 +2487,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea
2438pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2487pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2439 if (builtin.os.tag == .windows) {2488 if (builtin.os.tag == .windows) {
2440 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2489 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2441 return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);2490 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
2442 }2491 }
2443 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);2492 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
2444 switch (errno(rc)) {2493 switch (errno(rc)) {
...@@ -3959,7 +4008,7 @@ pub const RealPathError = error{...@@ -3959,7 +4008,7 @@ pub const RealPathError = error{
3959pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4008pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
3960 if (builtin.os.tag == .windows) {4009 if (builtin.os.tag == .windows) {
3961 const pathname_w = try windows.sliceToPrefixedFileW(pathname);4010 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
3962 return realpathW(pathname_w.span().ptr, out_buffer);4011 return realpathW(pathname_w.span(), out_buffer);
3963 }4012 }
3964 if (builtin.os.tag == .wasi) {4013 if (builtin.os.tag == .wasi) {
3965 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");4014 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
...@@ -3974,7 +4023,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");...@@ -3974,7 +4023,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");
3974pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4023pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
3975 if (builtin.os.tag == .windows) {4024 if (builtin.os.tag == .windows) {
3976 const pathname_w = try windows.cStrToPrefixedFileW(pathname);4025 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
3977 return realpathW(pathname_w.span().ptr, out_buffer);4026 return realpathW(pathname_w.span(), out_buffer);
3978 }4027 }
3979 if (builtin.os.tag == .linux and !builtin.link_libc) {4028 if (builtin.os.tag == .linux and !builtin.link_libc) {
3980 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {4029 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
...@@ -4010,22 +4059,43 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP...@@ -4010,22 +4059,43 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
4010 return mem.spanZ(result_path);4059 return mem.spanZ(result_path);
4011}4060}
40124061
4013/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.4062/// Same as `realpath` except `pathname` is UTF16LE-encoded.
4014/// TODO use ntdll for better semantics4063/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
4015pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4064pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4016 const h_file = try windows.CreateFileW(4065 const w = windows;
4017 pathname,4066
4018 windows.GENERIC_READ,4067 const dir = std.fs.cwd().fd;
4019 windows.FILE_SHARE_READ,4068 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
4020 null,4069 const share_access = w.FILE_SHARE_READ;
4021 windows.OPEN_EXISTING,4070 const creation = w.FILE_OPEN;
4022 windows.FILE_FLAG_BACKUP_SEMANTICS,4071 const h_file = blk: {
4023 null,4072 const res = w.OpenFile(pathname, .{
4024 );4073 .dir = dir,
4025 defer windows.CloseHandle(h_file);4074 .access_mask = access_mask,
4075 .share_access = share_access,
4076 .creation = creation,
4077 .io_mode = .blocking,
4078 }) catch |err| switch (err) {
4079 error.IsDir => break :blk w.OpenFile(pathname, .{
4080 .dir = dir,
4081 .access_mask = access_mask,
4082 .share_access = share_access,
4083 .creation = creation,
4084 .io_mode = .blocking,
4085 .open_dir = true,
4086 }) catch |er| switch (er) {
4087 error.WouldBlock => unreachable,
4088 else => |e2| return e2,
4089 },
4090 error.WouldBlock => unreachable,
4091 else => |e| return e,
4092 };
4093 break :blk res;
4094 };
4095 defer w.CloseHandle(h_file);
40264096
4027 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;4097 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
4028 const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);4098 const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
40294099
4030 // Windows returns \\?\ prepended to the path.4100 // Windows returns \\?\ prepended to the path.
4031 // We strip it to make this function consistent across platforms.4101 // We strip it to make this function consistent across platforms.
lib/std/os/bits/windows.zig+25
...@@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;...@@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;
237pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;237pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
238pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;238pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
239pub const IPPROTO_RM = ws2_32.IPPROTO_RM;239pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
240
241pub const O_RDONLY = 0o0;
242pub const O_WRONLY = 0o1;
243pub const O_RDWR = 0o2;
244
245pub const O_CREAT = 0o100;
246pub const O_EXCL = 0o200;
247pub const O_NOCTTY = 0o400;
248pub const O_TRUNC = 0o1000;
249pub const O_APPEND = 0o2000;
250pub const O_NONBLOCK = 0o4000;
251pub const O_DSYNC = 0o10000;
252pub const O_SYNC = 0o4010000;
253pub const O_RSYNC = 0o4010000;
254pub const O_DIRECTORY = 0o200000;
255pub const O_NOFOLLOW = 0o400000;
256pub const O_CLOEXEC = 0o2000000;
257
258pub const O_ASYNC = 0o20000;
259pub const O_DIRECT = 0o40000;
260pub const O_LARGEFILE = 0;
261pub const O_NOATIME = 0o1000000;
262pub const O_PATH = 0o10000000;
263pub const O_TMPFILE = 0o20200000;
264pub const O_NDELAY = O_NONBLOCK;
\ No newline at end of file
lib/std/os/test.zig+92-2
...@@ -3,6 +3,7 @@ const os = std.os;...@@ -3,6 +3,7 @@ const os = std.os;
3const testing = std.testing;3const testing = std.testing;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
6const expectError = testing.expectError;
6const io = std.io;7const io = std.io;
7const fs = std.fs;8const fs = std.fs;
8const mem = std.mem;9const mem = std.mem;
...@@ -19,6 +20,95 @@ const tmpDir = std.testing.tmpDir;...@@ -19,6 +20,95 @@ const tmpDir = std.testing.tmpDir;
19const Dir = std.fs.Dir;20const Dir = std.fs.Dir;
20const ArenaAllocator = std.heap.ArenaAllocator;21const ArenaAllocator = std.heap.ArenaAllocator;
2122
23test "open smoke test" {
24 if (builtin.os.tag == .wasi) return error.SkipZigTest;
25
26 // TODO verify file attributes using `fstat`
27
28 var tmp = tmpDir(.{});
29 defer tmp.cleanup();
30
31 // Get base abs path
32 var arena = ArenaAllocator.init(testing.allocator);
33 defer arena.deinit();
34
35 const base_path = blk: {
36 const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
37 break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
38 };
39
40 var file_path: []u8 = undefined;
41 var fd: os.fd_t = undefined;
42 const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
43
44 // Create some file using `open`.
45 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
46 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
47 os.close(fd);
48
49 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
50 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
51 expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
52
53 // Try opening without `O_EXCL` flag.
54 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
55 fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, mode);
56 os.close(fd);
57
58 // Try opening as a directory which should fail.
59 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
60 expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, mode));
61
62 // Create some directory
63 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
64 try os.mkdir(file_path, mode);
65
66 // Open dir using `open`
67 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
68 fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, mode);
69 os.close(fd);
70
71 // Try opening as file which should fail.
72 file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
73 expectError(error.IsDir, os.open(file_path, os.O_RDWR, mode));
74}
75
76test "openat smoke test" {
77 if (builtin.os.tag == .wasi) return error.SkipZigTest;
78
79 // TODO verify file attributes using `fstatat`
80
81 var tmp = tmpDir(.{});
82 defer tmp.cleanup();
83
84 var fd: os.fd_t = undefined;
85 const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
86
87 // Create some file using `openat`.
88 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
89 os.close(fd);
90
91 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
92 expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
93
94 // Try opening without `O_EXCL` flag.
95 fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, mode);
96 os.close(fd);
97
98 // Try opening as a directory which should fail.
99 expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, mode));
100
101 // Create some directory
102 try os.mkdirat(tmp.dir.fd, "some_dir", mode);
103
104 // Open dir using `open`
105 fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, mode);
106 os.close(fd);
107
108 // Try opening as file which should fail.
109 expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, mode));
110}
111
22test "symlink with relative paths" {112test "symlink with relative paths" {
23 if (builtin.os.tag == .wasi) return error.SkipZigTest;113 if (builtin.os.tag == .wasi) return error.SkipZigTest;
24114
...@@ -27,7 +117,7 @@ test "symlink with relative paths" {...@@ -27,7 +117,7 @@ test "symlink with relative paths" {
27 try cwd.writeFile("file.txt", "nonsense");117 try cwd.writeFile("file.txt", "nonsense");
28118
29 if (builtin.os.tag == .windows) {119 if (builtin.os.tag == .windows) {
30 try os.windows.CreateSymbolicLink(cwd.fd, "symlinked", "file.txt", false);120 try os.windows.CreateSymbolicLink(cwd.fd, &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
31 } else {121 } else {
32 try os.symlink("file.txt", "symlinked");122 try os.symlink("file.txt", "symlinked");
33 }123 }
...@@ -85,7 +175,7 @@ test "readlinkat" {...@@ -85,7 +175,7 @@ test "readlinkat" {
85175
86 // create a symbolic link176 // create a symbolic link
87 if (builtin.os.tag == .windows) {177 if (builtin.os.tag == .windows) {
88 try os.windows.CreateSymbolicLink(tmp.dir.fd, "link", "file.txt", false);178 try os.windows.CreateSymbolicLink(tmp.dir.fd, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
89 } else {179 } else {
90 try os.symlinkat("file.txt", tmp.dir.fd, "link");180 try os.symlinkat("file.txt", tmp.dir.fd, "link");
91 }181 }
lib/std/os/windows.zig+129-285
...@@ -25,76 +25,11 @@ pub usingnamespace @import("windows/bits.zig");...@@ -25,76 +25,11 @@ pub usingnamespace @import("windows/bits.zig");
2525
26pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));26pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
2727
28pub const CreateFileError = error{
29 SharingViolation,
30 PathAlreadyExists,
31
32 /// When any of the path components can not be found or the file component can not
33 /// be found. Some operating systems distinguish between path components not found and
34 /// file components not found, but they are collapsed into FileNotFound to gain
35 /// consistency across operating systems.
36 FileNotFound,
37
38 AccessDenied,
39 PipeBusy,
40 NameTooLong,
41
42 /// On Windows, file paths must be valid Unicode.
43 InvalidUtf8,
44
45 /// On Windows, file paths cannot contain these characters:
46 /// '/', '*', '?', '"', '<', '>', '|'
47 BadPathName,
48
49 Unexpected,
50};
51
52pub fn CreateFile(
53 file_path: []const u8,
54 desired_access: DWORD,
55 share_mode: DWORD,
56 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
57 creation_disposition: DWORD,
58 flags_and_attrs: DWORD,
59 hTemplateFile: ?HANDLE,
60) CreateFileError!HANDLE {
61 const file_path_w = try sliceToPrefixedFileW(file_path);
62 return CreateFileW(file_path_w.span().ptr, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
63}
64
65pub fn CreateFileW(
66 file_path_w: [*:0]const u16,
67 desired_access: DWORD,
68 share_mode: DWORD,
69 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
70 creation_disposition: DWORD,
71 flags_and_attrs: DWORD,
72 hTemplateFile: ?HANDLE,
73) CreateFileError!HANDLE {
74 const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
75
76 if (result == INVALID_HANDLE_VALUE) {
77 switch (kernel32.GetLastError()) {
78 .SHARING_VIOLATION => return error.SharingViolation,
79 .ALREADY_EXISTS => return error.PathAlreadyExists,
80 .FILE_EXISTS => return error.PathAlreadyExists,
81 .FILE_NOT_FOUND => return error.FileNotFound,
82 .PATH_NOT_FOUND => return error.FileNotFound,
83 .ACCESS_DENIED => return error.AccessDenied,
84 .PIPE_BUSY => return error.PipeBusy,
85 .FILENAME_EXCED_RANGE => return error.NameTooLong,
86 else => |err| return unexpectedError(err),
87 }
88 }
89
90 return result;
91}
92
93pub const OpenError = error{28pub const OpenError = error{
94 IsDir,29 IsDir,
30 NotDir,
95 FileNotFound,31 FileNotFound,
96 NoDevice,32 NoDevice,
97 SharingViolation,
98 AccessDenied,33 AccessDenied,
99 PipeBusy,34 PipeBusy,
100 PathAlreadyExists,35 PathAlreadyExists,
...@@ -111,15 +46,21 @@ pub const OpenFileOptions = struct {...@@ -111,15 +46,21 @@ pub const OpenFileOptions = struct {
111 share_access_nonblocking: bool = false,46 share_access_nonblocking: bool = false,
112 creation: ULONG,47 creation: ULONG,
113 io_mode: std.io.ModeOverride,48 io_mode: std.io.ModeOverride,
49 /// If true, tries to open path as a directory.
50 /// Defaults to false.
51 open_dir: bool = false,
52 /// If false, tries to open path as a reparse point without dereferencing it.
53 /// Defaults to true.
54 follow_symlinks: bool = true,
114};55};
11556
116/// TODO when share_access_nonblocking is false, this implementation uses57/// TODO when share_access_nonblocking is false, this implementation uses
117/// untinterruptible sleep() to block. This is not the final iteration of the API.58/// untinterruptible sleep() to block. This is not the final iteration of the API.
118pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {59pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
119 if (mem.eql(u16, sub_path_w, &[_]u16{'.'})) {60 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) {
120 return error.IsDir;61 return error.IsDir;
121 }62 }
122 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) {63 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) {
123 return error.IsDir;64 return error.IsDir;
124 }65 }
12566
...@@ -142,11 +83,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -142,11 +83,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
142 .SecurityQualityOfService = null,83 .SecurityQualityOfService = null,
143 };84 };
144 var io: IO_STATUS_BLOCK = undefined;85 var io: IO_STATUS_BLOCK = undefined;
86 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
87 const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE;
88 // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
89 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
14590
146 var delay: usize = 1;91 var delay: usize = 1;
147 while (true) {92 while (true) {
148 var flags: ULONG = undefined;
149 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
150 const rc = ntdll.NtCreateFile(93 const rc = ntdll.NtCreateFile(
151 &result,94 &result,
152 options.access_mask,95 options.access_mask,
...@@ -156,7 +99,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -156,7 +99,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
156 FILE_ATTRIBUTE_NORMAL,99 FILE_ATTRIBUTE_NORMAL,
157 options.share_access,100 options.share_access,
158 options.creation,101 options.creation,
159 FILE_NON_DIRECTORY_FILE | blocking_flag,102 flags,
160 null,103 null,
161 0,104 0,
162 );105 );
...@@ -184,6 +127,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -184,6 +127,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
184 .OBJECT_PATH_SYNTAX_BAD => unreachable,127 .OBJECT_PATH_SYNTAX_BAD => unreachable,
185 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,128 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
186 .FILE_IS_A_DIRECTORY => return error.IsDir,129 .FILE_IS_A_DIRECTORY => return error.IsDir,
130 .NOT_A_DIRECTORY => return error.NotDir,
187 else => return unexpectedStatus(rc),131 else => return unexpectedStatus(rc),
188 }132 }
189 }133 }
...@@ -215,30 +159,61 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,...@@ -215,30 +159,61 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
215 }159 }
216}160}
217161
162pub const DeviceIoControlError = error{Unexpected};
163
164/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
165/// It implements similar behavior to `DeviceIoControl` and is meant to serve
166/// as a direct substitute for that call.
167/// TODO work out if we need to expose other arguments to the underlying syscalls.
218pub fn DeviceIoControl(168pub fn DeviceIoControl(
219 h: HANDLE,169 h: HANDLE,
220 ioControlCode: DWORD,170 ioControlCode: ULONG,
221 in: ?[]const u8,171 in: ?[]const u8,
222 out: ?[]u8,172 out: ?[]u8,
223 overlapped: ?*OVERLAPPED,173) DeviceIoControlError!void {
224) !DWORD {174 // Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
225 var bytes: DWORD = undefined;175 const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
226 if (kernel32.DeviceIoControl(176
227 h,177 var io: IO_STATUS_BLOCK = undefined;
228 ioControlCode,178 const in_ptr = if (in) |i| i.ptr else null;
229 if (in) |i| i.ptr else null,179 const in_len = if (in) |i| @intCast(ULONG, i.len) else 0;
230 if (in) |i| @intCast(u32, i.len) else 0,180 const out_ptr = if (out) |o| o.ptr else null;
231 if (out) |o| o.ptr else null,181 const out_len = if (out) |o| @intCast(ULONG, o.len) else 0;
232 if (out) |o| @intCast(u32, o.len) else 0,182
233 &bytes,183 const rc = blk: {
234 overlapped,184 if (is_fsctl) {
235 ) == 0) {185 break :blk ntdll.NtFsControlFile(
236 switch (kernel32.GetLastError()) {186 h,
237 .IO_PENDING => if (overlapped == null) unreachable,187 null,
238 else => |err| return unexpectedError(err),188 null,
189 null,
190 &io,
191 ioControlCode,
192 in_ptr,
193 in_len,
194 out_ptr,
195 out_len,
196 );
197 } else {
198 break :blk ntdll.NtDeviceIoControlFile(
199 h,
200 null,
201 null,
202 null,
203 &io,
204 ioControlCode,
205 in_ptr,
206 in_len,
207 out_ptr,
208 out_len,
209 );
239 }210 }
211 };
212 switch (rc) {
213 .SUCCESS => {},
214 .INVALID_PARAMETER => unreachable,
215 else => return unexpectedStatus(rc),
240 }216 }
241 return bytes;
242}217}
243218
244pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {219pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
...@@ -607,27 +582,14 @@ pub const CreateSymbolicLinkError = error{...@@ -607,27 +582,14 @@ pub const CreateSymbolicLinkError = error{
607 PathAlreadyExists,582 PathAlreadyExists,
608 FileNotFound,583 FileNotFound,
609 NameTooLong,584 NameTooLong,
610 InvalidUtf8,
611 BadPathName,
612 NoDevice,585 NoDevice,
613 Unexpected,586 Unexpected,
614};587};
615588
616pub fn CreateSymbolicLink(589pub fn CreateSymbolicLink(
617 dir: ?HANDLE,590 dir: ?HANDLE,
618 sym_link_path: []const u8,591 sym_link_path: []const u16,
619 target_path: []const u8,592 target_path: []const u16,
620 is_directory: bool,
621) CreateSymbolicLinkError!void {
622 const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
623 const target_path_w = try sliceToPrefixedFileW(target_path);
624 return CreateSymbolicLinkW(dir, sym_link_path_w.span(), target_path_w.span(), is_directory);
625}
626
627pub fn CreateSymbolicLinkW(
628 dir: ?HANDLE,
629 sym_link_path: [:0]const u16,
630 target_path: [:0]const u16,
631 is_directory: bool,593 is_directory: bool,
632) CreateSymbolicLinkError!void {594) CreateSymbolicLinkError!void {
633 const SYMLINK_DATA = extern struct {595 const SYMLINK_DATA = extern struct {
...@@ -641,71 +603,19 @@ pub fn CreateSymbolicLinkW(...@@ -641,71 +603,19 @@ pub fn CreateSymbolicLinkW(
641 Flags: ULONG,603 Flags: ULONG,
642 };604 };
643605
644 var symlink_handle: HANDLE = undefined;606 const symlink_handle = OpenFile(sym_link_path, .{
645 if (is_directory) {607 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
646 const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {608 .dir = dir,
647 error.Overflow => return error.NameTooLong,609 .creation = FILE_CREATE,
648 };610 .io_mode = .blocking,
649 var nt_name = UNICODE_STRING{611 .open_dir = is_directory,
650 .Length = sym_link_len_bytes,612 }) catch |err| switch (err) {
651 .MaximumLength = sym_link_len_bytes,613 error.IsDir => return error.PathAlreadyExists,
652 .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),614 error.NotDir => unreachable,
653 };615 error.WouldBlock => unreachable,
654616 error.PipeBusy => unreachable,
655 if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {617 else => |e| return e,
656 // Windows does not recognize this, but it does work with empty string.618 };
657 nt_name.Length = 0;
658 }
659
660 var attr = OBJECT_ATTRIBUTES{
661 .Length = @sizeOf(OBJECT_ATTRIBUTES),
662 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir,
663 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
664 .ObjectName = &nt_name,
665 .SecurityDescriptor = null,
666 .SecurityQualityOfService = null,
667 };
668
669 var io: IO_STATUS_BLOCK = undefined;
670 const rc = ntdll.NtCreateFile(
671 &symlink_handle,
672 GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
673 &attr,
674 &io,
675 null,
676 FILE_ATTRIBUTE_NORMAL,
677 FILE_SHARE_READ,
678 FILE_CREATE,
679 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
680 null,
681 0,
682 );
683 switch (rc) {
684 .SUCCESS => {},
685 .OBJECT_NAME_INVALID => unreachable,
686 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
687 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
688 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
689 .INVALID_PARAMETER => unreachable,
690 .ACCESS_DENIED => return error.AccessDenied,
691 .OBJECT_PATH_SYNTAX_BAD => unreachable,
692 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
693 else => return unexpectedStatus(rc),
694 }
695 } else {
696 symlink_handle = OpenFile(sym_link_path, .{
697 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
698 .dir = dir,
699 .creation = FILE_CREATE,
700 .io_mode = .blocking,
701 }) catch |err| switch (err) {
702 error.WouldBlock => unreachable,
703 error.IsDir => return error.PathAlreadyExists,
704 error.PipeBusy => unreachable,
705 error.SharingViolation => return error.AccessDenied,
706 else => |e| return e,
707 };
708 }
709 defer CloseHandle(symlink_handle);619 defer CloseHandle(symlink_handle);
710620
711 // prepare reparse data buffer621 // prepare reparse data buffer
...@@ -727,8 +637,7 @@ pub fn CreateSymbolicLinkW(...@@ -727,8 +637,7 @@ pub fn CreateSymbolicLinkW(
727 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);637 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);
728 const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;638 const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;
729 @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);639 @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);
730 // TODO replace with NtDeviceIoControl640 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
731 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
732}641}
733642
734pub const ReadLinkError = error{643pub const ReadLinkError = error{
...@@ -737,44 +646,32 @@ pub const ReadLinkError = error{...@@ -737,44 +646,32 @@ pub const ReadLinkError = error{
737 Unexpected,646 Unexpected,
738 NameTooLong,647 NameTooLong,
739 UnsupportedReparsePointType,648 UnsupportedReparsePointType,
740 InvalidUtf8,
741 BadPathName,
742};649};
743650
744pub fn ReadLink(651pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
745 dir: ?HANDLE,652 // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
746 sub_path: []const u8,653 // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
747 out_buffer: []u8,654 // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
748) ReadLinkError![]u8 {655 // to open the symlink there and then.
749 const sub_path_w = try sliceToPrefixedFileW(sub_path);656 const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) {
750 return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);
751}
752
753pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
754 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
755 error.Overflow => return error.NameTooLong,657 error.Overflow => return error.NameTooLong,
756 };658 };
757 var nt_name = UNICODE_STRING{659 var nt_name = UNICODE_STRING{
758 .Length = path_len_bytes,660 .Length = path_len_bytes,
759 .MaximumLength = path_len_bytes,661 .MaximumLength = path_len_bytes,
760 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),662 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
761 };663 };
762
763 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
764 // Windows does not recognize this, but it does work with empty string.
765 nt_name.Length = 0;
766 }
767
768 var attr = OBJECT_ATTRIBUTES{664 var attr = OBJECT_ATTRIBUTES{
769 .Length = @sizeOf(OBJECT_ATTRIBUTES),665 .Length = @sizeOf(OBJECT_ATTRIBUTES),
770 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,666 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir,
771 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.667 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
772 .ObjectName = &nt_name,668 .ObjectName = &nt_name,
773 .SecurityDescriptor = null,669 .SecurityDescriptor = null,
774 .SecurityQualityOfService = null,670 .SecurityQualityOfService = null,
775 };671 };
776 var io: IO_STATUS_BLOCK = undefined;
777 var result_handle: HANDLE = undefined;672 var result_handle: HANDLE = undefined;
673 var io: IO_STATUS_BLOCK = undefined;
674
778 const rc = ntdll.NtCreateFile(675 const rc = ntdll.NtCreateFile(
779 &result_handle,676 &result_handle,
780 FILE_READ_ATTRIBUTES,677 FILE_READ_ATTRIBUTES,
...@@ -806,7 +703,7 @@ pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) Rea...@@ -806,7 +703,7 @@ pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) Rea
806 defer CloseHandle(result_handle);703 defer CloseHandle(result_handle);
807704
808 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;705 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
809 _ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null);706 _ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]);
810707
811 const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));708 const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
812 switch (reparse_struct.ReparseTag) {709 switch (reparse_struct.ReparseTag) {
...@@ -848,135 +745,83 @@ pub const DeleteFileError = error{...@@ -848,135 +745,83 @@ pub const DeleteFileError = error{
848 NameTooLong,745 NameTooLong,
849 FileBusy,746 FileBusy,
850 Unexpected,747 Unexpected,
748 NotDir,
749 IsDir,
851};750};
852751
853pub fn DeleteFile(filename: []const u8) DeleteFileError!void {752pub const DeleteFileOptions = struct {
854 const filename_w = try sliceToPrefixedFileW(filename);753 dir: ?HANDLE,
855 return DeleteFileW(filename_w.span().ptr);754 remove_dir: bool = false,
856}
857
858pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void {
859 if (kernel32.DeleteFileW(filename) == 0) {
860 switch (kernel32.GetLastError()) {
861 .FILE_NOT_FOUND => return error.FileNotFound,
862 .PATH_NOT_FOUND => return error.FileNotFound,
863 .ACCESS_DENIED => return error.AccessDenied,
864 .FILENAME_EXCED_RANGE => return error.NameTooLong,
865 .INVALID_PARAMETER => return error.NameTooLong,
866 .SHARING_VIOLATION => return error.FileBusy,
867 else => |err| return unexpectedError(err),
868 }
869 }
870}
871
872pub const MoveFileError = error{Unexpected};
873
874pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
875 const old_path_w = try sliceToPrefixedFileW(old_path);
876 const new_path_w = try sliceToPrefixedFileW(new_path);
877 return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
878}
879
880pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
881 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
882 switch (kernel32.GetLastError()) {
883 else => |err| return unexpectedError(err),
884 }
885 }
886}
887
888pub const CreateDirectoryError = error{
889 NameTooLong,
890 PathAlreadyExists,
891 FileNotFound,
892 NoDevice,
893 AccessDenied,
894 InvalidUtf8,
895 BadPathName,
896 Unexpected,
897};755};
898756
899/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.757pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
900pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {758 const create_options_flags: ULONG = if (options.remove_dir)
901 const pathname_w = try sliceToPrefixedFileW(pathname);759 FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
902 return CreateDirectoryW(dir, pathname_w.span().ptr, sa);760 else
903}761 FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
904762
905/// Same as `CreateDirectory` except takes a WTF-16 encoded path.763 const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
906pub fn CreateDirectoryW(
907 dir: ?HANDLE,
908 sub_path_w: [*:0]const u16,
909 sa: ?*SECURITY_ATTRIBUTES,
910) CreateDirectoryError!HANDLE {
911 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
912 error.Overflow => return error.NameTooLong,
913 };
914 var nt_name = UNICODE_STRING{764 var nt_name = UNICODE_STRING{
915 .Length = path_len_bytes,765 .Length = path_len_bytes,
916 .MaximumLength = path_len_bytes,766 .MaximumLength = path_len_bytes,
917 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),767 // The Windows API makes this mutable, but it will not mutate here.
768 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
918 };769 };
919770
920 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {771 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
921 // Windows does not recognize this, but it does work with empty string.772 // Windows does not recognize this, but it does work with empty string.
922 nt_name.Length = 0;773 nt_name.Length = 0;
923 }774 }
775 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
776 // Can't remove the parent directory with an open handle.
777 return error.FileBusy;
778 }
924779
925 var attr = OBJECT_ATTRIBUTES{780 var attr = OBJECT_ATTRIBUTES{
926 .Length = @sizeOf(OBJECT_ATTRIBUTES),781 .Length = @sizeOf(OBJECT_ATTRIBUTES),
927 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,782 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
928 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.783 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
929 .ObjectName = &nt_name,784 .ObjectName = &nt_name,
930 .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,785 .SecurityDescriptor = null,
931 .SecurityQualityOfService = null,786 .SecurityQualityOfService = null,
932 };787 };
933 var io: IO_STATUS_BLOCK = undefined;788 var io: IO_STATUS_BLOCK = undefined;
934 var result_handle: HANDLE = undefined;789 var tmp_handle: HANDLE = undefined;
935 const rc = ntdll.NtCreateFile(790 var rc = ntdll.NtCreateFile(
936 &result_handle,791 &tmp_handle,
937 GENERIC_READ | SYNCHRONIZE,792 SYNCHRONIZE | DELETE,
938 &attr,793 &attr,
939 &io,794 &io,
940 null,795 null,
941 FILE_ATTRIBUTE_NORMAL,796 0,
942 FILE_SHARE_READ,797 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
943 FILE_CREATE,798 FILE_OPEN,
944 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,799 create_options_flags,
945 null,800 null,
946 0,801 0,
947 );802 );
948 switch (rc) {803 switch (rc) {
949 .SUCCESS => return result_handle,804 .SUCCESS => return CloseHandle(tmp_handle),
950 .OBJECT_NAME_INVALID => unreachable,805 .OBJECT_NAME_INVALID => unreachable,
951 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,806 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
952 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
953 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
954 .INVALID_PARAMETER => unreachable,807 .INVALID_PARAMETER => unreachable,
955 .ACCESS_DENIED => return error.AccessDenied,808 .FILE_IS_A_DIRECTORY => return error.IsDir,
956 .OBJECT_PATH_SYNTAX_BAD => unreachable,809 .NOT_A_DIRECTORY => return error.NotDir,
957 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
958 else => return unexpectedStatus(rc),810 else => return unexpectedStatus(rc),
959 }811 }
960}812}
961813
962pub const RemoveDirectoryError = error{814pub const MoveFileError = error{Unexpected};
963 FileNotFound,
964 DirNotEmpty,
965 Unexpected,
966 NotDir,
967};
968815
969pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {816pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
970 const dir_path_w = try sliceToPrefixedFileW(dir_path);817 const old_path_w = try sliceToPrefixedFileW(old_path);
971 return RemoveDirectoryW(dir_path_w.span().ptr);818 const new_path_w = try sliceToPrefixedFileW(new_path);
819 return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
972}820}
973821
974pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void {822pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
975 if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {823 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
976 switch (kernel32.GetLastError()) {824 switch (kernel32.GetLastError()) {
977 .PATH_NOT_FOUND => return error.FileNotFound,
978 .DIR_NOT_EMPTY => return error.DirNotEmpty,
979 .DIRECTORY => return error.NotDir,
980 else => |err| return unexpectedError(err),825 else => |err| return unexpectedError(err),
981 }826 }
982 }827 }
...@@ -1463,8 +1308,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {...@@ -1463,8 +1308,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
1463}1308}
14641309
1465/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,1310/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1466/// it will get NT-style prefix `\??\` prepended automatically. For prepending1311/// it will get NT-style prefix `\??\` prepended automatically.
1467/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
1468pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {1312pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
1469 // TODO https://github.com/ziglang/zig/issues/27651313 // TODO https://github.com/ziglang/zig/issues/2765
1470 var path_space: PathSpace = undefined;1314 var path_space: PathSpace = undefined;
lib/std/os/windows/ntdll.zig+12
...@@ -54,6 +54,18 @@ pub extern "NtDll" fn NtDeviceIoControlFile(...@@ -54,6 +54,18 @@ pub extern "NtDll" fn NtDeviceIoControlFile(
54 OutputBuffer: ?PVOID,54 OutputBuffer: ?PVOID,
55 OutputBufferLength: ULONG,55 OutputBufferLength: ULONG,
56) callconv(.Stdcall) NTSTATUS;56) callconv(.Stdcall) NTSTATUS;
57pub extern "NtDll" fn NtFsControlFile(
58 FileHandle: HANDLE,
59 Event: ?HANDLE,
60 ApcRoutine: ?IO_APC_ROUTINE,
61 ApcContext: ?*c_void,
62 IoStatusBlock: *IO_STATUS_BLOCK,
63 FsControlCode: ULONG,
64 InputBuffer: ?*const c_void,
65 InputBufferLength: ULONG,
66 OutputBuffer: ?PVOID,
67 OutputBufferLength: ULONG,
68) callconv(.Stdcall) NTSTATUS;
57pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS;69pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS;
58pub extern "NtDll" fn RtlDosPathNameToNtPathName_U(70pub extern "NtDll" fn RtlDosPathNameToNtPathName_U(
59 DosPathName: [*:0]const u16,71 DosPathName: [*:0]const u16,
lib/std/special/compiler_rt.zig+1
...@@ -92,6 +92,7 @@ comptime {...@@ -92,6 +92,7 @@ comptime {
92 @export(@import("compiler_rt/floatunsidf.zig").__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });92 @export(@import("compiler_rt/floatunsidf.zig").__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });
93 @export(@import("compiler_rt/floatundidf.zig").__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });93 @export(@import("compiler_rt/floatundidf.zig").__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });
9494
95 @export(@import("compiler_rt/floatditf.zig").__floatditf, .{ .name = "__floatditf", .linkage = linkage });
95 @export(@import("compiler_rt/floattitf.zig").__floattitf, .{ .name = "__floattitf", .linkage = linkage });96 @export(@import("compiler_rt/floattitf.zig").__floattitf, .{ .name = "__floattitf", .linkage = linkage });
96 @export(@import("compiler_rt/floattidf.zig").__floattidf, .{ .name = "__floattidf", .linkage = linkage });97 @export(@import("compiler_rt/floattidf.zig").__floattidf, .{ .name = "__floattidf", .linkage = linkage });
97 @export(@import("compiler_rt/floattisf.zig").__floattisf, .{ .name = "__floattisf", .linkage = linkage });98 @export(@import("compiler_rt/floattisf.zig").__floattisf, .{ .name = "__floattisf", .linkage = linkage });
lib/std/special/compiler_rt/floatditf.zig created+38
...@@ -0,0 +1,38 @@
1const builtin = @import("builtin");
2const is_test = builtin.is_test;
3const std = @import("std");
4const maxInt = std.math.maxInt;
5
6const significandBits = 112;
7const exponentBias = 16383;
8const implicitBit = (@as(u128, 1) << significandBits);
9
10pub fn __floatditf(arg: i64) callconv(.C) f128 {
11 @setRuntimeSafety(is_test);
12
13 if (arg == 0)
14 return 0.0;
15
16 // All other cases begin by extracting the sign and absolute value of a
17 var sign: u128 = 0;
18 var aAbs = @bitCast(u64, arg);
19 if (arg < 0) {
20 sign = 1 << 127;
21 aAbs = ~@bitCast(u64, arg)+ 1;
22 }
23
24 // Exponent of (fp_t)a is the width of abs(a).
25 const exponent = 63 - @clz(u64, aAbs);
26 var result: u128 = undefined;
27
28 // Shift a into the significand field, rounding if it is a right-shift
29 const shift = significandBits - exponent;
30 result = @as(u128, aAbs) << shift ^ implicitBit;
31
32 result += (@as(u128, exponent) + exponentBias) << significandBits;
33 return @bitCast(f128, result | sign);
34}
35
36test "import floatditf" {
37 _ = @import("floatditf_test.zig");
38}
lib/std/special/compiler_rt/floatditf_test.zig created+26
...@@ -0,0 +1,26 @@
1const __floatditf = @import("floatditf.zig").__floatditf;
2const testing = @import("std").testing;
3
4fn test__floatditf(a: i64, expected: f128) void {
5 const x = __floatditf(a);
6 testing.expect(x == expected);
7}
8
9test "floatditf" {
10 test__floatditf(0x7fffffffffffffff, make_ti(0x403dffffffffffff, 0xfffc000000000000));
11 test__floatditf(0x123456789abcdef1, make_ti(0x403b23456789abcd, 0xef10000000000000));
12 test__floatditf(0x2, make_ti(0x4000000000000000, 0x0));
13 test__floatditf(0x1, make_ti(0x3fff000000000000, 0x0));
14 test__floatditf(0x0, make_ti(0x0, 0x0));
15 test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_ti(0xbfff000000000000, 0x0));
16 test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_ti(0xc000000000000000, 0x0));
17 test__floatditf(-0x123456789abcdef1, make_ti(0xc03b23456789abcd, 0xef10000000000000));
18 test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_ti(0xc03e000000000000, 0x0));
19}
20
21fn make_ti(high: u64, low: u64) f128 {
22 var result: u128 = high;
23 result <<= 64;
24 result |= low;
25 return @bitCast(f128, result);
26}
lib/std/testing.zig+53
...@@ -171,6 +171,59 @@ test "expectEqual.union(enum)" {...@@ -171,6 +171,59 @@ test "expectEqual.union(enum)" {
171 expectEqual(a10, a10);171 expectEqual(a10, a10);
172}172}
173173
174/// This function is intended to be used only in tests. When the actual value is not
175/// within the margin of the expected value,
176/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
177/// The types must be floating point
178pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void {
179 std.debug.assert(margin >= 0.0);
180
181 switch (@typeInfo(@TypeOf(actual))) {
182 .Float,
183 .ComptimeFloat,
184 => {
185 if (@fabs(expected - actual) > margin) {
186 std.debug.panic("actual {}, not within margin {} of expected {}", .{ actual, margin, expected });
187 }
188 },
189 else => @compileError("Unable to compare non floating point values"),
190 }
191}
192
193test "expectWithinMargin.f32" {
194 const x: f32 = 12.0;
195 const y: f32 = 12.06;
196
197 expectWithinMargin(x, y, 0.1);
198}
199
200/// This function is intended to be used only in tests. When the actual value is not
201/// within the epsilon of the expected value,
202/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
203/// The types must be floating point
204pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void {
205 std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0);
206
207 const margin = epsilon * expected;
208 switch (@typeInfo(@TypeOf(actual))) {
209 .Float,
210 .ComptimeFloat,
211 => {
212 if (@fabs(expected - actual) > margin) {
213 std.debug.panic("actual {}, not within epsilon {}, of expected {}", .{ actual, epsilon, expected });
214 }
215 },
216 else => @compileError("Unable to compare non floating point values"),
217 }
218}
219
220test "expectWithinEpsilon.f32" {
221 const x: f32 = 12.0;
222 const y: f32 = 13.2;
223
224 expectWithinEpsilon(x, y, 0.1);
225}
226
174/// This function is intended to be used only in tests. When the two slices are not227/// This function is intended to be used only in tests. When the two slices are not
175/// equal, prints diagnostics to stderr to show exactly how they are not equal,228/// equal, prints diagnostics to stderr to show exactly how they are not equal,
176/// then aborts.229/// then aborts.
lib/std/zig.zig+16
...@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi...@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
43 return .{ .line = line, .column = column };43 return .{ .line = line, .column = column };
44}44}
4545
46pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
47 var line: isize = 0;
48 if (end >= start) {
49 for (source[start..end]) |byte| switch (byte) {
50 '\n' => line += 1,
51 else => continue,
52 };
53 } else {
54 for (source[end..start]) |byte| switch (byte) {
55 '\n' => line -= 1,
56 else => continue,
57 };
58 }
59 return line;
60}
61
46/// Returns the standard file system basename of a binary generated by the Zig compiler.62/// Returns the standard file system basename of a binary generated by the Zig compiler.
47pub fn binNameAlloc(63pub fn binNameAlloc(
48 allocator: *std.mem.Allocator,64 allocator: *std.mem.Allocator,
lib/std/zig/ast.zig+6-2
...@@ -1299,6 +1299,10 @@ pub const Node = struct {...@@ -1299,6 +1299,10 @@ pub const Node = struct {
1299 });1299 });
1300 }1300 }
13011301
1302 pub fn body(self: *const FnProto) ?*Node {
1303 return self.getTrailer("body_node");
1304 }
1305
1302 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {1306 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
1303 const trailers_start = @alignCast(1307 const trailers_start = @alignCast(
1304 @alignOf(ParamDecl),1308 @alignOf(ParamDecl),
...@@ -1381,7 +1385,7 @@ pub const Node = struct {...@@ -1381,7 +1385,7 @@ pub const Node = struct {
1381 .Invalid => {},1385 .Invalid => {},
1382 }1386 }
13831387
1384 if (self.getTrailer("body_node")) |body_node| {1388 if (self.body()) |body_node| {
1385 if (i < 1) return body_node;1389 if (i < 1) return body_node;
1386 i -= 1;1390 i -= 1;
1387 }1391 }
...@@ -1397,7 +1401,7 @@ pub const Node = struct {...@@ -1397,7 +1401,7 @@ pub const Node = struct {
1397 }1401 }
13981402
1399 pub fn lastToken(self: *const FnProto) TokenIndex {1403 pub fn lastToken(self: *const FnProto) TokenIndex {
1400 if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();1404 if (self.body()) |body_node| return body_node.lastToken();
1401 switch (self.return_type) {1405 switch (self.return_type) {
1402 .Explicit, .InferErrorSet => |node| return node.lastToken(),1406 .Explicit, .InferErrorSet => |node| return node.lastToken(),
1403 .Invalid => |tok| return tok,1407 .Invalid => |tok| return tok,
lib/std/zig/parse.zig+10-1
...@@ -201,7 +201,16 @@ const Parser = struct {...@@ -201,7 +201,16 @@ const Parser = struct {
201 p.findNextContainerMember();201 p.findNextContainerMember();
202 const next = p.token_ids[p.tok_i];202 const next = p.token_ids[p.tok_i];
203 switch (next) {203 switch (next) {
204 .Eof => break,204 .Eof => {
205 // no invalid tokens were found
206 if (index == p.tok_i) break;
207
208 // Invalid tokens, add error and exit
209 try p.errors.append(p.gpa, .{
210 .ExpectedToken = .{ .token = index, .expected_id = .Comma },
211 });
212 break;
213 },
205 else => {214 else => {
206 if (next == .RBrace) {215 if (next == .RBrace) {
207 if (!top_level) break;216 if (!top_level) break;
lib/std/zig/parser_test.zig+8
...@@ -293,6 +293,14 @@ test "zig fmt: decl between fields" {...@@ -293,6 +293,14 @@ test "zig fmt: decl between fields" {
293 });293 });
294}294}
295295
296test "zig fmt: eof after missing comma" {
297 try testError(
298 \\foo()
299 , &[_]Error{
300 .ExpectedToken,
301 });
302}
303
296test "zig fmt: errdefer with payload" {304test "zig fmt: errdefer with payload" {
297 try testCanonical(305 try testCanonical(
298 \\pub fn main() anyerror!void {306 \\pub fn main() anyerror!void {
lib/std/zig/string_literal.zig-1
...@@ -104,7 +104,6 @@ pub fn parse(...@@ -104,7 +104,6 @@ pub fn parse(
104 return error.InvalidCharacter;104 return error.InvalidCharacter;
105 },105 },
106 },106 },
107 else => unreachable,
108 }107 }
109 }108 }
110 unreachable;109 unreachable;
src-self-hosted/Module.zig+196-1281
...@@ -6,6 +6,7 @@ const Value = @import("value.zig").Value;...@@ -6,6 +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 BigIntConst = std.math.big.int.Const;10const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;11const BigIntMutable = std.math.big.int.Mutable;
11const Target = std.Target;12const Target = std.Target;
...@@ -20,6 +21,7 @@ const ast = std.zig.ast;...@@ -20,6 +21,7 @@ const ast = std.zig.ast;
20const trace = @import("tracy.zig").trace;21const trace = @import("tracy.zig").trace;
21const liveness = @import("liveness.zig");22const liveness = @import("liveness.zig");
22const astgen = @import("astgen.zig");23const astgen = @import("astgen.zig");
24const zir_sema = @import("zir_sema.zig");
2325
24/// General-purpose allocator. Used for both temporary and long-term storage.26/// General-purpose allocator. Used for both temporary and long-term storage.
25gpa: *Allocator,27gpa: *Allocator,
...@@ -46,7 +48,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},...@@ -46,7 +48,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
46/// Maps fully qualified namespaced names to the Decl struct for them.48/// Maps fully qualified namespaced names to the Decl struct for them.
47decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},49decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
4850
49optimize_mode: std.builtin.Mode,
50link_error_flags: link.File.ErrorFlags = .{},51link_error_flags: link.File.ErrorFlags = .{},
5152
52work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),53work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
...@@ -75,6 +76,8 @@ next_anon_name_index: usize = 0,...@@ -75,6 +76,8 @@ next_anon_name_index: usize = 0,
75/// contains Decls that need to be deleted if they end up having no references to them.76/// contains Decls that need to be deleted if they end up having no references to them.
76deletion_set: std.ArrayListUnmanaged(*Decl) = .{},77deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
7778
79/// Owned by Module.
80root_name: []u8,
78keep_source_files_loaded: bool,81keep_source_files_loaded: bool,
7982
80pub const InnerError = error{ OutOfMemory, AnalysisFail };83pub const InnerError = error{ OutOfMemory, AnalysisFail };
...@@ -86,6 +89,9 @@ const WorkItem = union(enum) {...@@ -86,6 +89,9 @@ const WorkItem = union(enum) {
86 /// It may have already be analyzed, or it may have been determined89 /// It may have already be analyzed, or it may have been determined
87 /// to be outdated; in this case perform semantic analysis again.90 /// to be outdated; in this case perform semantic analysis again.
88 analyze_decl: *Decl,91 analyze_decl: *Decl,
92 /// The source file containing the Decl has been updated, and so the
93 /// Decl may need its line number information updated in the debug info.
94 update_line_number: *Decl,
89};95};
9096
91pub const Export = struct {97pub const Export = struct {
...@@ -173,6 +179,13 @@ pub const Decl = struct {...@@ -173,6 +179,13 @@ pub const Decl = struct {
173 /// This is populated regardless of semantic analysis and code generation.179 /// This is populated regardless of semantic analysis and code generation.
174 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,180 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
175181
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 but
184 /// `Fn` does not.
185 /// TODO Look into making `Fn` a longer lived structure and moving this field there
186 /// to save on memory usage.
187 fn_link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,
188
176 contents_hash: std.zig.SrcHash,189 contents_hash: std.zig.SrcHash,
177190
178 /// The shallow set of other decls whose typed_value could possibly change if this Decl's191 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
...@@ -233,7 +246,7 @@ pub const Decl = struct {...@@ -233,7 +246,7 @@ pub const Decl = struct {
233246
234 pub fn dump(self: *Decl) void {247 pub fn dump(self: *Decl) void {
235 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);248 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
236 std.debug.warn("{}:{}:{} name={} status={}", .{249 std.debug.print("{}:{}:{} name={} status={}", .{
237 self.scope.sub_file_path,250 self.scope.sub_file_path,
238 loc.line + 1,251 loc.line + 1,
239 loc.column + 1,252 loc.column + 1,
...@@ -241,12 +254,12 @@ pub const Decl = struct {...@@ -241,12 +254,12 @@ pub const Decl = struct {
241 @tagName(self.analysis),254 @tagName(self.analysis),
242 });255 });
243 if (self.typedValueManaged()) |tvm| {256 if (self.typedValueManaged()) |tvm| {
244 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });257 std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
245 }258 }
246 std.debug.warn("\n", .{});259 std.debug.print("\n", .{});
247 }260 }
248261
249 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {262 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
250 switch (self.typed_value) {263 switch (self.typed_value) {
251 .most_recent => |*x| return x,264 .most_recent => |*x| return x,
252 .never_succeeded => return null,265 .never_succeeded => return null,
...@@ -384,18 +397,6 @@ pub const Scope = struct {...@@ -384,18 +397,6 @@ pub const Scope = struct {
384 };397 };
385 }398 }
386399
387 pub fn dumpInst(self: *Scope, inst: *Inst) void {
388 const zir_module = self.namespace();
389 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
390 std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
391 zir_module.sub_file_path,
392 loc.line + 1,
393 loc.column + 1,
394 @tagName(inst.tag),
395 inst.ty,
396 });
397 }
398
399 /// Asserts the scope has a parent which is a ZIRModule or File and400 /// Asserts the scope has a parent which is a ZIRModule or File and
400 /// returns the sub_file_path field.401 /// returns the sub_file_path field.
401 pub fn subFilePath(base: *Scope) []const u8 {402 pub fn subFilePath(base: *Scope) []const u8 {
...@@ -551,7 +552,7 @@ pub const Scope = struct {...@@ -551,7 +552,7 @@ pub const Scope = struct {
551552
552 pub fn dumpSrc(self: *File, src: usize) void {553 pub fn dumpSrc(self: *File, src: usize) void {
553 const loc = std.zig.findLineColumn(self.source.bytes, src);554 const loc = std.zig.findLineColumn(self.source.bytes, src);
554 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });555 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
555 }556 }
556557
557 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {558 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
...@@ -653,7 +654,7 @@ pub const Scope = struct {...@@ -653,7 +654,7 @@ pub const Scope = struct {
653654
654 pub fn dumpSrc(self: *ZIRModule, src: usize) void {655 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
655 const loc = std.zig.findLineColumn(self.source.bytes, src);656 const loc = std.zig.findLineColumn(self.source.bytes, src);
656 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });657 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
657 }658 }
658659
659 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {660 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
...@@ -784,6 +785,7 @@ pub const AllErrors = struct {...@@ -784,6 +785,7 @@ pub const AllErrors = struct {
784785
785pub const InitOptions = struct {786pub const InitOptions = struct {
786 target: std.Target,787 target: std.Target,
788 root_name: []const u8,
787 root_pkg: *Package,789 root_pkg: *Package,
788 output_mode: std.builtin.OutputMode,790 output_mode: std.builtin.OutputMode,
789 bin_file_dir: ?std.fs.Dir = null,791 bin_file_dir: ?std.fs.Dir = null,
...@@ -795,12 +797,18 @@ pub const InitOptions = struct {...@@ -795,12 +797,18 @@ pub const InitOptions = struct {
795};797};
796798
797pub fn init(gpa: *Allocator, options: InitOptions) !Module {799pub fn init(gpa: *Allocator, options: InitOptions) !Module {
800 const root_name = try gpa.dupe(u8, options.root_name);
801 errdefer gpa.free(root_name);
802
798 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();803 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
799 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{804 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
805 .root_name = root_name,
806 .root_pkg = options.root_pkg,
800 .target = options.target,807 .target = options.target,
801 .output_mode = options.output_mode,808 .output_mode = options.output_mode,
802 .link_mode = options.link_mode orelse .Static,809 .link_mode = options.link_mode orelse .Static,
803 .object_format = options.object_format orelse options.target.getObjectFormat(),810 .object_format = options.object_format orelse options.target.getObjectFormat(),
811 .optimize_mode = options.optimize_mode,
804 });812 });
805 errdefer bin_file.destroy();813 errdefer bin_file.destroy();
806814
...@@ -832,12 +840,12 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -832,12 +840,12 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
832840
833 return Module{841 return Module{
834 .gpa = gpa,842 .gpa = gpa,
843 .root_name = root_name,
835 .root_pkg = options.root_pkg,844 .root_pkg = options.root_pkg,
836 .root_scope = root_scope,845 .root_scope = root_scope,
837 .bin_file_dir = bin_file_dir,846 .bin_file_dir = bin_file_dir,
838 .bin_file_path = options.bin_file_path,847 .bin_file_path = options.bin_file_path,
839 .bin_file = bin_file,848 .bin_file = bin_file,
840 .optimize_mode = options.optimize_mode,
841 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),849 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
842 .keep_source_files_loaded = options.keep_source_files_loaded,850 .keep_source_files_loaded = options.keep_source_files_loaded,
843 };851 };
...@@ -846,6 +854,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -846,6 +854,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
846pub fn deinit(self: *Module) void {854pub fn deinit(self: *Module) void {
847 self.bin_file.destroy();855 self.bin_file.destroy();
848 const gpa = self.gpa;856 const gpa = self.gpa;
857 self.gpa.free(self.root_name);
849 self.deletion_set.deinit(gpa);858 self.deletion_set.deinit(gpa);
850 self.work_queue.deinit();859 self.work_queue.deinit();
851860
...@@ -887,13 +896,18 @@ pub fn deinit(self: *Module) void {...@@ -887,13 +896,18 @@ pub fn deinit(self: *Module) void {
887896
888fn freeExportList(gpa: *Allocator, export_list: []*Export) void {897fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
889 for (export_list) |exp| {898 for (export_list) |exp| {
899 gpa.free(exp.options.name);
890 gpa.destroy(exp);900 gpa.destroy(exp);
891 }901 }
892 gpa.free(export_list);902 gpa.free(export_list);
893}903}
894904
895pub fn target(self: Module) std.Target {905pub fn target(self: Module) std.Target {
896 return self.bin_file.options().target;906 return self.bin_file.options.target;
907}
908
909pub fn optimizeMode(self: Module) std.builtin.Mode {
910 return self.bin_file.options.optimize_mode;
897}911}
898912
899/// Detect changes to source files, perform semantic analysis, and update the output files.913/// Detect changes to source files, perform semantic analysis, and update the output files.
...@@ -941,7 +955,6 @@ pub fn update(self: *Module) !void {...@@ -941,7 +955,6 @@ pub fn update(self: *Module) !void {
941 }955 }
942956
943 self.link_error_flags = self.bin_file.errorFlags();957 self.link_error_flags = self.bin_file.errorFlags();
944 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
945958
946 // If there are any errors, we anticipate the source files being loaded959 // If there are any errors, we anticipate the source files being loaded
947 // to report error messages. Otherwise we unload all source files to save memory.960 // to report error messages. Otherwise we unload all source files to save memory.
...@@ -1055,22 +1068,14 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1055,22 +1068,14 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1055 error.AnalysisFail => {1068 error.AnalysisFail => {
1056 decl.analysis = .dependency_failure;1069 decl.analysis = .dependency_failure;
1057 },1070 },
1058 error.CGenFailure => {
1059 // Error is handled by CBE, don't try adding it again
1060 },
1061 else => {1071 else => {
1062 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);1072 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1063 const result = self.failed_decls.getOrPutAssumeCapacity(decl);1073 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1064 if (result.found_existing) {1074 self.gpa,
1065 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });1075 decl.src(),
1066 } else {1076 "unable to codegen: {}",
1067 result.entry.value = try ErrorMsg.create(1077 .{@errorName(err)},
1068 self.gpa,1078 ));
1069 decl.src(),
1070 "unable to codegen: {}",
1071 .{@errorName(err)},
1072 );
1073 }
1074 decl.analysis = .codegen_failure_retryable;1079 decl.analysis = .codegen_failure_retryable;
1075 },1080 },
1076 };1081 };
...@@ -1082,10 +1087,22 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1082,10 +1087,22 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1082 error.AnalysisFail => continue,1087 error.AnalysisFail => continue,
1083 };1088 };
1084 },1089 },
1090 .update_line_number => |decl| {
1091 self.bin_file.updateDeclLineNumber(self, decl) catch |err| {
1092 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1093 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1094 self.gpa,
1095 decl.src(),
1096 "unable to update line number: {}",
1097 .{@errorName(err)},
1098 ));
1099 decl.analysis = .codegen_failure_retryable;
1100 };
1101 },
1085 };1102 };
1086}1103}
10871104
1088fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {1105pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1089 const tracy = trace(@src());1106 const tracy = trace(@src());
1090 defer tracy.end();1107 defer tracy.end();
10911108
...@@ -1099,12 +1116,10 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {...@@ -1099,12 +1116,10 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1099 .codegen_failure_retryable,1116 .codegen_failure_retryable,
1100 => return error.AnalysisFail,1117 => return error.AnalysisFail,
11011118
1102 .complete, .outdated => blk: {1119 .complete => return,
1103 if (decl.generation == self.generation) {1120
1104 assert(decl.analysis == .complete);1121 .outdated => blk: {
1105 return;1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});
1106 }
1107 //std.debug.warn("re-analyzing {}\n", .{decl.name});
11081123
1109 // The exports this Decl performs will be re-discovered, so we remove them here1124 // The exports this Decl performs will be re-discovered, so we remove them here
1110 // prior to re-analysis.1125 // prior to re-analysis.
...@@ -1129,7 +1144,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {...@@ -1129,7 +1144,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1129 };1144 };
11301145
1131 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|1146 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
1132 try self.analyzeZirDecl(decl, zir_module.contents.module.decls[decl.src_index])1147 try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
1133 else1148 else
1134 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {1149 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
1135 error.OutOfMemory => return error.OutOfMemory,1150 error.OutOfMemory => return error.OutOfMemory,
...@@ -1205,7 +1220,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1205,7 +1220,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1205 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);1220 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
12061221
1207 const fn_src = tree.token_locs[fn_proto.fn_token].start;1222 const fn_src = tree.token_locs[fn_proto.fn_token].start;
1208 const type_type = try self.addZIRInstConst(&fn_type_scope.base, fn_src, .{1223 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
1209 .ty = Type.initTag(.type),1224 .ty = Type.initTag(.type),
1210 .val = Value.initTag(.type_type),1225 .val = Value.initTag(.type_type),
1211 });1226 });
...@@ -1244,11 +1259,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1244,11 +1259,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1244 };1259 };
12451260
1246 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);1261 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
1247 const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{1262 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1248 .return_type = return_type_inst,1263 .return_type = return_type_inst,
1249 .param_types = param_types,1264 .param_types = param_types,
1250 }, .{});1265 }, .{});
1251 _ = try self.addZIRUnOp(&fn_type_scope.base, fn_src, .@"return", fn_type_inst);1266 _ = try astgen.addZIRUnOp(self, &fn_type_scope.base, fn_src, .@"return", fn_type_inst);
12521267
1253 // We need the memory for the Type to go into the arena for the Decl1268 // We need the memory for the Type to go into the arena for the Decl
1254 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);1269 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
...@@ -1264,7 +1279,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1264,7 +1279,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1264 };1279 };
1265 defer block_scope.instructions.deinit(self.gpa);1280 defer block_scope.instructions.deinit(self.gpa);
12661281
1267 const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{1282 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
1268 .instructions = fn_type_scope.instructions.items,1283 .instructions = fn_type_scope.instructions.items,
1269 });1284 });
1270 const new_func = try decl_arena.allocator.create(Fn);1285 const new_func = try decl_arena.allocator.create(Fn);
...@@ -1317,7 +1332,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1317,7 +1332,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1317 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))1332 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
1318 {1333 {
1319 const src = tree.token_locs[body_block.rbrace].start;1334 const src = tree.token_locs[body_block.rbrace].start;
1320 _ = try self.addZIRNoOp(&gen_scope.base, src, .returnvoid);1335 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
1321 }1336 }
13221337
1323 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);1338 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
...@@ -1387,19 +1402,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1387,19 +1402,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1387 }1402 }
1388}1403}
13891404
1390fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
1391 try self.analyzeBody(&block_scope.base, body);
1392 for (block_scope.instructions.items) |inst| {
1393 if (inst.castTag(.ret)) |ret| {
1394 const val = try self.resolveConstValue(&block_scope.base, ret.operand);
1395 return val.toType();
1396 } else {
1397 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
1398 }
1399 }
1400 unreachable;
1401}
1402
1403fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {1405fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
1404 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);1406 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
1405 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);1407 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
...@@ -1492,6 +1494,9 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1492,6 +1494,9 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1492}1494}
14931495
1494fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {1496fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1497 const tracy = trace(@src());
1498 defer tracy.end();
1499
1495 // We may be analyzing it for the first time, or this may be1500 // We may be analyzing it for the first time, or this may be
1496 // an incremental update. This code handles both cases.1501 // an incremental update. This code handles both cases.
1497 const tree = try self.getAstTree(root_scope);1502 const tree = try self.getAstTree(root_scope);
...@@ -1533,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1533,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1533 if (!srcHashEql(decl.contents_hash, contents_hash)) {1538 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1534 try self.markOutdatedDecl(decl);1539 try self.markOutdatedDecl(decl);
1535 decl.contents_hash = contents_hash;1540 decl.contents_hash = contents_hash;
1541 } else if (decl.fn_link.len != 0) {
1542 // TODO Look into detecting when this would be unnecessary by storing enough state
1543 // in `Decl` to notice that the line number did not change.
1544 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1536 }1545 }
1537 }1546 }
1538 } else {1547 } else {
...@@ -1551,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1551,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1551 // Handle explicitly deleted decls from the source code. Not to be confused1560 // Handle explicitly deleted decls from the source code. Not to be confused
1552 // with when we delete decls because they are no longer referenced.1561 // with when we delete decls because they are no longer referenced.
1553 for (deleted_decls.items()) |entry| {1562 for (deleted_decls.items()) |entry| {
1554 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});1563 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1555 try self.deleteDecl(entry.key);1564 try self.deleteDecl(entry.key);
1556 }1565 }
1557}1566}
...@@ -1580,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1580,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1580 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);1589 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
1581 if (self.decl_table.get(name_hash)) |decl| {1590 if (self.decl_table.get(name_hash)) |decl| {
1582 deleted_decls.removeAssertDiscard(decl);1591 deleted_decls.removeAssertDiscard(decl);
1583 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
1584 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {1592 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1585 try self.markOutdatedDecl(decl);1593 try self.markOutdatedDecl(decl);
1586 decl.contents_hash = src_decl.contents_hash;1594 decl.contents_hash = src_decl.contents_hash;
...@@ -1600,12 +1608,12 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1600,12 +1608,12 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1600 }1608 }
1601 }1609 }
1602 for (exports_to_resolve.items) |export_decl| {1610 for (exports_to_resolve.items) |export_decl| {
1603 _ = try self.resolveZirDecl(&root_scope.base, export_decl);1611 _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
1604 }1612 }
1605 // Handle explicitly deleted decls from the source code. Not to be confused1613 // Handle explicitly deleted decls from the source code. Not to be confused
1606 // with when we delete decls because they are no longer referenced.1614 // with when we delete decls because they are no longer referenced.
1607 for (deleted_decls.items()) |entry| {1615 for (deleted_decls.items()) |entry| {
1608 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});1616 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1609 try self.deleteDecl(entry.key);1617 try self.deleteDecl(entry.key);
1610 }1618 }
1611}1619}
...@@ -1617,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -1617,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
1617 // not be present in the set, and this does nothing.1625 // not be present in the set, and this does nothing.
1618 decl.scope.removeDecl(decl);1626 decl.scope.removeDecl(decl);
16191627
1620 //std.debug.warn("deleting decl '{}'\n", .{decl.name});1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});
1621 const name_hash = decl.fullyQualifiedNameHash();1629 const name_hash = decl.fullyQualifiedNameHash();
1622 self.decl_table.removeAssertDiscard(name_hash);1630 self.decl_table.removeAssertDiscard(name_hash);
1623 // Remove itself from its dependencies, because we are about to destroy the decl pointer.1631 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
...@@ -1679,6 +1687,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -1679,6 +1687,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
1679 entry.value.destroy(self.gpa);1687 entry.value.destroy(self.gpa);
1680 }1688 }
1681 _ = self.symbol_exports.remove(exp.options.name);1689 _ = self.symbol_exports.remove(exp.options.name);
1690 self.gpa.free(exp.options.name);
1682 self.gpa.destroy(exp);1691 self.gpa.destroy(exp);
1683 }1692 }
1684 self.gpa.free(kv.value);1693 self.gpa.free(kv.value);
...@@ -1703,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1703,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1703 const fn_zir = func.analysis.queued;1712 const fn_zir = func.analysis.queued;
1704 defer fn_zir.arena.promote(self.gpa).deinit();1713 defer fn_zir.arena.promote(self.gpa).deinit();
1705 func.analysis = .{ .in_progress = {} };1714 func.analysis = .{ .in_progress = {} };
1706 //std.debug.warn("set {} to in_progress\n", .{decl.name});1715 log.debug(.module, "set {} to in_progress\n", .{decl.name});
17071716
1708 try self.analyzeBody(&inner_block.base, fn_zir.body);1717 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
17091718
1710 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1719 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1711 func.analysis = .{ .success = .{ .instructions = instructions } };1720 func.analysis = .{ .success = .{ .instructions = instructions } };
1712 //std.debug.warn("set {} to success\n", .{decl.name});1721 log.debug(.module, "set {} to success\n", .{decl.name});
1713}1722}
17141723
1715fn markOutdatedDecl(self: *Module, decl: *Decl) !void {1724fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1716 //std.debug.warn("mark {} outdated\n", .{decl.name});1725 log.debug(.module, "mark {} outdated\n", .{decl.name});
1717 try self.work_queue.writeItem(.{ .analyze_decl = decl });1726 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1718 if (self.failed_decls.remove(decl)) |entry| {1727 if (self.failed_decls.remove(decl)) |entry| {
1719 entry.value.destroy(self.gpa);1728 entry.value.destroy(self.gpa);
...@@ -1758,131 +1767,18 @@ fn createNewDecl(...@@ -1758,131 +1767,18 @@ fn createNewDecl(
1758 return new_decl;1767 return new_decl;
1759}1768}
17601769
1761fn analyzeZirDecl(self: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
1762 var decl_scope: Scope.DeclAnalysis = .{
1763 .decl = decl,
1764 .arena = std.heap.ArenaAllocator.init(self.gpa),
1765 };
1766 errdefer decl_scope.arena.deinit();
1767
1768 decl.analysis = .in_progress;
1769
1770 const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst);
1771 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1772
1773 var prev_type_has_bits = false;
1774 var type_changed = true;
1775
1776 if (decl.typedValueManaged()) |tvm| {
1777 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1778 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
1779
1780 tvm.deinit(self.gpa);
1781 }
1782
1783 arena_state.* = decl_scope.arena.state;
1784 decl.typed_value = .{
1785 .most_recent = .{
1786 .typed_value = typed_value,
1787 .arena = arena_state,
1788 },
1789 };
1790 decl.analysis = .complete;
1791 decl.generation = self.generation;
1792 if (typed_value.ty.hasCodeGenBits()) {
1793 // We don't fully codegen the decl until later, but we do need to reserve a global
1794 // offset table index for it. This allows us to codegen decls out of dependency order,
1795 // increasing how many computations can be done in parallel.
1796 try self.bin_file.allocateDeclIndexes(decl);
1797 try self.work_queue.writeItem(.{ .codegen_decl = decl });
1798 } else if (prev_type_has_bits) {
1799 self.bin_file.freeDecl(decl);
1800 }
1801
1802 return type_changed;
1803}
1804
1805fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1806 const zir_module = self.root_scope.cast(Scope.ZIRModule).?;
1807 const entry = zir_module.contents.module.findDecl(src_decl.name).?;
1808 return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index);
1809}
1810
1811fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
1812 const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
1813 const decl = self.decl_table.get(name_hash).?;
1814 decl.src_index = src_index;
1815 try self.ensureDeclAnalyzed(decl);
1816 return decl;
1817}
1818
1819/// Declares a dependency on the decl.
1820fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1821 const decl = try self.resolveZirDecl(scope, src_decl);
1822 switch (decl.analysis) {
1823 .unreferenced => unreachable,
1824 .in_progress => unreachable,
1825 .outdated => unreachable,
1826
1827 .dependency_failure,
1828 .sema_failure,
1829 .sema_failure_retryable,
1830 .codegen_failure,
1831 .codegen_failure_retryable,
1832 => return error.AnalysisFail,
1833
1834 .complete => {},
1835 }
1836 return decl;
1837}
1838
1839/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
1840fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1841 if (old_inst.analyzed_inst) |inst| return inst;
1842
1843 // If this assert trips, the instruction that was referenced did not get properly
1844 // analyzed before it was referenced.
1845 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
1846 const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
1847 const decl_name = declval.positionals.name;
1848 const entry = zir_module.contents.module.findDecl(decl_name) orelse
1849 return self.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
1850 break :blk entry;
1851 } else blk: {
1852 // If this assert trips, the instruction that was referenced did not get
1853 // properly analyzed by a previous instruction analysis before it was
1854 // referenced by the current one.
1855 break :blk zir_module.contents.module.findInstDecl(old_inst).?;
1856 };
1857 const decl = try self.resolveCompleteZirDecl(scope, entry.decl);
1858 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
1859 // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
1860 // but this would prevent the analyzeDeclRef from happening, which is needed to properly
1861 // detect Decl dependencies and dependency failures on updates.
1862 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
1863}
1864
1865/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.1770/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
1866fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {1771pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
1867 return scope.cast(Scope.Block) orelse1772 return scope.cast(Scope.Block) orelse
1868 return self.fail(scope, src, "instruction illegal outside function body", .{});1773 return self.fail(scope, src, "instruction illegal outside function body", .{});
1869}1774}
18701775
1871fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {1776pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
1872 const new_inst = try self.resolveInst(scope, old_inst);
1873 const val = try self.resolveConstValue(scope, new_inst);
1874 return TypedValue{
1875 .ty = new_inst.ty,
1876 .val = val,
1877 };
1878}
1879
1880fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
1881 return (try self.resolveDefinedValue(scope, base)) orelse1777 return (try self.resolveDefinedValue(scope, base)) orelse
1882 return self.fail(scope, base.src, "unable to resolve comptime value", .{});1778 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
1883}1779}
18841780
1885fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {1781pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1886 if (base.value()) |val| {1782 if (base.value()) |val| {
1887 if (val.isUndef()) {1783 if (val.isUndef()) {
1888 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});1784 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
...@@ -1892,23 +1788,7 @@ fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {...@@ -1892,23 +1788,7 @@ fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1892 return null;1788 return null;
1893}1789}
18941790
1895fn resolveConstString(self: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {1791pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
1896 const new_inst = try self.resolveInst(scope, old_inst);
1897 const wanted_type = Type.initTag(.const_slice_u8);
1898 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
1899 const val = try self.resolveConstValue(scope, coerced_inst);
1900 return val.toAllocatedBytes(scope.arena());
1901}
1902
1903fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
1904 const new_inst = try self.resolveInst(scope, old_inst);
1905 const wanted_type = Type.initTag(.@"type");
1906 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
1907 const val = try self.resolveConstValue(scope, coerced_inst);
1908 return val.toType();
1909}
1910
1911fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
1912 try self.ensureDeclAnalyzed(exported_decl);1792 try self.ensureDeclAnalyzed(exported_decl);
1913 const typed_value = exported_decl.typed_value.most_recent.typed_value;1793 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1914 switch (typed_value.ty.zigTypeTag()) {1794 switch (typed_value.ty.zigTypeTag()) {
...@@ -1922,6 +1802,9 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -1922,6 +1802,9 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1922 const new_export = try self.gpa.create(Export);1802 const new_export = try self.gpa.create(Export);
1923 errdefer self.gpa.destroy(new_export);1803 errdefer self.gpa.destroy(new_export);
19241804
1805 const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
1806 errdefer self.gpa.free(symbol_name);
1807
1925 const owner_decl = scope.decl().?;1808 const owner_decl = scope.decl().?;
19261809
1927 new_export.* = .{1810 new_export.* = .{
...@@ -1934,7 +1817,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -1934,7 +1817,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1934 };1817 };
19351818
1936 // Add to export_owners table.1819 // Add to export_owners table.
1937 const eo_gop = self.export_owners.getOrPut(self.gpa, owner_decl) catch unreachable;1820 const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
1938 if (!eo_gop.found_existing) {1821 if (!eo_gop.found_existing) {
1939 eo_gop.entry.value = &[0]*Export{};1822 eo_gop.entry.value = &[0]*Export{};
1940 }1823 }
...@@ -1943,7 +1826,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -1943,7 +1826,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1943 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);1826 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
19441827
1945 // Add to exported_decl table.1828 // Add to exported_decl table.
1946 const de_gop = self.decl_exports.getOrPut(self.gpa, exported_decl) catch unreachable;1829 const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
1947 if (!de_gop.found_existing) {1830 if (!de_gop.found_existing) {
1948 de_gop.entry.value = &[0]*Export{};1831 de_gop.entry.value = &[0]*Export{};
1949 }1832 }
...@@ -1980,7 +1863,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -1980,7 +1863,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1980 };1863 };
1981}1864}
19821865
1983fn addNoOp(1866pub fn addNoOp(
1984 self: *Module,1867 self: *Module,
1985 block: *Scope.Block,1868 block: *Scope.Block,
1986 src: usize,1869 src: usize,
...@@ -1999,7 +1882,7 @@ fn addNoOp(...@@ -1999,7 +1882,7 @@ fn addNoOp(
1999 return &inst.base;1882 return &inst.base;
2000}1883}
20011884
2002fn addUnOp(1885pub fn addUnOp(
2003 self: *Module,1886 self: *Module,
2004 block: *Scope.Block,1887 block: *Scope.Block,
2005 src: usize,1888 src: usize,
...@@ -2020,7 +1903,7 @@ fn addUnOp(...@@ -2020,7 +1903,7 @@ fn addUnOp(
2020 return &inst.base;1903 return &inst.base;
2021}1904}
20221905
2023fn addBinOp(1906pub fn addBinOp(
2024 self: *Module,1907 self: *Module,
2025 block: *Scope.Block,1908 block: *Scope.Block,
2026 src: usize,1909 src: usize,
...@@ -2043,7 +1926,7 @@ fn addBinOp(...@@ -2043,7 +1926,7 @@ fn addBinOp(
2043 return &inst.base;1926 return &inst.base;
2044}1927}
20451928
2046fn addBr(1929pub fn addBr(
2047 self: *Module,1930 self: *Module,
2048 scope_block: *Scope.Block,1931 scope_block: *Scope.Block,
2049 src: usize,1932 src: usize,
...@@ -2064,7 +1947,7 @@ fn addBr(...@@ -2064,7 +1947,7 @@ fn addBr(
2064 return &inst.base;1947 return &inst.base;
2065}1948}
20661949
2067fn addCondBr(1950pub fn addCondBr(
2068 self: *Module,1951 self: *Module,
2069 block: *Scope.Block,1952 block: *Scope.Block,
2070 src: usize,1953 src: usize,
...@@ -2087,7 +1970,7 @@ fn addCondBr(...@@ -2087,7 +1970,7 @@ fn addCondBr(
2087 return &inst.base;1970 return &inst.base;
2088}1971}
20891972
2090fn addCall(1973pub fn addCall(
2091 self: *Module,1974 self: *Module,
2092 block: *Scope.Block,1975 block: *Scope.Block,
2093 src: usize,1976 src: usize,
...@@ -2109,138 +1992,7 @@ fn addCall(...@@ -2109,138 +1992,7 @@ fn addCall(
2109 return &inst.base;1992 return &inst.base;
2110}1993}
21111994
2112pub fn addZIRInstSpecial(1995pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
2113 self: *Module,
2114 scope: *Scope,
2115 src: usize,
2116 comptime T: type,
2117 positionals: std.meta.fieldInfo(T, "positionals").field_type,
2118 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
2119) !*T {
2120 const gen_zir = scope.getGenZIR();
2121 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
2122 const inst = try gen_zir.arena.create(T);
2123 inst.* = .{
2124 .base = .{
2125 .tag = T.base_tag,
2126 .src = src,
2127 },
2128 .positionals = positionals,
2129 .kw_args = kw_args,
2130 };
2131 gen_zir.instructions.appendAssumeCapacity(&inst.base);
2132 return inst;
2133}
2134
2135pub fn addZIRNoOpT(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
2136 const gen_zir = scope.getGenZIR();
2137 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
2138 const inst = try gen_zir.arena.create(zir.Inst.NoOp);
2139 inst.* = .{
2140 .base = .{
2141 .tag = tag,
2142 .src = src,
2143 },
2144 .positionals = .{},
2145 .kw_args = .{},
2146 };
2147 gen_zir.instructions.appendAssumeCapacity(&inst.base);
2148 return inst;
2149}
2150
2151pub fn addZIRNoOp(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
2152 const inst = try self.addZIRNoOpT(scope, src, tag);
2153 return &inst.base;
2154}
2155
2156pub fn addZIRUnOp(
2157 self: *Module,
2158 scope: *Scope,
2159 src: usize,
2160 tag: zir.Inst.Tag,
2161 operand: *zir.Inst,
2162) !*zir.Inst {
2163 const gen_zir = scope.getGenZIR();
2164 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
2165 const inst = try gen_zir.arena.create(zir.Inst.UnOp);
2166 inst.* = .{
2167 .base = .{
2168 .tag = tag,
2169 .src = src,
2170 },
2171 .positionals = .{
2172 .operand = operand,
2173 },
2174 .kw_args = .{},
2175 };
2176 gen_zir.instructions.appendAssumeCapacity(&inst.base);
2177 return &inst.base;
2178}
2179
2180pub fn addZIRBinOp(
2181 self: *Module,
2182 scope: *Scope,
2183 src: usize,
2184 tag: zir.Inst.Tag,
2185 lhs: *zir.Inst,
2186 rhs: *zir.Inst,
2187) !*zir.Inst {
2188 const gen_zir = scope.getGenZIR();
2189 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
2190 const inst = try gen_zir.arena.create(zir.Inst.BinOp);
2191 inst.* = .{
2192 .base = .{
2193 .tag = tag,
2194 .src = src,
2195 },
2196 .positionals = .{
2197 .lhs = lhs,
2198 .rhs = rhs,
2199 },
2200 .kw_args = .{},
2201 };
2202 gen_zir.instructions.appendAssumeCapacity(&inst.base);
2203 return &inst.base;
2204}
2205
2206pub fn addZIRInst(
2207 self: *Module,
2208 scope: *Scope,
2209 src: usize,
2210 comptime T: type,
2211 positionals: std.meta.fieldInfo(T, "positionals").field_type,
2212 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
2213) !*zir.Inst {
2214 const inst_special = try self.addZIRInstSpecial(scope, src, T, positionals, kw_args);
2215 return &inst_special.base;
2216}
2217
2218/// TODO The existence of this function is a workaround for a bug in stage1.
2219pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
2220 const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
2221 return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
2222}
2223
2224/// TODO The existence of this function is a workaround for a bug in stage1.
2225pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
2226 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
2227 return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{});
2228}
2229
2230fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
2231 const inst = try block.arena.create(T);
2232 inst.* = .{
2233 .base = .{
2234 .tag = T.base_tag,
2235 .ty = ty,
2236 .src = src,
2237 },
2238 };
2239 try block.instructions.append(self.gpa, &inst.base);
2240 return inst;
2241}
2242
2243fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
2244 const const_inst = try scope.arena().create(Inst.Constant);1996 const const_inst = try scope.arena().create(Inst.Constant);
2245 const_inst.* = .{1997 const_inst.* = .{
2246 .base = .{1998 .base = .{
...@@ -2253,42 +2005,42 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)...@@ -2253,42 +2005,42 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)
2253 return &const_inst.base;2005 return &const_inst.base;
2254}2006}
22552007
2256fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {2008pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2257 return self.constInst(scope, src, .{2009 return self.constInst(scope, src, .{
2258 .ty = Type.initTag(.type),2010 .ty = Type.initTag(.type),
2259 .val = try ty.toValue(scope.arena()),2011 .val = try ty.toValue(scope.arena()),
2260 });2012 });
2261}2013}
22622014
2263fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {2015pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
2264 return self.constInst(scope, src, .{2016 return self.constInst(scope, src, .{
2265 .ty = Type.initTag(.void),2017 .ty = Type.initTag(.void),
2266 .val = Value.initTag(.the_one_possible_value),2018 .val = Value.initTag(.void_value),
2267 });2019 });
2268}2020}
22692021
2270fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {2022pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2271 return self.constInst(scope, src, .{2023 return self.constInst(scope, src, .{
2272 .ty = Type.initTag(.noreturn),2024 .ty = Type.initTag(.noreturn),
2273 .val = Value.initTag(.the_one_possible_value),2025 .val = Value.initTag(.unreachable_value),
2274 });2026 });
2275}2027}
22762028
2277fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {2029pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2278 return self.constInst(scope, src, .{2030 return self.constInst(scope, src, .{
2279 .ty = ty,2031 .ty = ty,
2280 .val = Value.initTag(.undef),2032 .val = Value.initTag(.undef),
2281 });2033 });
2282}2034}
22832035
2284fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {2036pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2285 return self.constInst(scope, src, .{2037 return self.constInst(scope, src, .{
2286 .ty = Type.initTag(.bool),2038 .ty = Type.initTag(.bool),
2287 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],2039 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
2288 });2040 });
2289}2041}
22902042
2291fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {2043pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
2292 const int_payload = try scope.arena().create(Value.Payload.Int_u64);2044 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
2293 int_payload.* = .{ .int = int };2045 int_payload.* = .{ .int = int };
22942046
...@@ -2298,7 +2050,7 @@ fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64...@@ -2298,7 +2050,7 @@ fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64
2298 });2050 });
2299}2051}
23002052
2301fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {2053pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
2302 const int_payload = try scope.arena().create(Value.Payload.Int_i64);2054 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
2303 int_payload.* = .{ .int = int };2055 int_payload.* = .{ .int = int };
23042056
...@@ -2308,7 +2060,7 @@ fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64)...@@ -2308,7 +2060,7 @@ fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64)
2308 });2060 });
2309}2061}
23102062
2311fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {2063pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
2312 const val_payload = if (big_int.positive) blk: {2064 const val_payload = if (big_int.positive) blk: {
2313 if (big_int.to(u64)) |x| {2065 if (big_int.to(u64)) |x| {
2314 return self.constIntUnsigned(scope, src, ty, x);2066 return self.constIntUnsigned(scope, src, ty, x);
...@@ -2337,191 +2089,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI...@@ -2337,191 +2089,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI
2337 });2089 });
2338}2090}
23392091
2340fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {2092pub fn createAnonymousDecl(
2341 const new_inst = try self.analyzeInst(scope, old_inst);
2342 return TypedValue{
2343 .ty = new_inst.ty,
2344 .val = try self.resolveConstValue(scope, new_inst),
2345 };
2346}
2347
2348fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
2349 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
2350 // after analysis.
2351 const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
2352 return self.constInst(scope, const_inst.base.src, typed_value_copy);
2353}
2354
2355fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
2356 switch (old_inst.tag) {
2357 .alloc => return self.analyzeInstAlloc(scope, old_inst.castTag(.alloc).?),
2358 .alloc_inferred => return self.analyzeInstAllocInferred(scope, old_inst.castTag(.alloc_inferred).?),
2359 .arg => return self.analyzeInstArg(scope, old_inst.castTag(.arg).?),
2360 .bitcast_result_ptr => return self.analyzeInstBitCastResultPtr(scope, old_inst.castTag(.bitcast_result_ptr).?),
2361 .block => return self.analyzeInstBlock(scope, old_inst.castTag(.block).?),
2362 .@"break" => return self.analyzeInstBreak(scope, old_inst.castTag(.@"break").?),
2363 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.castTag(.breakpoint).?),
2364 .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.castTag(.breakvoid).?),
2365 .call => return self.analyzeInstCall(scope, old_inst.castTag(.call).?),
2366 .coerce_result_block_ptr => return self.analyzeInstCoerceResultBlockPtr(scope, old_inst.castTag(.coerce_result_block_ptr).?),
2367 .coerce_result_ptr => return self.analyzeInstCoerceResultPtr(scope, old_inst.castTag(.coerce_result_ptr).?),
2368 .coerce_to_ptr_elem => return self.analyzeInstCoerceToPtrElem(scope, old_inst.castTag(.coerce_to_ptr_elem).?),
2369 .compileerror => return self.analyzeInstCompileError(scope, old_inst.castTag(.compileerror).?),
2370 .@"const" => return self.analyzeInstConst(scope, old_inst.castTag(.@"const").?),
2371 .declref => return self.analyzeInstDeclRef(scope, old_inst.castTag(.declref).?),
2372 .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.castTag(.declref_str).?),
2373 .declval => return self.analyzeInstDeclVal(scope, old_inst.castTag(.declval).?),
2374 .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.castTag(.declval_in_module).?),
2375 .ensure_result_used => return self.analyzeInstEnsureResultUsed(scope, old_inst.castTag(.ensure_result_used).?),
2376 .ensure_result_non_error => return self.analyzeInstEnsureResultNonError(scope, old_inst.castTag(.ensure_result_non_error).?),
2377 .ret_ptr => return self.analyzeInstRetPtr(scope, old_inst.castTag(.ret_ptr).?),
2378 .ret_type => return self.analyzeInstRetType(scope, old_inst.castTag(.ret_type).?),
2379 .store => return self.analyzeInstStore(scope, old_inst.castTag(.store).?),
2380 .str => return self.analyzeInstStr(scope, old_inst.castTag(.str).?),
2381 .int => {
2382 const big_int = old_inst.castTag(.int).?.positionals.int;
2383 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
2384 },
2385 .inttype => return self.analyzeInstIntType(scope, old_inst.castTag(.inttype).?),
2386 .param_type => return self.analyzeInstParamType(scope, old_inst.castTag(.param_type).?),
2387 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.castTag(.ptrtoint).?),
2388 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.castTag(.fieldptr).?),
2389 .deref => return self.analyzeInstDeref(scope, old_inst.castTag(.deref).?),
2390 .as => return self.analyzeInstAs(scope, old_inst.castTag(.as).?),
2391 .@"asm" => return self.analyzeInstAsm(scope, old_inst.castTag(.@"asm").?),
2392 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.castTag(.@"unreachable").?),
2393 .@"return" => return self.analyzeInstRet(scope, old_inst.castTag(.@"return").?),
2394 .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.castTag(.returnvoid).?),
2395 .@"fn" => return self.analyzeInstFn(scope, old_inst.castTag(.@"fn").?),
2396 .@"export" => return self.analyzeInstExport(scope, old_inst.castTag(.@"export").?),
2397 .primitive => return self.analyzeInstPrimitive(scope, old_inst.castTag(.primitive).?),
2398 .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?),
2399 .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?),
2400 .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?),
2401 .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?),
2402 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?),
2403 .add, .sub => return self.analyzeInstArithmetic(scope, old_inst.cast(zir.Inst.BinOp).?),
2404 .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt),
2405 .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte),
2406 .cmp_eq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_eq).?, .eq),
2407 .cmp_gte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_gte).?, .gte),
2408 .cmp_gt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_gt).?, .gt),
2409 .cmp_neq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_neq).?, .neq),
2410 .condbr => return self.analyzeInstCondBr(scope, old_inst.castTag(.condbr).?),
2411 .isnull => return self.analyzeInstIsNonNull(scope, old_inst.castTag(.isnull).?, true),
2412 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.castTag(.isnonnull).?, false),
2413 .boolnot => return self.analyzeInstBoolNot(scope, old_inst.castTag(.boolnot).?),
2414 }
2415}
2416
2417fn analyzeInstCoerceResultBlockPtr(
2418 self: *Module,
2419 scope: *Scope,
2420 inst: *zir.Inst.CoerceResultBlockPtr,
2421) InnerError!*Inst {
2422 return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
2423}
2424
2425fn analyzeInstBitCastResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2426 return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
2427}
2428
2429fn analyzeInstCoerceResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2430 return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
2431}
2432
2433fn analyzeInstCoerceToPtrElem(self: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
2434 return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});
2435}
2436
2437fn analyzeInstRetPtr(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2438 return self.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
2439}
2440
2441fn analyzeInstRetType(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2442 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2443 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
2444 const ret_type = fn_ty.fnReturnType();
2445 return self.constType(scope, inst.base.src, ret_type);
2446}
2447
2448fn analyzeInstEnsureResultUsed(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2449 const operand = try self.resolveInst(scope, inst.positionals.operand);
2450 switch (operand.ty.zigTypeTag()) {
2451 .Void, .NoReturn => return self.constVoid(scope, operand.src),
2452 else => return self.fail(scope, operand.src, "expression value is ignored", .{}),
2453 }
2454}
2455
2456fn analyzeInstEnsureResultNonError(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2457 const operand = try self.resolveInst(scope, inst.positionals.operand);
2458 switch (operand.ty.zigTypeTag()) {
2459 .ErrorSet, .ErrorUnion => return self.fail(scope, operand.src, "error is discarded", .{}),
2460 else => return self.constVoid(scope, operand.src),
2461 }
2462}
2463
2464fn analyzeInstAlloc(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2465 return self.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{});
2466}
2467
2468fn analyzeInstAllocInferred(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2469 return self.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
2470}
2471
2472fn analyzeInstStore(self: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {
2473 return self.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});
2474}
2475
2476fn analyzeInstParamType(self: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
2477 const fn_inst = try self.resolveInst(scope, inst.positionals.func);
2478 const arg_index = inst.positionals.arg_index;
2479
2480 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
2481 .Fn => fn_inst.ty,
2482 .BoundFn => {
2483 return self.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{});
2484 },
2485 else => {
2486 return self.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
2487 },
2488 };
2489
2490 // TODO support C-style var args
2491 const param_count = fn_ty.fnParamLen();
2492 if (arg_index >= param_count) {
2493 return self.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{
2494 arg_index,
2495 fn_ty,
2496 param_count,
2497 });
2498 }
2499
2500 // TODO support generic functions
2501 const param_type = fn_ty.fnParamType(arg_index);
2502 return self.constType(scope, inst.base.src, param_type);
2503}
2504
2505fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
2506 // The bytes references memory inside the ZIR module, which can get deallocated
2507 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
2508 var new_decl_arena = std.heap.ArenaAllocator.init(self.gpa);
2509 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
2510
2511 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
2512 ty_payload.* = .{ .len = arena_bytes.len };
2513
2514 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
2515 bytes_payload.* = .{ .data = arena_bytes };
2516
2517 const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{
2518 .ty = Type.initPayload(&ty_payload.base),
2519 .val = Value.initPayload(&bytes_payload.base),
2520 });
2521 return self.analyzeDeclRef(scope, str_inst.base.src, new_decl);
2522}
2523
2524fn createAnonymousDecl(
2525 self: *Module,2093 self: *Module,
2526 scope: *Scope,2094 scope: *Scope,
2527 decl_arena: *std.heap.ArenaAllocator,2095 decl_arena: *std.heap.ArenaAllocator,
...@@ -2567,151 +2135,7 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De...@@ -2567,151 +2135,7 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De
2567 return self.decl_table.get(name_hash);2135 return self.decl_table.get(name_hash);
2568}2136}
25692137
2570fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {2138pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2571 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
2572 const exported_decl = self.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
2573 return self.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
2574 try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
2575 return self.constVoid(scope, export_inst.base.src);
2576}
2577
2578fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
2579 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
2580}
2581
2582fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2583 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2584 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
2585 const param_index = b.instructions.items.len;
2586 const param_count = fn_ty.fnParamLen();
2587 if (param_index >= param_count) {
2588 return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
2589 param_index,
2590 param_count,
2591 });
2592 }
2593 const param_type = fn_ty.fnParamType(param_index);
2594 return self.addNoOp(b, inst.base.src, param_type, .arg);
2595}
2596
2597fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
2598 const parent_block = scope.cast(Scope.Block).?;
2599
2600 // Reserve space for a Block instruction so that generated Break instructions can
2601 // point to it, even if it doesn't end up getting used because the code ends up being
2602 // comptime evaluated.
2603 const block_inst = try parent_block.arena.create(Inst.Block);
2604 block_inst.* = .{
2605 .base = .{
2606 .tag = Inst.Block.base_tag,
2607 .ty = undefined, // Set after analysis.
2608 .src = inst.base.src,
2609 },
2610 .body = undefined,
2611 };
2612
2613 var child_block: Scope.Block = .{
2614 .parent = parent_block,
2615 .func = parent_block.func,
2616 .decl = parent_block.decl,
2617 .instructions = .{},
2618 .arena = parent_block.arena,
2619 // TODO @as here is working around a miscompilation compiler bug :(
2620 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2621 .zir_block = inst,
2622 .results = .{},
2623 .block_inst = block_inst,
2624 }),
2625 };
2626 const label = &child_block.label.?;
2627
2628 defer child_block.instructions.deinit(self.gpa);
2629 defer label.results.deinit(self.gpa);
2630
2631 try self.analyzeBody(&child_block.base, inst.positionals.body);
2632
2633 // Blocks must terminate with noreturn instruction.
2634 assert(child_block.instructions.items.len != 0);
2635 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
2636
2637 // Need to set the type and emit the Block instruction. This allows machine code generation
2638 // to emit a jump instruction to after the block when it encounters the break.
2639 try parent_block.instructions.append(self.gpa, &block_inst.base);
2640 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2641 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2642 return &block_inst.base;
2643}
2644
2645fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2646 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2647 return self.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
2648}
2649
2650fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
2651 const operand = try self.resolveInst(scope, inst.positionals.operand);
2652 const block = inst.positionals.block;
2653 return self.analyzeBreak(scope, inst.base.src, block, operand);
2654}
2655
2656fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
2657 const block = inst.positionals.block;
2658 const void_inst = try self.constVoid(scope, inst.base.src);
2659 return self.analyzeBreak(scope, inst.base.src, block, void_inst);
2660}
2661
2662fn analyzeBreak(
2663 self: *Module,
2664 scope: *Scope,
2665 src: usize,
2666 zir_block: *zir.Inst.Block,
2667 operand: *Inst,
2668) InnerError!*Inst {
2669 var opt_block = scope.cast(Scope.Block);
2670 while (opt_block) |block| {
2671 if (block.label) |*label| {
2672 if (label.zir_block == zir_block) {
2673 try label.results.append(self.gpa, operand);
2674 const b = try self.requireRuntimeBlock(scope, src);
2675 return self.addBr(b, src, label.block_inst, operand);
2676 }
2677 }
2678 opt_block = block.parent;
2679 } else unreachable;
2680}
2681
2682fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
2683 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
2684 return self.analyzeDeclRefByName(scope, inst.base.src, decl_name);
2685}
2686
2687fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
2688 return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
2689}
2690
2691fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
2692 const decl_name = inst.positionals.name;
2693 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
2694 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
2695 return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
2696
2697 const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl);
2698
2699 return decl;
2700}
2701
2702fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
2703 const decl = try self.analyzeDeclVal(scope, inst);
2704 const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
2705 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
2706}
2707
2708fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
2709 const decl = inst.positionals.decl;
2710 const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
2711 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
2712}
2713
2714fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2715 const scope_decl = scope.decl().?;2139 const scope_decl = scope.decl().?;
2716 try self.declareDeclDependency(scope_decl, decl);2140 try self.declareDeclDependency(scope_decl, decl);
2717 self.ensureDeclAnalyzed(decl) catch |err| {2141 self.ensureDeclAnalyzed(decl) catch |err| {
...@@ -2739,410 +2163,7 @@ fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerEr...@@ -2739,410 +2163,7 @@ fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerEr
2739 });2163 });
2740}2164}
27412165
2742fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {2166pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
2743 const decl = self.lookupDeclName(scope, decl_name) orelse
2744 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
2745 return self.analyzeDeclRef(scope, src, decl);
2746}
2747
2748fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
2749 const func = try self.resolveInst(scope, inst.positionals.func);
2750 if (func.ty.zigTypeTag() != .Fn)
2751 return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
2752
2753 const cc = func.ty.fnCallingConvention();
2754 if (cc == .Naked) {
2755 // TODO add error note: declared here
2756 return self.fail(
2757 scope,
2758 inst.positionals.func.src,
2759 "unable to call function with naked calling convention",
2760 .{},
2761 );
2762 }
2763 const call_params_len = inst.positionals.args.len;
2764 const fn_params_len = func.ty.fnParamLen();
2765 if (func.ty.fnIsVarArgs()) {
2766 if (call_params_len < fn_params_len) {
2767 // TODO add error note: declared here
2768 return self.fail(
2769 scope,
2770 inst.positionals.func.src,
2771 "expected at least {} arguments, found {}",
2772 .{ fn_params_len, call_params_len },
2773 );
2774 }
2775 return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
2776 } else if (fn_params_len != call_params_len) {
2777 // TODO add error note: declared here
2778 return self.fail(
2779 scope,
2780 inst.positionals.func.src,
2781 "expected {} arguments, found {}",
2782 .{ fn_params_len, call_params_len },
2783 );
2784 }
2785
2786 if (inst.kw_args.modifier == .compile_time) {
2787 return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
2788 }
2789 if (inst.kw_args.modifier != .auto) {
2790 return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
2791 }
2792
2793 // TODO handle function calls of generic functions
2794
2795 const fn_param_types = try self.gpa.alloc(Type, fn_params_len);
2796 defer self.gpa.free(fn_param_types);
2797 func.ty.fnParamTypes(fn_param_types);
2798
2799 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
2800 for (inst.positionals.args) |src_arg, i| {
2801 const uncasted_arg = try self.resolveInst(scope, src_arg);
2802 casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg);
2803 }
2804
2805 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2806 return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args);
2807}
2808
2809fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
2810 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
2811 const fn_zir = blk: {
2812 var fn_arena = std.heap.ArenaAllocator.init(self.gpa);
2813 errdefer fn_arena.deinit();
2814
2815 const fn_zir = try scope.arena().create(Fn.ZIR);
2816 fn_zir.* = .{
2817 .body = .{
2818 .instructions = fn_inst.positionals.body.instructions,
2819 },
2820 .arena = fn_arena.state,
2821 };
2822 break :blk fn_zir;
2823 };
2824 const new_func = try scope.arena().create(Fn);
2825 new_func.* = .{
2826 .analysis = .{ .queued = fn_zir },
2827 .owner_decl = scope.decl().?,
2828 };
2829 const fn_payload = try scope.arena().create(Value.Payload.Function);
2830 fn_payload.* = .{ .func = new_func };
2831 return self.constInst(scope, fn_inst.base.src, .{
2832 .ty = fn_type,
2833 .val = Value.initPayload(&fn_payload.base),
2834 });
2835}
2836
2837fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
2838 return self.fail(scope, inttype.base.src, "TODO implement inttype", .{});
2839}
2840
2841fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
2842 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
2843
2844 // Hot path for some common function types.
2845 if (fntype.positionals.param_types.len == 0) {
2846 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
2847 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
2848 }
2849
2850 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
2851 return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
2852 }
2853
2854 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
2855 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
2856 }
2857
2858 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
2859 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
2860 }
2861 }
2862
2863 const arena = scope.arena();
2864 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
2865 for (fntype.positionals.param_types) |param_type, i| {
2866 param_types[i] = try self.resolveType(scope, param_type);
2867 }
2868
2869 const payload = try arena.create(Type.Payload.Function);
2870 payload.* = .{
2871 .cc = fntype.kw_args.cc,
2872 .return_type = return_type,
2873 .param_types = param_types,
2874 };
2875 return self.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
2876}
2877
2878fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
2879 return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
2880}
2881
2882fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
2883 const dest_type = try self.resolveType(scope, as.positionals.lhs);
2884 const new_inst = try self.resolveInst(scope, as.positionals.rhs);
2885 return self.coerce(scope, dest_type, new_inst);
2886}
2887
2888fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
2889 const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand);
2890 if (ptr.ty.zigTypeTag() != .Pointer) {
2891 return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
2892 }
2893 // TODO handle known-pointer-address
2894 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
2895 const ty = Type.initTag(.usize);
2896 return self.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
2897}
2898
2899fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
2900 const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr);
2901 const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name);
2902
2903 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
2904 .Pointer => object_ptr.ty.elemType(),
2905 else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
2906 };
2907 switch (elem_ty.zigTypeTag()) {
2908 .Array => {
2909 if (mem.eql(u8, field_name, "len")) {
2910 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
2911 len_payload.* = .{ .int = elem_ty.arrayLen() };
2912
2913 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
2914 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
2915
2916 return self.constInst(scope, fieldptr.base.src, .{
2917 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
2918 .val = Value.initPayload(&ref_payload.base),
2919 });
2920 } else {
2921 return self.fail(
2922 scope,
2923 fieldptr.positionals.field_name.src,
2924 "no member named '{}' in '{}'",
2925 .{ field_name, elem_ty },
2926 );
2927 }
2928 },
2929 else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
2930 }
2931}
2932
2933fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2934 const dest_type = try self.resolveType(scope, inst.positionals.lhs);
2935 const operand = try self.resolveInst(scope, inst.positionals.rhs);
2936
2937 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
2938 .ComptimeInt => true,
2939 .Int => false,
2940 else => return self.fail(
2941 scope,
2942 inst.positionals.lhs.src,
2943 "expected integer type, found '{}'",
2944 .{
2945 dest_type,
2946 },
2947 ),
2948 };
2949
2950 switch (operand.ty.zigTypeTag()) {
2951 .ComptimeInt, .Int => {},
2952 else => return self.fail(
2953 scope,
2954 inst.positionals.rhs.src,
2955 "expected integer type, found '{}'",
2956 .{operand.ty},
2957 ),
2958 }
2959
2960 if (operand.value() != null) {
2961 return self.coerce(scope, dest_type, operand);
2962 } else if (dest_is_comptime_int) {
2963 return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
2964 }
2965
2966 return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
2967}
2968
2969fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2970 const dest_type = try self.resolveType(scope, inst.positionals.lhs);
2971 const operand = try self.resolveInst(scope, inst.positionals.rhs);
2972 return self.bitcast(scope, dest_type, operand);
2973}
2974
2975fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2976 const dest_type = try self.resolveType(scope, inst.positionals.lhs);
2977 const operand = try self.resolveInst(scope, inst.positionals.rhs);
2978
2979 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
2980 .ComptimeFloat => true,
2981 .Float => false,
2982 else => return self.fail(
2983 scope,
2984 inst.positionals.lhs.src,
2985 "expected float type, found '{}'",
2986 .{
2987 dest_type,
2988 },
2989 ),
2990 };
2991
2992 switch (operand.ty.zigTypeTag()) {
2993 .ComptimeFloat, .Float, .ComptimeInt => {},
2994 else => return self.fail(
2995 scope,
2996 inst.positionals.rhs.src,
2997 "expected float type, found '{}'",
2998 .{operand.ty},
2999 ),
3000 }
3001
3002 if (operand.value() != null) {
3003 return self.coerce(scope, dest_type, operand);
3004 } else if (dest_is_comptime_float) {
3005 return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
3006 }
3007
3008 return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
3009}
3010
3011fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
3012 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
3013 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
3014 const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index);
3015
3016 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
3017 if (array_ptr.value()) |array_ptr_val| {
3018 if (elem_index.value()) |index_val| {
3019 // Both array pointer and index are compile-time known.
3020 const index_u64 = index_val.toUnsignedInt();
3021 // @intCast here because it would have been impossible to construct a value that
3022 // required a larger index.
3023 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
3024
3025 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
3026 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
3027
3028 return self.constInst(scope, inst.base.src, .{
3029 .ty = Type.initPayload(&type_payload.base),
3030 .val = elem_ptr,
3031 });
3032 }
3033 }
3034 }
3035
3036 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
3037}
3038
3039fn floatOpAllowed(tag: zir.Inst.Tag) bool {
3040 // extend this swich as additional operators are implemented
3041 return switch (tag) {
3042 .add, .sub => true,
3043 else => false,
3044 };
3045}
3046
3047fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3048 const tracy = trace(@src());
3049 defer tracy.end();
3050
3051 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
3052 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
3053
3054 const instructions = &[_]*Inst{ lhs, rhs };
3055 const resolved_type = try self.resolvePeerTypes(scope, instructions);
3056 const casted_lhs = try self.coerce(scope, resolved_type, lhs);
3057 const casted_rhs = try self.coerce(scope, resolved_type, rhs);
3058
3059 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
3060 resolved_type.elemType()
3061 else
3062 resolved_type;
3063
3064 const scalar_tag = scalar_type.zigTypeTag();
3065
3066 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
3067 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
3068 return self.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
3069 lhs.ty.arrayLen(),
3070 rhs.ty.arrayLen(),
3071 });
3072 }
3073 return self.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
3074 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
3075 return self.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
3076 lhs.ty,
3077 rhs.ty,
3078 });
3079 }
3080
3081 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
3082 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
3083
3084 if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
3085 return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
3086 }
3087
3088 if (casted_lhs.value()) |lhs_val| {
3089 if (casted_rhs.value()) |rhs_val| {
3090 return self.analyzeInstComptimeOp(scope, scalar_type, inst, lhs_val, rhs_val);
3091 }
3092 }
3093
3094 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3095 const ir_tag = switch (inst.base.tag) {
3096 .add => Inst.Tag.add,
3097 .sub => Inst.Tag.sub,
3098 else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}),
3099 };
3100
3101 return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
3102}
3103
3104/// Analyzes operands that are known at comptime
3105fn analyzeInstComptimeOp(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
3106 // incase rhs is 0, simply return lhs without doing any calculations
3107 // TODO Once division is implemented we should throw an error when dividing by 0.
3108 if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) {
3109 return self.constInst(scope, inst.base.src, .{
3110 .ty = res_type,
3111 .val = lhs_val,
3112 });
3113 }
3114 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
3115
3116 const value = try switch (inst.base.tag) {
3117 .add => blk: {
3118 const val = if (is_int)
3119 intAdd(scope.arena(), lhs_val, rhs_val)
3120 else
3121 self.floatAdd(scope, res_type, inst, lhs_val, rhs_val);
3122 break :blk val;
3123 },
3124 .sub => blk: {
3125 const val = if (is_int)
3126 intSub(scope.arena(), lhs_val, rhs_val)
3127 else
3128 self.floatSub(scope, res_type, inst, lhs_val, rhs_val);
3129 break :blk val;
3130 },
3131 else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}),
3132 };
3133
3134 return self.constInst(scope, inst.base.src, .{
3135 .ty = res_type,
3136 .val = value,
3137 });
3138}
3139
3140fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
3141 const ptr = try self.resolveInst(scope, deref.positionals.operand);
3142 return self.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src);
3143}
3144
3145fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
3146 const elem_ty = switch (ptr.ty.zigTypeTag()) {2167 const elem_ty = switch (ptr.ty.zigTypeTag()) {
3147 .Pointer => ptr.ty.elemType(),2168 .Pointer => ptr.ty.elemType(),
3148 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),2169 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
...@@ -3154,164 +2175,19 @@ fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: u...@@ -3154,164 +2175,19 @@ fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: u
3154 });2175 });
3155 }2176 }
31562177
3157 return self.fail(scope, src, "TODO implement runtime deref", .{});2178 const b = try self.requireRuntimeBlock(scope, src);
3158}2179 return self.addUnOp(b, src, elem_ty, .load, ptr);
3159
3160fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
3161 const return_type = try self.resolveType(scope, assembly.positionals.return_type);
3162 const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source);
3163 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null;
3164
3165 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
3166 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
3167 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
3168
3169 for (inputs) |*elem, i| {
3170 elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]);
3171 }
3172 for (clobbers) |*elem, i| {
3173 elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]);
3174 }
3175 for (args) |*elem, i| {
3176 const arg = try self.resolveInst(scope, assembly.kw_args.args[i]);
3177 elem.* = try self.coerce(scope, Type.initTag(.usize), arg);
3178 }
3179
3180 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
3181 const inst = try b.arena.create(Inst.Assembly);
3182 inst.* = .{
3183 .base = .{
3184 .tag = .assembly,
3185 .ty = return_type,
3186 .src = assembly.base.src,
3187 },
3188 .asm_source = asm_source,
3189 .is_volatile = assembly.kw_args.@"volatile",
3190 .output = output,
3191 .inputs = inputs,
3192 .clobbers = clobbers,
3193 .args = args,
3194 };
3195 try b.instructions.append(self.gpa, &inst.base);
3196 return &inst.base;
3197}
3198
3199fn analyzeInstCmp(
3200 self: *Module,
3201 scope: *Scope,
3202 inst: *zir.Inst.BinOp,
3203 op: std.math.CompareOperator,
3204) InnerError!*Inst {
3205 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
3206 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
3207
3208 const is_equality_cmp = switch (op) {
3209 .eq, .neq => true,
3210 else => false,
3211 };
3212 const lhs_ty_tag = lhs.ty.zigTypeTag();
3213 const rhs_ty_tag = rhs.ty.zigTypeTag();
3214 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
3215 // null == null, null != null
3216 return self.constBool(scope, inst.base.src, op == .eq);
3217 } else if (is_equality_cmp and
3218 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
3219 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
3220 {
3221 // comparing null with optionals
3222 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
3223 if (opt_operand.value()) |opt_val| {
3224 const is_null = opt_val.isNull();
3225 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
3226 }
3227 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3228 const inst_tag: Inst.Tag = switch (op) {
3229 .eq => .isnull,
3230 .neq => .isnonnull,
3231 else => unreachable,
3232 };
3233 return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
3234 } else if (is_equality_cmp and
3235 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
3236 {
3237 return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
3238 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
3239 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
3240 return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
3241 } else if (is_equality_cmp and
3242 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
3243 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
3244 {
3245 return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
3246 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
3247 if (!is_equality_cmp) {
3248 return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
3249 }
3250 return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
3251 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
3252 // This operation allows any combination of integer and float types, regardless of the
3253 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
3254 // numeric types.
3255 return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
3256 }
3257 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
3258}
3259
3260fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
3261 const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand);
3262 const bool_type = Type.initTag(.bool);
3263 const operand = try self.coerce(scope, bool_type, uncasted_operand);
3264 if (try self.resolveDefinedValue(scope, operand)) |val| {
3265 return self.constBool(scope, inst.base.src, !val.toBool());
3266 }
3267 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3268 return self.addUnOp(b, inst.base.src, bool_type, .not, operand);
3269}
3270
3271fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
3272 const operand = try self.resolveInst(scope, inst.positionals.operand);
3273 return self.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
3274}2180}
32752181
3276fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {2182pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
3277 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);2183 const decl = self.lookupDeclName(scope, decl_name) orelse
3278 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);2184 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
32792185 return self.analyzeDeclRef(scope, src, decl);
3280 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
3281 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
3282 try self.analyzeBody(scope, body.*);
3283 return self.constVoid(scope, inst.base.src);
3284 }
3285
3286 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
3287
3288 var true_block: Scope.Block = .{
3289 .parent = parent_block,
3290 .func = parent_block.func,
3291 .decl = parent_block.decl,
3292 .instructions = .{},
3293 .arena = parent_block.arena,
3294 };
3295 defer true_block.instructions.deinit(self.gpa);
3296 try self.analyzeBody(&true_block.base, inst.positionals.then_body);
3297
3298 var false_block: Scope.Block = .{
3299 .parent = parent_block,
3300 .func = parent_block.func,
3301 .decl = parent_block.decl,
3302 .instructions = .{},
3303 .arena = parent_block.arena,
3304 };
3305 defer false_block.instructions.deinit(self.gpa);
3306 try self.analyzeBody(&false_block.base, inst.positionals.else_body);
3307
3308 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
3309 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
3310 return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
3311}2186}
33122187
3313fn wantSafety(self: *Module, scope: *Scope) bool {2188pub fn wantSafety(self: *Module, scope: *Scope) bool {
3314 return switch (self.optimize_mode) {2189 // TODO take into account scope's safety overrides
2190 return switch (self.optimizeMode()) {
3315 .Debug => true,2191 .Debug => true,
3316 .ReleaseSafe => true,2192 .ReleaseSafe => true,
3317 .ReleaseFast => false,2193 .ReleaseFast => false,
...@@ -3319,33 +2195,12 @@ fn wantSafety(self: *Module, scope: *Scope) bool {...@@ -3319,33 +2195,12 @@ fn wantSafety(self: *Module, scope: *Scope) bool {
3319 };2195 };
3320}2196}
33212197
3322fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {2198pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
3323 const b = try self.requireRuntimeBlock(scope, unreach.base.src);2199 const b = try self.requireRuntimeBlock(scope, src);
3324 if (self.wantSafety(scope)) {2200 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
3325 // TODO Once we have a panic function to call, call it here instead of this.
3326 _ = try self.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
3327 }
3328 return self.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
3329}
3330
3331fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
3332 const operand = try self.resolveInst(scope, inst.positionals.operand);
3333 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3334 return self.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
3335}
3336
3337fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
3338 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3339 return self.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
3340}
3341
3342fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
3343 for (body.instructions) |src_inst| {
3344 src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst);
3345 }
3346}2201}
33472202
3348fn analyzeIsNull(2203pub fn analyzeIsNull(
3349 self: *Module,2204 self: *Module,
3350 scope: *Scope,2205 scope: *Scope,
3351 src: usize,2206 src: usize,
...@@ -3356,7 +2211,7 @@ fn analyzeIsNull(...@@ -3356,7 +2211,7 @@ fn analyzeIsNull(
3356}2211}
33572212
3358/// Asserts that lhs and rhs types are both numeric.2213/// Asserts that lhs and rhs types are both numeric.
3359fn cmpNumeric(2214pub fn cmpNumeric(
3360 self: *Module,2215 self: *Module,
3361 scope: *Scope,2216 scope: *Scope,
3362 src: usize,2217 src: usize,
...@@ -3539,7 +2394,7 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {...@@ -3539,7 +2394,7 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
3539 }2394 }
3540}2395}
35412396
3542fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {2397pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3543 if (instructions.len == 0)2398 if (instructions.len == 0)
3544 return Type.initTag(.noreturn);2399 return Type.initTag(.noreturn);
35452400
...@@ -3579,7 +2434,7 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {...@@ -3579,7 +2434,7 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3579 return prev_inst.ty;2434 return prev_inst.ty;
3580}2435}
35812436
3582fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {2437pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3583 // If the types are the same, we can return the operand.2438 // If the types are the same, we can return the operand.
3584 if (dest_type.eql(inst.ty))2439 if (dest_type.eql(inst.ty))
3585 return inst;2440 return inst;
...@@ -3599,7 +2454,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {...@@ -3599,7 +2454,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
35992454
3600 // *[N]T to []T2455 // *[N]T to []T
3601 if (inst.ty.isSinglePointer() and dest_type.isSlice() and2456 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
3602 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))2457 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
3603 {2458 {
3604 const array_type = inst.ty.elemType();2459 const array_type = inst.ty.elemType();
3605 const dst_elem_type = dest_type.elemType();2460 const dst_elem_type = dest_type.elemType();
...@@ -3675,7 +2530,23 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {...@@ -3675,7 +2530,23 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3675 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });2530 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
3676}2531}
36772532
3678fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {2533pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
2534 if (ptr.ty.isConstPtr())
2535 return self.fail(scope, src, "cannot assign to constant", .{});
2536
2537 const elem_ty = ptr.ty.elemType();
2538 const value = try self.coerce(scope, elem_ty, uncasted_value);
2539 if (elem_ty.onePossibleValue() != null)
2540 return self.constVoid(scope, src);
2541
2542 // TODO handle comptime pointer writes
2543 // TODO handle if the element type requires comptime
2544
2545 const b = try self.requireRuntimeBlock(scope, src);
2546 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
2547}
2548
2549pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3679 if (inst.value()) |val| {2550 if (inst.value()) |val| {
3680 // Keep the comptime Value representation; take the new type.2551 // Keep the comptime Value representation; take the new type.
3681 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });2552 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
...@@ -3822,7 +2693,7 @@ fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {...@@ -3822,7 +2693,7 @@ fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
3822 return @bitCast(u128, a) == @bitCast(u128, b);2693 return @bitCast(u128, a) == @bitCast(u128, b);
3823}2694}
38242695
3825fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {2696pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3826 // TODO is this a performance issue? maybe we should try the operation without2697 // TODO is this a performance issue? maybe we should try the operation without
3827 // resorting to BigInt first.2698 // resorting to BigInt first.
3828 var lhs_space: Value.BigIntSpace = undefined;2699 var lhs_space: Value.BigIntSpace = undefined;
...@@ -3850,7 +2721,7 @@ fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {...@@ -3850,7 +2721,7 @@ fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3850 return Value.initPayload(val_payload);2721 return Value.initPayload(val_payload);
3851}2722}
38522723
3853fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {2724pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3854 // TODO is this a performance issue? maybe we should try the operation without2725 // TODO is this a performance issue? maybe we should try the operation without
3855 // resorting to BigInt first.2726 // resorting to BigInt first.
3856 var lhs_space: Value.BigIntSpace = undefined;2727 var lhs_space: Value.BigIntSpace = undefined;
...@@ -3878,7 +2749,7 @@ fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {...@@ -3878,7 +2749,7 @@ fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
3878 return Value.initPayload(val_payload);2749 return Value.initPayload(val_payload);
3879}2750}
38802751
3881fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {2752pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
3882 var bit_count = switch (float_type.tag()) {2753 var bit_count = switch (float_type.tag()) {
3883 .comptime_float => 128,2754 .comptime_float => 128,
3884 else => float_type.floatBits(self.target()),2755 else => float_type.floatBits(self.target()),
...@@ -3887,7 +2758,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO...@@ -3887,7 +2758,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
3887 const allocator = scope.arena();2758 const allocator = scope.arena();
3888 const val_payload = switch (bit_count) {2759 const val_payload = switch (bit_count) {
3889 16 => {2760 16 => {
3890 return self.fail(scope, inst.base.src, "TODO Implement addition for soft floats", .{});2761 return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
3891 },2762 },
3892 32 => blk: {2763 32 => blk: {
3893 const lhs_val = lhs.toFloat(f32);2764 const lhs_val = lhs.toFloat(f32);
...@@ -3904,7 +2775,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO...@@ -3904,7 +2775,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
3904 break :blk &val_payload.base;2775 break :blk &val_payload.base;
3905 },2776 },
3906 128 => blk: {2777 128 => blk: {
3907 return self.fail(scope, inst.base.src, "TODO Implement addition for big floats", .{});2778 return self.fail(scope, src, "TODO Implement addition for big floats", .{});
3908 },2779 },
3909 else => unreachable,2780 else => unreachable,
3910 };2781 };
...@@ -3912,7 +2783,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO...@@ -3912,7 +2783,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
3912 return Value.initPayload(val_payload);2783 return Value.initPayload(val_payload);
3913}2784}
39142785
3915fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {2786pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
3916 var bit_count = switch (float_type.tag()) {2787 var bit_count = switch (float_type.tag()) {
3917 .comptime_float => 128,2788 .comptime_float => 128,
3918 else => float_type.floatBits(self.target()),2789 else => float_type.floatBits(self.target()),
...@@ -3921,7 +2792,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO...@@ -3921,7 +2792,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
3921 const allocator = scope.arena();2792 const allocator = scope.arena();
3922 const val_payload = switch (bit_count) {2793 const val_payload = switch (bit_count) {
3923 16 => {2794 16 => {
3924 return self.fail(scope, inst.base.src, "TODO Implement substraction for soft floats", .{});2795 return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
3925 },2796 },
3926 32 => blk: {2797 32 => blk: {
3927 const lhs_val = lhs.toFloat(f32);2798 const lhs_val = lhs.toFloat(f32);
...@@ -3938,10 +2809,54 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO...@@ -3938,10 +2809,54 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
3938 break :blk &val_payload.base;2809 break :blk &val_payload.base;
3939 },2810 },
3940 128 => blk: {2811 128 => blk: {
3941 return self.fail(scope, inst.base.src, "TODO Implement substraction for big floats", .{});2812 return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
3942 },2813 },
3943 else => unreachable,2814 else => unreachable,
3944 };2815 };
39452816
3946 return Value.initPayload(val_payload);2817 return Value.initPayload(val_payload);
3947}2818}
2819
2820pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2821 const type_payload = try scope.arena().create(Type.Payload.SingleMutPointer);
2822 type_payload.* = .{ .pointee_type = elem_ty };
2823 return Type.initPayload(&type_payload.base);
2824}
2825
2826pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2827 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2828 type_payload.* = .{ .pointee_type = elem_ty };
2829 return Type.initPayload(&type_payload.base);
2830}
2831
2832pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2833 const zir_module = scope.namespace();
2834 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
2835 const loc = std.zig.findLineColumn(source, inst.src);
2836 if (inst.tag == .constant) {
2837 std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
2838 inst.ty,
2839 inst.castTag(.constant).?.val,
2840 zir_module.subFilePath(),
2841 loc.line + 1,
2842 loc.column + 1,
2843 });
2844 } else if (inst.deaths == 0) {
2845 std.debug.print("{} ty={} src={}:{}:{}\n", .{
2846 @tagName(inst.tag),
2847 inst.ty,
2848 zir_module.subFilePath(),
2849 loc.line + 1,
2850 loc.column + 1,
2851 });
2852 } else {
2853 std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
2854 @tagName(inst.tag),
2855 inst.ty,
2856 inst.deaths,
2857 zir_module.subFilePath(),
2858 loc.line + 1,
2859 loc.column + 1,
2860 });
2861 }
2862}
src-self-hosted/Package.zig+11-5
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
1pub const Table = std.StringHashMap(*Package);1pub const Table = std.StringHashMap(*Package);
22
3/// This should be used for file operations.
3root_src_dir: std.fs.Dir,4root_src_dir: std.fs.Dir,
4/// Relative to `root_src_dir`.5/// This is for metadata purposes, for example putting into debug information.
5root_src_path: []const u8,6root_src_dir_path: []u8,
7/// Relative to `root_src_dir` and `root_src_dir_path`.
8root_src_path: []u8,
6table: Table,9table: Table,
710
8/// No references to `root_src_dir` and `root_src_path` are kept.11/// No references to `root_src_dir` and `root_src_path` are kept.
...@@ -18,8 +21,11 @@ pub fn create(...@@ -18,8 +21,11 @@ pub fn create(
18 errdefer allocator.destroy(ptr);21 errdefer allocator.destroy(ptr);
19 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);22 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
20 errdefer allocator.free(root_src_path_dupe);23 errdefer allocator.free(root_src_path_dupe);
24 const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir);
25 errdefer allocator.free(root_src_dir_path);
21 ptr.* = .{26 ptr.* = .{
22 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),27 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),
28 .root_src_dir_path = root_src_dir_path,
23 .root_src_path = root_src_path_dupe,29 .root_src_path = root_src_path_dupe,
24 .table = Table.init(allocator),30 .table = Table.init(allocator),
25 };31 };
...@@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {...@@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {
30 const allocator = self.table.allocator;36 const allocator = self.table.allocator;
31 self.root_src_dir.close();37 self.root_src_dir.close();
32 allocator.free(self.root_src_path);38 allocator.free(self.root_src_path);
39 allocator.free(self.root_src_dir_path);
33 {40 {
34 var it = self.table.iterator();41 var it = self.table.iterator();
35 while (it.next()) |kv| {42 while (it.next()) |kv| {
...@@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {...@@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {
41}48}
4249
43pub fn add(self: *Package, name: []const u8, package: *Package) !void {50pub fn add(self: *Package, name: []const u8, package: *Package) !void {
51 try self.table.ensureCapacity(self.table.items().len + 1);
44 const name_dupe = try mem.dupe(self.table.allocator, u8, name);52 const name_dupe = try mem.dupe(self.table.allocator, u8, name);
45 errdefer self.table.allocator.deinit(name_dupe);53 self.table.putAssumeCapacityNoClobber(name_dupe, package);
46 const entry = try self.table.put(name_dupe, package);
47 assert(entry == null);
48}54}
4955
50const std = @import("std");56const std = @import("std");
src-self-hosted/astgen.zig+287-102
...@@ -17,6 +17,9 @@ pub const ResultLoc = union(enum) {...@@ -17,6 +17,9 @@ pub const ResultLoc = union(enum) {
17 discard,17 discard,
18 /// The expression has an inferred type, and it will be evaluated as an rvalue.18 /// The expression has an inferred type, and it will be evaluated as an rvalue.
19 none,19 none,
20 /// The expression must generate a pointer rather than a value. For example, the left hand side
21 /// of an assignment uses an "LValue" result location.
22 lvalue,
20 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.23 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
21 ty: *zir.Inst,24 ty: *zir.Inst,
22 /// The expression must store its result into this typed pointer.25 /// The expression must store its result into this typed pointer.
...@@ -33,7 +36,7 @@ pub const ResultLoc = union(enum) {...@@ -33,7 +36,7 @@ pub const ResultLoc = union(enum) {
3336
34pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {37pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
35 const type_src = scope.tree().token_locs[type_node.firstToken()].start;38 const type_src = scope.tree().token_locs[type_node.firstToken()].start;
36 const type_type = try mod.addZIRInstConst(scope, type_src, .{39 const type_type = try addZIRInstConst(mod, scope, type_src, .{
37 .ty = Type.initTag(.type),40 .ty = Type.initTag(.type),
38 .val = Value.initTag(.type_type),41 .val = Value.initTag(.type_type),
39 });42 });
...@@ -46,18 +49,45 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -46,18 +49,45 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
46 switch (node.tag) {49 switch (node.tag) {
47 .VarDecl => unreachable, // Handled in `blockExpr`.50 .VarDecl => unreachable, // Handled in `blockExpr`.
48 .Assign => unreachable, // Handled in `blockExpr`.51 .Assign => unreachable, // Handled in `blockExpr`.
4952 .AssignBitAnd => unreachable, // Handled in `blockExpr`.
50 .Add => return arithmetic(mod, scope, rl, node.castTag(.Add).?, .add),53 .AssignBitOr => unreachable, // Handled in `blockExpr`.
51 .Sub => return arithmetic(mod, scope, rl, node.castTag(.Sub).?, .sub),54 .AssignBitShiftLeft => unreachable, // Handled in `blockExpr`.
5255 .AssignBitShiftRight => unreachable, // Handled in `blockExpr`.
53 .BangEqual => return cmp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),56 .AssignBitXor => unreachable, // Handled in `blockExpr`.
54 .EqualEqual => return cmp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),57 .AssignDiv => unreachable, // Handled in `blockExpr`.
55 .GreaterThan => return cmp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),58 .AssignSub => unreachable, // Handled in `blockExpr`.
56 .GreaterOrEqual => return cmp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),59 .AssignSubWrap => unreachable, // Handled in `blockExpr`.
57 .LessThan => return cmp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),60 .AssignMod => unreachable, // Handled in `blockExpr`.
58 .LessOrEqual => return cmp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),61 .AssignAdd => unreachable, // Handled in `blockExpr`.
5962 .AssignAddWrap => unreachable, // Handled in `blockExpr`.
60 .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),63 .AssignMul => unreachable, // Handled in `blockExpr`.
64 .AssignMulWrap => unreachable, // Handled in `blockExpr`.
65
66 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
67 .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
68 .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
69 .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
70 .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
71 .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
72 .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
73 .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
74 .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
75 .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
76 .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
77 .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
78 .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
79
80 .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
81 .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
82 .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
83 .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
84 .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
85 .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
86
87 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
88 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
89
90 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
61 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),91 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
62 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),92 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
63 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),93 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
...@@ -90,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block...@@ -90,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
90120
91 var scope = parent_scope;121 var scope = parent_scope;
92 for (block_node.statements()) |statement| {122 for (block_node.statements()) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;
124 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
93 switch (statement.tag) {125 switch (statement.tag) {
94 .VarDecl => {126 .VarDecl => {
95 const var_decl_node = statement.castTag(.VarDecl).?;127 const var_decl_node = statement.castTag(.VarDecl).?;
...@@ -99,10 +131,25 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block...@@ -99,10 +131,25 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
99 const ass = statement.castTag(.Assign).?;131 const ass = statement.castTag(.Assign).?;
100 try assign(mod, scope, ass);132 try assign(mod, scope, ass);
101 },133 },
134 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
135 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
136 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
137 .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
138 .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
139 .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
140 .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
141 .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
142 .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
143 .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
144 .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
145 .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
146 .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
147
102 else => {148 else => {
103 const possibly_unused_result = try expr(mod, scope, .none, statement);149 const possibly_unused_result = try expr(mod, scope, .none, statement);
104 const src = scope.tree().token_locs[statement.firstToken()].start;150 if (!possibly_unused_result.tag.isNoReturn()) {
105 _ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result);151 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
152 }
106 },153 },
107 }154 }
108 }155 }
...@@ -133,7 +180,7 @@ fn varDecl(...@@ -133,7 +180,7 @@ fn varDecl(
133 if (nodeMayNeedMemoryLocation(init_node)) {180 if (nodeMayNeedMemoryLocation(init_node)) {
134 if (node.getTrailer("type_node")) |type_node| {181 if (node.getTrailer("type_node")) |type_node| {
135 const type_inst = try typeExpr(mod, scope, type_node);182 const type_inst = try typeExpr(mod, scope, type_node);
136 const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);183 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
137 const result_loc: ResultLoc = .{ .ptr = alloc };184 const result_loc: ResultLoc = .{ .ptr = alloc };
138 const init_inst = try expr(mod, scope, result_loc, init_node);185 const init_inst = try expr(mod, scope, result_loc, init_node);
139 const sub_scope = try block_arena.create(Scope.LocalVal);186 const sub_scope = try block_arena.create(Scope.LocalVal);
...@@ -145,7 +192,7 @@ fn varDecl(...@@ -145,7 +192,7 @@ fn varDecl(
145 };192 };
146 return &sub_scope.base;193 return &sub_scope.base;
147 } else {194 } else {
148 const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred);195 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
149 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };196 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
150 const init_inst = try expr(mod, scope, result_loc, init_node);197 const init_inst = try expr(mod, scope, result_loc, init_node);
151 const sub_scope = try block_arena.create(Scope.LocalVal);198 const sub_scope = try block_arena.create(Scope.LocalVal);
...@@ -176,7 +223,7 @@ fn varDecl(...@@ -176,7 +223,7 @@ fn varDecl(
176 .Keyword_var => {223 .Keyword_var => {
177 if (node.getTrailer("type_node")) |type_node| {224 if (node.getTrailer("type_node")) |type_node| {
178 const type_inst = try typeExpr(mod, scope, type_node);225 const type_inst = try typeExpr(mod, scope, type_node);
179 const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);226 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
180 const result_loc: ResultLoc = .{ .ptr = alloc };227 const result_loc: ResultLoc = .{ .ptr = alloc };
181 const init_inst = try expr(mod, scope, result_loc, init_node);228 const init_inst = try expr(mod, scope, result_loc, init_node);
182 const sub_scope = try block_arena.create(Scope.LocalPtr);229 const sub_scope = try block_arena.create(Scope.LocalPtr);
...@@ -188,7 +235,7 @@ fn varDecl(...@@ -188,7 +235,7 @@ fn varDecl(
188 };235 };
189 return &sub_scope.base;236 return &sub_scope.base;
190 } else {237 } else {
191 const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred);238 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
192 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };239 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
193 const init_inst = try expr(mod, scope, result_loc, init_node);240 const init_inst = try expr(mod, scope, result_loc, init_node);
194 const sub_scope = try block_arena.create(Scope.LocalPtr);241 const sub_scope = try block_arena.create(Scope.LocalPtr);
...@@ -207,28 +254,44 @@ fn varDecl(...@@ -207,28 +254,44 @@ fn varDecl(
207254
208fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {255fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
209 if (infix_node.lhs.castTag(.Identifier)) |ident| {256 if (infix_node.lhs.castTag(.Identifier)) |ident| {
210 const tree = scope.tree();257 // This intentionally does not support @"_" syntax.
211 const ident_name = try identifierTokenString(mod, scope, ident.token);258 const ident_name = scope.tree().tokenSlice(ident.token);
212 if (std.mem.eql(u8, ident_name, "_")) {259 if (std.mem.eql(u8, ident_name, "_")) {
213 _ = try expr(mod, scope, .discard, infix_node.rhs);260 _ = try expr(mod, scope, .discard, infix_node.rhs);
214 return;261 return;
215 } else {
216 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
217 }262 }
218 } else {
219 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
220 }263 }
264 const lvalue = try expr(mod, scope, .lvalue, infix_node.lhs);
265 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
266}
267
268fn assignOp(
269 mod: *Module,
270 scope: *Scope,
271 infix_node: *ast.Node.SimpleInfixOp,
272 op_inst_tag: zir.Inst.Tag,
273) InnerError!void {
274 const lhs_ptr = try expr(mod, scope, .lvalue, infix_node.lhs);
275 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
276 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
277 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
278
279 const tree = scope.tree();
280 const src = tree.token_locs[infix_node.op_token].start;
281
282 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
283 _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
221}284}
222285
223fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {286fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
224 const tree = scope.tree();287 const tree = scope.tree();
225 const src = tree.token_locs[node.op_token].start;288 const src = tree.token_locs[node.op_token].start;
226 const bool_type = try mod.addZIRInstConst(scope, src, .{289 const bool_type = try addZIRInstConst(mod, scope, src, .{
227 .ty = Type.initTag(.type),290 .ty = Type.initTag(.type),
228 .val = Value.initTag(.bool_type),291 .val = Value.initTag(.bool_type),
229 });292 });
230 const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);293 const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
231 return mod.addZIRUnOp(scope, src, .boolnot, operand);294 return addZIRUnOp(mod, scope, src, .boolnot, operand);
232}295}
233296
234/// Identifier token -> String (allocated in scope.arena())297/// Identifier token -> String (allocated in scope.arena())
...@@ -257,7 +320,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke...@@ -257,7 +320,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke
257320
258 const ident_name = try identifierTokenString(mod, scope, node.token);321 const ident_name = try identifierTokenString(mod, scope, node.token);
259322
260 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});323 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
261}324}
262325
263fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {326fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
...@@ -268,47 +331,31 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!...@@ -268,47 +331,31 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!
268 const lhs = try expr(mod, scope, .none, node.lhs);331 const lhs = try expr(mod, scope, .none, node.lhs);
269 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);332 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
270333
271 const pointer = try mod.addZIRInst(scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});334 const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
272 return mod.addZIRUnOp(scope, src, .deref, pointer);335 return addZIRUnOp(mod, scope, src, .deref, pointer);
273}336}
274337
275fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {338fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
276 const tree = scope.tree();339 const tree = scope.tree();
277 const src = tree.token_locs[node.rtoken].start;340 const src = tree.token_locs[node.rtoken].start;
278 const lhs = try expr(mod, scope, .none, node.lhs);341 const lhs = try expr(mod, scope, .none, node.lhs);
279 return mod.addZIRUnOp(scope, src, .deref, lhs);342 return addZIRUnOp(mod, scope, src, .deref, lhs);
280}343}
281344
282fn cmp(345fn simpleBinOp(
283 mod: *Module,346 mod: *Module,
284 scope: *Scope,347 scope: *Scope,
285 rl: ResultLoc,348 rl: ResultLoc,
286 infix_node: *ast.Node.SimpleInfixOp,349 infix_node: *ast.Node.SimpleInfixOp,
287 cmp_inst_tag: zir.Inst.Tag,350 op_inst_tag: zir.Inst.Tag,
288) InnerError!*zir.Inst {351) InnerError!*zir.Inst {
289 const tree = scope.tree();352 const tree = scope.tree();
290 const src = tree.token_locs[infix_node.op_token].start;353 const src = tree.token_locs[infix_node.op_token].start;
291354
292 const lhs = try expr(mod, scope, .none, infix_node.lhs);
293 const rhs = try expr(mod, scope, .none, infix_node.rhs);
294 const result = try mod.addZIRBinOp(scope, src, cmp_inst_tag, lhs, rhs);
295 return rlWrap(mod, scope, rl, result);
296}
297
298fn arithmetic(
299 mod: *Module,
300 scope: *Scope,
301 rl: ResultLoc,
302 infix_node: *ast.Node.SimpleInfixOp,
303 op_inst_tag: zir.Inst.Tag,
304) InnerError!*zir.Inst {
305 const lhs = try expr(mod, scope, .none, infix_node.lhs);355 const lhs = try expr(mod, scope, .none, infix_node.lhs);
306 const rhs = try expr(mod, scope, .none, infix_node.rhs);356 const rhs = try expr(mod, scope, .none, infix_node.rhs);
307357
308 const tree = scope.tree();358 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
309 const src = tree.token_locs[infix_node.op_token].start;
310
311 const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs);
312 return rlWrap(mod, scope, rl, result);359 return rlWrap(mod, scope, rl, result);
313}360}
314361
...@@ -331,19 +378,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -331,19 +378,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
331378
332 const tree = scope.tree();379 const tree = scope.tree();
333 const if_src = tree.token_locs[if_node.if_token].start;380 const if_src = tree.token_locs[if_node.if_token].start;
334 const bool_type = try mod.addZIRInstConst(scope, if_src, .{381 const bool_type = try addZIRInstConst(mod, scope, if_src, .{
335 .ty = Type.initTag(.type),382 .ty = Type.initTag(.type),
336 .val = Value.initTag(.bool_type),383 .val = Value.initTag(.bool_type),
337 });384 });
338 const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);385 const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
339386
340 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{387 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
341 .condition = cond,388 .condition = cond,
342 .then_body = undefined, // populated below389 .then_body = undefined, // populated below
343 .else_body = undefined, // populated below390 .else_body = undefined, // populated below
344 }, .{});391 }, .{});
345392
346 const block = try mod.addZIRInstBlock(scope, if_src, .{393 const block = try addZIRInstBlock(mod, scope, if_src, .{
347 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),394 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
348 });395 });
349 var then_scope: Scope.GenZIR = .{396 var then_scope: Scope.GenZIR = .{
...@@ -359,14 +406,14 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -359,14 +406,14 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
359 // proper type inference requires peer type resolution on the if's406 // proper type inference requires peer type resolution on the if's
360 // branches.407 // branches.
361 const branch_rl: ResultLoc = switch (rl) {408 const branch_rl: ResultLoc = switch (rl) {
362 .discard, .none, .ty, .ptr => rl,409 .discard, .none, .ty, .ptr, .lvalue => rl,
363 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },410 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
364 };411 };
365412
366 const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);413 const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
367 if (!then_result.tag.isNoReturn()) {414 if (!then_result.tag.isNoReturn()) {
368 const then_src = tree.token_locs[if_node.body.lastToken()].start;415 const then_src = tree.token_locs[if_node.body.lastToken()].start;
369 _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{416 _ = try addZIRInst(mod, &then_scope.base, then_src, zir.Inst.Break, .{
370 .block = block,417 .block = block,
371 .operand = then_result,418 .operand = then_result,
372 }, .{});419 }, .{});
...@@ -387,7 +434,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -387,7 +434,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
387 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);434 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
388 if (!else_result.tag.isNoReturn()) {435 if (!else_result.tag.isNoReturn()) {
389 const else_src = tree.token_locs[else_node.body.lastToken()].start;436 const else_src = tree.token_locs[else_node.body.lastToken()].start;
390 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{437 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
391 .block = block,438 .block = block,
392 .operand = else_result,439 .operand = else_result,
393 }, .{});440 }, .{});
...@@ -396,7 +443,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -396,7 +443,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
396 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here443 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
397 // by directly allocating the body for this one instruction.444 // by directly allocating the body for this one instruction.
398 const else_src = tree.token_locs[if_node.lastToken()].start;445 const else_src = tree.token_locs[if_node.lastToken()].start;
399 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{446 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
400 .block = block,447 .block = block,
401 }, .{});448 }, .{});
402 }449 }
...@@ -412,20 +459,20 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE...@@ -412,20 +459,20 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
412 const src = tree.token_locs[cfe.ltoken].start;459 const src = tree.token_locs[cfe.ltoken].start;
413 if (cfe.getRHS()) |rhs_node| {460 if (cfe.getRHS()) |rhs_node| {
414 if (nodeMayNeedMemoryLocation(rhs_node)) {461 if (nodeMayNeedMemoryLocation(rhs_node)) {
415 const ret_ptr = try mod.addZIRNoOp(scope, src, .ret_ptr);462 const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
416 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);463 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
417 return mod.addZIRUnOp(scope, src, .@"return", operand);464 return addZIRUnOp(mod, scope, src, .@"return", operand);
418 } else {465 } else {
419 const fn_ret_ty = try mod.addZIRNoOp(scope, src, .ret_type);466 const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
420 const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);467 const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
421 return mod.addZIRUnOp(scope, src, .@"return", operand);468 return addZIRUnOp(mod, scope, src, .@"return", operand);
422 }469 }
423 } else {470 } else {
424 return mod.addZIRNoOp(scope, src, .returnvoid);471 return addZIRNoOp(mod, scope, src, .returnvoid);
425 }472 }
426}473}
427474
428fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst {475fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
429 const tracy = trace(@src());476 const tracy = trace(@src());
430 defer tracy.end();477 defer tracy.end();
431478
...@@ -437,7 +484,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -437,7 +484,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
437 }484 }
438485
439 if (getSimplePrimitiveValue(ident_name)) |typed_value| {486 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
440 return mod.addZIRInstConst(scope, src, typed_value);487 const result = try addZIRInstConst(mod, scope, src, typed_value);
488 return rlWrap(mod, scope, rl, result);
441 }489 }
442490
443 if (ident_name.len >= 2) integer: {491 if (ident_name.len >= 2) integer: {
...@@ -461,16 +509,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -461,16 +509,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
461 else => {509 else => {
462 const int_type_payload = try scope.arena().create(Value.Payload.IntType);510 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
463 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };511 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
464 return mod.addZIRInstConst(scope, src, .{512 const result = try addZIRInstConst(mod, scope, src, .{
465 .ty = Type.initTag(.comptime_int),513 .ty = Type.initTag(.comptime_int),
466 .val = Value.initPayload(&int_type_payload.base),514 .val = Value.initPayload(&int_type_payload.base),
467 });515 });
516 return rlWrap(mod, scope, rl, result);
468 },517 },
469 };518 };
470 return mod.addZIRInstConst(scope, src, .{519 const result = try addZIRInstConst(mod, scope, src, .{
471 .ty = Type.initTag(.type),520 .ty = Type.initTag(.type),
472 .val = val,521 .val = val,
473 });522 });
523 return rlWrap(mod, scope, rl, result);
474 }524 }
475 }525 }
476526
...@@ -481,14 +531,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -481,14 +531,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481 .local_val => {531 .local_val => {
482 const local_val = s.cast(Scope.LocalVal).?;532 const local_val = s.cast(Scope.LocalVal).?;
483 if (mem.eql(u8, local_val.name, ident_name)) {533 if (mem.eql(u8, local_val.name, ident_name)) {
484 return local_val.inst;534 return rlWrap(mod, scope, rl, local_val.inst);
485 }535 }
486 s = local_val.parent;536 s = local_val.parent;
487 },537 },
488 .local_ptr => {538 .local_ptr => {
489 const local_ptr = s.cast(Scope.LocalPtr).?;539 const local_ptr = s.cast(Scope.LocalPtr).?;
490 if (mem.eql(u8, local_ptr.name, ident_name)) {540 if (mem.eql(u8, local_ptr.name, ident_name)) {
491 return try mod.addZIRUnOp(scope, src, .deref, local_ptr.ptr);541 if (rl == .lvalue) {
542 return local_ptr.ptr;
543 } else {
544 const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
545 return rlWrap(mod, scope, rl, result);
546 }
492 }547 }
493 s = local_ptr.parent;548 s = local_ptr.parent;
494 },549 },
...@@ -498,7 +553,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -498,7 +553,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
498 }553 }
499554
500 if (mod.lookupDeclName(scope, ident_name)) |decl| {555 if (mod.lookupDeclName(scope, ident_name)) |decl| {
501 return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});556 // TODO handle lvalues
557 const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
558 return rlWrap(mod, scope, rl, result);
502 }559 }
503560
504 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});561 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
...@@ -520,7 +577,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner...@@ -520,7 +577,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner
520 };577 };
521578
522 const src = tree.token_locs[str_lit.token].start;579 const src = tree.token_locs[str_lit.token].start;
523 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});580 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
524}581}
525582
526fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {583fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
...@@ -545,7 +602,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne...@@ -545,7 +602,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne
545 const int_payload = try arena.create(Value.Payload.Int_u64);602 const int_payload = try arena.create(Value.Payload.Int_u64);
546 int_payload.* = .{ .int = small_int };603 int_payload.* = .{ .int = small_int };
547 const src = tree.token_locs[int_lit.token].start;604 const src = tree.token_locs[int_lit.token].start;
548 return mod.addZIRInstConst(scope, src, .{605 return addZIRInstConst(mod, scope, src, .{
549 .ty = Type.initTag(.comptime_int),606 .ty = Type.initTag(.comptime_int),
550 .val = Value.initPayload(&int_payload.base),607 .val = Value.initPayload(&int_payload.base),
551 });608 });
...@@ -568,7 +625,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne...@@ -568,7 +625,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne
568 const float_payload = try arena.create(Value.Payload.Float_128);625 const float_payload = try arena.create(Value.Payload.Float_128);
569 float_payload.* = .{ .val = val };626 float_payload.* = .{ .val = val };
570 const src = tree.token_locs[float_lit.token].start;627 const src = tree.token_locs[float_lit.token].start;
571 return mod.addZIRInstConst(scope, src, .{628 return addZIRInstConst(mod, scope, src, .{
572 .ty = Type.initTag(.comptime_float),629 .ty = Type.initTag(.comptime_float),
573 .val = Value.initPayload(&float_payload.base),630 .val = Value.initPayload(&float_payload.base),
574 });631 });
...@@ -578,7 +635,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro...@@ -578,7 +635,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro
578 const arena = scope.arena();635 const arena = scope.arena();
579 const tree = scope.tree();636 const tree = scope.tree();
580 const src = tree.token_locs[node.token].start;637 const src = tree.token_locs[node.token].start;
581 return mod.addZIRInstConst(scope, src, .{638 return addZIRInstConst(mod, scope, src, .{
582 .ty = Type.initTag(.@"undefined"),639 .ty = Type.initTag(.@"undefined"),
583 .val = Value.initTag(.undef),640 .val = Value.initTag(.undef),
584 });641 });
...@@ -588,7 +645,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError...@@ -588,7 +645,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
588 const arena = scope.arena();645 const arena = scope.arena();
589 const tree = scope.tree();646 const tree = scope.tree();
590 const src = tree.token_locs[node.token].start;647 const src = tree.token_locs[node.token].start;
591 return mod.addZIRInstConst(scope, src, .{648 return addZIRInstConst(mod, scope, src, .{
592 .ty = Type.initTag(.bool),649 .ty = Type.initTag(.bool),
593 .val = switch (tree.token_ids[node.token]) {650 .val = switch (tree.token_ids[node.token]) {
594 .Keyword_true => Value.initTag(.bool_true),651 .Keyword_true => Value.initTag(.bool_true),
...@@ -602,7 +659,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError...@@ -602,7 +659,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
602 const arena = scope.arena();659 const arena = scope.arena();
603 const tree = scope.tree();660 const tree = scope.tree();
604 const src = tree.token_locs[node.token].start;661 const src = tree.token_locs[node.token].start;
605 return mod.addZIRInstConst(scope, src, .{662 return addZIRInstConst(mod, scope, src, .{
606 .ty = Type.initTag(.@"null"),663 .ty = Type.initTag(.@"null"),
607 .val = Value.initTag(.null_value),664 .val = Value.initTag(.null_value),
608 });665 });
...@@ -620,7 +677,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi...@@ -620,7 +677,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
620677
621 const src = tree.token_locs[asm_node.asm_token].start;678 const src = tree.token_locs[asm_node.asm_token].start;
622679
623 const str_type = try mod.addZIRInstConst(scope, src, .{680 const str_type = try addZIRInstConst(mod, scope, src, .{
624 .ty = Type.initTag(.type),681 .ty = Type.initTag(.type),
625 .val = Value.initTag(.const_slice_u8_type),682 .val = Value.initTag(.const_slice_u8_type),
626 });683 });
...@@ -632,11 +689,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi...@@ -632,11 +689,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
632 args[i] = try expr(mod, scope, .none, input.expr);689 args[i] = try expr(mod, scope, .none, input.expr);
633 }690 }
634691
635 const return_type = try mod.addZIRInstConst(scope, src, .{692 const return_type = try addZIRInstConst(mod, scope, src, .{
636 .ty = Type.initTag(.type),693 .ty = Type.initTag(.type),
637 .val = Value.initTag(.void_type),694 .val = Value.initTag(.void_type),
638 });695 });
639 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{696 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
640 .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),697 .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
641 .return_type = return_type,698 .return_type = return_type,
642 }, .{699 }, .{
...@@ -666,14 +723,14 @@ fn simpleCast(...@@ -666,14 +723,14 @@ fn simpleCast(
666 try ensureBuiltinParamCount(mod, scope, call, 2);723 try ensureBuiltinParamCount(mod, scope, call, 2);
667 const tree = scope.tree();724 const tree = scope.tree();
668 const src = tree.token_locs[call.builtin_token].start;725 const src = tree.token_locs[call.builtin_token].start;
669 const type_type = try mod.addZIRInstConst(scope, src, .{726 const type_type = try addZIRInstConst(mod, scope, src, .{
670 .ty = Type.initTag(.type),727 .ty = Type.initTag(.type),
671 .val = Value.initTag(.type_type),728 .val = Value.initTag(.type_type),
672 });729 });
673 const params = call.params();730 const params = call.params();
674 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);731 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
675 const rhs = try expr(mod, scope, .none, params[1]);732 const rhs = try expr(mod, scope, .none, params[1]);
676 const result = try mod.addZIRBinOp(scope, src, inst_tag, dest_type, rhs);733 const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
677 return rlWrap(mod, scope, rl, result);734 return rlWrap(mod, scope, rl, result);
678}735}
679736
...@@ -682,7 +739,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError...@@ -682,7 +739,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError
682 const operand = try expr(mod, scope, .none, call.params()[0]);739 const operand = try expr(mod, scope, .none, call.params()[0]);
683 const tree = scope.tree();740 const tree = scope.tree();
684 const src = tree.token_locs[call.builtin_token].start;741 const src = tree.token_locs[call.builtin_token].start;
685 return mod.addZIRUnOp(scope, src, .ptrtoint, operand);742 return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
686}743}
687744
688fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {745fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
...@@ -695,15 +752,19 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I...@@ -695,15 +752,19 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
695 .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),752 .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
696 .discard => {753 .discard => {
697 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);754 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
698 _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);755 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
699 return result;756 return result;
700 },757 },
758 .lvalue => {
759 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
760 return addZIRUnOp(mod, scope, result.src, .ref, result);
761 },
701 .ty => |result_ty| {762 .ty => |result_ty| {
702 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);763 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
703 return mod.addZIRBinOp(scope, src, .as, result_ty, result);764 return addZIRBinOp(mod, scope, src, .as, result_ty, result);
704 },765 },
705 .ptr => |result_ptr| {766 .ptr => |result_ptr| {
706 const casted_result_ptr = try mod.addZIRBinOp(scope, src, .coerce_result_ptr, dest_type, result_ptr);767 const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
707 return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);768 return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
708 },769 },
709 .bitcasted_ptr => |bitcasted_ptr| {770 .bitcasted_ptr => |bitcasted_ptr| {
...@@ -715,7 +776,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I...@@ -715,7 +776,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
715 return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});776 return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
716 },777 },
717 .block_ptr => |block_ptr| {778 .block_ptr => |block_ptr| {
718 const casted_block_ptr = try mod.addZIRInst(scope, src, zir.Inst.CoerceResultBlockPtr, .{779 const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
719 .dest_type = dest_type,780 .dest_type = dest_type,
720 .block = block_ptr,781 .block = block_ptr,
721 }, .{});782 }, .{});
...@@ -728,7 +789,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa...@@ -728,7 +789,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
728 try ensureBuiltinParamCount(mod, scope, call, 2);789 try ensureBuiltinParamCount(mod, scope, call, 2);
729 const tree = scope.tree();790 const tree = scope.tree();
730 const src = tree.token_locs[call.builtin_token].start;791 const src = tree.token_locs[call.builtin_token].start;
731 const type_type = try mod.addZIRInstConst(scope, src, .{792 const type_type = try addZIRInstConst(mod, scope, src, .{
732 .ty = Type.initTag(.type),793 .ty = Type.initTag(.type),
733 .val = Value.initTag(.type_type),794 .val = Value.initTag(.type_type),
734 });795 });
...@@ -737,21 +798,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa...@@ -737,21 +798,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
737 switch (rl) {798 switch (rl) {
738 .none => {799 .none => {
739 const operand = try expr(mod, scope, .none, params[1]);800 const operand = try expr(mod, scope, .none, params[1]);
740 return mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);801 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
741 },802 },
742 .discard => {803 .discard => {
743 const operand = try expr(mod, scope, .none, params[1]);804 const operand = try expr(mod, scope, .none, params[1]);
744 const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);805 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
745 _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);806 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
807 return result;
808 },
809 .lvalue => {
810 const operand = try expr(mod, scope, .lvalue, params[1]);
811 const result = try addZIRBinOp(mod, scope, src, .bitcast_lvalue, dest_type, operand);
746 return result;812 return result;
747 },813 },
748 .ty => |result_ty| {814 .ty => |result_ty| {
749 const result = try expr(mod, scope, .none, params[1]);815 const result = try expr(mod, scope, .none, params[1]);
750 const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result);816 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
751 return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted);817 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
752 },818 },
753 .ptr => |result_ptr| {819 .ptr => |result_ptr| {
754 const casted_result_ptr = try mod.addZIRUnOp(scope, src, .bitcast_result_ptr, result_ptr);820 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
755 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);821 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
756 },822 },
757 .bitcasted_ptr => |bitcasted_ptr| {823 .bitcasted_ptr => |bitcasted_ptr| {
...@@ -799,7 +865,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In...@@ -799,7 +865,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
799 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);865 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
800 for (param_nodes) |param_node, i| {866 for (param_nodes) |param_node, i| {
801 const param_src = tree.token_locs[param_node.firstToken()].start;867 const param_src = tree.token_locs[param_node.firstToken()].start;
802 const param_type = try mod.addZIRInst(scope, param_src, zir.Inst.ParamType, .{868 const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
803 .func = lhs,869 .func = lhs,
804 .arg_index = i,870 .arg_index = i,
805 }, .{});871 }, .{});
...@@ -807,7 +873,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In...@@ -807,7 +873,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
807 }873 }
808874
809 const src = tree.token_locs[node.lhs.firstToken()].start;875 const src = tree.token_locs[node.lhs.firstToken()].start;
810 const result = try mod.addZIRInst(scope, src, zir.Inst.Call, .{876 const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
811 .func = lhs,877 .func = lhs,
812 .args = args,878 .args = args,
813 }, .{});879 }, .{});
...@@ -818,7 +884,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In...@@ -818,7 +884,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
818fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {884fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
819 const tree = scope.tree();885 const tree = scope.tree();
820 const src = tree.token_locs[unreach_node.token].start;886 const src = tree.token_locs[unreach_node.token].start;
821 return mod.addZIRNoOp(scope, src, .@"unreachable");887 return addZIRNoOp(mod, scope, src, .@"unreachable");
822}888}
823889
824fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {890fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
...@@ -1000,19 +1066,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1000,19 +1066,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1000 .none => return result,1066 .none => return result,
1001 .discard => {1067 .discard => {
1002 // Emit a compile error for discarding error values.1068 // Emit a compile error for discarding error values.
1003 _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);1069 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
1004 return result;1070 return result;
1005 },1071 },
1006 .ty => |ty_inst| return mod.addZIRBinOp(scope, result.src, .as, ty_inst, result),1072 .lvalue => {
1073 // We need a pointer but we have a value.
1074 return addZIRUnOp(mod, scope, result.src, .ref, result);
1075 },
1076 .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
1007 .ptr => |ptr_inst| {1077 .ptr => |ptr_inst| {
1008 const casted_result = try mod.addZIRInst(scope, result.src, zir.Inst.CoerceToPtrElem, .{1078 const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
1009 .ptr = ptr_inst,1079 .ptr = ptr_inst,
1010 .value = result,1080 .value = result,
1011 }, .{});1081 }, .{});
1012 _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{1082 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
1013 .ptr = ptr_inst,
1014 .value = casted_result,
1015 }, .{});
1016 return casted_result;1083 return casted_result;
1017 },1084 },
1018 .bitcasted_ptr => |bitcasted_ptr| {1085 .bitcasted_ptr => |bitcasted_ptr| {
...@@ -1026,3 +1093,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1026,3 +1093,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1026 },1093 },
1027 }1094 }
1028}1095}
1096
1097pub fn addZIRInstSpecial(
1098 mod: *Module,
1099 scope: *Scope,
1100 src: usize,
1101 comptime T: type,
1102 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1103 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1104) !*T {
1105 const gen_zir = scope.getGenZIR();
1106 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
1107 const inst = try gen_zir.arena.create(T);
1108 inst.* = .{
1109 .base = .{
1110 .tag = T.base_tag,
1111 .src = src,
1112 },
1113 .positionals = positionals,
1114 .kw_args = kw_args,
1115 };
1116 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1117 return inst;
1118}
1119
1120pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
1121 const gen_zir = scope.getGenZIR();
1122 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
1123 const inst = try gen_zir.arena.create(zir.Inst.NoOp);
1124 inst.* = .{
1125 .base = .{
1126 .tag = tag,
1127 .src = src,
1128 },
1129 .positionals = .{},
1130 .kw_args = .{},
1131 };
1132 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1133 return inst;
1134}
1135
1136pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
1137 const inst = try addZIRNoOpT(mod, scope, src, tag);
1138 return &inst.base;
1139}
1140
1141pub fn addZIRUnOp(
1142 mod: *Module,
1143 scope: *Scope,
1144 src: usize,
1145 tag: zir.Inst.Tag,
1146 operand: *zir.Inst,
1147) !*zir.Inst {
1148 const gen_zir = scope.getGenZIR();
1149 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
1150 const inst = try gen_zir.arena.create(zir.Inst.UnOp);
1151 inst.* = .{
1152 .base = .{
1153 .tag = tag,
1154 .src = src,
1155 },
1156 .positionals = .{
1157 .operand = operand,
1158 },
1159 .kw_args = .{},
1160 };
1161 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1162 return &inst.base;
1163}
1164
1165pub fn addZIRBinOp(
1166 mod: *Module,
1167 scope: *Scope,
1168 src: usize,
1169 tag: zir.Inst.Tag,
1170 lhs: *zir.Inst,
1171 rhs: *zir.Inst,
1172) !*zir.Inst {
1173 const gen_zir = scope.getGenZIR();
1174 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
1175 const inst = try gen_zir.arena.create(zir.Inst.BinOp);
1176 inst.* = .{
1177 .base = .{
1178 .tag = tag,
1179 .src = src,
1180 },
1181 .positionals = .{
1182 .lhs = lhs,
1183 .rhs = rhs,
1184 },
1185 .kw_args = .{},
1186 };
1187 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1188 return &inst.base;
1189}
1190
1191pub fn addZIRInst(
1192 mod: *Module,
1193 scope: *Scope,
1194 src: usize,
1195 comptime T: type,
1196 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1197 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1198) !*zir.Inst {
1199 const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
1200 return &inst_special.base;
1201}
1202
1203/// TODO The existence of this function is a workaround for a bug in stage1.
1204pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
1205 const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
1206 return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
1207}
1208
1209/// TODO The existence of this function is a workaround for a bug in stage1.
1210pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
1211 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
1212 return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
1213}
src-self-hosted/clang.zig+9
...@@ -828,6 +828,14 @@ pub const ZigClangExpr_ConstExprUsage = extern enum {...@@ -828,6 +828,14 @@ pub const ZigClangExpr_ConstExprUsage = extern enum {
828 EvaluateForMangling,828 EvaluateForMangling,
829};829};
830830
831pub const ZigClangUnaryExprOrTypeTrait_Kind = extern enum {
832 SizeOf,
833 AlignOf,
834 VecStep,
835 OpenMPRequiredSimdAlign,
836 PreferredAlignOf,
837};
838
831pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;839pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
832pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;840pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
833pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;841pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
...@@ -1225,6 +1233,7 @@ pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const...@@ -1225,6 +1233,7 @@ pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const
12251233
1226pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;1234pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
1227pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;1235pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
1236pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getKind(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangUnaryExprOrTypeTrait_Kind;
12281237
1229pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;1238pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
1230pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;1239pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
src-self-hosted/codegen.zig+928-211
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const math = std.math;
3const assert = std.debug.assert;4const assert = std.debug.assert;
4const ir = @import("ir.zig");5const ir = @import("ir.zig");
5const Type = @import("type.zig").Type;6const Type = @import("type.zig").Type;
...@@ -11,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;...@@ -11,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;
11const Target = std.Target;12const Target = std.Target;
12const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;14const trace = @import("tracy.zig").trace;
15const DW = std.dwarf;
16const leb128 = std.debug.leb;
17
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off
1420
15/// 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.
16pub const BlockData = struct {22pub const BlockData = struct {
...@@ -43,64 +49,66 @@ pub fn generateSymbol(...@@ -43,64 +49,66 @@ pub fn generateSymbol(
43 src: usize,49 src: usize,
44 typed_value: TypedValue,50 typed_value: TypedValue,
45 code: *std.ArrayList(u8),51 code: *std.ArrayList(u8),
52 dbg_line: *std.ArrayList(u8),
46) GenerateSymbolError!Result {53) GenerateSymbolError!Result {
47 const tracy = trace(@src());54 const tracy = trace(@src());
48 defer tracy.end();55 defer tracy.end();
4956
50 switch (typed_value.ty.zigTypeTag()) {57 switch (typed_value.ty.zigTypeTag()) {
51 .Fn => {58 .Fn => {
52 switch (bin_file.options.target.cpu.arch) {59 switch (bin_file.base.options.target.cpu.arch) {
53 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
54 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
55 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
56 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
57 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
58 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
59 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
60 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
61 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
62 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
63 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
64 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
65 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
66 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
67 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
68 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
69 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
70 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
71 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
72 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
73 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
74 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
75 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
77 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
78 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
85 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
86 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
87 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
88 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
89 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
90 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
91 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
92 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
93 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
94 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
95 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
96 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
97 .shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
98 .lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
99 .wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
100 .wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
101 .renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
102 .renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
103 .ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
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."),
104 }112 }
105 },113 },
106 .Array => {114 .Array => {
...@@ -112,7 +120,7 @@ pub fn generateSymbol(...@@ -112,7 +120,7 @@ pub fn generateSymbol(
112 switch (try generateSymbol(bin_file, src, .{120 switch (try generateSymbol(bin_file, src, .{
113 .ty = typed_value.ty.elemType(),121 .ty = typed_value.ty.elemType(),
114 .val = sentinel,122 .val = sentinel,
115 }, code)) {123 }, code, dbg_line)) {
116 .appended => return Result{ .appended = {} },124 .appended => return Result{ .appended = {} },
117 .externally_managed => |slice| {125 .externally_managed => |slice| {
118 code.appendSliceAssumeCapacity(slice);126 code.appendSliceAssumeCapacity(slice);
...@@ -141,7 +149,7 @@ pub fn generateSymbol(...@@ -141,7 +149,7 @@ pub fn generateSymbol(
141 // TODO handle the dependency of this symbol on the decl's vaddr.149 // TODO handle the dependency of this symbol on the decl's vaddr.
142 // If the decl changes vaddr, then this symbol needs to get regenerated.150 // If the decl changes vaddr, then this symbol needs to get regenerated.
143 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;151 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
144 const endian = bin_file.options.target.cpu.arch.endian();152 const endian = bin_file.base.options.target.cpu.arch.endian();
145 switch (bin_file.ptr_width) {153 switch (bin_file.ptr_width) {
146 .p32 => {154 .p32 => {
147 try code.resize(4);155 try code.resize(4);
...@@ -164,7 +172,7 @@ pub fn generateSymbol(...@@ -164,7 +172,7 @@ pub fn generateSymbol(
164 };172 };
165 },173 },
166 .Int => {174 .Int => {
167 const info = typed_value.ty.intInfo(bin_file.options.target);175 const info = typed_value.ty.intInfo(bin_file.base.options.target);
168 if (info.bits == 8 and !info.signed) {176 if (info.bits == 8 and !info.signed) {
169 const x = typed_value.val.toUnsignedInt();177 const x = typed_value.val.toUnsignedInt();
170 try code.append(@intCast(u8, x));178 try code.append(@intCast(u8, x));
...@@ -204,10 +212,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -204,10 +212,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
204 target: *const std.Target,212 target: *const std.Target,
205 mod_fn: *const Module.Fn,213 mod_fn: *const Module.Fn,
206 code: *std.ArrayList(u8),214 code: *std.ArrayList(u8),
215 dbg_line: *std.ArrayList(u8),
207 err_msg: ?*ErrorMsg,216 err_msg: ?*ErrorMsg,
208 args: []MCValue,217 args: []MCValue,
218 ret_mcv: MCValue,
219 fn_type: Type,
209 arg_index: usize,220 arg_index: usize,
210 src: usize,221 src: usize,
222 stack_align: u32,
223
224 /// Byte offset within the source file.
225 prev_di_src: usize,
226 /// Relative to the beginning of `code`.
227 prev_di_pc: usize,
228 /// Used to find newlines and count line deltas.
229 source: []const u8,
230 /// Byte offset within the source file of the ending curly.
231 rbrace_src: usize,
232
233 /// The value is an offset into the `Function` `code` from the beginning.
234 /// To perform the reloc, write 32-bit signed little-endian integer
235 /// which is a relative jump, based on the address following the reloc.
236 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
211237
212 /// Whenever there is a runtime branch, we push a Branch onto this stack,238 /// Whenever there is a runtime branch, we push a Branch onto this stack,
213 /// and pop it off when the runtime branch joins. This provides an "overlay"239 /// and pop it off when the runtime branch joins. This provides an "overlay"
...@@ -225,22 +251,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -225,22 +251,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
225 unreach,251 unreach,
226 /// No more references to this value remain.252 /// No more references to this value remain.
227 dead,253 dead,
254 /// The value is undefined.
255 undef,
228 /// A pointer-sized integer that fits in a register.256 /// A pointer-sized integer that fits in a register.
257 /// If the type is a pointer, this is the pointer address in virtual address space.
229 immediate: u64,258 immediate: u64,
230 /// The constant was emitted into the code, at this offset.259 /// The constant was emitted into the code, at this offset.
260 /// If the type is a pointer, it means the pointer address is embedded in the code.
231 embedded_in_code: usize,261 embedded_in_code: usize,
262 /// The value is a pointer to a constant which was emitted into the code, at this offset.
263 ptr_embedded_in_code: usize,
232 /// The value is in a target-specific register.264 /// The value is in a target-specific register.
233 register: Register,265 register: Register,
234 /// The value is in memory at a hard-coded address.266 /// The value is in memory at a hard-coded address.
267 /// If the type is a pointer, it means the pointer address is at this memory location.
235 memory: u64,268 memory: u64,
236 /// The value is one of the stack variables.269 /// The value is one of the stack variables.
237 stack_offset: u64,270 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
271 stack_offset: u32,
272 /// The value is a pointer to one of the stack variables (payload is stack offset).
273 ptr_stack_offset: u32,
238 /// The value is in the compare flags assuming an unsigned operation,274 /// The value is in the compare flags assuming an unsigned operation,
239 /// with this operator applied on top of it.275 /// with this operator applied on top of it.
240 compare_flags_unsigned: std.math.CompareOperator,276 compare_flags_unsigned: math.CompareOperator,
241 /// The value is in the compare flags assuming a signed operation,277 /// The value is in the compare flags assuming a signed operation,
242 /// with this operator applied on top of it.278 /// with this operator applied on top of it.
243 compare_flags_signed: std.math.CompareOperator,279 compare_flags_signed: math.CompareOperator,
244280
245 fn isMemory(mcv: MCValue) bool {281 fn isMemory(mcv: MCValue) bool {
246 return switch (mcv) {282 return switch (mcv) {
...@@ -267,6 +303,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -267,6 +303,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
267 .memory,303 .memory,
268 .compare_flags_unsigned,304 .compare_flags_unsigned,
269 .compare_flags_signed,305 .compare_flags_signed,
306 .ptr_stack_offset,
307 .ptr_embedded_in_code,
308 .undef,
270 => false,309 => false,
271310
272 .register,311 .register,
...@@ -279,10 +318,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -279,10 +318,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
279 const Branch = struct {318 const Branch = struct {
280 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},319 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
281 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},320 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
282 free_registers: FreeRegInt = std.math.maxInt(FreeRegInt),321 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
283322
284 /// Maps offset to what is stored there.323 /// Maps offset to what is stored there.
285 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},324 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
286 /// Offset from the stack base, representing the end of the stack frame.325 /// Offset from the stack base, representing the end of the stack frame.
287 max_end_stack: u32 = 0,326 max_end_stack: u32 = 0,
288 /// Represents the current end stack offset. If there is no existing slot327 /// Represents the current end stack offset. If there is no existing slot
...@@ -292,7 +331,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -292,7 +331,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
292 fn markRegUsed(self: *Branch, reg: Register) void {331 fn markRegUsed(self: *Branch, reg: Register) void {
293 if (FreeRegInt == u0) return;332 if (FreeRegInt == u0) return;
294 const index = reg.allocIndex() orelse return;333 const index = reg.allocIndex() orelse return;
295 const ShiftInt = std.math.Log2Int(FreeRegInt);334 const ShiftInt = math.Log2Int(FreeRegInt);
296 const shift = @intCast(ShiftInt, index);335 const shift = @intCast(ShiftInt, index);
297 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);336 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
298 }337 }
...@@ -300,11 +339,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -300,11 +339,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
300 fn markRegFree(self: *Branch, reg: Register) void {339 fn markRegFree(self: *Branch, reg: Register) void {
301 if (FreeRegInt == u0) return;340 if (FreeRegInt == u0) return;
302 const index = reg.allocIndex() orelse return;341 const index = reg.allocIndex() orelse return;
303 const ShiftInt = std.math.Log2Int(FreeRegInt);342 const ShiftInt = math.Log2Int(FreeRegInt);
304 const shift = @intCast(ShiftInt, index);343 const shift = @intCast(ShiftInt, index);
305 self.free_registers |= @as(FreeRegInt, 1) << shift;344 self.free_registers |= @as(FreeRegInt, 1) << shift;
306 }345 }
307346
347 /// Before calling, must ensureCapacity + 1 on branch.registers.
348 /// Returns `null` if all registers are allocated.
349 fn allocReg(self: *Branch, inst: *ir.Inst) ?Register {
350 const free_index = @ctz(FreeRegInt, self.free_registers);
351 if (free_index >= callee_preserved_regs.len) {
352 return null;
353 }
354 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
355 const reg = callee_preserved_regs[free_index];
356 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
357 return reg;
358 }
359
308 fn deinit(self: *Branch, gpa: *Allocator) void {360 fn deinit(self: *Branch, gpa: *Allocator) void {
309 self.inst_table.deinit(gpa);361 self.inst_table.deinit(gpa);
310 self.registers.deinit(gpa);362 self.registers.deinit(gpa);
...@@ -329,15 +381,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -329,15 +381,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
329 src: usize,381 src: usize,
330 typed_value: TypedValue,382 typed_value: TypedValue,
331 code: *std.ArrayList(u8),383 code: *std.ArrayList(u8),
384 dbg_line: *std.ArrayList(u8),
332 ) GenerateSymbolError!Result {385 ) GenerateSymbolError!Result {
333 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;386 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
334387
335 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;388 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
336 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
337 defer bin_file.allocator.free(param_types);
338 fn_type.fnParamTypes(param_types);
339 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
340 defer bin_file.allocator.free(mc_args);
341389
342 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);390 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
343 defer {391 defer {
...@@ -348,24 +396,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -348,24 +396,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
348 const branch = try branch_stack.addOne();396 const branch = try branch_stack.addOne();
349 branch.* = .{};397 branch.* = .{};
350398
399 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
400 if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
401 const tree = scope_file.contents.tree;
402 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
403 const block = fn_proto.body().?.castTag(.Block).?;
404 const lbrace_src = tree.token_locs[block.lbrace].start;
405 const rbrace_src = tree.token_locs[block.rbrace].start;
406 break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, .source = tree.source };
407 } else if (module_fn.owner_decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
408 const byte_off = zir_module.contents.module.decls[module_fn.owner_decl.src_index].inst.src;
409 break :blk .{ .lbrace_src = byte_off, .rbrace_src = byte_off, .source = zir_module.source.bytes };
410 } else {
411 unreachable;
412 }
413 };
414
351 var function = Self{415 var function = Self{
352 .gpa = bin_file.allocator,416 .gpa = bin_file.allocator,
353 .target = &bin_file.options.target,417 .target = &bin_file.base.options.target,
354 .bin_file = bin_file,418 .bin_file = bin_file,
355 .mod_fn = module_fn,419 .mod_fn = module_fn,
356 .code = code,420 .code = code,
421 .dbg_line = dbg_line,
357 .err_msg = null,422 .err_msg = null,
358 .args = mc_args,423 .args = undefined, // populated after `resolveCallingConventionValues`
424 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
425 .fn_type = fn_type,
359 .arg_index = 0,426 .arg_index = 0,
360 .branch_stack = &branch_stack,427 .branch_stack = &branch_stack,
361 .src = src,428 .src = src,
429 .stack_align = undefined,
430 .prev_di_pc = 0,
431 .prev_di_src = src_data.lbrace_src,
432 .rbrace_src = src_data.rbrace_src,
433 .source = src_data.source,
362 };434 };
435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
363436
364 const cc = fn_type.fnCallingConvention();437 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
365 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
366 error.CodegenFail => return Result{ .fail = function.err_msg.? },438 error.CodegenFail => return Result{ .fail = function.err_msg.? },
367 else => |e| return e,439 else => |e| return e,
368 };440 };
441 defer call_info.deinit(&function);
442
443 function.args = call_info.args;
444 function.ret_mcv = call_info.return_value;
445 function.stack_align = call_info.stack_align;
446 branch.max_end_stack = call_info.stack_byte_count;
369447
370 function.gen() catch |err| switch (err) {448 function.gen() catch |err| switch (err) {
371 error.CodegenFail => return Result{ .fail = function.err_msg.? },449 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -380,28 +458,81 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -380,28 +458,81 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
380 }458 }
381459
382 fn gen(self: *Self) !void {460 fn gen(self: *Self) !void {
383 try self.code.ensureCapacity(self.code.items.len + 11);461 switch (arch) {
384462 .x86_64 => {
385 // push rbp463 try self.code.ensureCapacity(self.code.items.len + 11);
386 // mov rbp, rsp464
387 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });465 const cc = self.fn_type.fnCallingConvention();
388466 if (cc != .Naked) {
389 // sub rsp, x467 // We want to subtract the aligned stack frame size from rsp here, but we don't
390 const stack_end = self.branch_stack.items[0].max_end_stack;468 // yet know how big it will be, so we leave room for a 4-byte stack size.
391 if (stack_end > std.math.maxInt(i32)) {469 // TODO During semantic analysis, check if there are no function calls. If there
392 return self.fail(self.src, "too much stack used in call parameters", .{});470 // are none, here we can omit the part where we subtract and then add rsp.
393 } else if (stack_end > std.math.maxInt(i8)) {471 self.code.appendSliceAssumeCapacity(&[_]u8{
394 // 48 83 ec xx sub rsp,0x10472 0x55, // push rbp
395 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });473 0x48, 0x89, 0xe5, // mov rbp, rsp
396 const x = @intCast(u32, stack_end);474 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
397 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);475 });
398 } else if (stack_end != 0) {476 const reloc_index = self.code.items.len;
399 // 48 81 ec xx xx xx xx sub rsp,0x80477 self.code.items.len += 4;
400 const x = @intCast(u8, stack_end);478
401 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });479 try self.dbgSetPrologueEnd();
402 }480 try self.genBody(self.mod_fn.analysis.success);
481
482 const stack_end = self.branch_stack.items[0].max_end_stack;
483 if (stack_end > math.maxInt(i32))
484 return self.fail(self.src, "too much stack used in call parameters", .{});
485 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
486 mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end));
487
488 if (self.code.items.len >= math.maxInt(i32)) {
489 return self.fail(self.src, "unable to perform relocation: jump too far", .{});
490 }
491 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
492 const amt = self.code.items.len - (jmp_reloc + 4);
493 // If it wouldn't jump at all, elide it.
494 if (amt == 0) {
495 self.code.items.len -= 5;
496 continue;
497 }
498 const s32_amt = @intCast(i32, amt);
499 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
500 }
501
502 // Important to be after the possible self.code.items.len -= 5 above.
503 try self.dbgSetEpilogueBegin();
504
505 try self.code.ensureCapacity(self.code.items.len + 9);
506 // add rsp, x
507 if (aligned_stack_end > math.maxInt(i8)) {
508 // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff
509 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xc4 });
510 const x = @intCast(u32, aligned_stack_end);
511 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
512 } else if (aligned_stack_end != 0) {
513 // example: 48 83 c4 7f add rsp,0x7f
514 const x = @intCast(u8, aligned_stack_end);
515 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xc4, x });
516 }
403517
404 try self.genBody(self.mod_fn.analysis.success);518 self.code.appendSliceAssumeCapacity(&[_]u8{
519 0x5d, // pop rbp
520 0xc3, // ret
521 });
522 } else {
523 try self.dbgSetPrologueEnd();
524 try self.genBody(self.mod_fn.analysis.success);
525 try self.dbgSetEpilogueBegin();
526 }
527 },
528 else => {
529 try self.dbgSetPrologueEnd();
530 try self.genBody(self.mod_fn.analysis.success);
531 try self.dbgSetEpilogueBegin();
532 },
533 }
534 // Drop them off at the rbrace.
535 try self.dbgAdvancePCAndLine(self.rbrace_src);
405 }536 }
406537
407 fn genBody(self: *Self, body: ir.Body) InnerError!void {538 fn genBody(self: *Self, body: ir.Body) InnerError!void {
...@@ -418,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -418,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
418 }549 }
419 }550 }
420551
552 fn dbgSetPrologueEnd(self: *Self) InnerError!void {
553 try self.dbg_line.append(DW.LNS_set_prologue_end);
554 try self.dbgAdvancePCAndLine(self.prev_di_src);
555 }
556
557 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
558 try self.dbg_line.append(DW.LNS_set_epilogue_begin);
559 try self.dbgAdvancePCAndLine(self.prev_di_src);
560 }
561
562 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
563 // TODO Look into improving the performance here by adding a token-index-to-line
564 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
565 // this involves scanning over the source code for newlines
566 // (but only from the previous byte offset to the new one).
567 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
568 const delta_pc = self.code.items.len - self.prev_di_pc;
569 self.prev_di_src = src;
570 self.prev_di_pc = self.code.items.len;
571 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
572 // single-byte opcodes that add different numbers to both the PC and the line number
573 // at the same time.
574 try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);
575 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
576 leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;
577 if (delta_line != 0) {
578 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
579 leb128.writeILEB128(self.dbg_line.writer(), delta_line) catch unreachable;
580 }
581 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
582 }
583
421 fn processDeath(self: *Self, inst: *ir.Inst) void {584 fn processDeath(self: *Self, inst: *ir.Inst) void {
422 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];585 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
423 const entry = branch.inst_table.getEntry(inst) orelse return;586 const entry = branch.inst_table.getEntry(inst) orelse return;
...@@ -425,8 +588,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -425,8 +588,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
425 entry.value = .dead;588 entry.value = .dead;
426 switch (prev_value) {589 switch (prev_value) {
427 .register => |reg| {590 .register => |reg| {
428 _ = branch.registers.remove(reg);591 const canon_reg = toCanonicalReg(reg);
429 branch.markRegFree(reg);592 _ = branch.registers.remove(canon_reg);
593 branch.markRegFree(canon_reg);
430 },594 },
431 else => {}, // TODO process stack allocation death595 else => {}, // TODO process stack allocation death
432 }596 }
...@@ -435,6 +599,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -435,6 +599,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
435 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {599 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
436 switch (inst.tag) {600 switch (inst.tag) {
437 .add => return self.genAdd(inst.castTag(.add).?),601 .add => return self.genAdd(inst.castTag(.add).?),
602 .alloc => return self.genAlloc(inst.castTag(.alloc).?),
438 .arg => return self.genArg(inst.castTag(.arg).?),603 .arg => return self.genArg(inst.castTag(.arg).?),
439 .assembly => return self.genAsm(inst.castTag(.assembly).?),604 .assembly => return self.genAsm(inst.castTag(.assembly).?),
440 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),605 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
...@@ -451,19 +616,91 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -451,19 +616,91 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
451 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),616 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
452 .condbr => return self.genCondBr(inst.castTag(.condbr).?),617 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
453 .constant => unreachable, // excluded from function bodies618 .constant => unreachable, // excluded from function bodies
619 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
620 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
621 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
454 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),622 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
455 .isnull => return self.genIsNull(inst.castTag(.isnull).?),623 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
624 .load => return self.genLoad(inst.castTag(.load).?),
625 .not => return self.genNot(inst.castTag(.not).?),
456 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),626 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
627 .ref => return self.genRef(inst.castTag(.ref).?),
457 .ret => return self.genRet(inst.castTag(.ret).?),628 .ret => return self.genRet(inst.castTag(.ret).?),
458 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),629 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
630 .store => return self.genStore(inst.castTag(.store).?),
459 .sub => return self.genSub(inst.castTag(.sub).?),631 .sub => return self.genSub(inst.castTag(.sub).?),
460 .unreach => return MCValue{ .unreach = {} },632 .unreach => return MCValue{ .unreach = {} },
461 .not => return self.genNot(inst.castTag(.not).?),
462 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
463 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
464 }633 }
465 }634 }
466635
636 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
637 if (abi_align > self.stack_align)
638 self.stack_align = abi_align;
639 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
640 // TODO find a free slot instead of always appending
641 const offset = mem.alignForwardGeneric(u32, branch.next_stack_offset, abi_align);
642 branch.next_stack_offset = offset + abi_size;
643 if (branch.next_stack_offset > branch.max_end_stack)
644 branch.max_end_stack = branch.next_stack_offset;
645 try branch.stack.putNoClobber(self.gpa, offset, .{
646 .inst = inst,
647 .size = abi_size,
648 });
649 return offset;
650 }
651
652 /// Use a pointer instruction as the basis for allocating stack memory.
653 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
654 const elem_ty = inst.ty.elemType();
655 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
656 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
657 };
658 // TODO swap this for inst.ty.ptrAlign
659 const abi_align = elem_ty.abiAlignment(self.target.*);
660 return self.allocMem(inst, abi_size, abi_align);
661 }
662
663 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {
664 const elem_ty = inst.ty;
665 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
666 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
667 };
668 const abi_align = elem_ty.abiAlignment(self.target.*);
669 if (abi_align > self.stack_align)
670 self.stack_align = abi_align;
671 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
672
673 // Make sure the type can fit in a register before we try to allocate one.
674 const ptr_bits = arch.ptrBitWidth();
675 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
676 if (abi_size <= ptr_bytes) {
677 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
678 if (branch.allocReg(inst)) |reg| {
679 return MCValue{ .register = registerAlias(reg, abi_size) };
680 }
681 }
682 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
683 return MCValue{ .stack_offset = stack_offset };
684 }
685
686 /// Does not "move" the instruction.
687 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
688 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
689 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
690
691 const reg = branch.allocReg(inst) orelse
692 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
693 const old_mcv = branch.inst_table.get(inst).?;
694 const new_mcv: MCValue = .{ .register = reg };
695 try self.genSetReg(inst.src, reg, old_mcv);
696 return new_mcv;
697 }
698
699 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
700 const stack_offset = try self.allocMemPtr(&inst.base);
701 return MCValue{ .ptr_stack_offset = stack_offset };
702 }
703
467 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {704 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
468 // No side effects, so if it's unreferenced, do nothing.705 // No side effects, so if it's unreferenced, do nothing.
469 if (inst.base.isUnused())706 if (inst.base.isUnused())
...@@ -542,6 +779,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -542,6 +779,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
542 }779 }
543 }780 }
544781
782 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
783 const elem_ty = inst.base.ty;
784 if (!elem_ty.hasCodeGenBits())
785 return MCValue.none;
786 const ptr = try self.resolveInst(inst.operand);
787 const is_volatile = inst.operand.ty.isVolatilePtr();
788 if (inst.base.isUnused() and !is_volatile)
789 return MCValue.dead;
790 const dst_mcv: MCValue = blk: {
791 if (inst.base.operandDies(0) and ptr.isMutable()) {
792 // The MCValue that holds the pointer can be re-used as the value.
793 // TODO track this in the register/stack allocation metadata.
794 break :blk ptr;
795 } else {
796 break :blk try self.allocRegOrMem(&inst.base);
797 }
798 };
799 switch (ptr) {
800 .none => unreachable,
801 .undef => unreachable,
802 .unreach => unreachable,
803 .dead => unreachable,
804 .compare_flags_unsigned => unreachable,
805 .compare_flags_signed => unreachable,
806 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
807 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
808 .ptr_embedded_in_code => |off| {
809 try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
810 },
811 .embedded_in_code => {
812 return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
813 },
814 .register => {
815 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
816 },
817 .memory => {
818 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
819 },
820 .stack_offset => {
821 return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
822 },
823 }
824 return dst_mcv;
825 }
826
827 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
828 const ptr = try self.resolveInst(inst.lhs);
829 const value = try self.resolveInst(inst.rhs);
830 const elem_ty = inst.rhs.ty;
831 switch (ptr) {
832 .none => unreachable,
833 .undef => unreachable,
834 .unreach => unreachable,
835 .dead => unreachable,
836 .compare_flags_unsigned => unreachable,
837 .compare_flags_signed => unreachable,
838 .immediate => |imm| {
839 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
840 },
841 .ptr_stack_offset => |off| {
842 try self.genSetStack(inst.base.src, elem_ty, off, value);
843 },
844 .ptr_embedded_in_code => |off| {
845 try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
846 },
847 .embedded_in_code => {
848 return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
849 },
850 .register => {
851 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
852 },
853 .memory => {
854 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
855 },
856 .stack_offset => {
857 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
858 },
859 }
860 return .none;
861 }
862
545 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {863 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
546 // No side effects, so if it's unreferenced, do nothing.864 // No side effects, so if it's unreferenced, do nothing.
547 if (inst.base.isUnused())865 if (inst.base.isUnused())
...@@ -609,7 +927,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -609,7 +927,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
609 // and as a register.927 // and as a register.
610 switch (src_mcv) {928 switch (src_mcv) {
611 .immediate => |imm| {929 .immediate => |imm| {
612 if (imm > std.math.maxInt(u31)) {930 if (imm > math.maxInt(u31)) {
613 src_mcv = try self.copyToNewRegister(src_inst);931 src_mcv = try self.copyToNewRegister(src_inst);
614 }932 }
615 },933 },
...@@ -624,13 +942,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -624,13 +942,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
624 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {942 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
625 switch (dst_mcv) {943 switch (dst_mcv) {
626 .none => unreachable,944 .none => unreachable,
945 .undef => unreachable,
627 .dead, .unreach, .immediate => unreachable,946 .dead, .unreach, .immediate => unreachable,
628 .compare_flags_unsigned => unreachable,947 .compare_flags_unsigned => unreachable,
629 .compare_flags_signed => unreachable,948 .compare_flags_signed => unreachable,
949 .ptr_stack_offset => unreachable,
950 .ptr_embedded_in_code => unreachable,
630 .register => |dst_reg| {951 .register => |dst_reg| {
631 switch (src_mcv) {952 switch (src_mcv) {
632 .none => unreachable,953 .none => unreachable,
954 .undef => try self.genSetReg(src, dst_reg, .undef),
633 .dead, .unreach => unreachable,955 .dead, .unreach => unreachable,
956 .ptr_stack_offset => unreachable,
957 .ptr_embedded_in_code => unreachable,
634 .register => |src_reg| {958 .register => |src_reg| {
635 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });959 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
636 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });960 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
...@@ -638,7 +962,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -638,7 +962,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
638 .immediate => |imm| {962 .immediate => |imm| {
639 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.963 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
640 // 81 /opx id964 // 81 /opx id
641 if (imm32 <= std.math.maxInt(u7)) {965 if (imm32 <= math.maxInt(u7)) {
642 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });966 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
643 self.code.appendSliceAssumeCapacity(&[_]u8{967 self.code.appendSliceAssumeCapacity(&[_]u8{
644 0x83,968 0x83,
...@@ -699,26 +1023,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -699,26 +1023,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
699 .i386, .x86_64 => {1023 .i386, .x86_64 => {
700 try self.code.append(0xcc); // int31024 try self.code.append(0xcc); // int3
701 },1025 },
1026 .riscv64 => {
1027 const full = @bitCast(u32, instructions.CallBreak{
1028 .mode = @enumToInt(instructions.CallBreak.Mode.ebreak),
1029 });
1030
1031 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1032 },
702 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1033 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
703 }1034 }
704 return .none;1035 return .none;
705 }1036 }
7061037
707 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {1038 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
708 const fn_ty = inst.func.ty;1039 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
709 const cc = fn_ty.fnCallingConvention();1040 defer info.deinit(self);
710 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
711 defer self.gpa.free(param_types);
712 fn_ty.fnParamTypes(param_types);
713 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
714 defer self.gpa.free(mc_args);
715 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
7161041
717 switch (arch) {1042 switch (arch) {
718 .x86_64 => {1043 .x86_64 => {
719 for (mc_args) |mc_arg, arg_i| {1044 for (info.args) |mc_arg, arg_i| {
720 const arg = inst.args[arg_i];1045 const arg = inst.args[arg_i];
721 const arg_mcv = try self.resolveInst(inst.args[arg_i]);1046 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1047 // Here we do not use setRegOrMem even though the logic is similar, because
1048 // the function call will move the stack pointer, so the offsets are different.
722 switch (mc_arg) {1049 switch (mc_arg) {
723 .none => continue,1050 .none => continue,
724 .register => |reg| {1051 .register => |reg| {
...@@ -730,6 +1057,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -730,6 +1057,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
730 // mov qword ptr [rsp + stack_offset], x1057 // mov qword ptr [rsp + stack_offset], x
731 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});1058 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
732 },1059 },
1060 .ptr_stack_offset => {
1061 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1062 },
1063 .ptr_embedded_in_code => {
1064 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1065 },
1066 .undef => unreachable,
733 .immediate => unreachable,1067 .immediate => unreachable,
734 .unreach => unreachable,1068 .unreach => unreachable,
735 .dead => unreachable,1069 .dead => unreachable,
...@@ -758,30 +1092,86 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -758,30 +1092,86 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});1092 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
759 }1093 }
760 },1094 },
1095 .riscv64 => {
1096 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1097
1098 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1099 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1100 const func = func_val.func;
1101 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1102 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1103 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);
1105
1106 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1107 const jalr = instructions.Jalr{
1108 .rd = Register.ra.id(),
1109 .rs1 = Register.ra.id(),
1110 .offset = 0,
1111 };
1112 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
1113 } else {
1114 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1115 }
1116 } else {
1117 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1118 }
1119 },
761 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1120 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
762 }1121 }
7631122
764 const return_type = fn_ty.fnReturnType();1123 return info.return_value;
765 switch (return_type.zigTypeTag()) {1124 }
766 .Void => return MCValue{ .none = {} },1125
767 .NoReturn => return MCValue{ .unreach = {} },1126 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
768 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),1127 const operand = try self.resolveInst(inst.operand);
1128 switch (operand) {
1129 .unreach => unreachable,
1130 .dead => unreachable,
1131 .none => return .none,
1132
1133 .immediate,
1134 .register,
1135 .ptr_stack_offset,
1136 .ptr_embedded_in_code,
1137 .compare_flags_unsigned,
1138 .compare_flags_signed,
1139 => {
1140 const stack_offset = try self.allocMemPtr(&inst.base);
1141 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
1142 return MCValue{ .ptr_stack_offset = stack_offset };
1143 },
1144
1145 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
1146 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
1147 .memory => |vaddr| return MCValue{ .immediate = vaddr },
1148
1149 .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
769 }1150 }
770 }1151 }
7711152
772 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {1153 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
773 if (mcv != .none) {1154 const ret_ty = self.fn_type.fnReturnType();
774 return self.fail(src, "TODO implement return with non-void operand", .{});1155 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
775 }
776 switch (arch) {1156 switch (arch) {
777 .i386 => {1157 .i386 => {
778 try self.code.append(0xc3); // ret1158 try self.code.append(0xc3); // ret
779 },1159 },
780 .x86_64 => {1160 .x86_64 => {
781 try self.code.appendSlice(&[_]u8{1161 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
782 0x5d, // pop rbp1162 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
783 0xc3, // ret1163 // which is available if the jump is 127 bytes or less forward.
784 });1164 try self.code.resize(self.code.items.len + 5);
1165 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
1166 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1167 },
1168 .riscv64 => {
1169 const jalr = instructions.Jalr{
1170 .rd = Register.zero.id(),
1171 .rs1 = Register.ra.id(),
1172 .offset = 0,
1173 };
1174 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), @bitCast(u32, jalr));
785 },1175 },
786 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1176 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
787 }1177 }
...@@ -797,7 +1187,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -797,7 +1187,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
797 return self.ret(inst.base.src, .none);1187 return self.ret(inst.base.src, .none);
798 }1188 }
7991189
800 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: std.math.CompareOperator) !MCValue {1190 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {
801 // No side effects, so if it's unreferenced, do nothing.1191 // No side effects, so if it's unreferenced, do nothing.
802 if (inst.base.isUnused())1192 if (inst.base.isUnused())
803 return MCValue.dead;1193 return MCValue.dead;
...@@ -830,6 +1220,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -830,6 +1220,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
830 }1220 }
831 }1221 }
8321222
1223 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1224 try self.dbgAdvancePCAndLine(inst.base.src);
1225 return MCValue.none;
1226 }
1227
833 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {1228 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
834 switch (arch) {1229 switch (arch) {
835 .x86_64 => {1230 .x86_64 => {
...@@ -865,7 +1260,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -865,7 +1260,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
865 // test reg, 11260 // test reg, 1
866 // TODO detect al, ax, eax1261 // TODO detect al, ax, eax
867 try self.code.ensureCapacity(self.code.items.len + 4);1262 try self.code.ensureCapacity(self.code.items.len + 4);
868 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });1263 // TODO audit this codegen: we force w = true here to make
1264 // the value affect the big register
1265 self.rex(.{ .b = reg.isExtended(), .w = true });
869 self.code.appendSliceAssumeCapacity(&[_]u8{1266 self.code.appendSliceAssumeCapacity(&[_]u8{
870 0xf6,1267 0xf6,
871 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),1268 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
...@@ -921,7 +1318,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -921,7 +1318,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
921 switch (reloc) {1318 switch (reloc) {
922 .rel32 => |pos| {1319 .rel32 => |pos| {
923 const amt = self.code.items.len - (pos + 4);1320 const amt = self.code.items.len - (pos + 4);
924 const s32_amt = std.math.cast(i32, amt) catch1321 // If it wouldn't jump at all, elide it.
1322 if (amt == 0) {
1323 self.code.items.len -= 5;
1324 return;
1325 }
1326 const s32_amt = math.cast(i32, amt) catch
925 return self.fail(src, "unable to perform relocation: jump too far", .{});1327 return self.fail(src, "unable to perform relocation: jump too far", .{});
926 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);1328 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
927 },1329 },
...@@ -963,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -963,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
963 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {1365 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
964 if (!inst.is_volatile and inst.base.isUnused())1366 if (!inst.is_volatile and inst.base.isUnused())
965 return MCValue.dead;1367 return MCValue.dead;
966 if (arch != .x86_64 and arch != .i386) {1368 switch (arch) {
967 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});1369 .riscv64 => {
968 }1370 for (inst.inputs) |input, i| {
969 for (inst.inputs) |input, i| {1371 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
970 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1372 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
971 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});1373 }
972 }1374 const reg_name = input[1 .. input.len - 1];
973 const reg_name = input[1 .. input.len - 1];1375 const reg = parseRegName(reg_name) orelse
974 const reg = parseRegName(reg_name) orelse1376 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
975 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1377 const arg = try self.resolveInst(inst.args[i]);
976 const arg = try self.resolveInst(inst.args[i]);1378 try self.genSetReg(inst.base.src, reg, arg);
977 try self.genSetReg(inst.base.src, reg, arg);1379 }
978 }
9791380
980 if (mem.eql(u8, inst.asm_source, "syscall")) {1381 if (mem.eql(u8, inst.asm_source, "ecall")) {
981 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });1382 const full = @bitCast(u32, instructions.CallBreak{
982 } else {1383 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
983 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});1384 });
984 }
9851385
986 if (inst.output) |output| {1386 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
987 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {1387 } else {
988 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});1388 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
989 }1389 }
990 const reg_name = output[2 .. output.len - 1];1390
991 const reg = parseRegName(reg_name) orelse1391 if (inst.output) |output| {
992 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});1392 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
993 return MCValue{ .register = reg };1393 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
994 } else {1394 }
995 return MCValue.none;1395 const reg_name = output[2 .. output.len - 1];
1396 const reg = parseRegName(reg_name) orelse
1397 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1398 return MCValue{ .register = reg };
1399 } else {
1400 return MCValue.none;
1401 }
1402 },
1403 .x86_64, .i386 => {
1404 for (inst.inputs) |input, i| {
1405 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1406 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1407 }
1408 const reg_name = input[1 .. input.len - 1];
1409 const reg = parseRegName(reg_name) orelse
1410 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1411 const arg = try self.resolveInst(inst.args[i]);
1412 try self.genSetReg(inst.base.src, reg, arg);
1413 }
1414
1415 if (mem.eql(u8, inst.asm_source, "syscall")) {
1416 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1417 } else {
1418 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1419 }
1420
1421 if (inst.output) |output| {
1422 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1423 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1424 }
1425 const reg_name = output[2 .. output.len - 1];
1426 const reg = parseRegName(reg_name) orelse
1427 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1428 return MCValue{ .register = reg };
1429 } else {
1430 return MCValue.none;
1431 }
1432 },
1433 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
996 }1434 }
997 }1435 }
9981436
...@@ -1024,15 +1462,211 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1024,15 +1462,211 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1024 }1462 }
1025 }1463 }
10261464
1027 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {1465 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1466 fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
1467 switch (loc) {
1468 .none => return,
1469 .register => |reg| return self.genSetReg(src, reg, val),
1470 .stack_offset => |off| return self.genSetStack(src, ty, off, val),
1471 .memory => {
1472 return self.fail(src, "TODO implement setRegOrMem for memory", .{});
1473 },
1474 else => unreachable,
1475 }
1476 }
1477
1478 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1028 switch (arch) {1479 switch (arch) {
1029 .x86_64 => switch (mcv) {1480 .x86_64 => switch (mcv) {
1030 .dead => unreachable,1481 .dead => unreachable,
1031 .none => unreachable,1482 .ptr_stack_offset => unreachable,
1032 .unreach => unreachable,1483 .ptr_embedded_in_code => unreachable,
1484 .unreach, .none => return, // Nothing to do.
1485 .undef => {
1486 if (!self.wantSafety())
1487 return; // The already existing value will do just fine.
1488 // TODO Upgrade this to a memset call when we have that available.
1489 switch (ty.abiSize(self.target.*)) {
1490 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
1491 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
1492 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1493 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1494 else => return self.fail(src, "TODO implement memset", .{}),
1495 }
1496 },
1497 .compare_flags_unsigned => |op| {
1498 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
1499 },
1500 .compare_flags_signed => |op| {
1501 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
1502 },
1503 .immediate => |x_big| {
1504 const abi_size = ty.abiSize(self.target.*);
1505 const adj_off = stack_offset + abi_size;
1506 if (adj_off > 128) {
1507 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
1508 }
1509 try self.code.ensureCapacity(self.code.items.len + 8);
1510 switch (abi_size) {
1511 1 => {
1512 return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});
1513 },
1514 2 => {
1515 return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});
1516 },
1517 4 => {
1518 const x = @intCast(u32, x_big);
1519 // We have a positive stack offset value but we want a twos complement negative
1520 // offset from rbp, which is at the top of the stack frame.
1521 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1522 const twos_comp = @bitCast(u8, negative_offset);
1523 // mov DWORD PTR [rbp+offset], immediate
1524 self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
1525 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
1526 },
1527 8 => {
1528 return self.fail(src, "TODO implement set abi_size=8 stack variable with immediate", .{});
1529 },
1530 else => {
1531 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
1532 },
1533 }
1534 if (x_big <= math.maxInt(u32)) {} else {
1535 return self.fail(src, "TODO implement set stack variable with large immediate", .{});
1536 }
1537 },
1538 .embedded_in_code => |code_offset| {
1539 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
1540 },
1541 .register => |reg| {
1542 const abi_size = ty.abiSize(self.target.*);
1543 const adj_off = stack_offset + abi_size;
1544 try self.code.ensureCapacity(self.code.items.len + 7);
1545 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1546 const reg_id: u8 = @truncate(u3, reg.id());
1547 if (adj_off <= 128) {
1548 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1549 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1550 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1551 const twos_comp = @bitCast(u8, negative_offset);
1552 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp });
1553 } else if (adj_off <= 2147483648) {
1554 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1555 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1556 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1557 const twos_comp = @bitCast(u32, negative_offset);
1558 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM });
1559 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1560 } else {
1561 return self.fail(src, "stack offset too large", .{});
1562 }
1563 },
1564 .memory => |vaddr| {
1565 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
1566 },
1567 .stack_offset => |off| {
1568 if (stack_offset == off)
1569 return; // Copy stack variable to itself; nothing to do.
1570 return self.fail(src, "TODO implement copy stack variable to stack variable", .{});
1571 },
1572 },
1573 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
1574 }
1575 }
1576
1577 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1578 switch (arch) {
1579 .riscv64 => switch (mcv) {
1580 .dead => unreachable,
1581 .ptr_stack_offset => unreachable,
1582 .ptr_embedded_in_code => unreachable,
1583 .unreach, .none => return, // Nothing to do.
1584 .undef => {
1585 if (!self.wantSafety())
1586 return; // The already existing value will do just fine.
1587 // Write the debug undefined value.
1588 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1589 },
1590 .immediate => |unsigned_x| {
1591 const x = @bitCast(i64, unsigned_x);
1592 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1593 const instruction = @bitCast(u32, instructions.Addi{
1594 .mode = @enumToInt(instructions.Addi.Mode.addi),
1595 .imm = @truncate(i12, x),
1596 .rs1 = Register.zero.id(),
1597 .rd = reg.id(),
1598 });
1599
1600 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), instruction);
1601 return;
1602 }
1603 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1604 const split = @bitCast(packed struct {
1605 low12: i12,
1606 up20: i20,
1607 }, @truncate(i32, x));
1608 if (split.low12 < 0) return self.fail(src, "TODO support riscv64 genSetReg i32 immediates with 12th bit set to 1", .{});
1609
1610 const lui = @bitCast(u32, instructions.Lui{
1611 .imm = split.up20,
1612 .rd = reg.id(),
1613 });
1614 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), lui);
1615
1616 const addi = @bitCast(u32, instructions.Addi{
1617 .mode = @enumToInt(instructions.Addi.Mode.addi),
1618 .imm = @truncate(i12, split.low12),
1619 .rs1 = reg.id(),
1620 .rd = reg.id(),
1621 });
1622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), addi);
1623 return;
1624 }
1625 // li rd, immediate
1626 // "Myriad sequences"
1627 return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
1628 },
1629 .memory => |addr| {
1630 // The value is in memory at a hard-coded address.
1631 // If the type is a pointer, it means the pointer address is at this memory location.
1632 try self.genSetReg(src, reg, .{ .immediate = addr });
1633
1634 const ld = @bitCast(u32, instructions.Load{
1635 .mode = @enumToInt(instructions.Load.Mode.ld),
1636 .rs1 = reg.id(),
1637 .rd = reg.id(),
1638 .offset = 0,
1639 });
1640
1641 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ld);
1642 // LOAD imm=[i12 offset = 0], rs1 =
1643
1644 // return self.fail("TODO implement genSetReg memory for riscv64");
1645 },
1646 else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
1647 },
1648 .x86_64 => switch (mcv) {
1649 .dead => unreachable,
1650 .ptr_stack_offset => unreachable,
1651 .ptr_embedded_in_code => unreachable,
1652 .unreach, .none => return, // Nothing to do.
1653 .undef => {
1654 if (!self.wantSafety())
1655 return; // The already existing value will do just fine.
1656 // Write the debug undefined value.
1657 switch (reg.size()) {
1658 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
1659 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
1660 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
1661 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1662 else => unreachable,
1663 }
1664 },
1033 .compare_flags_unsigned => |op| {1665 .compare_flags_unsigned => |op| {
1034 try self.code.ensureCapacity(self.code.items.len + 3);1666 try self.code.ensureCapacity(self.code.items.len + 3);
1035 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });1667 // TODO audit this codegen: we force w = true here to make
1668 // the value affect the big register
1669 self.rex(.{ .b = reg.isExtended(), .w = true });
1036 const opcode: u8 = switch (op) {1670 const opcode: u8 = switch (op) {
1037 .gte => 0x93,1671 .gte => 0x93,
1038 .gt => 0x97,1672 .gt => 0x97,
...@@ -1048,9 +1682,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1048,9 +1682,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1048 return self.fail(src, "TODO set register with compare flags value (signed)", .{});1682 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
1049 },1683 },
1050 .immediate => |x| {1684 .immediate => |x| {
1051 if (reg.size() != 64) {
1052 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1053 }
1054 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit1685 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
1055 // register is the fastest way to zero a register.1686 // register is the fastest way to zero a register.
1056 if (x == 0) {1687 if (x == 0) {
...@@ -1071,7 +1702,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1071,7 +1702,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1071 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });1702 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
1072 return;1703 return;
1073 }1704 }
1074 if (x <= std.math.maxInt(u32)) {1705 if (x <= math.maxInt(u32)) {
1075 // Next best case: if we set the lower four bytes, the upper four will be zeroed.1706 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
1076 //1707 //
1077 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.1708 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
...@@ -1103,16 +1734,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1103,16 +1734,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1103 //1734 //
1104 // In this case, the encoding of the REX byte is 0b0100100B1735 // In this case, the encoding of the REX byte is 0b0100100B
1105 try self.code.ensureCapacity(self.code.items.len + 10);1736 try self.code.ensureCapacity(self.code.items.len + 10);
1106 self.rex(.{ .w = true, .b = reg.isExtended() });1737 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1107 self.code.items.len += 9;1738 self.code.items.len += 9;
1108 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);1739 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1109 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];1740 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1110 mem.writeIntLittle(u64, imm_ptr, x);1741 mem.writeIntLittle(u64, imm_ptr, x);
1111 },1742 },
1112 .embedded_in_code => |code_offset| {1743 .embedded_in_code => |code_offset| {
1113 if (reg.size() != 64) {
1114 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1115 }
1116 // We need the offset from RIP in a signed i32 twos complement.1744 // We need the offset from RIP in a signed i32 twos complement.
1117 // The instruction is 7 bytes long and RIP points to the next instruction.1745 // The instruction is 7 bytes long and RIP points to the next instruction.
1118 try self.code.ensureCapacity(self.code.items.len + 7);1746 try self.code.ensureCapacity(self.code.items.len + 7);
...@@ -1120,7 +1748,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1120,7 +1748,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1120 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three1748 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1121 // bits as five.1749 // bits as five.
1122 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.1750 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1123 self.rex(.{ .w = true, .b = reg.isExtended() });1751 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1124 self.code.items.len += 6;1752 self.code.items.len += 6;
1125 const rip = self.code.items.len;1753 const rip = self.code.items.len;
1126 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);1754 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
...@@ -1131,9 +1759,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1131,9 +1759,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1131 mem.writeIntLittle(i32, imm_ptr, offset);1759 mem.writeIntLittle(i32, imm_ptr, offset);
1132 },1760 },
1133 .register => |src_reg| {1761 .register => |src_reg| {
1134 if (reg.size() != 64) {1762 // If the registers are the same, nothing to do.
1135 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});1763 if (src_reg.id() == reg.id())
1136 }1764 return;
1765
1137 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.1766 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1138 // This is thus three bytes: REX 0x8B R/M.1767 // This is thus three bytes: REX 0x8B R/M.
1139 // If the destination is extended, the R field must be 1.1768 // If the destination is extended, the R field must be 1.
...@@ -1141,15 +1770,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1141,15 +1770,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1141 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle1770 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1142 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.1771 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1143 try self.code.ensureCapacity(self.code.items.len + 3);1772 try self.code.ensureCapacity(self.code.items.len + 3);
1144 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });1773 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended(), .b = src_reg.isExtended() });
1145 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);1774 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1146 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });1775 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1147 },1776 },
1148 .memory => |x| {1777 .memory => |x| {
1149 if (reg.size() != 64) {1778 if (x <= math.maxInt(u32)) {
1150 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1151 }
1152 if (x <= std.math.maxInt(u32)) {
1153 // Moving from memory to a register is a variant of `8B /r`.1779 // Moving from memory to a register is a variant of `8B /r`.
1154 // Since we're using 64-bit moves, we require a REX.1780 // Since we're using 64-bit moves, we require a REX.
1155 // This variant also requires a SIB, as it would otherwise be RIP-relative.1781 // This variant also requires a SIB, as it would otherwise be RIP-relative.
...@@ -1158,7 +1784,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1158,7 +1784,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1158 // 0b00RRR100, where RRR is the lower three bits of the register ID.1784 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1159 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.1785 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1160 try self.code.ensureCapacity(self.code.items.len + 8);1786 try self.code.ensureCapacity(self.code.items.len + 8);
1161 self.rex(.{ .w = true, .b = reg.isExtended() });1787 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1162 self.code.appendSliceAssumeCapacity(&[_]u8{1788 self.code.appendSliceAssumeCapacity(&[_]u8{
1163 0x8B,1789 0x8B,
1164 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R1790 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
...@@ -1186,7 +1812,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1186,7 +1812,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1186 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with1812 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1187 // this instruction.1813 // this instruction.
1188 const id3 = @truncate(u3, reg.id());1814 const id3 = @truncate(u3, reg.id());
1189 std.debug.assert(id3 != 4 and id3 != 5);1815 assert(id3 != 4 and id3 != 5);
11901816
1191 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.1817 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1192 try self.genSetReg(src, reg, MCValue{ .immediate = x });1818 try self.genSetReg(src, reg, MCValue{ .immediate = x });
...@@ -1201,17 +1827,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1201,17 +1827,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1201 //1827 //
1202 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*1828 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1203 // register operands need to be marked as extended.1829 // register operands need to be marked as extended.
1204 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });1830 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
1205 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());1831 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1206 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });1832 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1207 }1833 }
1208 }1834 }
1209 },1835 },
1210 .stack_offset => |off| {1836 .stack_offset => |unadjusted_off| {
1211 return self.fail(src, "TODO implement genSetReg for stack variables", .{});1837 try self.code.ensureCapacity(self.code.items.len + 7);
1838 const size_bytes = @divExact(reg.size(), 8);
1839 const off = unadjusted_off + size_bytes;
1840 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
1841 const reg_id: u8 = @truncate(u3, reg.id());
1842 if (off <= 128) {
1843 // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f]
1844 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1845 const negative_offset = @intCast(i8, -@intCast(i32, off));
1846 const twos_comp = @bitCast(u8, negative_offset);
1847 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM, twos_comp });
1848 } else if (off <= 2147483648) {
1849 // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80]
1850 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1851 const negative_offset = @intCast(i32, -@intCast(i33, off));
1852 const twos_comp = @bitCast(u32, negative_offset);
1853 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM });
1854 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1855 } else {
1856 return self.fail(src, "stack offset too large", .{});
1857 }
1212 },1858 },
1213 },1859 },
1214 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),1860 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
1215 }1861 }
1216 }1862 }
12171863
...@@ -1247,24 +1893,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1247,24 +1893,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1247 }1893 }
1248 }1894 }
12491895
1250 /// Does not "move" the instruction.
1251 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1252 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1253 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1254 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
1255
1256 const free_index = @ctz(FreeRegInt, branch.free_registers);
1257 if (free_index >= callee_preserved_regs.len)
1258 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
1259 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
1260 const reg = callee_preserved_regs[free_index];
1261 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
1262 const old_mcv = branch.inst_table.get(inst).?;
1263 const new_mcv: MCValue = .{ .register = reg };
1264 try self.genSetReg(inst.src, reg, old_mcv);
1265 return new_mcv;
1266 }
1267
1268 /// If the MCValue is an immediate, and it does not fit within this type,1896 /// If the MCValue is an immediate, and it does not fit within this type,
1269 /// we put it in a register.1897 /// we put it in a register.
1270 /// A potential opportunity for future optimization here would be keeping track1898 /// A potential opportunity for future optimization here would be keeping track
...@@ -1282,7 +1910,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1282,7 +1910,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1282 .is_signed = false,1910 .is_signed = false,
1283 },1911 },
1284 });1912 });
1285 if (imm >= std.math.maxInt(U)) {1913 if (imm >= math.maxInt(U)) {
1286 return self.copyToNewRegister(inst);1914 return self.copyToNewRegister(inst);
1287 }1915 }
1288 },1916 },
...@@ -1292,6 +1920,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1292,6 +1920,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1292 }1920 }
12931921
1294 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {1922 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1923 if (typed_value.val.isUndef())
1924 return MCValue.undef;
1295 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1925 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1296 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1926 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1297 switch (typed_value.ty.zigTypeTag()) {1927 switch (typed_value.ty.zigTypeTag()) {
...@@ -1320,19 +1950,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1320,19 +1950,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1320 }1950 }
1321 }1951 }
13221952
1323 fn resolveParameters(1953 const CallMCValues = struct {
1324 self: *Self,1954 args: []MCValue,
1325 src: usize,1955 return_value: MCValue,
1326 cc: std.builtin.CallingConvention,1956 stack_byte_count: u32,
1327 param_types: []const Type,1957 stack_align: u32,
1328 results: []MCValue,1958
1329 ) !u32 {1959 fn deinit(self: *CallMCValues, func: *Self) void {
1960 func.gpa.free(self.args);
1961 self.* = undefined;
1962 }
1963 };
1964
1965 /// Caller must call `CallMCValues.deinit`.
1966 fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
1967 const cc = fn_ty.fnCallingConvention();
1968 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1969 defer self.gpa.free(param_types);
1970 fn_ty.fnParamTypes(param_types);
1971 var result: CallMCValues = .{
1972 .args = try self.gpa.alloc(MCValue, param_types.len),
1973 // These undefined values must be populated before returning from this function.
1974 .return_value = undefined,
1975 .stack_byte_count = undefined,
1976 .stack_align = undefined,
1977 };
1978 errdefer self.gpa.free(result.args);
1979
1980 const ret_ty = fn_ty.fnReturnType();
1981
1330 switch (arch) {1982 switch (arch) {
1331 .x86_64 => {1983 .x86_64 => {
1332 switch (cc) {1984 switch (cc) {
1333 .Naked => {1985 .Naked => {
1334 assert(results.len == 0);1986 assert(result.args.len == 0);
1335 return 0;1987 result.return_value = .{ .unreach = {} };
1988 result.stack_byte_count = 0;
1989 result.stack_align = 1;
1990 return result;
1336 },1991 },
1337 .Unspecified, .C => {1992 .Unspecified, .C => {
1338 var next_int_reg: usize = 0;1993 var next_int_reg: usize = 0;
...@@ -1341,24 +1996,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1341,24 +1996,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1341 for (param_types) |ty, i| {1996 for (param_types) |ty, i| {
1342 switch (ty.zigTypeTag()) {1997 switch (ty.zigTypeTag()) {
1343 .Bool, .Int => {1998 .Bool, .Int => {
1999 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1344 if (next_int_reg >= c_abi_int_param_regs.len) {2000 if (next_int_reg >= c_abi_int_param_regs.len) {
1345 results[i] = .{ .stack_offset = next_stack_offset };2001 result.args[i] = .{ .stack_offset = next_stack_offset };
1346 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));2002 next_stack_offset += param_size;
1347 } else {2003 } else {
1348 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };2004 const aliased_reg = registerAlias(
2005 c_abi_int_param_regs[next_int_reg],
2006 param_size,
2007 );
2008 result.args[i] = .{ .register = aliased_reg };
1349 next_int_reg += 1;2009 next_int_reg += 1;
1350 }2010 }
1351 },2011 },
1352 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),2012 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1353 }2013 }
1354 }2014 }
1355 return next_stack_offset;2015 result.stack_byte_count = next_stack_offset;
2016 result.stack_align = 16;
1356 },2017 },
1357 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),2018 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1358 }2019 }
1359 },2020 },
1360 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),2021 else => if (param_types.len != 0)
2022 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
1361 }2023 }
2024
2025 if (ret_ty.zigTypeTag() == .NoReturn) {
2026 result.return_value = .{ .unreach = {} };
2027 } else if (!ret_ty.hasCodeGenBits()) {
2028 result.return_value = .{ .none = {} };
2029 } else switch (arch) {
2030 .x86_64 => switch (cc) {
2031 .Naked => unreachable,
2032 .Unspecified, .C => {
2033 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2034 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
2035 result.return_value = .{ .register = aliased_reg };
2036 },
2037 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2038 },
2039 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
2040 }
2041 return result;
2042 }
2043
2044 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
2045 fn wantSafety(self: *Self) bool {
2046 return switch (self.bin_file.base.options.optimize_mode) {
2047 .Debug => true,
2048 .ReleaseSafe => true,
2049 .ReleaseFast => false,
2050 .ReleaseSmall => false,
2051 };
1362 }2052 }
13632053
1364 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {2054 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
...@@ -1371,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1371,6 +2061,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1371 usingnamespace switch (arch) {2061 usingnamespace switch (arch) {
1372 .i386 => @import("codegen/x86.zig"),2062 .i386 => @import("codegen/x86.zig"),
1373 .x86_64 => @import("codegen/x86_64.zig"),2063 .x86_64 => @import("codegen/x86_64.zig"),
2064 .riscv64 => @import("codegen/riscv64.zig"),
1374 else => struct {2065 else => struct {
1375 pub const Register = enum {2066 pub const Register = enum {
1376 dummy,2067 dummy,
...@@ -1387,7 +2078,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1387,7 +2078,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1387 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });2078 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
13882079
1389 fn parseRegName(name: []const u8) ?Register {2080 fn parseRegName(name: []const u8) ?Register {
2081 if (@hasDecl(Register, "parseRegName")) {
2082 return Register.parseRegName(name);
2083 }
1390 return std.meta.stringToEnum(Register, name);2084 return std.meta.stringToEnum(Register, name);
1391 }2085 }
2086
2087 fn registerAlias(reg: Register, size_bytes: u32) Register {
2088 switch (arch) {
2089 // For x86_64 we have to pick a smaller register alias depending on abi size.
2090 .x86_64 => switch (size_bytes) {
2091 1 => return reg.to8(),
2092 2 => return reg.to16(),
2093 4 => return reg.to32(),
2094 8 => return reg.to64(),
2095 else => unreachable,
2096 },
2097 else => return reg,
2098 }
2099 }
2100
2101 /// For most architectures this does nothing. For x86_64 it resolves any aliased registers
2102 /// to the 64-bit wide ones.
2103 fn toCanonicalReg(reg: Register) Register {
2104 return switch (arch) {
2105 .x86_64 => reg.to64(),
2106 else => reg,
2107 };
2108 }
1392 };2109 };
1393}2110}
src-self-hosted/codegen/c.zig+11-5
...@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {
89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
90 const instructions = func.analysis.success.instructions;90 const instructions = func.analysis.success.instructions;
91 if (instructions.len > 0) {91 if (instructions.len > 0) {
92 try writer.writeAll("\n");
92 for (instructions) |inst| {93 for (instructions) |inst| {
93 try writer.writeAll("\n ");
94 switch (inst.tag) {94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.castTag(.call).?, decl),96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),98 .retvoid => try file.main.writer().print(" return;\n", .{}),
99 .dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),100 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }101 }
101 }102 }
102 try writer.writeAll("\n");
103 }103 }
104104
105 try writer.writeAll("}\n\n");105 try writer.writeAll("}\n\n");
...@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !...@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !
112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
113 const writer = file.main.writer();113 const writer = file.main.writer();
114 const header = file.header.writer();114 const header = file.header.writer();
115 try writer.writeAll(" ");
115 if (inst.func.castTag(.constant)) |func_inst| {116 if (inst.func.castTag(.constant)) |func_inst| {
116 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
117 const target = func_val.func.owner_decl;118 const target = func_val.func.owner_decl;
...@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
126 try renderFunctionSignature(file, header, target);127 try renderFunctionSignature(file, header, target);
127 try header.writeAll(";\n");128 try header.writeAll(";\n");
128 }129 }
129 try writer.print("{}();", .{tname});130 try writer.print("{}();\n", .{tname});
130 } else {131 } else {
131 return file.fail(decl.src(), "TODO non-function call target?", .{});132 return file.fail(decl.src(), "TODO non-function call target?", .{});
132 }133 }
...@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
138 }139 }
139}140}
140141
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
143 // TODO emit #line directive here with line number and filename
144}
145
141fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
142 const writer = file.main.writer();147 const writer = file.main.writer();
148 try writer.writeAll(" ");
143 for (as.inputs) |i, index| {149 for (as.inputs) |i, index| {
144 if (i[0] == '{' and i[i.len - 1] == '}') {150 if (i[0] == '{' and i[i.len - 1] == '}') {
145 const reg = i[1 .. i.len - 1];151 const reg = i[1 .. i.len - 1];
...@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {...@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
187 }193 }
188 }194 }
189 }195 }
190 try writer.writeAll(");");196 try writer.writeAll(");\n");
191}197}
src-self-hosted/codegen/riscv64.zig created+92
...@@ -0,0 +1,92 @@
1const std = @import("std");
2
3pub const instructions = struct {
4 pub const CallBreak = packed struct {
5 pub const Mode = packed enum(u12) { ecall, ebreak };
6 opcode: u7 = 0b1110011,
7 unused1: u5 = 0,
8 unused2: u3 = 0,
9 unused3: u5 = 0,
10 mode: u12, //: Mode
11 };
12 // I-type
13 pub const Addi = packed struct {
14 pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
15 opcode: u7 = 0b0010011,
16 rd: u5,
17 mode: u3, //: Mode
18 rs1: u5,
19 imm: i12,
20 };
21 pub const Lui = packed struct {
22 opcode: u7 = 0b0110111,
23 rd: u5,
24 imm: i20,
25 };
26 // I_type
27 pub const Load = packed struct {
28 pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
29 opcode: u7 = 0b0000011,
30 rd: u5,
31 mode: u3, //: Mode
32 rs1: u5,
33 offset: i12,
34 };
35 // I-type
36 pub const Jalr = packed struct {
37 opcode: u7 = 0b1100111,
38 rd: u5,
39 mode: u3 = 0,
40 rs1: u5,
41 offset: i12,
42 };
43};
44
45// zig fmt: off
46pub const RawRegister = enum(u8) {
47 x0, x1, x2, x3, x4, x5, x6, x7,
48 x8, x9, x10, x11, x12, x13, x14, x15,
49 x16, x17, x18, x19, x20, x21, x22, x23,
50 x24, x25, x26, x27, x28, x29, x30, x31,
51};
52
53pub const Register = enum(u8) {
54 // 64 bit registers
55 zero, // zero
56 ra, // return address. caller saved
57 sp, // stack pointer. callee saved.
58 gp, // global pointer
59 tp, // thread pointer
60 t0, t1, t2, // temporaries. caller saved.
61 s0, // s0/fp, callee saved.
62 s1, // callee saved.
63 a0, a1, // fn args/return values. caller saved.
64 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
65 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
66 t3, t4, t5, t6, // caller saved
67
68 pub fn parseRegName(name: []const u8) ?Register {
69 if(std.meta.stringToEnum(Register, name)) |reg| return reg;
70 if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
71 return null;
72 }
73
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`.
80 pub fn allocIndex(self: Register) ?u4 {
81 inline for(callee_preserved_regs) |cpreg, i| {
82 if(self == cpreg) return i;
83 }
84 return null;
85 }
86};
87
88// zig fmt: on
89
90pub const callee_preserved_regs = [_]Register{
91 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
92};
src-self-hosted/codegen/x86_64.zig+21
...@@ -81,6 +81,26 @@ pub const Register = enum(u8) {...@@ -81,6 +81,26 @@ pub const Register = enum(u8) {
81 else => null,81 else => null,
82 };82 };
83 }83 }
84
85 /// Convert from any register to its 64 bit alias.
86 pub fn to64(self: Register) Register {
87 return @intToEnum(Register, self.id());
88 }
89
90 /// Convert from any register to its 32 bit alias.
91 pub fn to32(self: Register) Register {
92 return @intToEnum(Register, @as(u8, self.id()) + 16);
93 }
94
95 /// Convert from any register to its 16 bit alias.
96 pub fn to16(self: Register) Register {
97 return @intToEnum(Register, @as(u8, self.id()) + 32);
98 }
99
100 /// Convert from any register to its 8 bit alias.
101 pub fn to8(self: Register) Register {
102 return @intToEnum(Register, @as(u8, self.id()) + 48);
103 }
84};104};
85105
86// zig fmt: on106// zig fmt: on
...@@ -88,3 +108,4 @@ pub const Register = enum(u8) {...@@ -88,3 +108,4 @@ pub const Register = enum(u8) {
88/// These registers belong to the called function.108/// These registers belong to the called function.
89pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };109pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
90pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };110pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
111pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
src-self-hosted/ir.zig+14-5
...@@ -4,6 +4,7 @@ const Type = @import("type.zig").Type;...@@ -4,6 +4,7 @@ const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");4const Module = @import("Module.zig");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");6const codegen = @import("codegen.zig");
7const ast = std.zig.ast;
78
8/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation9/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
9/// of instructions that correspond to the ZIR text format.10/// of instructions that correspond to the ZIR text format.
...@@ -47,6 +48,7 @@ pub const Inst = struct {...@@ -47,6 +48,7 @@ pub const Inst = struct {
4748
48 pub const Tag = enum {49 pub const Tag = enum {
49 add,50 add,
51 alloc,
50 arg,52 arg,
51 assembly,53 assembly,
52 bitcast,54 bitcast,
...@@ -63,28 +65,34 @@ pub const Inst = struct {...@@ -63,28 +65,34 @@ pub const Inst = struct {
63 cmp_neq,65 cmp_neq,
64 condbr,66 condbr,
65 constant,67 constant,
68 dbg_stmt,
66 isnonnull,69 isnonnull,
67 isnull,70 isnull,
71 /// Read a value from a pointer.
72 load,
68 ptrtoint,73 ptrtoint,
74 ref,
69 ret,75 ret,
70 retvoid,76 retvoid,
77 /// Write a value to a pointer. LHS is pointer, RHS is value.
78 store,
71 sub,79 sub,
72 unreach,80 unreach,
73 not,81 not,
74 floatcast,82 floatcast,
75 intcast,83 intcast,
7684
77 /// There is one-to-one correspondence between tag and type for now,
78 /// but this will not always be the case. For example, binary operations
79 /// such as + and - will have different tags but the same type.
80 pub fn Type(tag: Tag) type {85 pub fn Type(tag: Tag) type {
81 return switch (tag) {86 return switch (tag) {
87 .alloc,
82 .retvoid,88 .retvoid,
83 .unreach,89 .unreach,
84 .arg,90 .arg,
85 .breakpoint,91 .breakpoint,
92 .dbg_stmt,
86 => NoOp,93 => NoOp,
8794
95 .ref,
88 .ret,96 .ret,
89 .bitcast,97 .bitcast,
90 .not,98 .not,
...@@ -93,6 +101,7 @@ pub const Inst = struct {...@@ -93,6 +101,7 @@ pub const Inst = struct {
93 .ptrtoint,101 .ptrtoint,
94 .floatcast,102 .floatcast,
95 .intcast,103 .intcast,
104 .load,
96 => UnOp,105 => UnOp,
97106
98 .add,107 .add,
...@@ -103,6 +112,7 @@ pub const Inst = struct {...@@ -103,6 +112,7 @@ pub const Inst = struct {
103 .cmp_gte,112 .cmp_gte,
104 .cmp_gt,113 .cmp_gt,
105 .cmp_neq,114 .cmp_neq,
115 .store,
106 => BinOp,116 => BinOp,
107117
108 .assembly => Assembly,118 .assembly => Assembly,
...@@ -157,8 +167,7 @@ pub const Inst = struct {...@@ -157,8 +167,7 @@ pub const Inst = struct {
157167
158 /// Returns `null` if runtime-known.168 /// Returns `null` if runtime-known.
159 pub fn value(base: *Inst) ?Value {169 pub fn value(base: *Inst) ?Value {
160 if (base.ty.onePossibleValue())170 if (base.ty.onePossibleValue()) |opv| return opv;
161 return Value.initTag(.the_one_possible_value);
162171
163 const inst = base.cast(Constant) orelse return null;172 const inst = base.cast(Constant) orelse return null;
164 return inst.val;173 return inst.val;
src-self-hosted/link.zig+1063-280
...@@ -8,6 +8,15 @@ const fs = std.fs;...@@ -8,6 +8,15 @@ 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;
12const DW = std.dwarf;
13const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;
15const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;
17
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off
1120
12const default_entry_addr = 0x8000000;21const default_entry_addr = 0x8000000;
1322
...@@ -16,6 +25,9 @@ pub const Options = struct {...@@ -16,6 +25,9 @@ pub const Options = struct {
16 output_mode: std.builtin.OutputMode,25 output_mode: std.builtin.OutputMode,
17 link_mode: std.builtin.LinkMode,26 link_mode: std.builtin.LinkMode,
18 object_format: std.builtin.ObjectFormat,27 object_format: std.builtin.ObjectFormat,
28 optimize_mode: std.builtin.Mode,
29 root_name: []const u8,
30 root_pkg: *const Package,
19 /// Used for calculating how much space to reserve for symbols in case the binary file31 /// Used for calculating how much space to reserve for symbols in case the binary file
20 /// does not already have a symbol table.32 /// does not already have a symbol table.
21 symbol_count_hint: u64 = 32,33 symbol_count_hint: u64 = 32,
...@@ -24,96 +36,27 @@ pub const Options = struct {...@@ -24,96 +36,27 @@ pub const Options = struct {
24 program_code_size_hint: u64 = 256 * 1024,36 program_code_size_hint: u64 = 256 * 1024,
25};37};
2638
27/// Attempts incremental linking, if the file already exists.39pub const File = struct {
28/// If incremental linking fails, falls back to truncating the file and rewriting it.40 tag: Tag,
29/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
30/// This operation is not atomic.
31pub fn openBinFilePath(
32 allocator: *Allocator,
33 dir: fs.Dir,
34 sub_path: []const u8,
35 options: Options,41 options: Options,
36) !*File {
37 const cbe = options.object_format == .c;
38 const file = try dir.createFile(sub_path, .{ .truncate = cbe, .read = true, .mode = determineMode(options) });
39 errdefer file.close();
40
41 if (cbe) {
42 var bin_file = try allocator.create(File.C);
43 errdefer allocator.destroy(bin_file);
44 bin_file.* = try openCFile(allocator, file, options);
45 return &bin_file.base;
46 } else {
47 var bin_file = try allocator.create(File.Elf);
48 errdefer allocator.destroy(bin_file);
49 bin_file.* = try openBinFile(allocator, file, options);
50 bin_file.owns_file_handle = true;
51 return &bin_file.base;
52 }
53}
5442
55/// Atomically overwrites the old file, if present.43 /// Attempts incremental linking, if the file already exists. If
56pub fn writeFilePath(44 /// incremental linking fails, falls back to truncating the file and
57 allocator: *Allocator,45 /// rewriting it. A malicious file is detected as incremental link failure
58 dir: fs.Dir,46 /// and does not cause Illegal Behavior. This operation is not atomic.
59 sub_path: []const u8,47 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
60 module: Module,48 switch (options.object_format) {
61 errors: *std.ArrayList(Module.ErrorMsg),49 .unknown => unreachable,
62) !void {50 .coff => return error.TODOImplementCoff,
63 const options: Options = .{51 .elf => return Elf.openPath(allocator, dir, sub_path, options),
64 .target = module.target,52 .macho => return error.TODOImplementMacho,
65 .output_mode = module.output_mode,53 .wasm => return error.TODOImplementWasm,
66 .link_mode = module.link_mode,54 .c => return C.openPath(allocator, dir, sub_path, options),
67 .object_format = module.object_format,55 .hex => return error.TODOImplementHex,
68 .symbol_count_hint = module.decls.items.len,56 .raw => return error.TODOImplementRaw,
69 };57 }
70 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
71 defer af.deinit();
72
73 const elf_file = try createElfFile(allocator, af.file, options);
74 for (module.decls.items) |decl| {
75 try elf_file.updateDecl(module, decl, errors);
76 }
77 try elf_file.flush();
78 if (elf_file.error_flags.no_entry_point_found) {
79 try errors.ensureCapacity(errors.items.len + 1);
80 errors.appendAssumeCapacity(.{
81 .byte_offset = 0,
82 .msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}),
83 });
84 }58 }
85 try af.finish();
86 return result;
87}
8859
89fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
90 return File.C{
91 .allocator = allocator,
92 .file = file,
93 .options = options,
94 .main = std.ArrayList(u8).init(allocator),
95 .header = std.ArrayList(u8).init(allocator),
96 .constants = std.ArrayList(u8).init(allocator),
97 .called = std.StringHashMap(void).init(allocator),
98 };
99}
100
101/// Attempts incremental linking, if the file already exists.
102/// If incremental linking fails, falls back to truncating the file and rewriting it.
103/// Returns an error if `file` is not already open with +read +write +seek abilities.
104/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
105/// This operation is not atomic.
106pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
107 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
108 error.IncrFailed => {
109 return createElfFile(allocator, file, options);
110 },
111 else => |e| return e,
112 };
113}
114
115pub const File = struct {
116 tag: Tag,
117 pub fn cast(base: *File, comptime T: type) ?*T {60 pub fn cast(base: *File, comptime T: type) ?*T {
118 if (base.tag != T.base_tag)61 if (base.tag != T.base_tag)
119 return null;62 return null;
...@@ -123,86 +66,82 @@ pub const File = struct {...@@ -123,86 +66,82 @@ pub const File = struct {
12366
124 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {67 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
125 switch (base.tag) {68 switch (base.tag) {
126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),69 .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
127 .C => {},70 .c => {},
128 else => unreachable,
129 }71 }
130 }72 }
13173
132 pub fn makeExecutable(base: *File) !void {74 pub fn makeExecutable(base: *File) !void {
133 switch (base.tag) {75 switch (base.tag) {
134 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),76 .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
135 else => unreachable,77 .c => unreachable,
136 }78 }
137 }79 }
13880
139 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {81 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
140 switch (base.tag) {82 switch (base.tag) {
141 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),83 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
142 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),84 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
143 else => unreachable,85 }
86 }
87
88 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
89 switch (base.tag) {
90 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
91 .c => {},
144 }92 }
145 }93 }
14694
147 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {95 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
148 switch (base.tag) {96 switch (base.tag) {
149 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),97 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
150 .C => {},98 .c => {},
151 else => unreachable,
152 }99 }
153 }100 }
154101
155 pub fn deinit(base: *File) void {102 pub fn deinit(base: *File) void {
156 switch (base.tag) {103 switch (base.tag) {
157 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),104 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
158 .C => @fieldParentPtr(C, "base", base).deinit(),105 .c => @fieldParentPtr(C, "base", base).deinit(),
159 else => unreachable,
160 }106 }
161 }107 }
162108
163 pub fn destroy(base: *File) void {109 pub fn destroy(base: *File) void {
164 switch (base.tag) {110 switch (base.tag) {
165 .Elf => {111 .elf => {
166 const parent = @fieldParentPtr(Elf, "base", base);112 const parent = @fieldParentPtr(Elf, "base", base);
167 parent.deinit();113 parent.deinit();
168 parent.allocator.destroy(parent);114 parent.allocator.destroy(parent);
169 },115 },
170 .C => {116 .c => {
171 const parent = @fieldParentPtr(C, "base", base);117 const parent = @fieldParentPtr(C, "base", base);
172 parent.deinit();118 parent.deinit();
173 parent.allocator.destroy(parent);119 parent.allocator.destroy(parent);
174 },120 },
175 else => unreachable,
176 }121 }
177 }122 }
178123
179 pub fn flush(base: *File) !void {124 pub fn flush(base: *File) !void {
125 const tracy = trace(@src());
126 defer tracy.end();
127
180 try switch (base.tag) {128 try switch (base.tag) {
181 .Elf => @fieldParentPtr(Elf, "base", base).flush(),129 .elf => @fieldParentPtr(Elf, "base", base).flush(),
182 .C => @fieldParentPtr(C, "base", base).flush(),130 .c => @fieldParentPtr(C, "base", base).flush(),
183 else => unreachable,
184 };131 };
185 }132 }
186133
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {134 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {135 switch (base.tag) {
189 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),136 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 else => unreachable,137 .c => unreachable,
191 }138 }
192 }139 }
193140
194 pub fn errorFlags(base: *File) ErrorFlags {141 pub fn errorFlags(base: *File) ErrorFlags {
195 return switch (base.tag) {142 return switch (base.tag) {
196 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,143 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .C => return .{ .no_entry_point_found = false },144 .c => return .{ .no_entry_point_found = false },
198 else => unreachable,
199 };
200 }
201
202 pub fn options(base: *File) Options {
203 return switch (base.tag) {
204 .Elf => @fieldParentPtr(Elf, "base", base).options,
205 .C => @fieldParentPtr(C, "base", base).options,
206 };145 };
207 }146 }
208147
...@@ -214,14 +153,14 @@ pub const File = struct {...@@ -214,14 +153,14 @@ pub const File = struct {
214 exports: []const *Module.Export,153 exports: []const *Module.Export,
215 ) !void {154 ) !void {
216 switch (base.tag) {155 switch (base.tag) {
217 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),156 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
218 .C => return {},157 .c => return {},
219 }158 }
220 }159 }
221160
222 pub const Tag = enum {161 pub const Tag = enum {
223 Elf,162 elf,
224 C,163 c,
225 };164 };
226165
227 pub const ErrorFlags = struct {166 pub const ErrorFlags = struct {
...@@ -229,24 +168,49 @@ pub const File = struct {...@@ -229,24 +168,49 @@ pub const File = struct {
229 };168 };
230169
231 pub const C = struct {170 pub const C = struct {
232 pub const base_tag: Tag = .C;171 pub const base_tag: Tag = .c;
233 base: File = File{ .tag = base_tag },172
173 base: File,
234174
235 allocator: *Allocator,175 allocator: *Allocator,
236 header: std.ArrayList(u8),176 header: std.ArrayList(u8),
237 constants: std.ArrayList(u8),177 constants: std.ArrayList(u8),
238 main: std.ArrayList(u8),178 main: std.ArrayList(u8),
239 file: ?fs.File,179 file: ?fs.File,
240 options: Options,
241 called: std.StringHashMap(void),180 called: std.StringHashMap(void),
242 need_stddef: bool = false,181 need_stddef: bool = false,
243 need_stdint: bool = false,182 need_stdint: bool = false,
244 need_noreturn: bool = false,183 need_noreturn: bool = false,
245 error_msg: *Module.ErrorMsg = undefined,184 error_msg: *Module.ErrorMsg = undefined,
246185
186 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
187 assert(options.object_format == .c);
188
189 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = determineMode(options) });
190 errdefer file.close();
191
192 var c_file = try allocator.create(C);
193 errdefer allocator.destroy(c_file);
194
195 c_file.* = File.C{
196 .base = .{
197 .tag = .c,
198 .options = options,
199 },
200 .allocator = allocator,
201 .file = file,
202 .main = std.ArrayList(u8).init(allocator),
203 .header = std.ArrayList(u8).init(allocator),
204 .constants = std.ArrayList(u8).init(allocator),
205 .called = std.StringHashMap(void).init(allocator),
206 };
207
208 return &c_file.base;
209 }
210
247 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
248 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
249 return error.CGenFailure;213 return error.AnalysisFail;
250 }214 }
251215
252 pub fn deinit(self: *File.C) void {216 pub fn deinit(self: *File.C) void {
...@@ -260,7 +224,7 @@ pub const File = struct {...@@ -260,7 +224,7 @@ pub const File = struct {
260224
261 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {225 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
262 c_codegen.generate(self, decl) catch |err| {226 c_codegen.generate(self, decl) catch |err| {
263 if (err == error.CGenFailure) {227 if (err == error.AnalysisFail) {
264 try module.failed_decls.put(module.gpa, decl, self.error_msg);228 try module.failed_decls.put(module.gpa, decl, self.error_msg);
265 }229 }
266 return err;230 return err;
...@@ -301,13 +265,13 @@ pub const File = struct {...@@ -301,13 +265,13 @@ pub const File = struct {
301 };265 };
302266
303 pub const Elf = struct {267 pub const Elf = struct {
304 pub const base_tag: Tag = .Elf;268 pub const base_tag: Tag = .elf;
305 base: File = File{ .tag = base_tag },269
270 base: File,
306271
307 allocator: *Allocator,272 allocator: *Allocator,
308 file: ?fs.File,273 file: ?fs.File,
309 owns_file_handle: bool,274 owns_file_handle: bool,
310 options: Options,
311 ptr_width: enum { p32, p64 },275 ptr_width: enum { p32, p64 },
312276
313 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.277 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
...@@ -326,12 +290,20 @@ pub const File = struct {...@@ -326,12 +290,20 @@ pub const File = struct {
326 phdr_got_index: ?u16 = null,290 phdr_got_index: ?u16 = null,
327 entry_addr: ?u64 = null,291 entry_addr: ?u64 = null,
328292
293 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
329 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},294 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
330 shstrtab_index: ?u16 = null,295 shstrtab_index: ?u16 = null,
331296
332 text_section_index: ?u16 = null,297 text_section_index: ?u16 = null,
333 symtab_section_index: ?u16 = null,298 symtab_section_index: ?u16 = null,
334 got_section_index: ?u16 = null,299 got_section_index: ?u16 = null,
300 debug_info_section_index: ?u16 = null,
301 debug_abbrev_section_index: ?u16 = null,
302 debug_str_section_index: ?u16 = null,
303 debug_aranges_section_index: ?u16 = null,
304 debug_line_section_index: ?u16 = null,
305
306 debug_abbrev_table_offset: ?u64 = null,
335307
336 /// The same order as in the file. ELF requires global symbols to all be after the308 /// The same order as in the file. ELF requires global symbols to all be after the
337 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and309 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
...@@ -352,7 +324,12 @@ pub const File = struct {...@@ -352,7 +324,12 @@ pub const File = struct {
352 phdr_table_dirty: bool = false,324 phdr_table_dirty: bool = false,
353 shdr_table_dirty: bool = false,325 shdr_table_dirty: bool = false,
354 shstrtab_dirty: bool = false,326 shstrtab_dirty: bool = false,
327 debug_strtab_dirty: bool = false,
355 offset_table_count_dirty: bool = false,328 offset_table_count_dirty: bool = false,
329 debug_info_section_dirty: bool = false,
330 debug_abbrev_section_dirty: bool = false,
331 debug_aranges_section_dirty: bool = false,
332 debug_line_header_dirty: bool = false,
356333
357 error_flags: ErrorFlags = ErrorFlags{},334 error_flags: ErrorFlags = ErrorFlags{},
358335
...@@ -374,6 +351,12 @@ pub const File = struct {...@@ -374,6 +351,12 @@ pub const File = struct {
374 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},351 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
375 last_text_block: ?*TextBlock = null,352 last_text_block: ?*TextBlock = null,
376353
354 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
355 /// This is the same concept as `text_block_free_list`; see those doc comments.
356 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
357 dbg_line_fn_first: ?*SrcFn = null,
358 dbg_line_fn_last: ?*SrcFn = null,
359
377 /// `alloc_num / alloc_den` is the factor of padding when allocating.360 /// `alloc_num / alloc_den` is the factor of padding when allocating.
378 const alloc_num = 4;361 const alloc_num = 4;
379 const alloc_den = 3;362 const alloc_den = 3;
...@@ -437,16 +420,139 @@ pub const File = struct {...@@ -437,16 +420,139 @@ pub const File = struct {
437 sym_index: ?u32 = null,420 sym_index: ?u32 = null,
438 };421 };
439422
423 pub const SrcFn = struct {
424 /// Offset from the beginning of the Debug Line Program header that contains this function.
425 off: u32,
426 /// Size of the line number program component belonging to this function, not
427 /// including padding.
428 len: u32,
429
430 /// Points to the previous and next neighbors, based on the offset from .debug_line.
431 /// This can be used to find, for example, the capacity of this `SrcFn`.
432 prev: ?*SrcFn,
433 next: ?*SrcFn,
434
435 pub const empty: SrcFn = .{
436 .off = 0,
437 .len = 0,
438 .prev = null,
439 .next = null,
440 };
441 };
442
443 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
444 assert(options.object_format == .elf);
445
446 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
447 errdefer file.close();
448
449 var elf_file = try allocator.create(Elf);
450 errdefer allocator.destroy(elf_file);
451
452 elf_file.* = openFile(allocator, file, options) catch |err| switch (err) {
453 error.IncrFailed => try createFile(allocator, file, options),
454 else => |e| return e,
455 };
456
457 elf_file.owns_file_handle = true;
458 return &elf_file.base;
459 }
460
461 /// Returns error.IncrFailed if incremental update could not be performed.
462 fn openFile(allocator: *Allocator, file: fs.File, options: Options) !Elf {
463 switch (options.output_mode) {
464 .Exe => {},
465 .Obj => {},
466 .Lib => return error.IncrFailed,
467 }
468 var self: Elf = .{
469 .base = .{
470 .tag = .elf,
471 .options = options,
472 },
473 .allocator = allocator,
474 .file = file,
475 .owns_file_handle = false,
476 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
477 32 => .p32,
478 64 => .p64,
479 else => return error.UnsupportedELFArchitecture,
480 },
481 };
482 errdefer self.deinit();
483
484 // TODO implement reading the elf file
485 return error.IncrFailed;
486 //try self.populateMissingMetadata();
487 //return self;
488 }
489
490 /// Truncates the existing file contents and overwrites the contents.
491 /// Returns an error if `file` is not already open with +read +write +seek abilities.
492 fn createFile(allocator: *Allocator, file: fs.File, options: Options) !Elf {
493 switch (options.output_mode) {
494 .Exe => {},
495 .Obj => {},
496 .Lib => return error.TODOImplementWritingLibFiles,
497 }
498 var self: Elf = .{
499 .base = .{
500 .tag = .elf,
501 .options = options,
502 },
503 .allocator = allocator,
504 .file = file,
505 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
506 32 => .p32,
507 64 => .p64,
508 else => return error.UnsupportedELFArchitecture,
509 },
510 .shdr_table_dirty = true,
511 .owns_file_handle = false,
512 };
513 errdefer self.deinit();
514
515 // Index 0 is always a null symbol.
516 try self.local_symbols.append(allocator, .{
517 .st_name = 0,
518 .st_info = 0,
519 .st_other = 0,
520 .st_shndx = 0,
521 .st_value = 0,
522 .st_size = 0,
523 });
524
525 // There must always be a null section in index 0
526 try self.sections.append(allocator, .{
527 .sh_name = 0,
528 .sh_type = elf.SHT_NULL,
529 .sh_flags = 0,
530 .sh_addr = 0,
531 .sh_offset = 0,
532 .sh_size = 0,
533 .sh_link = 0,
534 .sh_info = 0,
535 .sh_addralign = 0,
536 .sh_entsize = 0,
537 });
538
539 try self.populateMissingMetadata();
540
541 return self;
542 }
543
440 pub fn deinit(self: *Elf) void {544 pub fn deinit(self: *Elf) void {
441 self.sections.deinit(self.allocator);545 self.sections.deinit(self.allocator);
442 self.program_headers.deinit(self.allocator);546 self.program_headers.deinit(self.allocator);
443 self.shstrtab.deinit(self.allocator);547 self.shstrtab.deinit(self.allocator);
548 self.debug_strtab.deinit(self.allocator);
444 self.local_symbols.deinit(self.allocator);549 self.local_symbols.deinit(self.allocator);
445 self.global_symbols.deinit(self.allocator);550 self.global_symbols.deinit(self.allocator);
446 self.global_symbol_free_list.deinit(self.allocator);551 self.global_symbol_free_list.deinit(self.allocator);
447 self.local_symbol_free_list.deinit(self.allocator);552 self.local_symbol_free_list.deinit(self.allocator);
448 self.offset_table_free_list.deinit(self.allocator);553 self.offset_table_free_list.deinit(self.allocator);
449 self.text_block_free_list.deinit(self.allocator);554 self.text_block_free_list.deinit(self.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);
450 self.offset_table.deinit(self.allocator);556 self.offset_table.deinit(self.allocator);
451 if (self.owns_file_handle) {557 if (self.owns_file_handle) {
452 if (self.file) |f| f.close();558 if (self.file) |f| f.close();
...@@ -467,13 +573,21 @@ pub const File = struct {...@@ -467,13 +573,21 @@ pub const File = struct {
467 self.file = try dir.createFile(sub_path, .{573 self.file = try dir.createFile(sub_path, .{
468 .truncate = false,574 .truncate = false,
469 .read = true,575 .read = true,
470 .mode = determineMode(self.options),576 .mode = determineMode(self.base.options),
471 });577 });
472 }578 }
473579
580 fn getDebugLineProgramOff(self: Elf) u32 {
581 return self.dbg_line_fn_first.?.off;
582 }
583
584 fn getDebugLineProgramEnd(self: Elf) u32 {
585 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
586 }
587
474 /// Returns end pos of collision, if any.588 /// Returns end pos of collision, if any.
475 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {589 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
476 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;590 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
477 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);591 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
478 if (start < ehdr_size)592 if (start < ehdr_size)
479 return ehdr_size;593 return ehdr_size;
...@@ -518,6 +632,8 @@ pub const File = struct {...@@ -518,6 +632,8 @@ pub const File = struct {
518 }632 }
519633
520 fn allocatedSize(self: *Elf, start: u64) u64 {634 fn allocatedSize(self: *Elf, start: u64) u64 {
635 if (start == 0)
636 return 0;
521 var min_pos: u64 = std.math.maxInt(u64);637 var min_pos: u64 = std.math.maxInt(u64);
522 if (self.shdr_table_offset) |off| {638 if (self.shdr_table_offset) |off| {
523 if (off > start and off < min_pos) min_pos = off;639 if (off > start and off < min_pos) min_pos = off;
...@@ -544,6 +660,7 @@ pub const File = struct {...@@ -544,6 +660,7 @@ pub const File = struct {
544 return start;660 return start;
545 }661 }
546662
663 /// TODO Improve this to use a table.
547 fn makeString(self: *Elf, bytes: []const u8) !u32 {664 fn makeString(self: *Elf, bytes: []const u8) !u32 {
548 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);665 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
549 const result = self.shstrtab.items.len;666 const result = self.shstrtab.items.len;
...@@ -552,6 +669,15 @@ pub const File = struct {...@@ -552,6 +669,15 @@ pub const File = struct {
552 return @intCast(u32, result);669 return @intCast(u32, result);
553 }670 }
554671
672 /// TODO Improve this to use a table.
673 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
674 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
675 const result = self.debug_strtab.items.len;
676 self.debug_strtab.appendSliceAssumeCapacity(bytes);
677 self.debug_strtab.appendAssumeCapacity(0);
678 return @intCast(u32, result);
679 }
680
555 fn getString(self: *Elf, str_off: u32) []const u8 {681 fn getString(self: *Elf, str_off: u32) []const u8 {
556 assert(str_off < self.shstrtab.items.len);682 assert(str_off < self.shstrtab.items.len);
557 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));683 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
...@@ -570,16 +696,13 @@ pub const File = struct {...@@ -570,16 +696,13 @@ pub const File = struct {
570 .p32 => true,696 .p32 => true,
571 .p64 => false,697 .p64 => false,
572 };698 };
573 const ptr_size: u8 = switch (self.ptr_width) {699 const ptr_size: u8 = self.ptrWidthBytes();
574 .p32 => 4,
575 .p64 => 8,
576 };
577 if (self.phdr_load_re_index == null) {700 if (self.phdr_load_re_index == null) {
578 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);701 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
579 const file_size = self.options.program_code_size_hint;702 const file_size = self.base.options.program_code_size_hint;
580 const p_align = 0x1000;703 const p_align = 0x1000;
581 const off = self.findFreeSpace(file_size, p_align);704 const off = self.findFreeSpace(file_size, p_align);
582 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });705 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
583 try self.program_headers.append(self.allocator, .{706 try self.program_headers.append(self.allocator, .{
584 .p_type = elf.PT_LOAD,707 .p_type = elf.PT_LOAD,
585 .p_offset = off,708 .p_offset = off,
...@@ -595,12 +718,12 @@ pub const File = struct {...@@ -595,12 +718,12 @@ pub const File = struct {
595 }718 }
596 if (self.phdr_got_index == null) {719 if (self.phdr_got_index == null) {
597 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);720 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
598 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;721 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
599 // We really only need ptr alignment but since we are using PROGBITS, linux requires722 // We really only need ptr alignment but since we are using PROGBITS, linux requires
600 // page align.723 // page align.
601 const p_align = 0x1000;724 const p_align = 0x1000;
602 const off = self.findFreeSpace(file_size, p_align);725 const off = self.findFreeSpace(file_size, p_align);
603 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });726 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
604 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.727 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
605 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something728 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
606 // else in virtual memory.729 // else in virtual memory.
...@@ -622,7 +745,7 @@ pub const File = struct {...@@ -622,7 +745,7 @@ pub const File = struct {
622 assert(self.shstrtab.items.len == 0);745 assert(self.shstrtab.items.len == 0);
623 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0746 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
624 const off = self.findFreeSpace(self.shstrtab.items.len, 1);747 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
625 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });748 log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
626 try self.sections.append(self.allocator, .{749 try self.sections.append(self.allocator, .{
627 .sh_name = try self.makeString(".shstrtab"),750 .sh_name = try self.makeString(".shstrtab"),
628 .sh_type = elf.SHT_STRTAB,751 .sh_type = elf.SHT_STRTAB,
...@@ -678,9 +801,9 @@ pub const File = struct {...@@ -678,9 +801,9 @@ pub const File = struct {
678 self.symtab_section_index = @intCast(u16, self.sections.items.len);801 self.symtab_section_index = @intCast(u16, self.sections.items.len);
679 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);802 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
680 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);803 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
681 const file_size = self.options.symbol_count_hint * each_size;804 const file_size = self.base.options.symbol_count_hint * each_size;
682 const off = self.findFreeSpace(file_size, min_align);805 const off = self.findFreeSpace(file_size, min_align);
683 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });806 log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
684807
685 try self.sections.append(self.allocator, .{808 try self.sections.append(self.allocator, .{
686 .sh_name = try self.makeString(".symtab"),809 .sh_name = try self.makeString(".symtab"),
...@@ -698,6 +821,124 @@ pub const File = struct {...@@ -698,6 +821,124 @@ pub const File = struct {
698 self.shdr_table_dirty = true;821 self.shdr_table_dirty = true;
699 try self.writeSymbol(0);822 try self.writeSymbol(0);
700 }823 }
824 if (self.debug_str_section_index == null) {
825 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
826 assert(self.debug_strtab.items.len == 0);
827 try self.sections.append(self.allocator, .{
828 .sh_name = try self.makeString(".debug_str"),
829 .sh_type = elf.SHT_PROGBITS,
830 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
831 .sh_addr = 0,
832 .sh_offset = 0,
833 .sh_size = self.debug_strtab.items.len,
834 .sh_link = 0,
835 .sh_info = 0,
836 .sh_addralign = 1,
837 .sh_entsize = 1,
838 });
839 self.debug_strtab_dirty = true;
840 self.shdr_table_dirty = true;
841 }
842 if (self.debug_info_section_index == null) {
843 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
844
845 const file_size_hint = 200;
846 const p_align = 1;
847 const off = self.findFreeSpace(file_size_hint, p_align);
848 log.debug(.link, "found .debug_info free space 0x{x} to 0x{x}\n", .{
849 off,
850 off + file_size_hint,
851 });
852 try self.sections.append(self.allocator, .{
853 .sh_name = try self.makeString(".debug_info"),
854 .sh_type = elf.SHT_PROGBITS,
855 .sh_flags = 0,
856 .sh_addr = 0,
857 .sh_offset = off,
858 .sh_size = file_size_hint,
859 .sh_link = 0,
860 .sh_info = 0,
861 .sh_addralign = p_align,
862 .sh_entsize = 0,
863 });
864 self.shdr_table_dirty = true;
865 self.debug_info_section_dirty = true;
866 }
867 if (self.debug_abbrev_section_index == null) {
868 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
869
870 const file_size_hint = 128;
871 const p_align = 1;
872 const off = self.findFreeSpace(file_size_hint, p_align);
873 log.debug(.link, "found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
874 off,
875 off + file_size_hint,
876 });
877 try self.sections.append(self.allocator, .{
878 .sh_name = try self.makeString(".debug_abbrev"),
879 .sh_type = elf.SHT_PROGBITS,
880 .sh_flags = 0,
881 .sh_addr = 0,
882 .sh_offset = off,
883 .sh_size = file_size_hint,
884 .sh_link = 0,
885 .sh_info = 0,
886 .sh_addralign = p_align,
887 .sh_entsize = 0,
888 });
889 self.shdr_table_dirty = true;
890 self.debug_abbrev_section_dirty = true;
891 }
892 if (self.debug_aranges_section_index == null) {
893 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
894
895 const file_size_hint = 160;
896 const p_align = 16;
897 const off = self.findFreeSpace(file_size_hint, p_align);
898 log.debug(.link, "found .debug_aranges free space 0x{x} to 0x{x}\n", .{
899 off,
900 off + file_size_hint,
901 });
902 try self.sections.append(self.allocator, .{
903 .sh_name = try self.makeString(".debug_aranges"),
904 .sh_type = elf.SHT_PROGBITS,
905 .sh_flags = 0,
906 .sh_addr = 0,
907 .sh_offset = off,
908 .sh_size = file_size_hint,
909 .sh_link = 0,
910 .sh_info = 0,
911 .sh_addralign = p_align,
912 .sh_entsize = 0,
913 });
914 self.shdr_table_dirty = true;
915 self.debug_aranges_section_dirty = true;
916 }
917 if (self.debug_line_section_index == null) {
918 self.debug_line_section_index = @intCast(u16, self.sections.items.len);
919
920 const file_size_hint = 250;
921 const p_align = 1;
922 const off = self.findFreeSpace(file_size_hint, p_align);
923 log.debug(.link, "found .debug_line free space 0x{x} to 0x{x}\n", .{
924 off,
925 off + file_size_hint,
926 });
927 try self.sections.append(self.allocator, .{
928 .sh_name = try self.makeString(".debug_line"),
929 .sh_type = elf.SHT_PROGBITS,
930 .sh_flags = 0,
931 .sh_addr = 0,
932 .sh_offset = off,
933 .sh_size = file_size_hint,
934 .sh_link = 0,
935 .sh_info = 0,
936 .sh_addralign = p_align,
937 .sh_entsize = 0,
938 });
939 self.shdr_table_dirty = true;
940 self.debug_line_header_dirty = true;
941 }
701 const shsize: u64 = switch (self.ptr_width) {942 const shsize: u64 = switch (self.ptr_width) {
702 .p32 => @sizeOf(elf.Elf32_Shdr),943 .p32 => @sizeOf(elf.Elf32_Shdr),
703 .p64 => @sizeOf(elf.Elf64_Shdr),944 .p64 => @sizeOf(elf.Elf64_Shdr),
...@@ -733,12 +974,307 @@ pub const File = struct {...@@ -733,12 +974,307 @@ pub const File = struct {
733974
734 /// Commit pending changes and write headers.975 /// Commit pending changes and write headers.
735 pub fn flush(self: *Elf) !void {976 pub fn flush(self: *Elf) !void {
736 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();977 const target_endian = self.base.options.target.cpu.arch.endian();
978 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
979 const ptr_width_bytes: u8 = self.ptrWidthBytes();
980 const init_len_size: usize = switch (self.ptr_width) {
981 .p32 => 4,
982 .p64 => 12,
983 };
737984
738 // Unfortunately these have to be buffered and done at the end because ELF does not allow985 // Unfortunately these have to be buffered and done at the end because ELF does not allow
739 // mixing local and global symbols within a symbol table.986 // mixing local and global symbols within a symbol table.
740 try self.writeAllGlobalSymbols();987 try self.writeAllGlobalSymbols();
741988
989 if (self.debug_abbrev_section_dirty) {
990 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
991
992 // These are LEB encoded but since the values are all less than 127
993 // we can simply append these bytes.
994 const abbrev_buf = [_]u8{
995 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
996 DW.AT_stmt_list, DW.FORM_sec_offset,
997 DW.AT_low_pc , DW.FORM_addr,
998 DW.AT_high_pc , DW.FORM_addr,
999 DW.AT_name , DW.FORM_strp,
1000 DW.AT_comp_dir , DW.FORM_strp,
1001 DW.AT_producer , DW.FORM_strp,
1002 DW.AT_language , DW.FORM_data2,
1003 0, 0, // table sentinel
1004
1005 0, 0, 0, // section sentinel
1006 };
1007
1008 const needed_size = abbrev_buf.len;
1009 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
1010 if (needed_size > allocated_size) {
1011 debug_abbrev_sect.sh_size = 0; // free the space
1012 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1013 }
1014 debug_abbrev_sect.sh_size = needed_size;
1015 log.debug(.link, ".debug_abbrev start=0x{x} end=0x{x}\n", .{
1016 debug_abbrev_sect.sh_offset,
1017 debug_abbrev_sect.sh_offset + needed_size,
1018 });
1019
1020 const abbrev_offset = 0;
1021 self.debug_abbrev_table_offset = abbrev_offset;
1022 try self.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
1023 if (!self.shdr_table_dirty) {
1024 // Then it won't get written with the others and we need to do it.
1025 try self.writeSectHeader(self.debug_abbrev_section_index.?);
1026 }
1027
1028 self.debug_abbrev_section_dirty = false;
1029 }
1030 if (self.debug_info_section_dirty) {
1031 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
1032
1033 var di_buf = std.ArrayList(u8).init(self.allocator);
1034 defer di_buf.deinit();
1035
1036 // Enough for a 64-bit header and main compilation unit without resizing.
1037 try di_buf.ensureCapacity(100);
1038
1039 // initial length - length of the .debug_info contribution for this compilation unit,
1040 // not including the initial length itself.
1041 // We have to come back and write it later after we know the size.
1042 const init_len_index = di_buf.items.len;
1043 di_buf.items.len += init_len_size;
1044 const after_init_len = di_buf.items.len;
1045 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
1046 const abbrev_offset = self.debug_abbrev_table_offset.?;
1047 switch (self.ptr_width) {
1048 .p32 => {
1049 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
1050 di_buf.appendAssumeCapacity(4); // address size
1051 },
1052 .p64 => {
1053 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
1054 di_buf.appendAssumeCapacity(8); // address size
1055 },
1056 }
1057 // 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);
1059 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)");
1061 // 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.
1063 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1064 const low_pc = text_phdr.p_vaddr;
1065 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1066
1067 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
1068 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
1069 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
1070 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
1071 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
1072 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
1073 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
1074 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
1075 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
1076 // Until then we say it is C99.
1077 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
1078
1079 const init_len = di_buf.items.len - after_init_len;
1080 switch (self.ptr_width) {
1081 .p32 => {
1082 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1083 },
1084 .p64 => {
1085 // initial length - length of the .debug_info contribution for this compilation unit,
1086 // not including the initial length itself.
1087 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1088 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
1089 },
1090 }
1091
1092 const needed_size = di_buf.items.len;
1093 const allocated_size = self.allocatedSize(debug_info_sect.sh_offset);
1094 if (needed_size > allocated_size) {
1095 debug_info_sect.sh_size = 0; // free the space
1096 debug_info_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1097 }
1098 debug_info_sect.sh_size = needed_size;
1099 log.debug(.link, ".debug_info start=0x{x} end=0x{x}\n", .{
1100 debug_info_sect.sh_offset,
1101 debug_info_sect.sh_offset + needed_size,
1102 });
1103
1104 try self.file.?.pwriteAll(di_buf.items, debug_info_sect.sh_offset);
1105 if (!self.shdr_table_dirty) {
1106 // Then it won't get written with the others and we need to do it.
1107 try self.writeSectHeader(self.debug_info_section_index.?);
1108 }
1109
1110 self.debug_info_section_dirty = false;
1111 }
1112 if (self.debug_aranges_section_dirty) {
1113 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
1114
1115 var di_buf = std.ArrayList(u8).init(self.allocator);
1116 defer di_buf.deinit();
1117
1118 // Enough for all the data without resizing. When support for more compilation units
1119 // is added, the size of this section will become more variable.
1120 try di_buf.ensureCapacity(100);
1121
1122 // initial length - length of the .debug_aranges contribution for this compilation unit,
1123 // not including the initial length itself.
1124 // We have to come back and write it later after we know the size.
1125 const init_len_index = di_buf.items.len;
1126 di_buf.items.len += init_len_size;
1127 const after_init_len = di_buf.items.len;
1128 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
1129 // When more than one compilation unit is supported, this will be the offset to it.
1130 // For now it is always at offset 0 in .debug_info.
1131 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
1132 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
1133 di_buf.appendAssumeCapacity(0); // segment_selector_size
1134
1135 const end_header_offset = di_buf.items.len;
1136 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
1137 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
1138
1139 // Currently only one compilation unit is supported, so the address range is simply
1140 // identical to the main program header virtual address and memory size.
1141 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1142 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
1143 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
1144
1145 // Sentinel.
1146 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1147 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1148
1149 // Go back and populate the initial length.
1150 const init_len = di_buf.items.len - after_init_len;
1151 switch (self.ptr_width) {
1152 .p32 => {
1153 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1154 },
1155 .p64 => {
1156 // initial length - length of the .debug_aranges contribution for this compilation unit,
1157 // not including the initial length itself.
1158 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1159 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
1160 },
1161 }
1162
1163 const needed_size = di_buf.items.len;
1164 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
1165 if (needed_size > allocated_size) {
1166 debug_aranges_sect.sh_size = 0; // free the space
1167 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
1168 }
1169 debug_aranges_sect.sh_size = needed_size;
1170 log.debug(.link, ".debug_aranges start=0x{x} end=0x{x}\n", .{
1171 debug_aranges_sect.sh_offset,
1172 debug_aranges_sect.sh_offset + needed_size,
1173 });
1174
1175 try self.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
1176 if (!self.shdr_table_dirty) {
1177 // Then it won't get written with the others and we need to do it.
1178 try self.writeSectHeader(self.debug_aranges_section_index.?);
1179 }
1180
1181 self.debug_aranges_section_dirty = false;
1182 }
1183 if (self.debug_line_header_dirty) {
1184 const dbg_line_prg_off = self.getDebugLineProgramOff();
1185 const dbg_line_prg_end = self.getDebugLineProgramEnd();
1186 assert(dbg_line_prg_end != 0);
1187
1188 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1189
1190 var di_buf = std.ArrayList(u8).init(self.allocator);
1191 defer di_buf.deinit();
1192
1193 // The size of this header is variable, depending on the number of directories,
1194 // files, and padding. We have a function to compute the upper bound size, however,
1195 // because it's needed for determining where to put the offset of the first `SrcFn`.
1196 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
1197
1198 // initial length - length of the .debug_line contribution for this compilation unit,
1199 // not including the initial length itself.
1200 const after_init_len = di_buf.items.len + init_len_size;
1201 const init_len = dbg_line_prg_end - after_init_len;
1202 switch (self.ptr_width) {
1203 .p32 => {
1204 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1205 },
1206 .p64 => {
1207 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1208 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1209 },
1210 }
1211
1212 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
1213
1214 // Empirically, debug info consumers do not respect this field, or otherwise
1215 // consider it to be an error when it does not point exactly to the end of the header.
1216 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
1217 // padding rather than this field.
1218 const before_header_len = di_buf.items.len;
1219 di_buf.items.len += ptr_width_bytes; // We will come back and write this.
1220 const after_header_len = di_buf.items.len;
1221
1222 const opcode_base = DW.LNS_set_isa + 1;
1223 di_buf.appendSliceAssumeCapacity(&[_]u8{
1224 1, // minimum_instruction_length
1225 1, // maximum_operations_per_instruction
1226 1, // default_is_stmt
1227 1, // line_base (signed)
1228 1, // line_range
1229 opcode_base,
1230
1231 // Standard opcode lengths. The number of items here is based on `opcode_base`.
1232 // The value is the number of LEB128 operands the instruction takes.
1233 0, // `DW.LNS_copy`
1234 1, // `DW.LNS_advance_pc`
1235 1, // `DW.LNS_advance_line`
1236 1, // `DW.LNS_set_file`
1237 1, // `DW.LNS_set_column`
1238 0, // `DW.LNS_negate_stmt`
1239 0, // `DW.LNS_set_basic_block`
1240 0, // `DW.LNS_const_add_pc`
1241 1, // `DW.LNS_fixed_advance_pc`
1242 0, // `DW.LNS_set_prologue_end`
1243 0, // `DW.LNS_set_epilogue_begin`
1244 1, // `DW.LNS_set_isa`
1245
1246 0, // include_directories (none except the compilation unit cwd)
1247 });
1248 // file_names[0]
1249 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name
1250 di_buf.appendSliceAssumeCapacity(&[_]u8{
1251 0, // null byte for the relative path name
1252 0, // directory_index
1253 0, // mtime (TODO supply this)
1254 0, // file size bytes (TODO supply this)
1255 0, // file_names sentinel
1256 });
1257
1258 const header_len = di_buf.items.len - after_header_len;
1259 switch (self.ptr_width) {
1260 .p32 => {
1261 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
1262 },
1263 .p64 => {
1264 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
1265 },
1266 }
1267
1268 // We use NOPs because consumers empirically do not respect the header length field.
1269 if (di_buf.items.len > dbg_line_prg_off) {
1270 // Move the first N files to the end to make more padding for the header.
1271 @panic("TODO: handle .debug_line header exceeding its padding");
1272 }
1273 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);
1275 self.debug_line_header_dirty = false;
1276 }
1277
742 if (self.phdr_table_dirty) {1278 if (self.phdr_table_dirty) {
743 const phsize: u64 = switch (self.ptr_width) {1279 const phsize: u64 = switch (self.ptr_width) {
744 .p32 => @sizeOf(elf.Elf32_Phdr),1280 .p32 => @sizeOf(elf.Elf32_Phdr),
...@@ -796,7 +1332,7 @@ pub const File = struct {...@@ -796,7 +1332,7 @@ pub const File = struct {
796 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);1332 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
797 }1333 }
798 shstrtab_sect.sh_size = needed_size;1334 shstrtab_sect.sh_size = needed_size;
799 std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + 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 });
8001336
801 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);1337 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
802 if (!self.shdr_table_dirty) {1338 if (!self.shdr_table_dirty) {
...@@ -806,6 +1342,27 @@ pub const File = struct {...@@ -806,6 +1342,27 @@ pub const File = struct {
806 self.shstrtab_dirty = false;1342 self.shstrtab_dirty = false;
807 }1343 }
808 }1344 }
1345 {
1346 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
1347 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
1348 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
1349 const needed_size = self.debug_strtab.items.len;
1350
1351 if (needed_size > allocated_size) {
1352 debug_strtab_sect.sh_size = 0; // free the space
1353 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1354 }
1355 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 });
1357
1358 try self.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
1359 if (!self.shdr_table_dirty) {
1360 // Then it won't get written with the others and we need to do it.
1361 try self.writeSectHeader(self.debug_str_section_index.?);
1362 }
1363 self.debug_strtab_dirty = false;
1364 }
1365 }
809 if (self.shdr_table_dirty) {1366 if (self.shdr_table_dirty) {
810 const shsize: u64 = switch (self.ptr_width) {1367 const shsize: u64 = switch (self.ptr_width) {
811 .p32 => @sizeOf(elf.Elf32_Shdr),1368 .p32 => @sizeOf(elf.Elf32_Shdr),
...@@ -842,7 +1399,7 @@ pub const File = struct {...@@ -842,7 +1399,7 @@ pub const File = struct {
8421399
843 for (buf) |*shdr, i| {1400 for (buf) |*shdr, i| {
844 shdr.* = self.sections.items[i];1401 shdr.* = self.sections.items[i];
845 std.log.debug(.link, "writing section {}\n", .{shdr.*});1402 log.debug(.link, "writing section {}\n", .{shdr.*});
846 if (foreign_endian) {1403 if (foreign_endian) {
847 bswapAllFields(elf.Elf64_Shdr, shdr);1404 bswapAllFields(elf.Elf64_Shdr, shdr);
848 }1405 }
...@@ -852,8 +1409,8 @@ pub const File = struct {...@@ -852,8 +1409,8 @@ pub const File = struct {
852 }1409 }
853 self.shdr_table_dirty = false;1410 self.shdr_table_dirty = false;
854 }1411 }
855 if (self.entry_addr == null and self.options.output_mode == .Exe) {1412 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
856 std.log.debug(.link, "no_entry_point_found = true\n", .{});1413 log.debug(.link, "no_entry_point_found = true\n", .{});
857 self.error_flags.no_entry_point_found = true;1414 self.error_flags.no_entry_point_found = true;
858 } else {1415 } else {
859 self.error_flags.no_entry_point_found = false;1416 self.error_flags.no_entry_point_found = false;
...@@ -861,14 +1418,27 @@ pub const File = struct {...@@ -861,14 +1418,27 @@ pub const File = struct {
861 }1418 }
8621419
863 // The point of flush() is to commit changes, so nothing should be dirty after this.1420 // The point of flush() is to commit changes, so nothing should be dirty after this.
1421 assert(!self.debug_info_section_dirty);
1422 assert(!self.debug_abbrev_section_dirty);
1423 assert(!self.debug_aranges_section_dirty);
1424 assert(!self.debug_line_header_dirty);
864 assert(!self.phdr_table_dirty);1425 assert(!self.phdr_table_dirty);
865 assert(!self.shdr_table_dirty);1426 assert(!self.shdr_table_dirty);
866 assert(!self.shstrtab_dirty);1427 assert(!self.shstrtab_dirty);
1428 assert(!self.debug_strtab_dirty);
867 assert(!self.offset_table_count_dirty);1429 assert(!self.offset_table_count_dirty);
868 const syms_sect = &self.sections.items[self.symtab_section_index.?];1430 const syms_sect = &self.sections.items[self.symtab_section_index.?];
869 assert(syms_sect.sh_info == self.local_symbols.items.len);1431 assert(syms_sect.sh_info == self.local_symbols.items.len);
870 }1432 }
8711433
1434 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1435 const target_endian = self.base.options.target.cpu.arch.endian();
1436 switch (self.ptr_width) {
1437 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
1438 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
1439 }
1440 }
1441
872 fn writeElfHeader(self: *Elf) !void {1442 fn writeElfHeader(self: *Elf) !void {
873 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;1443 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
8741444
...@@ -882,7 +1452,7 @@ pub const File = struct {...@@ -882,7 +1452,7 @@ pub const File = struct {
882 };1452 };
883 index += 1;1453 index += 1;
8841454
885 const endian = self.options.target.cpu.arch.endian();1455 const endian = self.base.options.target.cpu.arch.endian();
886 hdr_buf[index] = switch (endian) {1456 hdr_buf[index] = switch (endian) {
887 .Little => elf.ELFDATA2LSB,1457 .Little => elf.ELFDATA2LSB,
888 .Big => elf.ELFDATA2MSB,1458 .Big => elf.ELFDATA2MSB,
...@@ -900,10 +1470,10 @@ pub const File = struct {...@@ -900,10 +1470,10 @@ pub const File = struct {
9001470
901 assert(index == 16);1471 assert(index == 16);
9021472
903 const elf_type = switch (self.options.output_mode) {1473 const elf_type = switch (self.base.options.output_mode) {
904 .Exe => elf.ET.EXEC,1474 .Exe => elf.ET.EXEC,
905 .Obj => elf.ET.REL,1475 .Obj => elf.ET.REL,
906 .Lib => switch (self.options.link_mode) {1476 .Lib => switch (self.base.options.link_mode) {
907 .Static => elf.ET.REL,1477 .Static => elf.ET.REL,
908 .Dynamic => elf.ET.DYN,1478 .Dynamic => elf.ET.DYN,
909 },1479 },
...@@ -911,7 +1481,7 @@ pub const File = struct {...@@ -911,7 +1481,7 @@ pub const File = struct {
911 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);1481 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
912 index += 2;1482 index += 2;
9131483
914 const machine = self.options.target.cpu.arch.toElfMachine();1484 const machine = self.base.options.target.cpu.arch.toElfMachine();
915 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);1485 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
916 index += 2;1486 index += 2;
9171487
...@@ -1129,6 +1699,15 @@ pub const File = struct {...@@ -1129,6 +1699,15 @@ pub const File = struct {
1129 phdr.p_memsz = needed_size;1699 phdr.p_memsz = needed_size;
1130 phdr.p_filesz = needed_size;1700 phdr.p_filesz = needed_size;
11311701
1702 // 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,
1704 // so the .debug_info section becomes dirty.
1705 self.debug_info_section_dirty = true;
1706 // 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 to
1708 // model each package as a different compilation unit.
1709 self.debug_aranges_section_dirty = true;
1710
1132 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1711 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1133 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1712 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1134 }1713 }
...@@ -1160,17 +1739,14 @@ pub const File = struct {...@@ -1160,17 +1739,14 @@ pub const File = struct {
1160 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {1739 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1161 if (decl.link.local_sym_index != 0) return;1740 if (decl.link.local_sym_index != 0) return;
11621741
1163 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1164 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);1742 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1165 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1166 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);1743 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1167 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
11681744
1169 if (self.local_symbol_free_list.popOrNull()) |i| {1745 if (self.local_symbol_free_list.popOrNull()) |i| {
1170 std.log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });1746 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
1171 decl.link.local_sym_index = i;1747 decl.link.local_sym_index = i;
1172 } else {1748 } else {
1173 std.log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });1749 log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1174 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);1750 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1175 _ = self.local_symbols.addOneAssumeCapacity();1751 _ = self.local_symbols.addOneAssumeCapacity();
1176 }1752 }
...@@ -1197,23 +1773,107 @@ pub const File = struct {...@@ -1197,23 +1773,107 @@ pub const File = struct {
1197 }1773 }
11981774
1199 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {1775 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.
1200 self.freeTextBlock(&decl.link);1777 self.freeTextBlock(&decl.link);
1201 if (decl.link.local_sym_index != 0) {1778 if (decl.link.local_sym_index != 0) {
1202 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};
1203 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_index) catch {};
12041781
1205 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;1782 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
12061783
1207 decl.link.local_sym_index = 0;1784 decl.link.local_sym_index = 0;
1208 }1785 }
1786 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1787 // is desired for both.
1788 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link);
1789 if (decl.fn_link.prev) |prev| {
1790 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
1791 prev.next = decl.fn_link.next;
1792 if (decl.fn_link.next) |next| {
1793 next.prev = prev;
1794 } else {
1795 self.dbg_line_fn_last = prev;
1796 }
1797 } else if (decl.fn_link.next) |next| {
1798 self.dbg_line_fn_first = next;
1799 next.prev = null;
1800 }
1801 if (self.dbg_line_fn_first == &decl.fn_link) {
1802 self.dbg_line_fn_first = null;
1803 }
1804 if (self.dbg_line_fn_last == &decl.fn_link) {
1805 self.dbg_line_fn_last = null;
1806 }
1209 }1807 }
12101808
1211 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {1809 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1810 const tracy = trace(@src());
1811 defer tracy.end();
1812
1212 var code_buffer = std.ArrayList(u8).init(self.allocator);1813 var code_buffer = std.ArrayList(u8).init(self.allocator);
1213 defer code_buffer.deinit();1814 defer code_buffer.deinit();
12141815
1816 var dbg_line_buffer = std.ArrayList(u8).init(self.allocator);
1817 defer dbg_line_buffer.deinit();
1818
1215 const typed_value = decl.typed_value.most_recent.typed_value;1819 const typed_value = decl.typed_value.most_recent.typed_value;
1216 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {1820 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1821 .Fn => true,
1822 else => false,
1823 };
1824 if (is_fn) {
1825 // For functions we need to add a prologue to the debug line program.
1826 try dbg_line_buffer.ensureCapacity(26);
1827
1828 const line_off: u28 = blk: {
1829 if (decl.scope.cast(Module.Scope.File)) |scope_file| {
1830 const tree = scope_file.contents.tree;
1831 const file_ast_decls = tree.root_node.decls();
1832 // TODO Look into improving the performance here by adding a token-index-to-line
1833 // lookup table. Currently this involves scanning over the source code for newlines.
1834 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1835 const block = fn_proto.body().?.castTag(.Block).?;
1836 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1837 break :blk @intCast(u28, line_delta);
1838 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1839 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1840 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1841 break :blk @intCast(u28, line_delta);
1842 } else {
1843 unreachable;
1844 }
1845 };
1846
1847 const ptr_width_bytes = self.ptrWidthBytes();
1848 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1849 DW.LNS_extended_op,
1850 ptr_width_bytes + 1,
1851 DW.LNE_set_address,
1852 });
1853 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1854 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1855 dbg_line_buffer.items.len += ptr_width_bytes;
1856
1857 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1858 // This is the "relocatable" relative line offset from the previous function's end curly
1859 // to this function's begin curly.
1860 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1861 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1862 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1863
1864 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1865 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1866 // Once we support more than one source file, this will have the ability to be more
1867 // than one possible value.
1868 const file_index = 1;
1869 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1870
1871 // 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.
1873 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1874 }
1875 const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer);
1876 const code = switch (res) {
1217 .externally_managed => |x| x,1877 .externally_managed => |x| x,
1218 .appended => code_buffer.items,1878 .appended => code_buffer.items,
1219 .fail => |em| {1879 .fail => |em| {
...@@ -1223,12 +1883,9 @@ pub const File = struct {...@@ -1223,12 +1883,9 @@ pub const File = struct {
1223 },1883 },
1224 };1884 };
12251885
1226 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1886 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
12271887
1228 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1888 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
1229 .Fn => elf.STT_FUNC,
1230 else => elf.STT_OBJECT,
1231 };
12321889
1233 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()1890 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1234 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
...@@ -1238,11 +1895,11 @@ pub const File = struct {...@@ -1238,11 +1895,11 @@ pub const File = struct {
1238 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1895 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1239 if (need_realloc) {1896 if (need_realloc) {
1240 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);1897 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1241 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });1898 log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1242 if (vaddr != local_sym.st_value) {1899 if (vaddr != local_sym.st_value) {
1243 local_sym.st_value = vaddr;1900 local_sym.st_value = vaddr;
12441901
1245 std.log.debug(.link, " (writing new offset table entry)\n", .{});1902 log.debug(.link, " (writing new offset table entry)\n", .{});
1246 self.offset_table.items[decl.link.offset_table_index] = vaddr;1903 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1247 try self.writeOffsetTableEntry(decl.link.offset_table_index);1904 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1248 }1905 }
...@@ -1260,7 +1917,7 @@ pub const File = struct {...@@ -1260,7 +1917,7 @@ pub const File = struct {
1260 const decl_name = mem.spanZ(decl.name);1917 const decl_name = mem.spanZ(decl.name);
1261 const name_str_index = try self.makeString(decl_name);1918 const name_str_index = try self.makeString(decl_name);
1262 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);1919 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1263 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });1920 log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1264 errdefer self.freeTextBlock(&decl.link);1921 errdefer self.freeTextBlock(&decl.link);
12651922
1266 local_sym.* = .{1923 local_sym.* = .{
...@@ -1281,6 +1938,94 @@ pub const File = struct {...@@ -1281,6 +1938,94 @@ pub const File = struct {
1281 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;1938 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1282 try self.file.?.pwriteAll(code, file_offset);1939 try self.file.?.pwriteAll(code, file_offset);
12831940
1941 // If the Decl is a function, we need to update the .debug_line program.
1942 if (is_fn) {
1943 // Perform the relocation based on vaddr.
1944 const target_endian = self.base.options.target.cpu.arch.endian();
1945 switch (self.ptr_width) {
1946 .p32 => {
1947 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
1948 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
1949 },
1950 .p64 => {
1951 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1952 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
1953 },
1954 }
1955
1956 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1957
1958 // Now we have the full contents and may allocate a region to store it.
1959
1960 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1961 const src_fn = &decl.fn_link;
1962 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1963 if (self.dbg_line_fn_last) |last| {
1964 if (src_fn.next) |next| {
1965 // Update existing function - non-last item.
1966 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.
1968 if (src_fn.prev) |prev| {
1969 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
1970 prev.next = src_fn.next;
1971 }
1972 next.prev = src_fn.prev;
1973 src_fn.next = null;
1974 // Populate where it used to be with NOPs.
1975 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1976 try self.pwriteWithNops(0, &[0]u8{}, src_fn.len, file_pos);
1977 // TODO Look at the free list before appending at the end.
1978 src_fn.prev = last;
1979 last.next = src_fn;
1980 self.dbg_line_fn_last = src_fn;
1981
1982 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1983 }
1984 } else if (src_fn.prev == null) {
1985 // Append new function.
1986 // TODO Look at the free list before appending at the end.
1987 src_fn.prev = last;
1988 last.next = src_fn;
1989 self.dbg_line_fn_last = src_fn;
1990
1991 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1992 }
1993 } else {
1994 // This is the first function of the Line Number Program.
1995 self.dbg_line_fn_first = src_fn;
1996 self.dbg_line_fn_last = src_fn;
1997
1998 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
1999 }
2000
2001 const last_src_fn = self.dbg_line_fn_last.?;
2002 const needed_size = last_src_fn.off + last_src_fn.len;
2003 if (needed_size != debug_line_sect.sh_size) {
2004 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2005 const new_offset = self.findFreeSpace(needed_size, 1);
2006 const existing_size = last_src_fn.off;
2007 log.debug(.link, "moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
2008 existing_size,
2009 debug_line_sect.sh_offset,
2010 new_offset,
2011 });
2012 const amt = try self.file.?.copyRangeAll(debug_line_sect.sh_offset, self.file.?, new_offset, existing_size);
2013 if (amt != existing_size) return error.InputOutput;
2014 debug_line_sect.sh_offset = new_offset;
2015 }
2016 debug_line_sect.sh_size = needed_size;
2017 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2018 self.debug_line_header_dirty = true;
2019 }
2020 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
2021 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
2022
2023 // We only have support for one compilation unit so far, so the offsets are directly
2024 // from the .debug_line section.
2025 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);
2027 }
2028
1284 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2029 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1285 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};2030 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1286 return self.updateDeclExports(module, decl, decl_exports);2031 return self.updateDeclExports(module, decl, decl_exports);
...@@ -1293,10 +2038,10 @@ pub const File = struct {...@@ -1293,10 +2038,10 @@ pub const File = struct {
1293 decl: *const Module.Decl,2038 decl: *const Module.Decl,
1294 exports: []const *Module.Export,2039 exports: []const *Module.Export,
1295 ) !void {2040 ) !void {
1296 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of2041 const tracy = trace(@src());
1297 // them, so that deleting exports is guaranteed to succeed.2042 defer tracy.end();
2043
1298 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);2044 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1299 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1300 const typed_value = decl.typed_value.most_recent.typed_value;2045 const typed_value = decl.typed_value.most_recent.typed_value;
1301 if (decl.link.local_sym_index == 0) return;2046 if (decl.link.local_sym_index == 0) return;
1302 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];2047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
...@@ -1361,16 +2106,38 @@ pub const File = struct {...@@ -1361,16 +2106,38 @@ pub const File = struct {
1361 }2106 }
1362 }2107 }
13632108
2109 /// Must be called only after a successful call to `updateDecl`.
2110 pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
2111 const tracy = trace(@src());
2112 defer tracy.end();
2113
2114 const scope_file = decl.scope.cast(Module.Scope.File).?;
2115 const tree = scope_file.contents.tree;
2116 const file_ast_decls = tree.root_node.decls();
2117 // TODO Look into improving the performance here by adding a token-index-to-line
2118 // lookup table. Currently this involves scanning over the source code for newlines.
2119 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2120 const block = fn_proto.body().?.castTag(.Block).?;
2121 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2122 const casted_line_off = @intCast(u28, line_delta);
2123
2124 const shdr = &self.sections.items[self.debug_line_section_index.?];
2125 const file_pos = shdr.sh_offset + decl.fn_link.off + self.getRelocDbgLineOff();
2126 var data: [4]u8 = undefined;
2127 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2128 try self.file.?.pwriteAll(&data, file_pos);
2129 }
2130
1364 pub fn deleteExport(self: *Elf, exp: Export) void {2131 pub fn deleteExport(self: *Elf, exp: Export) void {
1365 const sym_index = exp.sym_index orelse return;2132 const sym_index = exp.sym_index orelse return;
1366 self.global_symbol_free_list.appendAssumeCapacity(sym_index);2133 self.global_symbol_free_list.append(self.allocator, sym_index) catch {};
1367 self.global_symbols.items[sym_index].st_info = 0;2134 self.global_symbols.items[sym_index].st_info = 0;
1368 }2135 }
13692136
1370 fn writeProgHeader(self: *Elf, index: usize) !void {2137 fn writeProgHeader(self: *Elf, index: usize) !void {
1371 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2138 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1372 const offset = self.program_headers.items[index].p_offset;2139 const offset = self.program_headers.items[index].p_offset;
1373 switch (self.options.target.cpu.arch.ptrBitWidth()) {2140 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
1374 32 => {2141 32 => {
1375 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};2142 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1376 if (foreign_endian) {2143 if (foreign_endian) {
...@@ -1390,15 +2157,15 @@ pub const File = struct {...@@ -1390,15 +2157,15 @@ pub const File = struct {
1390 }2157 }
13912158
1392 fn writeSectHeader(self: *Elf, index: usize) !void {2159 fn writeSectHeader(self: *Elf, index: usize) !void {
1393 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2160 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1394 const offset = self.sections.items[index].sh_offset;2161 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
1395 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1396 32 => {2162 32 => {
1397 var shdr: [1]elf.Elf32_Shdr = undefined;2163 var shdr: [1]elf.Elf32_Shdr = undefined;
1398 shdr[0] = sectHeaderTo32(self.sections.items[index]);2164 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1399 if (foreign_endian) {2165 if (foreign_endian) {
1400 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);2166 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1401 }2167 }
2168 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
1402 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2169 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1403 },2170 },
1404 64 => {2171 64 => {
...@@ -1406,6 +2173,7 @@ pub const File = struct {...@@ -1406,6 +2173,7 @@ pub const File = struct {
1406 if (foreign_endian) {2173 if (foreign_endian) {
1407 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);2174 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1408 }2175 }
2176 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
1409 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2177 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1410 },2178 },
1411 else => return error.UnsupportedArchitecture,2179 else => return error.UnsupportedArchitecture,
...@@ -1415,10 +2183,7 @@ pub const File = struct {...@@ -1415,10 +2183,7 @@ pub const File = struct {
1415 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {2183 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1416 const shdr = &self.sections.items[self.got_section_index.?];2184 const shdr = &self.sections.items[self.got_section_index.?];
1417 const phdr = &self.program_headers.items[self.phdr_got_index.?];2185 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1418 const entry_size: u16 = switch (self.ptr_width) {2186 const entry_size: u16 = self.ptrWidthBytes();
1419 .p32 => 4,
1420 .p64 => 8,
1421 };
1422 if (self.offset_table_count_dirty) {2187 if (self.offset_table_count_dirty) {
1423 // TODO Also detect virtual address collisions.2188 // TODO Also detect virtual address collisions.
1424 const allocated_size = self.allocatedSize(shdr.sh_offset);2189 const allocated_size = self.allocatedSize(shdr.sh_offset);
...@@ -1440,7 +2205,7 @@ pub const File = struct {...@@ -1440,7 +2205,7 @@ pub const File = struct {
14402205
1441 self.offset_table_count_dirty = false;2206 self.offset_table_count_dirty = false;
1442 }2207 }
1443 const endian = self.options.target.cpu.arch.endian();2208 const endian = self.base.options.target.cpu.arch.endian();
1444 const off = shdr.sh_offset + @as(u64, entry_size) * index;2209 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1445 switch (self.ptr_width) {2210 switch (self.ptr_width) {
1446 .p32 => {2211 .p32 => {
...@@ -1482,7 +2247,7 @@ pub const File = struct {...@@ -1482,7 +2247,7 @@ pub const File = struct {
1482 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later2247 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1483 self.shdr_table_dirty = true; // TODO look into only writing one section2248 self.shdr_table_dirty = true; // TODO look into only writing one section
1484 }2249 }
1485 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2250 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1486 switch (self.ptr_width) {2251 switch (self.ptr_width) {
1487 .p32 => {2252 .p32 => {
1488 var sym = [1]elf.Elf32_Sym{2253 var sym = [1]elf.Elf32_Sym{
...@@ -1518,7 +2283,7 @@ pub const File = struct {...@@ -1518,7 +2283,7 @@ pub const File = struct {
1518 .p32 => @sizeOf(elf.Elf32_Sym),2283 .p32 => @sizeOf(elf.Elf32_Sym),
1519 .p64 => @sizeOf(elf.Elf64_Sym),2284 .p64 => @sizeOf(elf.Elf64_Sym),
1520 };2285 };
1521 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2286 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1522 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;2287 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1523 switch (self.ptr_width) {2288 switch (self.ptr_width) {
1524 .p32 => {2289 .p32 => {
...@@ -1561,106 +2326,124 @@ pub const File = struct {...@@ -1561,106 +2326,124 @@ pub const File = struct {
1561 },2326 },
1562 }2327 }
1563 }2328 }
1564 };
1565};
15662329
1567/// Truncates the existing file contents and overwrites the contents.2330 fn ptrWidthBytes(self: Elf) u8 {
1568/// Returns an error if `file` is not already open with +read +write +seek abilities.2331 return switch (self.ptr_width) {
1569pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {2332 .p32 => 4,
1570 switch (options.output_mode) {2333 .p64 => 8,
1571 .Exe => {},2334 };
1572 .Obj => {},2335 }
1573 .Lib => return error.TODOImplementWritingLibFiles,
1574 }
1575 switch (options.object_format) {
1576 .c => unreachable,
1577 .unknown => unreachable, // TODO remove this tag from the enum
1578 .coff => return error.TODOImplementWritingCOFF,
1579 .elf => {},
1580 .macho => return error.TODOImplementWritingMachO,
1581 .wasm => return error.TODOImplementWritingWasmObjects,
1582 .hex => return error.TODOImplementWritingHex,
1583 .raw => return error.TODOImplementWritingRaw,
1584 }
15852336
1586 var self: File.Elf = .{2337 /// The reloc offset for the virtual address of a function in its Line Number Program.
1587 .allocator = allocator,2338 /// Size is a virtual address integer.
1588 .file = file,2339 const dbg_line_vaddr_reloc_index = 3;
1589 .options = options,
1590 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1591 32 => .p32,
1592 64 => .p64,
1593 else => return error.UnsupportedELFArchitecture,
1594 },
1595 .shdr_table_dirty = true,
1596 .owns_file_handle = false,
1597 };
1598 errdefer self.deinit();
1599
1600 // Index 0 is always a null symbol.
1601 try self.local_symbols.append(allocator, .{
1602 .st_name = 0,
1603 .st_info = 0,
1604 .st_other = 0,
1605 .st_shndx = 0,
1606 .st_value = 0,
1607 .st_size = 0,
1608 });
1609
1610 // There must always be a null section in index 0
1611 try self.sections.append(allocator, .{
1612 .sh_name = 0,
1613 .sh_type = elf.SHT_NULL,
1614 .sh_flags = 0,
1615 .sh_addr = 0,
1616 .sh_offset = 0,
1617 .sh_size = 0,
1618 .sh_link = 0,
1619 .sh_info = 0,
1620 .sh_addralign = 0,
1621 .sh_entsize = 0,
1622 });
1623
1624 try self.populateMissingMetadata();
1625
1626 return self;
1627}
16282340
1629/// Returns error.IncrFailed if incremental update could not be performed.2341 /// The reloc offset for the line offset of a function from the previous function's line.
1630fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {2342 /// It's a fixed-size 4-byte ULEB128.
1631 switch (options.output_mode) {2343 fn getRelocDbgLineOff(self: Elf) usize {
1632 .Exe => {},2344 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
1633 .Obj => {},2345 }
1634 .Lib => return error.IncrFailed,
1635 }
1636 switch (options.object_format) {
1637 .unknown => unreachable, // TODO remove this tag from the enum
1638 .c => unreachable,
1639 .coff => return error.IncrFailed,
1640 .elf => {},
1641 .macho => return error.IncrFailed,
1642 .wasm => return error.IncrFailed,
1643 .hex => return error.IncrFailed,
1644 .raw => return error.IncrFailed,
1645 }
1646 var self: File.Elf = .{
1647 .allocator = allocator,
1648 .file = file,
1649 .owns_file_handle = false,
1650 .options = options,
1651 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1652 32 => .p32,
1653 64 => .p64,
1654 else => return error.UnsupportedELFArchitecture,
1655 },
1656 };
1657 errdefer self.deinit();
16582346
1659 // TODO implement reading the elf file2347 fn getRelocDbgFileIndex(self: Elf) usize {
1660 return error.IncrFailed;2348 return self.getRelocDbgLineOff() + 5;
1661 //try self.populateMissingMetadata();2349 }
1662 //return self;2350
1663}2351 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2352 const directory_entry_format_count = 1;
2353 const file_name_entry_format_count = 1;
2354 const directory_count = 1;
2355 const file_name_count = 1;
2356 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
2357 directory_count * 8 + file_name_count * 8 +
2358 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
2359 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2360 self.base.options.root_pkg.root_src_dir_path.len +
2361 self.base.options.root_pkg.root_src_path.len);
2362
2363 }
2364
2365 /// 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 bytes
2367 /// 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 fixed
2369 /// amount by increasing the length of `vecs`).
2370 fn pwriteWithNops(
2371 self: *Elf,
2372 prev_padding_size: usize,
2373 buf: []const u8,
2374 next_padding_size: usize,
2375 offset: usize,
2376 ) !void {
2377 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};
2379 var vecs: [32]std.os.iovec_const = undefined;
2380 var vec_index: usize = 0;
2381 {
2382 var padding_left = prev_padding_size;
2383 if (padding_left % 2 != 0) {
2384 vecs[vec_index] = .{
2385 .iov_base = &three_byte_nop,
2386 .iov_len = three_byte_nop.len,
2387 };
2388 vec_index += 1;
2389 padding_left -= three_byte_nop.len;
2390 }
2391 while (padding_left > page_of_nops.len) {
2392 vecs[vec_index] = .{
2393 .iov_base = &page_of_nops,
2394 .iov_len = page_of_nops.len,
2395 };
2396 vec_index += 1;
2397 padding_left -= page_of_nops.len;
2398 }
2399 if (padding_left > 0) {
2400 vecs[vec_index] = .{
2401 .iov_base = &page_of_nops,
2402 .iov_len = padding_left,
2403 };
2404 vec_index += 1;
2405 }
2406 }
2407
2408 vecs[vec_index] = .{
2409 .iov_base = buf.ptr,
2410 .iov_len = buf.len,
2411 };
2412 vec_index += 1;
2413
2414 {
2415 var padding_left = next_padding_size;
2416 if (padding_left % 2 != 0) {
2417 vecs[vec_index] = .{
2418 .iov_base = &three_byte_nop,
2419 .iov_len = three_byte_nop.len,
2420 };
2421 vec_index += 1;
2422 padding_left -= three_byte_nop.len;
2423 }
2424 while (padding_left > page_of_nops.len) {
2425 vecs[vec_index] = .{
2426 .iov_base = &page_of_nops,
2427 .iov_len = page_of_nops.len,
2428 };
2429 vec_index += 1;
2430 padding_left -= page_of_nops.len;
2431 }
2432 if (padding_left > 0) {
2433 vecs[vec_index] = .{
2434 .iov_base = &page_of_nops,
2435 .iov_len = padding_left,
2436 };
2437 vec_index += 1;
2438 }
2439 }
2440 try self.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2441 }
2442
2443 const min_nop_size = 2;
2444
2445 };
2446};
16642447
1665/// Saturating multiplication2448/// Saturating multiplication
1666fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {2449fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
src-self-hosted/main.zig+12-13
...@@ -10,9 +10,7 @@ const Module = @import("Module.zig");...@@ -10,9 +10,7 @@ const Module = @import("Module.zig");
10const link = @import("link.zig");10const link = @import("link.zig");
11const Package = @import("Package.zig");11const Package = @import("Package.zig");
12const zir = @import("zir.zig");12const zir = @import("zir.zig");
1313const build_options = @import("build_options");
14// TODO Improve async I/O enough that we feel comfortable doing this.
15//pub const io_mode = .evented;
1614
17pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB15pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
1816
...@@ -47,18 +45,16 @@ pub fn log(...@@ -47,18 +45,16 @@ pub fn log(
47 if (@enumToInt(level) > @enumToInt(std.log.level))45 if (@enumToInt(level) > @enumToInt(std.log.level))
48 return;46 return;
4947
50 const scope_prefix = "(" ++ switch (scope) {48 const scope_name = @tagName(scope);
51 // Uncomment to hide logs49 const ok = comptime for (build_options.log_scopes) |log_scope| {
52 //.compiler,50 if (mem.eql(u8, log_scope, scope_name))
53 .module,51 break true;
54 .liveness,52 } else false;
55 .link,
56 => return,
5753
58 else => @tagName(scope),54 if (!ok)
59 } ++ "): ";55 return;
6056
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;57 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
6258
63 // Print the message to stderr, silently ignoring any errors59 // Print the message to stderr, silently ignoring any errors
64 std.debug.print(prefix ++ format, args);60 std.debug.print(prefix ++ format, args);
...@@ -94,6 +90,8 @@ pub fn main() !void {...@@ -94,6 +90,8 @@ pub fn main() !void {
94 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);90 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
95 } else if (mem.eql(u8, cmd, "version")) {91 } else if (mem.eql(u8, cmd, "version")) {
96 // Need to set up the build script to give the version as a comptime value.92 // Need to set up the build script to give the version as a comptime value.
93 // TODO when you solve this, also take a look at link.zig, there is a placeholder
94 // that says "TODO version here".
97 std.debug.print("TODO version command not implemented yet\n", .{});95 std.debug.print("TODO version command not implemented yet\n", .{});
98 return error.Unimplemented;96 return error.Unimplemented;
99 } else if (mem.eql(u8, cmd, "zen")) {97 } else if (mem.eql(u8, cmd, "zen")) {
...@@ -492,6 +490,7 @@ fn buildOutputType(...@@ -492,6 +490,7 @@ fn buildOutputType(
492 defer root_pkg.destroy();490 defer root_pkg.destroy();
493491
494 var module = try Module.init(gpa, .{492 var module = try Module.init(gpa, .{
493 .root_name = root_name,
495 .target = target_info.target,494 .target = target_info.target,
496 .output_mode = output_mode,495 .output_mode = output_mode,
497 .root_pkg = root_pkg,496 .root_pkg = root_pkg,
src-self-hosted/test.zig+67-15
...@@ -4,6 +4,11 @@ const Module = @import("Module.zig");...@@ -4,6 +4,11 @@ const Module = @import("Module.zig");
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const zir = @import("zir.zig");5const zir = @import("zir.zig");
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
7const build_options = @import("build_options");
8const enable_qemu: bool = build_options.enable_qemu;
9const enable_wine: bool = build_options.enable_wine;
10const enable_wasmtime: bool = build_options.enable_wasmtime;
11const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
712
8const cheader = @embedFile("cbe.h");13const cheader = @embedFile("cbe.h");
914
...@@ -401,8 +406,6 @@ pub const TestContext = struct {...@@ -401,8 +406,6 @@ pub const TestContext = struct {
401 const root_node = try progress.start("tests", self.cases.items.len);406 const root_node = try progress.start("tests", self.cases.items.len);
402 defer root_node.end();407 defer root_node.end();
403408
404 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
405
406 for (self.cases.items) |case| {409 for (self.cases.items) |case| {
407 std.testing.base_allocator_instance.reset();410 std.testing.base_allocator_instance.reset();
408411
...@@ -415,13 +418,19 @@ pub const TestContext = struct {...@@ -415,13 +418,19 @@ pub const TestContext = struct {
415 progress.initial_delay_ns = 0;418 progress.initial_delay_ns = 0;
416 progress.refresh_rate_ns = 0;419 progress.refresh_rate_ns = 0;
417420
418 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);421 try self.runOneCase(std.testing.allocator, &prg_node, case);
419 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
420 try std.testing.allocator_instance.validate();422 try std.testing.allocator_instance.validate();
421 }423 }
422 }424 }
423425
424 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {426 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);
428 const target = target_info.target;
429
430 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
431 defer arena_allocator.deinit();
432 const arena = &arena_allocator.allocator;
433
425 var tmp = std.testing.tmpDir(.{});434 var tmp = std.testing.tmpDir(.{});
426 defer tmp.cleanup();435 defer tmp.cleanup();
427436
...@@ -429,10 +438,10 @@ pub const TestContext = struct {...@@ -429,10 +438,10 @@ pub const TestContext = struct {
429 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);438 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
430 defer root_pkg.destroy();439 defer root_pkg.destroy();
431440
432 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);441 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
433 defer allocator.free(bin_name);
434442
435 var module = try Module.init(allocator, .{443 var module = try Module.init(allocator, .{
444 .root_name = "test_case",
436 .target = target,445 .target = target,
437 // TODO: support tests for object file building, and library builds446 // TODO: support tests for object file building, and library builds
438 // and linking. This will require a rework to support multi-file447 // and linking. This will require a rework to support multi-file
...@@ -484,8 +493,7 @@ pub const TestContext = struct {...@@ -484,8 +493,7 @@ pub const TestContext = struct {
484 // incremental updates493 // incremental updates
485 var file = try tmp.dir.openFile(bin_name, .{ .read = true });494 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
486 defer file.close();495 defer file.close();
487 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");496 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
488 defer allocator.free(out);
489497
490 if (expected_output.len != out.len) {498 if (expected_output.len != out.len) {
491 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });499 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
...@@ -532,8 +540,7 @@ pub const TestContext = struct {...@@ -532,8 +540,7 @@ pub const TestContext = struct {
532 var test_node = update_node.start("assert", null);540 var test_node = update_node.start("assert", null);
533 test_node.activate();541 test_node.activate();
534 defer test_node.end();542 defer test_node.end();
535 var handled_errors = try allocator.alloc(bool, e.len);543 var handled_errors = try arena.alloc(bool, e.len);
536 defer allocator.free(handled_errors);
537 for (handled_errors) |*h| {544 for (handled_errors) |*h| {
538 h.* = false;545 h.* = false;
539 }546 }
...@@ -568,14 +575,59 @@ pub const TestContext = struct {...@@ -568,14 +575,59 @@ pub const TestContext = struct {
568 exec_node.activate();575 exec_node.activate();
569 defer exec_node.end();576 defer exec_node.end();
570577
571 try module.makeBinFileExecutable();578 var argv = std.ArrayList([]const u8).init(allocator);
579 defer argv.deinit();
580
581 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
582
583 switch (case.target.getExternalExecutor()) {
584 .native => try argv.append(exe_path),
585 .unavailable => return, // No executor available; pass test.
586
587 .qemu => |qemu_bin_name| if (enable_qemu) {
588 // TODO Ability for test cases to specify whether to link libc.
589 const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
590 const glibc_dir_arg = if (need_cross_glibc)
591 glibc_multi_install_dir orelse return // glibc dir not available; pass test
592 else
593 null;
594 try argv.append(qemu_bin_name);
595 if (glibc_dir_arg) |dir| {
596 const linux_triple = try target.linuxTriple(arena);
597 const full_dir = try std.fs.path.join(arena, &[_][]const u8{
598 dir,
599 linux_triple,
600 });
601
602 try argv.append("-L");
603 try argv.append(full_dir);
604 }
605 try argv.append(exe_path);
606 } else {
607 return; // QEMU not available; pass test.
608 },
609
610 .wine => |wine_bin_name| if (enable_wine) {
611 try argv.append(wine_bin_name);
612 try argv.append(exe_path);
613 } else {
614 return; // Wine not available; pass test.
615 },
616
617 .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
618 try argv.append(wasmtime_bin_name);
619 try argv.append("--dir=.");
620 try argv.append(exe_path);
621 } else {
622 return; // wasmtime not available; pass test.
623 },
624 }
572625
573 const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});626 try module.makeBinFileExecutable();
574 defer allocator.free(exe_path);
575627
576 break :x try std.ChildProcess.exec(.{628 break :x try std.ChildProcess.exec(.{
577 .allocator = allocator,629 .allocator = allocator,
578 .argv = &[_][]const u8{exe_path},630 .argv = argv.items,
579 .cwd_dir = tmp.dir,631 .cwd_dir = tmp.dir,
580 });632 });
581 };633 };
src-self-hosted/translate_c.zig+245-200
...@@ -8,7 +8,6 @@ const Token = std.zig.Token;...@@ -8,7 +8,6 @@ const Token = std.zig.Token;
8usingnamespace @import("clang.zig");8usingnamespace @import("clang.zig");
9const ctok = std.c.tokenizer;9const ctok = std.c.tokenizer;
10const CToken = std.c.Token;10const CToken = std.c.Token;
11const CTokenList = std.c.tokenizer.Source.TokenList;
12const mem = std.mem;11const mem = std.mem;
13const math = std.math;12const math = std.math;
1413
...@@ -2864,7 +2863,6 @@ fn transCharLiteral(...@@ -2864,7 +2863,6 @@ fn transCharLiteral(
2864 "TODO: support character literal kind {}",2863 "TODO: support character literal kind {}",
2865 .{kind},2864 .{kind},
2866 ),2865 ),
2867 else => unreachable,
2868 };2866 };
2869 if (suppress_as == .no_as) {2867 if (suppress_as == .no_as) {
2870 return maybeSuppressResult(rp, scope, result_used, int_lit_node);2868 return maybeSuppressResult(rp, scope, result_used, int_lit_node);
...@@ -3070,13 +3068,30 @@ fn transUnaryExprOrTypeTraitExpr(...@@ -3070,13 +3068,30 @@ fn transUnaryExprOrTypeTraitExpr(
3070 stmt: *const ZigClangUnaryExprOrTypeTraitExpr,3068 stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
3071 result_used: ResultUsed,3069 result_used: ResultUsed,
3072) TransError!*ast.Node {3070) TransError!*ast.Node {
3071 const loc = ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt);
3073 const type_node = try transQualType(3072 const type_node = try transQualType(
3074 rp,3073 rp,
3075 ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),3074 ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
3076 ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt),3075 loc,
3077 );3076 );
30783077
3079 const builtin_node = try rp.c.createBuiltinCall("@sizeOf", 1);3078 const kind = ZigClangUnaryExprOrTypeTraitExpr_getKind(stmt);
3079 const kind_str = switch (kind) {
3080 .SizeOf => "@sizeOf",
3081 .AlignOf => "@alignOf",
3082 .PreferredAlignOf,
3083 .VecStep,
3084 .OpenMPRequiredSimdAlign,
3085 => return revertAndWarn(
3086 rp,
3087 error.UnsupportedTranslation,
3088 loc,
3089 "Unsupported type trait kind {}",
3090 .{kind},
3091 ),
3092 };
3093
3094 const builtin_node = try rp.c.createBuiltinCall(kind_str, 1);
3080 builtin_node.params()[0] = type_node;3095 builtin_node.params()[0] = type_node;
3081 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");3096 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3082 return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);3097 return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
...@@ -4515,7 +4530,7 @@ const CtrlFlow = struct {...@@ -4515,7 +4530,7 @@ const CtrlFlow = struct {
4515 const ltoken = try appendToken(c, kw, kw_text);4530 const ltoken = try appendToken(c, kw, kw_text);
4516 const label_token = if (label) |l| blk: {4531 const label_token = if (label) |l| blk: {
4517 _ = try appendToken(c, .Colon, ":");4532 _ = try appendToken(c, .Colon, ":");
4518 break :blk try appendToken(c, .Identifier, l);4533 break :blk try appendIdentifier(c, l);
4519 } else null;4534 } else null;
4520 return CtrlFlow{4535 return CtrlFlow{
4521 .c = c,4536 .c = c,
...@@ -5197,16 +5212,39 @@ pub fn freeErrors(errors: []ClangErrMsg) void {...@@ -5197,16 +5212,39 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
5197 ZigClangErrorMsg_delete(errors.ptr, errors.len);5212 ZigClangErrorMsg_delete(errors.ptr, errors.len);
5198}5213}
51995214
5215const CTokIterator = struct {
5216 source: []const u8,
5217 list: []const CToken,
5218 i: usize = 0,
5219
5220 fn peek(self: *CTokIterator) ?CToken.Id {
5221 if (self.i >= self.list.len) return null;
5222 return self.list[self.i + 1].id;
5223 }
5224
5225 fn next(self: *CTokIterator) ?CToken.Id {
5226 if (self.i >= self.list.len) return null;
5227 self.i += 1;
5228 return self.list[self.i].id;
5229 }
5230
5231 fn slice(self: *CTokIterator, index: usize) []const u8 {
5232 const tok = self.list[index];
5233 return self.source[tok.start..tok.end];
5234 }
5235};
5236
5200fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {5237fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5201 // TODO if we see #undef, delete it from the table5238 // TODO if we see #undef, delete it from the table
5202 var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);5239 var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
5203 const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);5240 const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);
5204 var tok_list = CTokenList.init(c.arena);5241 var tok_list = std.ArrayList(CToken).init(c.gpa);
5242 defer tok_list.deinit();
5205 const scope = c.global_scope;5243 const scope = c.global_scope;
52065244
5207 while (it.I != it_end.I) : (it.I += 1) {5245 while (it.I != it_end.I) : (it.I += 1) {
5208 const entity = ZigClangPreprocessingRecord_iterator_deref(it);5246 const entity = ZigClangPreprocessingRecord_iterator_deref(it);
5209 tok_list.shrink(0);5247 tok_list.items.len = 0;
5210 switch (ZigClangPreprocessedEntity_getKind(entity)) {5248 switch (ZigClangPreprocessedEntity_getKind(entity)) {
5211 .MacroDefinitionKind => {5249 .MacroDefinitionKind => {
5212 const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);5250 const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
...@@ -5224,38 +5262,34 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5224,38 +5262,34 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5224 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);5262 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
5225 const slice = begin_c[0..mem.len(begin_c)];5263 const slice = begin_c[0..mem.len(begin_c)];
52265264
5227 tok_list.shrink(0);
5228 var tokenizer = std.c.Tokenizer{5265 var tokenizer = std.c.Tokenizer{
5229 .source = &std.c.tokenizer.Source{5266 .buffer = slice,
5230 .buffer = slice,
5231 .file_name = undefined,
5232 .tokens = undefined,
5233 },
5234 };5267 };
5235 while (true) {5268 while (true) {
5236 const tok = tokenizer.next();5269 const tok = tokenizer.next();
5237 switch (tok.id) {5270 switch (tok.id) {
5238 .Nl, .Eof => {5271 .Nl, .Eof => {
5239 try tok_list.push(tok);5272 try tok_list.append(tok);
5240 break;5273 break;
5241 },5274 },
5242 .LineComment, .MultiLineComment => continue,5275 .LineComment, .MultiLineComment => continue,
5243 else => {},5276 else => {},
5244 }5277 }
5245 try tok_list.push(tok);5278 try tok_list.append(tok);
5246 }5279 }
52475280
5248 var tok_it = tok_list.iterator(0);5281 var tok_it = CTokIterator{
5249 const first_tok = tok_it.next().?;5282 .source = slice,
5250 assert(mem.eql(u8, slice[first_tok.start..first_tok.end], name));5283 .list = tok_list.items,
5284 };
5285 assert(mem.eql(u8, tok_it.slice(0), name));
52515286
5252 var macro_fn = false;5287 var macro_fn = false;
5253 const next = tok_it.peek().?;5288 switch (tok_it.peek().?) {
5254 switch (next.id) {
5255 .Identifier => {5289 .Identifier => {
5256 // if it equals itself, ignore. for example, from stdio.h:5290 // if it equals itself, ignore. for example, from stdio.h:
5257 // #define stdin stdin5291 // #define stdin stdin
5258 if (mem.eql(u8, name, slice[next.start..next.end])) {5292 if (mem.eql(u8, name, tok_it.slice(1))) {
5259 continue;5293 continue;
5260 }5294 }
5261 },5295 },
...@@ -5266,15 +5300,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5266,15 +5300,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5266 },5300 },
5267 .LParen => {5301 .LParen => {
5268 // if the name is immediately followed by a '(' then it is a function5302 // if the name is immediately followed by a '(' then it is a function
5269 macro_fn = first_tok.end == next.start;5303 macro_fn = tok_it.list[0].end == tok_it.list[1].start;
5270 },5304 },
5271 else => {},5305 else => {},
5272 }5306 }
52735307
5274 (if (macro_fn)5308 (if (macro_fn)
5275 transMacroFnDefine(c, &tok_it, slice, mangled_name, begin_loc)5309 transMacroFnDefine(c, &tok_it, mangled_name, begin_loc)
5276 else5310 else
5277 transMacroDefine(c, &tok_it, slice, mangled_name, begin_loc)) catch |err| switch (err) {5311 transMacroDefine(c, &tok_it, mangled_name, begin_loc)) catch |err| switch (err) {
5278 error.ParseError => continue,5312 error.ParseError => continue,
5279 error.OutOfMemory => |e| return e,5313 error.OutOfMemory => |e| return e,
5280 };5314 };
...@@ -5284,7 +5318,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5284,7 +5318,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5284 }5318 }
5285}5319}
52865320
5287fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {5321fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
5288 const scope = &c.global_scope.base;5322 const scope = &c.global_scope.base;
52895323
5290 const visib_tok = try appendToken(c, .Keyword_pub, "pub");5324 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
...@@ -5292,15 +5326,15 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n...@@ -5292,15 +5326,15 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
5292 const name_tok = try appendIdentifier(c, name);5326 const name_tok = try appendIdentifier(c, name);
5293 const eq_token = try appendToken(c, .Equal, "=");5327 const eq_token = try appendToken(c, .Equal, "=");
52945328
5295 const init_node = try parseCExpr(c, it, source, source_loc, scope);5329 const init_node = try parseCExpr(c, it, source_loc, scope);
5296 const last = it.next().?;5330 const last = it.next().?;
5297 if (last.id != .Eof and last.id != .Nl)5331 if (last != .Eof and last != .Nl)
5298 return failDecl(5332 return failDecl(
5299 c,5333 c,
5300 source_loc,5334 source_loc,
5301 name,5335 name,
5302 "unable to translate C expr: unexpected token .{}",5336 "unable to translate C expr: unexpected token .{}",
5303 .{@tagName(last.id)},5337 .{@tagName(last)},
5304 );5338 );
53055339
5306 const semicolon_token = try appendToken(c, .Semicolon, ";");5340 const semicolon_token = try appendToken(c, .Semicolon, ";");
...@@ -5316,7 +5350,7 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n...@@ -5316,7 +5350,7 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
5316 _ = try c.global_scope.macro_table.put(name, &node.base);5350 _ = try c.global_scope.macro_table.put(name, &node.base);
5317}5351}
53185352
5319fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {5353fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
5320 var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);5354 var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
5321 defer block_scope.deinit();5355 defer block_scope.deinit();
5322 const scope = &block_scope.base;5356 const scope = &block_scope.base;
...@@ -5327,7 +5361,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5327,7 +5361,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5327 const name_tok = try appendIdentifier(c, name);5361 const name_tok = try appendIdentifier(c, name);
5328 _ = try appendToken(c, .LParen, "(");5362 _ = try appendToken(c, .LParen, "(");
53295363
5330 if (it.next().?.id != .LParen) {5364 if (it.next().? != .LParen) {
5331 return failDecl(5365 return failDecl(
5332 c,5366 c,
5333 source_loc,5367 source_loc,
...@@ -5341,8 +5375,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5341,8 +5375,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5341 defer fn_params.deinit();5375 defer fn_params.deinit();
53425376
5343 while (true) {5377 while (true) {
5344 const param_tok = it.next().?;5378 if (it.next().? != .Identifier) {
5345 if (param_tok.id != .Identifier) {
5346 return failDecl(5379 return failDecl(
5347 c,5380 c,
5348 source_loc,5381 source_loc,
...@@ -5352,7 +5385,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5352,7 +5385,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5352 );5385 );
5353 }5386 }
53545387
5355 const mangled_name = try block_scope.makeMangledName(c, source[param_tok.start..param_tok.end]);5388 const mangled_name = try block_scope.makeMangledName(c, it.slice(it.i));
5356 const param_name_tok = try appendIdentifier(c, mangled_name);5389 const param_name_tok = try appendIdentifier(c, mangled_name);
5357 _ = try appendToken(c, .Colon, ":");5390 _ = try appendToken(c, .Colon, ":");
53585391
...@@ -5370,13 +5403,13 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5370,13 +5403,13 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5370 .param_type = .{ .any_type = &any_type.base },5403 .param_type = .{ .any_type = &any_type.base },
5371 };5404 };
53725405
5373 if (it.peek().?.id != .Comma)5406 if (it.peek().? != .Comma)
5374 break;5407 break;
5375 _ = it.next();5408 _ = it.next();
5376 _ = try appendToken(c, .Comma, ",");5409 _ = try appendToken(c, .Comma, ",");
5377 }5410 }
53785411
5379 if (it.next().?.id != .RParen) {5412 if (it.next().? != .RParen) {
5380 return failDecl(5413 return failDecl(
5381 c,5414 c,
5382 source_loc,5415 source_loc,
...@@ -5391,15 +5424,15 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5391,15 +5424,15 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5391 const type_of = try c.createBuiltinCall("@TypeOf", 1);5424 const type_of = try c.createBuiltinCall("@TypeOf", 1);
53925425
5393 const return_kw = try appendToken(c, .Keyword_return, "return");5426 const return_kw = try appendToken(c, .Keyword_return, "return");
5394 const expr = try parseCExpr(c, it, source, source_loc, scope);5427 const expr = try parseCExpr(c, it, source_loc, scope);
5395 const last = it.next().?;5428 const last = it.next().?;
5396 if (last.id != .Eof and last.id != .Nl)5429 if (last != .Eof and last != .Nl)
5397 return failDecl(5430 return failDecl(
5398 c,5431 c,
5399 source_loc,5432 source_loc,
5400 name,5433 name,
5401 "unable to translate C expr: unexpected token .{}",5434 "unable to translate C expr: unexpected token .{}",
5402 .{@tagName(last.id)},5435 .{@tagName(last)},
5403 );5436 );
5404 _ = try appendToken(c, .Semicolon, ";");5437 _ = try appendToken(c, .Semicolon, ";");
5405 const type_of_arg = if (expr.tag != .Block) expr else blk: {5438 const type_of_arg = if (expr.tag != .Block) expr else blk: {
...@@ -5436,28 +5469,27 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5436,28 +5469,27 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
54365469
5437const ParseError = Error || error{ParseError};5470const ParseError = Error || error{ParseError};
54385471
5439fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {5472fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5440 const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5473 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
5441 switch (it.next().?.id) {5474 switch (it.next().?) {
5442 .QuestionMark => {5475 .QuestionMark => {
5443 // must come immediately after expr5476 // must come immediately after expr
5444 _ = try appendToken(c, .RParen, ")");5477 _ = try appendToken(c, .RParen, ")");
5445 const if_node = try transCreateNodeIf(c);5478 const if_node = try transCreateNodeIf(c);
5446 if_node.condition = node;5479 if_node.condition = node;
5447 if_node.body = try parseCPrimaryExpr(c, it, source, source_loc, scope);5480 if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);
5448 if (it.next().?.id != .Colon) {5481 if (it.next().? != .Colon) {
5449 const first_tok = it.list.at(0);
5450 try failDecl(5482 try failDecl(
5451 c,5483 c,
5452 source_loc,5484 source_loc,
5453 source[first_tok.start..first_tok.end],5485 it.slice(0),
5454 "unable to translate C expr: expected ':'",5486 "unable to translate C expr: expected ':'",
5455 .{},5487 .{},
5456 );5488 );
5457 return error.ParseError;5489 return error.ParseError;
5458 }5490 }
5459 if_node.@"else" = try transCreateNodeElse(c);5491 if_node.@"else" = try transCreateNodeElse(c);
5460 if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source, source_loc, scope);5492 if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope);
5461 return &if_node.base;5493 return &if_node.base;
5462 },5494 },
5463 .Comma => {5495 .Comma => {
...@@ -5480,10 +5512,10 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_...@@ -5480,10 +5512,10 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
5480 };5512 };
5481 try block_scope.statements.append(&op_node.base);5513 try block_scope.statements.append(&op_node.base);
54825514
5483 last = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5515 last = try parseCPrefixOpExpr(c, it, source_loc, scope);
5484 _ = try appendToken(c, .Semicolon, ";");5516 _ = try appendToken(c, .Semicolon, ";");
5485 if (it.next().?.id != .Comma) {5517 if (it.next().? != .Comma) {
5486 _ = it.prev();5518 it.i -= 1;
5487 break;5519 break;
5488 }5520 }
5489 }5521 }
...@@ -5494,70 +5526,74 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_...@@ -5494,70 +5526,74 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
5494 return &block_node.base;5526 return &block_node.base;
5495 },5527 },
5496 else => {5528 else => {
5497 _ = it.prev();5529 it.i -= 1;
5498 return node;5530 return node;
5499 },5531 },
5500 }5532 }
5501}5533}
55025534
5503fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {5535fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
5504 var lit_bytes = source[tok.start..tok.end];5536 var lit_bytes = it.slice(it.i);
55055537
5506 if (tok.id == .IntegerLiteral) {5538 switch (it.list[it.i].id) {
5507 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {5539 .IntegerLiteral => |suffix| {
5508 switch (lit_bytes[1]) {5540 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
5509 '0'...'7' => {5541 switch (lit_bytes[1]) {
5510 // Octal5542 '0'...'7' => {
5511 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});5543 // Octal
5512 },5544 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
5513 'X' => {5545 },
5514 // Hexadecimal with capital X, valid in C but not in Zig5546 'X' => {
5515 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});5547 // Hexadecimal with capital X, valid in C but not in Zig
5516 },5548 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
5517 else => {},5549 },
5550 else => {},
5551 }
5518 }5552 }
5519 }
55205553
5521 if (tok.id.IntegerLiteral == .None) {5554 if (suffix == .none) {
5522 return transCreateNodeInt(c, lit_bytes);5555 return transCreateNodeInt(c, lit_bytes);
5523 }5556 }
55245557
5525 const cast_node = try c.createBuiltinCall("@as", 2);5558 const cast_node = try c.createBuiltinCall("@as", 2);
5526 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.IntegerLiteral) {5559 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5527 .U => "c_uint",5560 .u => "c_uint",
5528 .L => "c_long",5561 .l => "c_long",
5529 .LU => "c_ulong",5562 .lu => "c_ulong",
5530 .LL => "c_longlong",5563 .ll => "c_longlong",
5531 .LLU => "c_ulonglong",5564 .llu => "c_ulonglong",
5532 else => unreachable,5565 else => unreachable,
5533 });5566 });
5534 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (tok.id.IntegerLiteral) {5567 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
5535 .U, .L => @as(u8, 1),5568 .u, .l => @as(u8, 1),
5536 .LU, .LL => 2,5569 .lu, .ll => 2,
5537 .LLU => 3,5570 .llu => 3,
5538 else => unreachable,5571 else => unreachable,
5539 }];5572 }];
5540 _ = try appendToken(c, .Comma, ",");5573 _ = try appendToken(c, .Comma, ",");
5541 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);5574 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
5542 cast_node.rparen_token = try appendToken(c, .RParen, ")");5575 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5543 return &cast_node.base;5576 return &cast_node.base;
5544 } else if (tok.id == .FloatLiteral) {5577 },
5545 if (lit_bytes[0] == '.')5578 .FloatLiteral => |suffix| {
5546 lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});5579 if (lit_bytes[0] == '.')
5547 if (tok.id.FloatLiteral == .None) {5580 lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
5548 return transCreateNodeFloat(c, lit_bytes);5581 if (suffix == .none) {
5549 }5582 return transCreateNodeFloat(c, lit_bytes);
5550 const cast_node = try c.createBuiltinCall("@as", 2);5583 }
5551 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.FloatLiteral) {5584 const cast_node = try c.createBuiltinCall("@as", 2);
5552 .F => "f32",5585 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5553 .L => "c_longdouble",5586 .f => "f32",
5554 else => unreachable,5587 .l => "c_longdouble",
5555 });5588 else => unreachable,
5556 _ = try appendToken(c, .Comma, ",");5589 });
5557 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);5590 _ = try appendToken(c, .Comma, ",");
5558 cast_node.rparen_token = try appendToken(c, .RParen, ")");5591 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
5559 return &cast_node.base;5592 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5560 } else unreachable;5593 return &cast_node.base;
5594 },
5595 else => unreachable,
5596 }
5561}5597}
55625598
5563fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ![]const u8 {5599fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ![]const u8 {
...@@ -5720,13 +5756,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const...@@ -5720,13 +5756,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
5720 return bytes[0..i];5756 return bytes[0..i];
5721}5757}
57225758
5723fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {5759fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5724 const tok = it.next().?;5760 const tok = it.next().?;
5725 switch (tok.id) {5761 const slice = it.slice(it.i);
5762 switch (tok) {
5726 .CharLiteral => {5763 .CharLiteral => {
5727 const first_tok = it.list.at(0);5764 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
5728 if (source[tok.start] != '\'' or source[tok.start + 1] == '\\' or tok.end - tok.start == 3) {5765 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
5729 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
5730 const node = try c.arena.create(ast.Node.OneToken);5766 const node = try c.arena.create(ast.Node.OneToken);
5731 node.* = .{5767 node.* = .{
5732 .base = .{ .tag = .CharLiteral },5768 .base = .{ .tag = .CharLiteral },
...@@ -5734,7 +5770,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5734,7 +5770,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5734 };5770 };
5735 return &node.base;5771 return &node.base;
5736 } else {5772 } else {
5737 const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{source[tok.start + 1 .. tok.end - 1]});5773 const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
5738 const node = try c.arena.create(ast.Node.OneToken);5774 const node = try c.arena.create(ast.Node.OneToken);
5739 node.* = .{5775 node.* = .{
5740 .base = .{ .tag = .IntegerLiteral },5776 .base = .{ .tag = .IntegerLiteral },
...@@ -5744,8 +5780,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5744,8 +5780,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5744 }5780 }
5745 },5781 },
5746 .StringLiteral => {5782 .StringLiteral => {
5747 const first_tok = it.list.at(0);5783 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
5748 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
5749 const node = try c.arena.create(ast.Node.OneToken);5784 const node = try c.arena.create(ast.Node.OneToken);
5750 node.* = .{5785 node.* = .{
5751 .base = .{ .tag = .StringLiteral },5786 .base = .{ .tag = .StringLiteral },
...@@ -5754,7 +5789,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5754,7 +5789,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5754 return &node.base;5789 return &node.base;
5755 },5790 },
5756 .IntegerLiteral, .FloatLiteral => {5791 .IntegerLiteral, .FloatLiteral => {
5757 return parseCNumLit(c, tok, source, source_loc);5792 return parseCNumLit(c, it, source_loc);
5758 },5793 },
5759 // eventually this will be replaced by std.c.parse which will handle these correctly5794 // eventually this will be replaced by std.c.parse which will handle these correctly
5760 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),5795 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
...@@ -5764,22 +5799,50 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5764,22 +5799,50 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5764 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),5799 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
5765 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),5800 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
5766 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),5801 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
5767 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "c_char"),5802 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
5768 .Keyword_unsigned => return transCreateNodeIdentifierUnchecked(c, "c_uint"),5803 .Keyword_unsigned => if (it.next()) |t| switch (t) {
5804 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
5805 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
5806 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
5807 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
5808 _ = it.next();
5809 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
5810 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
5811 else => {
5812 it.i -= 1;
5813 return transCreateNodeIdentifierUnchecked(c, "c_uint");
5814 },
5815 } else {
5816 return transCreateNodeIdentifierUnchecked(c, "c_uint");
5817 },
5818 .Keyword_signed => if (it.next()) |t| switch (t) {
5819 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
5820 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
5821 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
5822 .Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
5823 _ = it.next();
5824 return transCreateNodeIdentifierUnchecked(c, "c_longlong");
5825 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
5826 else => {
5827 it.i -= 1;
5828 return transCreateNodeIdentifierUnchecked(c, "c_int");
5829 },
5830 } else {
5831 return transCreateNodeIdentifierUnchecked(c, "c_int");
5832 },
5769 .Identifier => {5833 .Identifier => {
5770 const mangled_name = scope.getAlias(source[tok.start..tok.end]);5834 const mangled_name = scope.getAlias(it.slice(it.i));
5771 return transCreateNodeIdentifier(c, mangled_name);5835 return transCreateNodeIdentifier(c, mangled_name);
5772 },5836 },
5773 .LParen => {5837 .LParen => {
5774 const inner_node = try parseCExpr(c, it, source, source_loc, scope);5838 const inner_node = try parseCExpr(c, it, source_loc, scope);
57755839
5776 const next_id = it.next().?.id;5840 const next_id = it.next().?;
5777 if (next_id != .RParen) {5841 if (next_id != .RParen) {
5778 const first_tok = it.list.at(0);
5779 try failDecl(5842 try failDecl(
5780 c,5843 c,
5781 source_loc,5844 source_loc,
5782 source[first_tok.start..first_tok.end],5845 it.slice(0),
5783 "unable to translate C expr: expected ')'' instead got: {}",5846 "unable to translate C expr: expected ')'' instead got: {}",
5784 .{@tagName(next_id)},5847 .{@tagName(next_id)},
5785 );5848 );
...@@ -5787,7 +5850,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5787,7 +5850,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5787 }5850 }
5788 var saw_l_paren = false;5851 var saw_l_paren = false;
5789 var saw_integer_literal = false;5852 var saw_integer_literal = false;
5790 switch (it.peek().?.id) {5853 switch (it.peek().?) {
5791 // (type)(to_cast)5854 // (type)(to_cast)
5792 .LParen => {5855 .LParen => {
5793 saw_l_paren = true;5856 saw_l_paren = true;
...@@ -5805,14 +5868,13 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5805,14 +5868,13 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5805 // hack to get zig fmt to render a comma in builtin calls5868 // hack to get zig fmt to render a comma in builtin calls
5806 _ = try appendToken(c, .Comma, ",");5869 _ = try appendToken(c, .Comma, ",");
58075870
5808 const node_to_cast = try parseCExpr(c, it, source, source_loc, scope);5871 const node_to_cast = try parseCExpr(c, it, source_loc, scope);
58095872
5810 if (saw_l_paren and it.next().?.id != .RParen) {5873 if (saw_l_paren and it.next().? != .RParen) {
5811 const first_tok = it.list.at(0);
5812 try failDecl(5874 try failDecl(
5813 c,5875 c,
5814 source_loc,5876 source_loc,
5815 source[first_tok.start..first_tok.end],5877 it.slice(0),
5816 "unable to translate C expr: expected ')''",5878 "unable to translate C expr: expected ')''",
5817 .{},5879 .{},
5818 );5880 );
...@@ -5843,13 +5905,12 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5843,13 +5905,12 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5843 return &group_node.base;5905 return &group_node.base;
5844 },5906 },
5845 else => {5907 else => {
5846 const first_tok = it.list.at(0);
5847 try failDecl(5908 try failDecl(
5848 c,5909 c,
5849 source_loc,5910 source_loc,
5850 source[first_tok.start..first_tok.end],5911 it.slice(0),
5851 "unable to translate C expr: unexpected token .{}",5912 "unable to translate C expr: unexpected token .{}",
5852 .{@tagName(tok.id)},5913 .{@tagName(tok)},
5853 );5914 );
5854 return error.ParseError;5915 return error.ParseError;
5855 },5916 },
...@@ -5957,61 +6018,52 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {...@@ -5957,61 +6018,52 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
5957 return &group_node.base;6018 return &group_node.base;
5958}6019}
59596020
5960fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {6021fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5961 var node = try parseCPrimaryExpr(c, it, source, source_loc, scope);6022 var node = try parseCPrimaryExpr(c, it, source_loc, scope);
5962 while (true) {6023 while (true) {
5963 const tok = it.next().?;
5964 var op_token: ast.TokenIndex = undefined;6024 var op_token: ast.TokenIndex = undefined;
5965 var op_id: ast.Node.Tag = undefined;6025 var op_id: ast.Node.Tag = undefined;
5966 var bool_op = false;6026 var bool_op = false;
5967 switch (tok.id) {6027 switch (it.next().?) {
5968 .Period => {6028 .Period => {
5969 const name_tok = it.next().?;6029 if (it.next().? != .Identifier) {
5970 if (name_tok.id != .Identifier) {
5971 const first_tok = it.list.at(0);
5972 try failDecl(6030 try failDecl(
5973 c,6031 c,
5974 source_loc,6032 source_loc,
5975 source[first_tok.start..first_tok.end],6033 it.slice(0),
5976 "unable to translate C expr: expected identifier",6034 "unable to translate C expr: expected identifier",
5977 .{},6035 .{},
5978 );6036 );
5979 return error.ParseError;6037 return error.ParseError;
5980 }6038 }
59816039
5982 node = try transCreateNodeFieldAccess(c, node, source[name_tok.start..name_tok.end]);6040 node = try transCreateNodeFieldAccess(c, node, it.slice(it.i));
5983 continue;6041 continue;
5984 },6042 },
5985 .Arrow => {6043 .Arrow => {
5986 const name_tok = it.next().?;6044 if (it.next().? != .Identifier) {
5987 if (name_tok.id != .Identifier) {
5988 const first_tok = it.list.at(0);
5989 try failDecl(6045 try failDecl(
5990 c,6046 c,
5991 source_loc,6047 source_loc,
5992 source[first_tok.start..first_tok.end],6048 it.slice(0),
5993 "unable to translate C expr: expected identifier",6049 "unable to translate C expr: expected identifier",
5994 .{},6050 .{},
5995 );6051 );
5996 return error.ParseError;6052 return error.ParseError;
5997 }6053 }
5998 const deref = try transCreateNodePtrDeref(c, node);6054 const deref = try transCreateNodePtrDeref(c, node);
5999 node = try transCreateNodeFieldAccess(c, deref, source[name_tok.start..name_tok.end]);6055 node = try transCreateNodeFieldAccess(c, deref, it.slice(it.i));
6000 continue;6056 continue;
6001 },6057 },
6002 .Asterisk => {6058 .Asterisk => {
6003 if (it.peek().?.id == .RParen) {6059 if (it.peek().? == .RParen) {
6004 // type *)6060 // type *)
60056061
6006 // hack to get zig fmt to render a comma in builtin calls6062 // hack to get zig fmt to render a comma in builtin calls
6007 _ = try appendToken(c, .Comma, ",");6063 _ = try appendToken(c, .Comma, ",");
60086064
6009 // * token
6010 _ = it.prev();
6011 // last token of `node`6065 // last token of `node`
6012 const prev_id = it.prev().?.id;6066 const prev_id = it.list[it.i - 1].id;
6013 _ = it.next();
6014 _ = it.next();
60156067
6016 if (prev_id == .Keyword_void) {6068 if (prev_id == .Keyword_void) {
6017 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);6069 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
...@@ -6082,15 +6134,14 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6082,15 +6134,14 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6082 },6134 },
6083 .LBracket => {6135 .LBracket => {
6084 const arr_node = try transCreateNodeArrayAccess(c, node);6136 const arr_node = try transCreateNodeArrayAccess(c, node);
6085 arr_node.index_expr = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6137 arr_node.index_expr = try parseCPrefixOpExpr(c, it, source_loc, scope);
6086 arr_node.rtoken = try appendToken(c, .RBracket, "]");6138 arr_node.rtoken = try appendToken(c, .RBracket, "]");
6087 node = &arr_node.base;6139 node = &arr_node.base;
6088 if (it.next().?.id != .RBracket) {6140 if (it.next().? != .RBracket) {
6089 const first_tok = it.list.at(0);
6090 try failDecl(6141 try failDecl(
6091 c,6142 c,
6092 source_loc,6143 source_loc,
6093 source[first_tok.start..first_tok.end],6144 it.slice(0),
6094 "unable to translate C expr: expected ']'",6145 "unable to translate C expr: expected ']'",
6095 .{},6146 .{},
6096 );6147 );
...@@ -6103,23 +6154,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6103,23 +6154,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6103 var call_params = std.ArrayList(*ast.Node).init(c.gpa);6154 var call_params = std.ArrayList(*ast.Node).init(c.gpa);
6104 defer call_params.deinit();6155 defer call_params.deinit();
6105 while (true) {6156 while (true) {
6106 const arg = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6157 const arg = try parseCPrefixOpExpr(c, it, source_loc, scope);
6107 try call_params.append(arg);6158 try call_params.append(arg);
6108 const next = it.next().?;6159 switch (it.next().?) {
6109 if (next.id == .Comma)6160 .Comma => _ = try appendToken(c, .Comma, ","),
6110 _ = try appendToken(c, .Comma, ",")6161 .RParen => break,
6111 else if (next.id == .RParen)6162 else => {
6112 break6163 try failDecl(
6113 else {6164 c,
6114 const first_tok = it.list.at(0);6165 source_loc,
6115 try failDecl(6166 it.slice(0),
6116 c,6167 "unable to translate C expr: expected ',' or ')'",
6117 source_loc,6168 .{},
6118 source[first_tok.start..first_tok.end],6169 );
6119 "unable to translate C expr: expected ',' or ')'",6170 return error.ParseError;
6120 .{},6171 },
6121 );
6122 return error.ParseError;
6123 }6172 }
6124 }6173 }
6125 const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);6174 const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
...@@ -6144,23 +6193,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6144,23 +6193,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6144 defer init_vals.deinit();6193 defer init_vals.deinit();
61456194
6146 while (true) {6195 while (true) {
6147 const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6196 const val = try parseCPrefixOpExpr(c, it, source_loc, scope);
6148 try init_vals.append(val);6197 try init_vals.append(val);
6149 const next = it.next().?;6198 switch (it.next().?) {
6150 if (next.id == .Comma)6199 .Comma => _ = try appendToken(c, .Comma, ","),
6151 _ = try appendToken(c, .Comma, ",")6200 .RBrace => break,
6152 else if (next.id == .RBrace)6201 else => {
6153 break6202 try failDecl(
6154 else {6203 c,
6155 const first_tok = it.list.at(0);6204 source_loc,
6156 try failDecl(6205 it.slice(0),
6157 c,6206 "unable to translate C expr: expected ',' or '}}'",
6158 source_loc,6207 .{},
6159 source[first_tok.start..first_tok.end],6208 );
6160 "unable to translate C expr: expected ',' or '}}'",6209 return error.ParseError;
6161 .{},6210 },
6162 );
6163 return error.ParseError;
6164 }6211 }
6165 }6212 }
6166 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);6213 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
...@@ -6207,22 +6254,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6207,22 +6254,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6207 op_id = .ArrayCat;6254 op_id = .ArrayCat;
6208 op_token = try appendToken(c, .PlusPlus, "++");6255 op_token = try appendToken(c, .PlusPlus, "++");
62096256
6210 _ = it.prev();6257 it.i -= 1;
6211 },6258 },
6212 .Identifier => {6259 .Identifier => {
6213 op_id = .ArrayCat;6260 op_id = .ArrayCat;
6214 op_token = try appendToken(c, .PlusPlus, "++");6261 op_token = try appendToken(c, .PlusPlus, "++");
62156262
6216 _ = it.prev();6263 it.i -= 1;
6217 },6264 },
6218 else => {6265 else => {
6219 _ = it.prev();6266 it.i -= 1;
6220 return node;6267 return node;
6221 },6268 },
6222 }6269 }
6223 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;6270 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
6224 const lhs_node = try cast_fn(c, node);6271 const lhs_node = try cast_fn(c, node);
6225 const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6272 const rhs_node = try parseCPrefixOpExpr(c, it, source_loc, scope);
6226 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);6273 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6227 op_node.* = .{6274 op_node.* = .{
6228 .base = .{ .tag = op_id },6275 .base = .{ .tag = op_id },
...@@ -6234,38 +6281,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6234,38 +6281,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6234 }6281 }
6235}6282}
62366283
6237fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {6284fn parseCPrefixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6238 const op_tok = it.next().?;6285 switch (it.next().?) {
6239
6240 switch (op_tok.id) {
6241 .Bang => {6286 .Bang => {
6242 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");6287 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
6243 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6288 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6244 return &node.base;6289 return &node.base;
6245 },6290 },
6246 .Minus => {6291 .Minus => {
6247 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");6292 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
6248 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6293 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6249 return &node.base;6294 return &node.base;
6250 },6295 },
6251 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),6296 .Plus => return try parseCPrefixOpExpr(c, it, source_loc, scope),
6252 .Tilde => {6297 .Tilde => {
6253 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");6298 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
6254 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6299 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6255 return &node.base;6300 return &node.base;
6256 },6301 },
6257 .Asterisk => {6302 .Asterisk => {
6258 const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6303 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
6259 return try transCreateNodePtrDeref(c, node);6304 return try transCreateNodePtrDeref(c, node);
6260 },6305 },
6261 .Ampersand => {6306 .Ampersand => {
6262 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");6307 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
6263 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6308 node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
6264 return &node.base;6309 return &node.base;
6265 },6310 },
6266 else => {6311 else => {
6267 _ = it.prev();6312 it.i -= 1;
6268 return try parseCSuffixOpExpr(c, it, source, source_loc, scope);6313 return try parseCSuffixOpExpr(c, it, source_loc, scope);
6269 },6314 },
6270 }6315 }
6271}6316}
src-self-hosted/type.zig+132-16
...@@ -67,6 +67,7 @@ pub const Type = extern union {...@@ -67,6 +67,7 @@ pub const Type = extern union {
6767
68 .array, .array_u8_sentinel_0 => return .Array,68 .array, .array_u8_sentinel_0 => return .Array,
69 .single_const_pointer => return .Pointer,69 .single_const_pointer => return .Pointer,
70 .single_mut_pointer => return .Pointer,
70 .single_const_pointer_to_comptime_int => return .Pointer,71 .single_const_pointer_to_comptime_int => return .Pointer,
71 .const_slice_u8 => return .Pointer,72 .const_slice_u8 => return .Pointer,
72 }73 }
...@@ -261,6 +262,15 @@ pub const Type = extern union {...@@ -261,6 +262,15 @@ pub const Type = extern union {
261 };262 };
262 return Type{ .ptr_otherwise = &new_payload.base };263 return Type{ .ptr_otherwise = &new_payload.base };
263 },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 },
264 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),274 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
265 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),275 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
266 .function => {276 .function => {
...@@ -368,6 +378,12 @@ pub const Type = extern union {...@@ -368,6 +378,12 @@ pub const Type = extern union {
368 ty = payload.pointee_type;378 ty = payload.pointee_type;
369 continue;379 continue;
370 },380 },
381 .single_mut_pointer => {
382 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);
383 try out_stream.writeAll("*");
384 ty = payload.pointee_type;
385 continue;
386 },
371 .int_signed => {387 .int_signed => {
372 const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);388 const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
373 return out_stream.print("i{}", .{payload.bits});389 return out_stream.print("i{}", .{payload.bits});
...@@ -467,6 +483,7 @@ pub const Type = extern union {...@@ -467,6 +483,7 @@ pub const Type = extern union {
467 .array_u8_sentinel_0,483 .array_u8_sentinel_0,
468 .array, // TODO check for zero bits484 .array, // TODO check for zero bits
469 .single_const_pointer,485 .single_const_pointer,
486 .single_mut_pointer,
470 .int_signed, // TODO check for zero bits487 .int_signed, // TODO check for zero bits
471 .int_unsigned, // TODO check for zero bits488 .int_unsigned, // TODO check for zero bits
472 => true,489 => true,
...@@ -493,13 +510,18 @@ pub const Type = extern union {...@@ -493,13 +510,18 @@ pub const Type = extern union {
493 .u8,510 .u8,
494 .i8,511 .i8,
495 .bool,512 .bool,
513 .array_u8_sentinel_0,
514 => return 1,
515
496 .fn_noreturn_no_args, // represents machine code; not a pointer516 .fn_noreturn_no_args, // represents machine code; not a pointer
497 .fn_void_no_args, // represents machine code; not a pointer517 .fn_void_no_args, // represents machine code; not a pointer
498 .fn_naked_noreturn_no_args, // represents machine code; not a pointer518 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
499 .fn_ccc_void_no_args, // represents machine code; not a pointer519 .fn_ccc_void_no_args, // represents machine code; not a pointer
500 .function, // represents machine code; not a pointer520 .function, // represents machine code; not a pointer
501 .array_u8_sentinel_0,521 => return switch (target.cpu.arch) {
502 => return 1,522 .riscv64 => 2,
523 else => 1,
524 },
503525
504 .i16, .u16 => return 2,526 .i16, .u16 => return 2,
505 .i32, .u32 => return 4,527 .i32, .u32 => return 4,
...@@ -510,6 +532,7 @@ pub const Type = extern union {...@@ -510,6 +532,7 @@ pub const Type = extern union {
510 .single_const_pointer_to_comptime_int,532 .single_const_pointer_to_comptime_int,
511 .const_slice_u8,533 .const_slice_u8,
512 .single_const_pointer,534 .single_const_pointer,
535 .single_mut_pointer,
513 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),536 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
514537
515 .c_short => return @divExact(CType.short.sizeInBits(target), 8),538 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -591,6 +614,7 @@ pub const Type = extern union {...@@ -591,6 +614,7 @@ pub const Type = extern union {
591 .single_const_pointer_to_comptime_int,614 .single_const_pointer_to_comptime_int,
592 .const_slice_u8,615 .const_slice_u8,
593 .single_const_pointer,616 .single_const_pointer,
617 .single_mut_pointer,
594 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),618 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
595619
596 .c_short => return @divExact(CType.short.sizeInBits(target), 8),620 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -671,6 +695,7 @@ pub const Type = extern union {...@@ -671,6 +695,7 @@ pub const Type = extern union {
671 => false,695 => false,
672696
673 .single_const_pointer,697 .single_const_pointer,
698 .single_mut_pointer,
674 .single_const_pointer_to_comptime_int,699 .single_const_pointer_to_comptime_int,
675 => true,700 => true,
676 };701 };
...@@ -714,6 +739,7 @@ pub const Type = extern union {...@@ -714,6 +739,7 @@ pub const Type = extern union {
714 .array,739 .array,
715 .array_u8_sentinel_0,740 .array_u8_sentinel_0,
716 .single_const_pointer,741 .single_const_pointer,
742 .single_mut_pointer,
717 .single_const_pointer_to_comptime_int,743 .single_const_pointer_to_comptime_int,
718 .fn_noreturn_no_args,744 .fn_noreturn_no_args,
719 .fn_void_no_args,745 .fn_void_no_args,
...@@ -728,8 +754,7 @@ pub const Type = extern union {...@@ -728,8 +754,7 @@ pub const Type = extern union {
728 };754 };
729 }755 }
730756
731 /// Asserts the type is a pointer type.757 pub fn isConstPtr(self: Type) bool {
732 pub fn pointerIsConst(self: Type) bool {
733 return switch (self.tag()) {758 return switch (self.tag()) {
734 .u8,759 .u8,
735 .i8,760 .i8,
...@@ -773,7 +798,8 @@ pub const Type = extern union {...@@ -773,7 +798,8 @@ pub const Type = extern union {
773 .function,798 .function,
774 .int_unsigned,799 .int_unsigned,
775 .int_signed,800 .int_signed,
776 => unreachable,801 .single_mut_pointer,
802 => false,
777803
778 .single_const_pointer,804 .single_const_pointer,
779 .single_const_pointer_to_comptime_int,805 .single_const_pointer_to_comptime_int,
...@@ -782,6 +808,58 @@ pub const Type = extern union {...@@ -782,6 +808,58 @@ pub const Type = extern union {
782 };808 };
783 }809 }
784810
811 pub fn isVolatilePtr(self: Type) bool {
812 return switch (self.tag()) {
813 .u8,
814 .i8,
815 .u16,
816 .i16,
817 .u32,
818 .i32,
819 .u64,
820 .i64,
821 .usize,
822 .isize,
823 .c_short,
824 .c_ushort,
825 .c_int,
826 .c_uint,
827 .c_long,
828 .c_ulong,
829 .c_longlong,
830 .c_ulonglong,
831 .c_longdouble,
832 .f16,
833 .f32,
834 .f64,
835 .f128,
836 .c_void,
837 .bool,
838 .void,
839 .type,
840 .anyerror,
841 .comptime_int,
842 .comptime_float,
843 .noreturn,
844 .@"null",
845 .@"undefined",
846 .array,
847 .array_u8_sentinel_0,
848 .fn_noreturn_no_args,
849 .fn_void_no_args,
850 .fn_naked_noreturn_no_args,
851 .fn_ccc_void_no_args,
852 .function,
853 .int_unsigned,
854 .int_signed,
855 .single_mut_pointer,
856 .single_const_pointer,
857 .single_const_pointer_to_comptime_int,
858 .const_slice_u8,
859 => false,
860 };
861 }
862
785 /// Asserts the type is a pointer or array type.863 /// Asserts the type is a pointer or array type.
786 pub fn elemType(self: Type) Type {864 pub fn elemType(self: Type) Type {
787 return switch (self.tag()) {865 return switch (self.tag()) {
...@@ -829,6 +907,7 @@ pub const Type = extern union {...@@ -829,6 +907,7 @@ pub const Type = extern union {
829907
830 .array => self.cast(Payload.Array).?.elem_type,908 .array => self.cast(Payload.Array).?.elem_type,
831 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,909 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
910 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,
832 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),911 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
833 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),912 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
834 };913 };
...@@ -876,6 +955,7 @@ pub const Type = extern union {...@@ -876,6 +955,7 @@ pub const Type = extern union {
876 .fn_ccc_void_no_args,955 .fn_ccc_void_no_args,
877 .function,956 .function,
878 .single_const_pointer,957 .single_const_pointer,
958 .single_mut_pointer,
879 .single_const_pointer_to_comptime_int,959 .single_const_pointer_to_comptime_int,
880 .const_slice_u8,960 .const_slice_u8,
881 .int_unsigned,961 .int_unsigned,
...@@ -929,6 +1009,7 @@ pub const Type = extern union {...@@ -929,6 +1009,7 @@ pub const Type = extern union {
929 .fn_ccc_void_no_args,1009 .fn_ccc_void_no_args,
930 .function,1010 .function,
931 .single_const_pointer,1011 .single_const_pointer,
1012 .single_mut_pointer,
932 .single_const_pointer_to_comptime_int,1013 .single_const_pointer_to_comptime_int,
933 .const_slice_u8,1014 .const_slice_u8,
934 .int_unsigned,1015 .int_unsigned,
...@@ -970,6 +1051,7 @@ pub const Type = extern union {...@@ -970,6 +1051,7 @@ pub const Type = extern union {
970 .function,1051 .function,
971 .array,1052 .array,
972 .single_const_pointer,1053 .single_const_pointer,
1054 .single_mut_pointer,
973 .single_const_pointer_to_comptime_int,1055 .single_const_pointer_to_comptime_int,
974 .array_u8_sentinel_0,1056 .array_u8_sentinel_0,
975 .const_slice_u8,1057 .const_slice_u8,
...@@ -1024,6 +1106,7 @@ pub const Type = extern union {...@@ -1024,6 +1106,7 @@ pub const Type = extern union {
1024 .function,1106 .function,
1025 .array,1107 .array,
1026 .single_const_pointer,1108 .single_const_pointer,
1109 .single_mut_pointer,
1027 .single_const_pointer_to_comptime_int,1110 .single_const_pointer_to_comptime_int,
1028 .array_u8_sentinel_0,1111 .array_u8_sentinel_0,
1029 .const_slice_u8,1112 .const_slice_u8,
...@@ -1078,6 +1161,7 @@ pub const Type = extern union {...@@ -1078,6 +1161,7 @@ pub const Type = extern union {
1078 .function,1161 .function,
1079 .array,1162 .array,
1080 .single_const_pointer,1163 .single_const_pointer,
1164 .single_mut_pointer,
1081 .single_const_pointer_to_comptime_int,1165 .single_const_pointer_to_comptime_int,
1082 .array_u8_sentinel_0,1166 .array_u8_sentinel_0,
1083 .const_slice_u8,1167 .const_slice_u8,
...@@ -1130,6 +1214,7 @@ pub const Type = extern union {...@@ -1130,6 +1214,7 @@ pub const Type = extern union {
1130 .function,1214 .function,
1131 .array,1215 .array,
1132 .single_const_pointer,1216 .single_const_pointer,
1217 .single_mut_pointer,
1133 .single_const_pointer_to_comptime_int,1218 .single_const_pointer_to_comptime_int,
1134 .array_u8_sentinel_0,1219 .array_u8_sentinel_0,
1135 .const_slice_u8,1220 .const_slice_u8,
...@@ -1211,6 +1296,7 @@ pub const Type = extern union {...@@ -1211,6 +1296,7 @@ pub const Type = extern union {
1211 .@"undefined",1296 .@"undefined",
1212 .array,1297 .array,
1213 .single_const_pointer,1298 .single_const_pointer,
1299 .single_mut_pointer,
1214 .single_const_pointer_to_comptime_int,1300 .single_const_pointer_to_comptime_int,
1215 .array_u8_sentinel_0,1301 .array_u8_sentinel_0,
1216 .const_slice_u8,1302 .const_slice_u8,
...@@ -1268,6 +1354,7 @@ pub const Type = extern union {...@@ -1268,6 +1354,7 @@ pub const Type = extern union {
1268 .@"undefined",1354 .@"undefined",
1269 .array,1355 .array,
1270 .single_const_pointer,1356 .single_const_pointer,
1357 .single_mut_pointer,
1271 .single_const_pointer_to_comptime_int,1358 .single_const_pointer_to_comptime_int,
1272 .array_u8_sentinel_0,1359 .array_u8_sentinel_0,
1273 .const_slice_u8,1360 .const_slice_u8,
...@@ -1324,6 +1411,7 @@ pub const Type = extern union {...@@ -1324,6 +1411,7 @@ pub const Type = extern union {
1324 .@"undefined",1411 .@"undefined",
1325 .array,1412 .array,
1326 .single_const_pointer,1413 .single_const_pointer,
1414 .single_mut_pointer,
1327 .single_const_pointer_to_comptime_int,1415 .single_const_pointer_to_comptime_int,
1328 .array_u8_sentinel_0,1416 .array_u8_sentinel_0,
1329 .const_slice_u8,1417 .const_slice_u8,
...@@ -1380,6 +1468,7 @@ pub const Type = extern union {...@@ -1380,6 +1468,7 @@ pub const Type = extern union {
1380 .@"undefined",1468 .@"undefined",
1381 .array,1469 .array,
1382 .single_const_pointer,1470 .single_const_pointer,
1471 .single_mut_pointer,
1383 .single_const_pointer_to_comptime_int,1472 .single_const_pointer_to_comptime_int,
1384 .array_u8_sentinel_0,1473 .array_u8_sentinel_0,
1385 .const_slice_u8,1474 .const_slice_u8,
...@@ -1433,6 +1522,7 @@ pub const Type = extern union {...@@ -1433,6 +1522,7 @@ pub const Type = extern union {
1433 .@"undefined",1522 .@"undefined",
1434 .array,1523 .array,
1435 .single_const_pointer,1524 .single_const_pointer,
1525 .single_mut_pointer,
1436 .single_const_pointer_to_comptime_int,1526 .single_const_pointer_to_comptime_int,
1437 .array_u8_sentinel_0,1527 .array_u8_sentinel_0,
1438 .const_slice_u8,1528 .const_slice_u8,
...@@ -1486,6 +1576,7 @@ pub const Type = extern union {...@@ -1486,6 +1576,7 @@ pub const Type = extern union {
1486 .@"undefined",1576 .@"undefined",
1487 .array,1577 .array,
1488 .single_const_pointer,1578 .single_const_pointer,
1579 .single_mut_pointer,
1489 .single_const_pointer_to_comptime_int,1580 .single_const_pointer_to_comptime_int,
1490 .array_u8_sentinel_0,1581 .array_u8_sentinel_0,
1491 .const_slice_u8,1582 .const_slice_u8,
...@@ -1559,6 +1650,7 @@ pub const Type = extern union {...@@ -1559,6 +1650,7 @@ pub const Type = extern union {
1559 .function,1650 .function,
1560 .array,1651 .array,
1561 .single_const_pointer,1652 .single_const_pointer,
1653 .single_mut_pointer,
1562 .single_const_pointer_to_comptime_int,1654 .single_const_pointer_to_comptime_int,
1563 .array_u8_sentinel_0,1655 .array_u8_sentinel_0,
1564 .const_slice_u8,1656 .const_slice_u8,
...@@ -1566,7 +1658,7 @@ pub const Type = extern union {...@@ -1566,7 +1658,7 @@ pub const Type = extern union {
1566 };1658 };
1567 }1659 }
15681660
1569 pub fn onePossibleValue(self: Type) bool {1661 pub fn onePossibleValue(self: Type) ?Value {
1570 var ty = self;1662 var ty = self;
1571 while (true) switch (ty.tag()) {1663 while (true) switch (ty.tag()) {
1572 .f16,1664 .f16,
...@@ -1605,21 +1697,32 @@ pub const Type = extern union {...@@ -1605,21 +1697,32 @@ pub const Type = extern union {
1605 .single_const_pointer_to_comptime_int,1697 .single_const_pointer_to_comptime_int,
1606 .array_u8_sentinel_0,1698 .array_u8_sentinel_0,
1607 .const_slice_u8,1699 .const_slice_u8,
1608 => return false,
1609
1610 .c_void,1700 .c_void,
1611 .void,1701 => return null,
1612 .noreturn,1702
1613 .@"null",1703 .void => return Value.initTag(.void_value),
1614 .@"undefined",1704 .noreturn => return Value.initTag(.unreachable_value),
1615 => return true,1705 .@"null" => return Value.initTag(.null_value),
1706 .@"undefined" => return Value.initTag(.undef),
16161707
1617 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,1708 .int_unsigned => {
1618 .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,1709 if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
1710 return Value.initTag(.zero);
1711 } else {
1712 return null;
1713 }
1714 },
1715 .int_signed => {
1716 if (ty.cast(Payload.IntSigned).?.bits == 0) {
1717 return Value.initTag(.zero);
1718 } else {
1719 return null;
1720 }
1721 },
1619 .array => {1722 .array => {
1620 const array = ty.cast(Payload.Array).?;1723 const array = ty.cast(Payload.Array).?;
1621 if (array.len == 0)1724 if (array.len == 0)
1622 return true;1725 return Value.initTag(.empty_array);
1623 ty = array.elem_type;1726 ty = array.elem_type;
1624 continue;1727 continue;
1625 },1728 },
...@@ -1628,6 +1731,11 @@ pub const Type = extern union {...@@ -1628,6 +1731,11 @@ pub const Type = extern union {
1628 ty = ptr.pointee_type;1731 ty = ptr.pointee_type;
1629 continue;1732 continue;
1630 },1733 },
1734 .single_mut_pointer => {
1735 const ptr = ty.cast(Payload.SingleMutPointer).?;
1736 ty = ptr.pointee_type;
1737 continue;
1738 },
1631 };1739 };
1632 }1740 }
16331741
...@@ -1678,6 +1786,7 @@ pub const Type = extern union {...@@ -1678,6 +1786,7 @@ pub const Type = extern union {
1678 .int_signed,1786 .int_signed,
1679 .array,1787 .array,
1680 .single_const_pointer,1788 .single_const_pointer,
1789 .single_mut_pointer,
1681 => return false,1790 => return false,
1682 };1791 };
1683 }1792 }
...@@ -1734,6 +1843,7 @@ pub const Type = extern union {...@@ -1734,6 +1843,7 @@ pub const Type = extern union {
1734 array_u8_sentinel_0,1843 array_u8_sentinel_0,
1735 array,1844 array,
1736 single_const_pointer,1845 single_const_pointer,
1846 single_mut_pointer,
1737 int_signed,1847 int_signed,
1738 int_unsigned,1848 int_unsigned,
1739 function,1849 function,
...@@ -1764,6 +1874,12 @@ pub const Type = extern union {...@@ -1764,6 +1874,12 @@ pub const Type = extern union {
1764 pointee_type: Type,1874 pointee_type: Type,
1765 };1875 };
17661876
1877 pub const SingleMutPointer = struct {
1878 base: Payload = Payload{ .tag = .single_mut_pointer },
1879
1880 pointee_type: Type,
1881 };
1882
1767 pub const IntSigned = struct {1883 pub const IntSigned = struct {
1768 base: Payload = Payload{ .tag = .int_signed },1884 base: Payload = Payload{ .tag = .int_signed },
17691885
src-self-hosted/value.zig+51-24
...@@ -63,7 +63,9 @@ pub const Value = extern union {...@@ -63,7 +63,9 @@ pub const Value = extern union {
6363
64 undef,64 undef,
65 zero,65 zero,
66 the_one_possible_value, // when the type only has one possible value66 void_value,
67 unreachable_value,
68 empty_array,
67 null_value,69 null_value,
68 bool_true,70 bool_true,
69 bool_false, // See last_no_payload_tag below.71 bool_false, // See last_no_payload_tag below.
...@@ -164,7 +166,9 @@ pub const Value = extern union {...@@ -164,7 +166,9 @@ pub const Value = extern union {
164 .const_slice_u8_type,166 .const_slice_u8_type,
165 .undef,167 .undef,
166 .zero,168 .zero,
167 .the_one_possible_value,169 .void_value,
170 .unreachable_value,
171 .empty_array,
168 .null_value,172 .null_value,
169 .bool_true,173 .bool_true,
170 .bool_false,174 .bool_false,
...@@ -285,7 +289,8 @@ pub const Value = extern union {...@@ -285,7 +289,8 @@ pub const Value = extern union {
285 .null_value => return out_stream.writeAll("null"),289 .null_value => return out_stream.writeAll("null"),
286 .undef => return out_stream.writeAll("undefined"),290 .undef => return out_stream.writeAll("undefined"),
287 .zero => return out_stream.writeAll("0"),291 .zero => return out_stream.writeAll("0"),
288 .the_one_possible_value => return out_stream.writeAll("(one possible value)"),292 .void_value => return out_stream.writeAll("{}"),
293 .unreachable_value => return out_stream.writeAll("unreachable"),
289 .bool_true => return out_stream.writeAll("true"),294 .bool_true => return out_stream.writeAll("true"),
290 .bool_false => return out_stream.writeAll("false"),295 .bool_false => return out_stream.writeAll("false"),
291 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),296 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
...@@ -312,6 +317,7 @@ pub const Value = extern union {...@@ -312,6 +317,7 @@ pub const Value = extern union {
312 try out_stream.print("&[{}] ", .{elem_ptr.index});317 try out_stream.print("&[{}] ", .{elem_ptr.index});
313 val = elem_ptr.array_ptr;318 val = elem_ptr.array_ptr;
314 },319 },
320 .empty_array => return out_stream.writeAll(".{}"),
315 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),321 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
316 .repeated => {322 .repeated => {
317 try out_stream.writeAll("(repeated) ");323 try out_stream.writeAll("(repeated) ");
...@@ -388,7 +394,9 @@ pub const Value = extern union {...@@ -388,7 +394,9 @@ pub const Value = extern union {
388394
389 .undef,395 .undef,
390 .zero,396 .zero,
391 .the_one_possible_value,397 .void_value,
398 .unreachable_value,
399 .empty_array,
392 .bool_true,400 .bool_true,
393 .bool_false,401 .bool_false,
394 .null_value,402 .null_value,
...@@ -460,15 +468,18 @@ pub const Value = extern union {...@@ -460,15 +468,18 @@ pub const Value = extern union {
460 .decl_ref,468 .decl_ref,
461 .elem_ptr,469 .elem_ptr,
462 .bytes,470 .bytes,
463 .undef,
464 .repeated,471 .repeated,
465 .float_16,472 .float_16,
466 .float_32,473 .float_32,
467 .float_64,474 .float_64,
468 .float_128,475 .float_128,
476 .void_value,
477 .unreachable_value,
478 .empty_array,
469 => unreachable,479 => unreachable,
470480
471 .the_one_possible_value, // An integer with one possible value is always zero.481 .undef => unreachable,
482
472 .zero,483 .zero,
473 .bool_false,484 .bool_false,
474 => return BigIntMutable.init(&space.limbs, 0).toConst(),485 => return BigIntMutable.init(&space.limbs, 0).toConst(),
...@@ -532,16 +543,19 @@ pub const Value = extern union {...@@ -532,16 +543,19 @@ pub const Value = extern union {
532 .decl_ref,543 .decl_ref,
533 .elem_ptr,544 .elem_ptr,
534 .bytes,545 .bytes,
535 .undef,
536 .repeated,546 .repeated,
537 .float_16,547 .float_16,
538 .float_32,548 .float_32,
539 .float_64,549 .float_64,
540 .float_128,550 .float_128,
551 .void_value,
552 .unreachable_value,
553 .empty_array,
541 => unreachable,554 => unreachable,
542555
556 .undef => unreachable,
557
543 .zero,558 .zero,
544 .the_one_possible_value, // an integer with one possible value is always zero
545 .bool_false,559 .bool_false,
546 => return 0,560 => return 0,
547561
...@@ -570,10 +584,9 @@ pub const Value = extern union {...@@ -570,10 +584,9 @@ pub const Value = extern union {
570 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),584 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
571 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),585 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
572586
573 .zero, .the_one_possible_value => 0,587 .zero => 0,
574 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),588 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
575 // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),589 .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
576 .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
577590
578 .int_big_positive, .int_big_negative => @panic("big int to f128"),591 .int_big_positive, .int_big_negative => @panic("big int to f128"),
579 else => unreachable,592 else => unreachable,
...@@ -637,9 +650,11 @@ pub const Value = extern union {...@@ -637,9 +650,11 @@ pub const Value = extern union {
637 .float_32,650 .float_32,
638 .float_64,651 .float_64,
639 .float_128,652 .float_128,
653 .void_value,
654 .unreachable_value,
655 .empty_array,
640 => unreachable,656 => unreachable,
641657
642 .the_one_possible_value, // an integer with one possible value is always zero
643 .zero,658 .zero,
644 .bool_false,659 .bool_false,
645 => return 0,660 => return 0,
...@@ -714,11 +729,13 @@ pub const Value = extern union {...@@ -714,11 +729,13 @@ pub const Value = extern union {
714 .float_32,729 .float_32,
715 .float_64,730 .float_64,
716 .float_128,731 .float_128,
732 .void_value,
733 .unreachable_value,
734 .empty_array,
717 => unreachable,735 => unreachable,
718736
719 .zero,737 .zero,
720 .undef,738 .undef,
721 .the_one_possible_value, // an integer with one possible value is always zero
722 .bool_false,739 .bool_false,
723 => return true,740 => return true,
724741
...@@ -797,13 +814,13 @@ pub const Value = extern union {...@@ -797,13 +814,13 @@ pub const Value = extern union {
797 // return Value.initPayload(&res_payload.base).copy(allocator);814 // return Value.initPayload(&res_payload.base).copy(allocator);
798 },815 },
799 32 => {816 32 => {
800 var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};817 var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
801 if (!self.eql(Value.initPayload(&res_payload.base)))818 if (!self.eql(Value.initPayload(&res_payload.base)))
802 return error.Overflow;819 return error.Overflow;
803 return Value.initPayload(&res_payload.base).copy(allocator);820 return Value.initPayload(&res_payload.base).copy(allocator);
804 },821 },
805 64 => {822 64 => {
806 var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};823 var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
807 if (!self.eql(Value.initPayload(&res_payload.base)))824 if (!self.eql(Value.initPayload(&res_payload.base)))
808 return error.Overflow;825 return error.Overflow;
809 return Value.initPayload(&res_payload.base).copy(allocator);826 return Value.initPayload(&res_payload.base).copy(allocator);
...@@ -875,7 +892,9 @@ pub const Value = extern union {...@@ -875,7 +892,9 @@ pub const Value = extern union {
875 .int_i64,892 .int_i64,
876 .int_big_positive,893 .int_big_positive,
877 .int_big_negative,894 .int_big_negative,
878 .the_one_possible_value,895 .empty_array,
896 .void_value,
897 .unreachable_value,
879 => unreachable,898 => unreachable,
880899
881 .zero => false,900 .zero => false,
...@@ -939,10 +958,12 @@ pub const Value = extern union {...@@ -939,10 +958,12 @@ pub const Value = extern union {
939 .bytes,958 .bytes,
940 .repeated,959 .repeated,
941 .undef,960 .undef,
961 .void_value,
962 .unreachable_value,
963 .empty_array,
942 => unreachable,964 => unreachable,
943965
944 .zero,966 .zero,
945 .the_one_possible_value, // an integer with one possible value is always zero
946 .bool_false,967 .bool_false,
947 => .eq,968 => .eq,
948969
...@@ -964,8 +985,8 @@ pub const Value = extern union {...@@ -964,8 +985,8 @@ pub const Value = extern union {
964 pub fn order(lhs: Value, rhs: Value) std.math.Order {985 pub fn order(lhs: Value, rhs: Value) std.math.Order {
965 const lhs_tag = lhs.tag();986 const lhs_tag = lhs.tag();
966 const rhs_tag = rhs.tag();987 const rhs_tag = rhs.tag();
967 const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;988 const lhs_is_zero = lhs_tag == .zero;
968 const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;989 const rhs_is_zero = rhs_tag == .zero;
969 if (lhs_is_zero) return rhs.orderAgainstZero().invert();990 if (lhs_is_zero) return rhs.orderAgainstZero().invert();
970 if (rhs_is_zero) return lhs.orderAgainstZero();991 if (rhs_is_zero) return lhs.orderAgainstZero();
971992
...@@ -1071,9 +1092,11 @@ pub const Value = extern union {...@@ -1071,9 +1092,11 @@ pub const Value = extern union {
1071 .float_32,1092 .float_32,
1072 .float_64,1093 .float_64,
1073 .float_128,1094 .float_128,
1095 .void_value,
1096 .unreachable_value,
1097 .empty_array,
1074 => unreachable,1098 => unreachable,
10751099
1076 .the_one_possible_value => Value.initTag(.the_one_possible_value),
1077 .ref_val => self.cast(Payload.RefVal).?.val,1100 .ref_val => self.cast(Payload.RefVal).?.val,
1078 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),1101 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
1079 .elem_ptr => {1102 .elem_ptr => {
...@@ -1130,7 +1153,6 @@ pub const Value = extern union {...@@ -1130,7 +1153,6 @@ pub const Value = extern union {
1130 .single_const_pointer_to_comptime_int_type,1153 .single_const_pointer_to_comptime_int_type,
1131 .const_slice_u8_type,1154 .const_slice_u8_type,
1132 .zero,1155 .zero,
1133 .the_one_possible_value,
1134 .bool_true,1156 .bool_true,
1135 .bool_false,1157 .bool_false,
1136 .null_value,1158 .null_value,
...@@ -1147,8 +1169,12 @@ pub const Value = extern union {...@@ -1147,8 +1169,12 @@ pub const Value = extern union {
1147 .float_32,1169 .float_32,
1148 .float_64,1170 .float_64,
1149 .float_128,1171 .float_128,
1172 .void_value,
1173 .unreachable_value,
1150 => unreachable,1174 => unreachable,
11511175
1176 .empty_array => unreachable, // out of bounds array index
1177
1152 .bytes => {1178 .bytes => {
1153 const int_payload = try allocator.create(Payload.Int_u64);1179 const int_payload = try allocator.create(Payload.Int_u64);
1154 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };1180 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
...@@ -1175,8 +1201,7 @@ pub const Value = extern union {...@@ -1175,8 +1201,7 @@ pub const Value = extern union {
1175 return self.tag() == .undef;1201 return self.tag() == .undef;
1176 }1202 }
11771203
1178 /// Valid for all types. Asserts the value is not undefined.1204 /// Valid for all types. Asserts the value is not undefined and not unreachable.
1179 /// `.the_one_possible_value` is reported as not null.
1180 pub fn isNull(self: Value) bool {1205 pub fn isNull(self: Value) bool {
1181 return switch (self.tag()) {1206 return switch (self.tag()) {
1182 .ty,1207 .ty,
...@@ -1221,7 +1246,7 @@ pub const Value = extern union {...@@ -1221,7 +1246,7 @@ pub const Value = extern union {
1221 .single_const_pointer_to_comptime_int_type,1246 .single_const_pointer_to_comptime_int_type,
1222 .const_slice_u8_type,1247 .const_slice_u8_type,
1223 .zero,1248 .zero,
1224 .the_one_possible_value,1249 .empty_array,
1225 .bool_true,1250 .bool_true,
1226 .bool_false,1251 .bool_false,
1227 .function,1252 .function,
...@@ -1238,9 +1263,11 @@ pub const Value = extern union {...@@ -1238,9 +1263,11 @@ pub const Value = extern union {
1238 .float_32,1263 .float_32,
1239 .float_64,1264 .float_64,
1240 .float_128,1265 .float_128,
1266 .void_value,
1241 => false,1267 => false,
12421268
1243 .undef => unreachable,1269 .undef => unreachable,
1270 .unreachable_value => unreachable,
1244 .null_value => true,1271 .null_value => true,
1245 };1272 };
1246 }1273 }
src-self-hosted/zir.zig+246-77
...@@ -34,25 +34,63 @@ pub const Inst = struct {...@@ -34,25 +34,63 @@ pub const Inst = struct {
3434
35 /// These names are used directly as the instruction names in the text format.35 /// These names are used directly as the instruction names in the text format.
36 pub const Tag = enum {36 pub const Tag = enum {
37 /// Arithmetic addition, asserts no integer overflow.
38 add,
39 /// Twos complement wrapping integer addition.
40 addwrap,
37 /// Allocates stack local memory. Its lifetime ends when the block ends that contains41 /// Allocates stack local memory. Its lifetime ends when the block ends that contains
38 /// this instruction.42 /// this instruction. The operand is the type of the allocated object.
39 alloc,43 alloc,
40 /// Same as `alloc` except the type is inferred.44 /// Same as `alloc` except the type is inferred.
41 alloc_inferred,45 alloc_inferred,
46 /// Array concatenation. `a ++ b`
47 array_cat,
48 /// Array multiplication `a ** b`
49 array_mul,
42 /// Function parameter value. These must be first in a function's main block,50 /// Function parameter value. These must be first in a function's main block,
43 /// in respective order with the parameters.51 /// in respective order with the parameters.
44 arg,52 arg,
53 /// Type coercion.
54 as,
55 /// Inline assembly.
56 @"asm",
57 /// Bitwise AND. `&`
58 bitand,
59 /// TODO delete this instruction, it has no purpose.
60 bitcast,
61 /// An arbitrary typed pointer, which is to be used as an L-Value, is pointer-casted
62 /// to a new L-Value. The destination type is given by LHS. The cast is to be evaluated
63 /// as if it were a bit-cast operation from the operand pointer element type to the
64 /// provided destination type.
65 bitcast_lvalue,
45 /// A typed result location pointer is bitcasted to a new result location pointer.66 /// A typed result location pointer is bitcasted to a new result location pointer.
46 /// The new result location pointer has an inferred type.67 /// The new result location pointer has an inferred type.
47 bitcast_result_ptr,68 bitcast_result_ptr,
69 /// Bitwise OR. `|`
70 bitor,
48 /// A labeled block of code, which can return a value.71 /// A labeled block of code, which can return a value.
49 block,72 block,
73 /// Boolean NOT. See also `bitnot`.
74 boolnot,
50 /// Return a value from a `Block`.75 /// Return a value from a `Block`.
51 @"break",76 @"break",
52 breakpoint,77 breakpoint,
53 /// Same as `break` but without an operand; the operand is assumed to be the void value.78 /// Same as `break` but without an operand; the operand is assumed to be the void value.
54 breakvoid,79 breakvoid,
80 /// Function call.
55 call,81 call,
82 /// `<`
83 cmp_lt,
84 /// `<=`
85 cmp_lte,
86 /// `==`
87 cmp_eq,
88 /// `>=`
89 cmp_gte,
90 /// `>`
91 cmp_gt,
92 /// `!=`
93 cmp_neq,
56 /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-94 /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
57 /// as type coercion from the new element type to the old element type.95 /// as type coercion from the new element type to the old element type.
58 /// LHS is destination element type, RHS is result pointer.96 /// LHS is destination element type, RHS is result pointer.
...@@ -65,8 +103,12 @@ pub const Inst = struct {...@@ -65,8 +103,12 @@ pub const Inst = struct {
65 coerce_to_ptr_elem,103 coerce_to_ptr_elem,
66 /// Emit an error message and fail compilation.104 /// Emit an error message and fail compilation.
67 compileerror,105 compileerror,
106 /// Conditional branch. Splits control flow based on a boolean condition value.
107 condbr,
68 /// Special case, has no textual representation.108 /// Special case, has no textual representation.
69 @"const",109 @"const",
110 /// Declares the beginning of a statement. Used for debug info.
111 dbg_stmt,
70 /// Represents a pointer to a global decl by name.112 /// Represents a pointer to a global decl by name.
71 declref,113 declref,
72 /// Represents a pointer to a global decl by string name.114 /// Represents a pointer to a global decl by string name.
...@@ -76,61 +118,108 @@ pub const Inst = struct {...@@ -76,61 +118,108 @@ pub const Inst = struct {
76 declval,118 declval,
77 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.119 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
78 declval_in_module,120 declval_in_module,
121 /// Load the value from a pointer.
122 deref,
123 /// Arithmetic division. Asserts no integer overflow.
124 div,
125 /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
126 /// the provided index.
127 elemptr,
79 /// Emits a compile error if the operand is not `void`.128 /// Emits a compile error if the operand is not `void`.
80 ensure_result_used,129 ensure_result_used,
81 /// Emits a compile error if an error is ignored.130 /// Emits a compile error if an error is ignored.
82 ensure_result_non_error,131 ensure_result_non_error,
83 boolnot,132 /// Export the provided Decl as the provided name in the compilation's output object file.
84 /// Obtains a pointer to the return value.133 @"export",
85 ret_ptr,134 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
86 /// Obtains the return type of the in-scope function.135 /// to the named field.
87 ret_type,
88 /// Write a value to a pointer.
89 store,
90 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
91 str,
92 int,
93 inttype,
94 ptrtoint,
95 fieldptr,136 fieldptr,
96 deref,137 /// Convert a larger float type to any other float type, possibly causing a loss of precision.
97 as,138 floatcast,
98 @"asm",139 /// Declare a function body.
99 @"unreachable",
100 @"return",
101 returnvoid,
102 @"fn",140 @"fn",
141 /// Returns a function type.
103 fntype,142 fntype,
104 @"export",143 /// Integer literal.
144 int,
145 /// Convert an integer value to another integer type, asserting that the destination type
146 /// can hold the same mathematical value.
147 intcast,
148 /// Make an integer type out of signedness and bit count.
149 inttype,
150 /// Return a boolean false if an optional is null. `x != null`
151 isnonnull,
152 /// Return a boolean true if an optional is null. `x == null`
153 isnull,
154 /// 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,
156 /// a compile error is emitted. Otherwise the computation is performed.
157 mod_rem,
158 /// Arithmetic multiplication. Asserts no integer overflow.
159 mul,
160 /// Twos complement wrapping integer multiplication.
161 mulwrap,
105 /// Given a reference to a function and a parameter index, returns the162 /// Given a reference to a function and a parameter index, returns the
106 /// type of the parameter. TODO what happens when the parameter is `anytype`?163 /// type of the parameter. TODO what happens when the parameter is `anytype`?
107 param_type,164 param_type,
165 /// An alternative to using `const` for simple primitive values such as `true` or `u8`.
166 /// TODO flatten so that each primitive has its own ZIR Inst Tag.
108 primitive,167 primitive,
109 intcast,168 /// Convert a pointer to a `usize` integer.
110 bitcast,169 ptrtoint,
111 floatcast,170 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
112 elemptr,171 /// stores it in a memory location, and returns a const pointer to it. If the value
113 add,172 /// is `comptime`, the memory location is global static constant data. Otherwise,
173 /// the memory location is in the stack frame, local to the scope containing the
174 /// instruction.
175 ref,
176 /// Obtains a pointer to the return value.
177 ret_ptr,
178 /// Obtains the return type of the in-scope function.
179 ret_type,
180 /// Sends control flow back to the function's callee. Takes an operand as the return value.
181 @"return",
182 /// Same as `return` but there is no operand; the operand is implicitly the void value.
183 returnvoid,
184 /// Integer shift-left. Zeroes are shifted in from the right hand side.
185 shl,
186 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
187 shr,
188 /// Create a const pointer type based on the element type. `*const T`
189 single_const_ptr_type,
190 /// Create a mutable pointer type based on the element type. `*T`
191 single_mut_ptr_type,
192 /// Write a value to a pointer. For loading, see `deref`.
193 store,
194 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
195 str,
196 /// Arithmetic subtraction. Asserts no integer overflow.
114 sub,197 sub,
115 cmp_lt,198 /// Twos complement wrapping integer subtraction.
116 cmp_lte,199 subwrap,
117 cmp_eq,200 /// Returns the type of a value.
118 cmp_gte,201 typeof,
119 cmp_gt,202 /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
120 cmp_neq,203 /// will assume the correctness of this instruction.
121 condbr,204 unreach_nocheck,
122 isnull,205 /// Asserts control-flow will not reach this instruction. In safety-checked modes,
123 isnonnull,206 /// this will generate a call to the panic function unless it can be proven unreachable
207 /// by the compiler.
208 @"unreachable",
209 /// Bitwise XOR. `^`
210 xor,
124211
125 pub fn Type(tag: Tag) type {212 pub fn Type(tag: Tag) type {
126 return switch (tag) {213 return switch (tag) {
127 .arg,214 .arg,
128 .breakpoint,215 .breakpoint,
129 .@"unreachable",216 .dbg_stmt,
130 .returnvoid,217 .returnvoid,
131 .alloc_inferred,218 .alloc_inferred,
132 .ret_ptr,219 .ret_ptr,
133 .ret_type,220 .ret_type,
221 .unreach_nocheck,
222 .@"unreachable",
134 => NoOp,223 => NoOp,
135224
136 .boolnot,225 .boolnot,
...@@ -143,10 +232,28 @@ pub const Inst = struct {...@@ -143,10 +232,28 @@ pub const Inst = struct {
143 .ensure_result_used,232 .ensure_result_used,
144 .ensure_result_non_error,233 .ensure_result_non_error,
145 .bitcast_result_ptr,234 .bitcast_result_ptr,
235 .ref,
236 .bitcast_lvalue,
237 .typeof,
238 .single_const_ptr_type,
239 .single_mut_ptr_type,
146 => UnOp,240 => UnOp,
147241
148 .add,242 .add,
243 .addwrap,
244 .array_cat,
245 .array_mul,
246 .bitand,
247 .bitor,
248 .div,
249 .mod_rem,
250 .mul,
251 .mulwrap,
252 .shl,
253 .shr,
254 .store,
149 .sub,255 .sub,
256 .subwrap,
150 .cmp_lt,257 .cmp_lt,
151 .cmp_lte,258 .cmp_lte,
152 .cmp_eq,259 .cmp_eq,
...@@ -158,6 +265,7 @@ pub const Inst = struct {...@@ -158,6 +265,7 @@ pub const Inst = struct {
158 .intcast,265 .intcast,
159 .bitcast,266 .bitcast,
160 .coerce_result_ptr,267 .coerce_result_ptr,
268 .xor,
161 => BinOp,269 => BinOp,
162270
163 .block => Block,271 .block => Block,
...@@ -172,7 +280,6 @@ pub const Inst = struct {...@@ -172,7 +280,6 @@ pub const Inst = struct {
172 .coerce_result_block_ptr => CoerceResultBlockPtr,280 .coerce_result_block_ptr => CoerceResultBlockPtr,
173 .compileerror => CompileError,281 .compileerror => CompileError,
174 .@"const" => Const,282 .@"const" => Const,
175 .store => Store,
176 .str => Str,283 .str => Str,
177 .int => Int,284 .int => Int,
178 .inttype => IntType,285 .inttype => IntType,
...@@ -192,63 +299,83 @@ pub const Inst = struct {...@@ -192,63 +299,83 @@ pub const Inst = struct {
192 /// Function calls do not count.299 /// Function calls do not count.
193 pub fn isNoReturn(tag: Tag) bool {300 pub fn isNoReturn(tag: Tag) bool {
194 return switch (tag) {301 return switch (tag) {
302 .add,
303 .addwrap,
195 .alloc,304 .alloc,
196 .alloc_inferred,305 .alloc_inferred,
306 .array_cat,
307 .array_mul,
197 .arg,308 .arg,
309 .as,
310 .@"asm",
311 .bitand,
312 .bitcast,
313 .bitcast_lvalue,
198 .bitcast_result_ptr,314 .bitcast_result_ptr,
315 .bitor,
199 .block,316 .block,
317 .boolnot,
200 .breakpoint,318 .breakpoint,
201 .call,319 .call,
320 .cmp_lt,
321 .cmp_lte,
322 .cmp_eq,
323 .cmp_gte,
324 .cmp_gt,
325 .cmp_neq,
202 .coerce_result_ptr,326 .coerce_result_ptr,
203 .coerce_result_block_ptr,327 .coerce_result_block_ptr,
204 .coerce_to_ptr_elem,328 .coerce_to_ptr_elem,
205 .@"const",329 .@"const",
330 .dbg_stmt,
206 .declref,331 .declref,
207 .declref_str,332 .declref_str,
208 .declval,333 .declval,
209 .declval_in_module,334 .declval_in_module,
335 .deref,
336 .div,
337 .elemptr,
210 .ensure_result_used,338 .ensure_result_used,
211 .ensure_result_non_error,339 .ensure_result_non_error,
212 .ret_ptr,340 .@"export",
213 .ret_type,341 .floatcast,
214 .store,
215 .str,
216 .int,
217 .inttype,
218 .ptrtoint,
219 .fieldptr,342 .fieldptr,
220 .deref,
221 .as,
222 .@"asm",
223 .@"fn",343 .@"fn",
224 .fntype,344 .fntype,
225 .@"export",345 .int,
346 .intcast,
347 .inttype,
348 .isnonnull,
349 .isnull,
350 .mod_rem,
351 .mul,
352 .mulwrap,
226 .param_type,353 .param_type,
227 .primitive,354 .primitive,
228 .intcast,355 .ptrtoint,
229 .bitcast,356 .ref,
230 .floatcast,357 .ret_ptr,
231 .elemptr,358 .ret_type,
232 .add,359 .shl,
360 .shr,
361 .single_const_ptr_type,
362 .single_mut_ptr_type,
363 .store,
364 .str,
233 .sub,365 .sub,
234 .cmp_lt,366 .subwrap,
235 .cmp_lte,367 .typeof,
236 .cmp_eq,368 .xor,
237 .cmp_gte,
238 .cmp_gt,
239 .cmp_neq,
240 .isnull,
241 .isnonnull,
242 .boolnot,
243 => false,369 => false,
244370
245 .condbr,
246 .@"unreachable",
247 .@"return",
248 .returnvoid,
249 .@"break",371 .@"break",
250 .breakvoid,372 .breakvoid,
373 .condbr,
251 .compileerror,374 .compileerror,
375 .@"return",
376 .returnvoid,
377 .unreach_nocheck,
378 .@"unreachable",
252 => true,379 => true,
253 };380 };
254 }381 }
...@@ -430,17 +557,6 @@ pub const Inst = struct {...@@ -430,17 +557,6 @@ pub const Inst = struct {
430 kw_args: struct {},557 kw_args: struct {},
431 };558 };
432559
433 pub const Store = struct {
434 pub const base_tag = Tag.store;
435 base: Inst,
436
437 positionals: struct {
438 ptr: *Inst,
439 value: *Inst,
440 },
441 kw_args: struct {},
442 };
443
444 pub const Str = struct {560 pub const Str = struct {
445 pub const base_tag = Tag.str;561 pub const base_tag = Tag.str;
446 base: Inst,562 base: Inst,
...@@ -630,7 +746,7 @@ pub const Inst = struct {...@@ -630,7 +746,7 @@ pub const Inst = struct {
630 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },746 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
631 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },747 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
632 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },748 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
633 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },749 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
634 };750 };
635 }751 }
636 };752 };
...@@ -1486,6 +1602,21 @@ const EmitZIR = struct {...@@ -1486,6 +1602,21 @@ const EmitZIR = struct {
1486 const decl = decl_ref.decl;1602 const decl = decl_ref.decl;
1487 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));1603 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
1488 }1604 }
1605 if (typed_value.val.isUndef()) {
1606 const as_inst = try self.arena.allocator.create(Inst.BinOp);
1607 as_inst.* = .{
1608 .base = .{
1609 .tag = .as,
1610 .src = src,
1611 },
1612 .positionals = .{
1613 .lhs = (try self.emitType(src, typed_value.ty)).inst,
1614 .rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
1615 },
1616 .kw_args = .{},
1617 };
1618 return self.emitUnnamedDecl(&as_inst.base);
1619 }
1489 switch (typed_value.ty.zigTypeTag()) {1620 switch (typed_value.ty.zigTypeTag()) {
1490 .Pointer => {1621 .Pointer => {
1491 const ptr_elem_type = typed_value.ty.elemType();1622 const ptr_elem_type = typed_value.ty.elemType();
...@@ -1716,15 +1847,19 @@ const EmitZIR = struct {...@@ -1716,15 +1847,19 @@ const EmitZIR = struct {
1716 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),1847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
1717 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),1848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
1718 .retvoid => try self.emitNoOp(inst.src, .returnvoid),1849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
17191851
1720 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),1852 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
1721 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),1853 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
1722 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),1854 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
1723 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),1855 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
1724 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),1856 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
1857 .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),
17251859
1726 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),1860 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1727 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),1861 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
1862 .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
1728 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),1863 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
1729 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),1864 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
1730 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),1865 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
...@@ -1736,6 +1871,21 @@ const EmitZIR = struct {...@@ -1736,6 +1871,21 @@ const EmitZIR = struct {
1736 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),1871 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
1737 .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),1872 .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),
17381873
1874 .alloc => blk: {
1875 const new_inst = try self.arena.allocator.create(Inst.UnOp);
1876 new_inst.* = .{
1877 .base = .{
1878 .src = inst.src,
1879 .tag = .alloc,
1880 },
1881 .positionals = .{
1882 .operand = (try self.emitType(inst.src, inst.ty)).inst,
1883 },
1884 .kw_args = .{},
1885 };
1886 break :blk &new_inst.base;
1887 },
1888
1739 .block => blk: {1889 .block => blk: {
1740 const old_inst = inst.castTag(.block).?;1890 const old_inst = inst.castTag(.block).?;
1741 const new_inst = try self.arena.allocator.create(Inst.Block);1891 const new_inst = try self.arena.allocator.create(Inst.Block);
...@@ -1973,6 +2123,25 @@ const EmitZIR = struct {...@@ -1973,6 +2123,25 @@ const EmitZIR = struct {
1973 };2123 };
1974 return self.emitUnnamedDecl(&inttype_inst.base);2124 return self.emitUnnamedDecl(&inttype_inst.base);
1975 },2125 },
2126 .Pointer => {
2127 if (ty.isSinglePointer()) {
2128 const inst = try self.arena.allocator.create(Inst.UnOp);
2129 const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type;
2130 inst.* = .{
2131 .base = .{
2132 .src = src,
2133 .tag = tag,
2134 },
2135 .positionals = .{
2136 .operand = (try self.emitType(src, ty.elemType())).inst,
2137 },
2138 .kw_args = .{},
2139 };
2140 return self.emitUnnamedDecl(&inst.base);
2141 } else {
2142 std.debug.panic("TODO implement emitType for {}", .{ty});
2143 }
2144 },
1976 else => std.debug.panic("TODO implement emitType for {}", .{ty}),2145 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1977 },2146 },
1978 }2147 }
src-self-hosted/zir_sema.zig created+1160
...@@ -0,0 +1,1160 @@
1//! Semantic analysis of ZIR instructions.
2//! This file operates on a `Module` instance, transforming untyped ZIR
3//! instructions into semantically-analyzed IR instructions. It does type
4//! checking, comptime control flow, and safety-check generation. This is the
5//! the heart of the Zig compiler.
6//! When deciding if something goes into this file or into Module, here is a
7//! guiding principle: if it has to do with (untyped) ZIR instructions, it goes
8//! here. If the analysis operates on typed IR instructions, it goes in Module.
9
10const std = @import("std");
11const mem = std.mem;
12const Allocator = std.mem.Allocator;
13const Value = @import("value.zig").Value;
14const Type = @import("type.zig").Type;
15const TypedValue = @import("TypedValue.zig");
16const assert = std.debug.assert;
17const ir = @import("ir.zig");
18const zir = @import("zir.zig");
19const Module = @import("Module.zig");
20const Inst = ir.Inst;
21const Body = ir.Body;
22const trace = @import("tracy.zig").trace;
23const Scope = Module.Scope;
24const InnerError = Module.InnerError;
25const Decl = Module.Decl;
26
27pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
28 switch (old_inst.tag) {
29 .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
30 .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
31 .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
32 .bitcast_lvalue => return analyzeInstBitCastLValue(mod, scope, old_inst.castTag(.bitcast_lvalue).?),
33 .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
34 .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?),
35 .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
36 .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
37 .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
38 .call => return analyzeInstCall(mod, scope, old_inst.castTag(.call).?),
39 .coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?),
40 .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
41 .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
42 .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
43 .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
44 .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
45 .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
46 .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
47 .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
48 .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?),
49 .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?),
50 .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
51 .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
52 .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
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).?),
55 .single_mut_ptr_type => return analyzeInstSingleMutPtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?),
56 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
57 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
58 .int => {
59 const big_int = old_inst.castTag(.int).?.positionals.int;
60 return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
61 },
62 .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
63 .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
64 .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
65 .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
66 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
67 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
68 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
69 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?),
70 .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?),
71 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
72 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
73 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
74 .@"export" => return analyzeInstExport(mod, scope, old_inst.castTag(.@"export").?),
75 .primitive => return analyzeInstPrimitive(mod, scope, old_inst.castTag(.primitive).?),
76 .fntype => return analyzeInstFnType(mod, scope, old_inst.castTag(.fntype).?),
77 .intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?),
78 .bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?),
79 .floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?),
80 .elemptr => return analyzeInstElemPtr(mod, scope, old_inst.castTag(.elemptr).?),
81 .add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?),
82 .addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?),
83 .sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?),
84 .subwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.subwrap).?),
85 .mul => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mul).?),
86 .mulwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mulwrap).?),
87 .div => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.div).?),
88 .mod_rem => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mod_rem).?),
89 .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
90 .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
91 .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),
92 .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),
93 .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),
94 .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?),
95 .shr => return analyzeInstShr(mod, scope, old_inst.castTag(.shr).?),
96 .cmp_lt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt),
97 .cmp_lte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte),
98 .cmp_eq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq),
99 .cmp_gte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte),
100 .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),
101 .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),
102 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
103 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
104 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
105 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
106 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
107 }
108}
109
110pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
111 for (body.instructions) |src_inst| {
112 src_inst.analyzed_inst = try analyzeInst(mod, scope, src_inst);
113 }
114}
115
116pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
117 try analyzeBody(mod, &block_scope.base, body);
118 for (block_scope.instructions.items) |inst| {
119 if (inst.castTag(.ret)) |ret| {
120 const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
121 return val.toType();
122 } else {
123 return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
124 }
125 }
126 unreachable;
127}
128
129pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
130 var decl_scope: Scope.DeclAnalysis = .{
131 .decl = decl,
132 .arena = std.heap.ArenaAllocator.init(mod.gpa),
133 };
134 errdefer decl_scope.arena.deinit();
135
136 decl.analysis = .in_progress;
137
138 const typed_value = try analyzeConstInst(mod, &decl_scope.base, src_decl.inst);
139 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
140
141 var prev_type_has_bits = false;
142 var type_changed = true;
143
144 if (decl.typedValueManaged()) |tvm| {
145 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
146 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
147
148 tvm.deinit(mod.gpa);
149 }
150
151 arena_state.* = decl_scope.arena.state;
152 decl.typed_value = .{
153 .most_recent = .{
154 .typed_value = typed_value,
155 .arena = arena_state,
156 },
157 };
158 decl.analysis = .complete;
159 decl.generation = mod.generation;
160 if (typed_value.ty.hasCodeGenBits()) {
161 // We don't fully codegen the decl until later, but we do need to reserve a global
162 // offset table index for it. This allows us to codegen decls out of dependency order,
163 // increasing how many computations can be done in parallel.
164 try mod.bin_file.allocateDeclIndexes(decl);
165 try mod.work_queue.writeItem(.{ .codegen_decl = decl });
166 } else if (prev_type_has_bits) {
167 mod.bin_file.freeDecl(decl);
168 }
169
170 return type_changed;
171}
172
173pub fn resolveZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
174 const zir_module = mod.root_scope.cast(Scope.ZIRModule).?;
175 const entry = zir_module.contents.module.findDecl(src_decl.name).?;
176 return resolveZirDeclHavingIndex(mod, scope, src_decl, entry.index);
177}
178
179fn resolveZirDeclHavingIndex(mod: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
180 const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
181 const decl = mod.decl_table.get(name_hash).?;
182 decl.src_index = src_index;
183 try mod.ensureDeclAnalyzed(decl);
184 return decl;
185}
186
187/// Declares a dependency on the decl.
188fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
189 const decl = try resolveZirDecl(mod, scope, src_decl);
190 switch (decl.analysis) {
191 .unreferenced => unreachable,
192 .in_progress => unreachable,
193 .outdated => unreachable,
194
195 .dependency_failure,
196 .sema_failure,
197 .sema_failure_retryable,
198 .codegen_failure,
199 .codegen_failure_retryable,
200 => return error.AnalysisFail,
201
202 .complete => {},
203 }
204 return decl;
205}
206
207/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
208pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
209 if (old_inst.analyzed_inst) |inst| return inst;
210
211 // If this assert trips, the instruction that was referenced did not get properly
212 // analyzed before it was referenced.
213 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
214 const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
215 const decl_name = declval.positionals.name;
216 const entry = zir_module.contents.module.findDecl(decl_name) orelse
217 return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
218 break :blk entry;
219 } else blk: {
220 // If this assert trips, the instruction that was referenced did not get
221 // properly analyzed by a previous instruction analysis before it was
222 // referenced by the current one.
223 break :blk zir_module.contents.module.findInstDecl(old_inst).?;
224 };
225 const decl = try resolveCompleteZirDecl(mod, scope, entry.decl);
226 const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl);
227 // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
228 // but this would prevent the analyzeDeclRef from happening, which is needed to properly
229 // detect Decl dependencies and dependency failures on updates.
230 return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
231}
232
233fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
234 const new_inst = try resolveInst(mod, scope, old_inst);
235 const wanted_type = Type.initTag(.const_slice_u8);
236 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
237 const val = try mod.resolveConstValue(scope, coerced_inst);
238 return val.toAllocatedBytes(scope.arena());
239}
240
241fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
242 const new_inst = try resolveInst(mod, scope, old_inst);
243 const wanted_type = Type.initTag(.@"type");
244 const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
245 const val = try mod.resolveConstValue(scope, coerced_inst);
246 return val.toType();
247}
248
249pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
250 const new_inst = try resolveInst(mod, scope, old_inst);
251 const val = try mod.resolveConstValue(scope, new_inst);
252 return TypedValue{
253 .ty = new_inst.ty,
254 .val = val,
255 };
256}
257
258fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
259 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
260 // after analysis.
261 const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
262 return mod.constInst(scope, const_inst.base.src, typed_value_copy);
263}
264
265fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
266 const new_inst = try analyzeInst(mod, scope, old_inst);
267 return TypedValue{
268 .ty = new_inst.ty,
269 .val = try mod.resolveConstValue(scope, new_inst),
270 };
271}
272
273fn analyzeInstCoerceResultBlockPtr(
274 mod: *Module,
275 scope: *Scope,
276 inst: *zir.Inst.CoerceResultBlockPtr,
277) InnerError!*Inst {
278 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
279}
280
281fn analyzeInstBitCastLValue(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
282 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{});
283}
284
285fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
286 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
287}
288
289fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
290 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
291}
292
293/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
294fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
295 const ptr = try resolveInst(mod, scope, inst.positionals.ptr);
296 const operand = try resolveInst(mod, scope, inst.positionals.value);
297 return mod.coerce(scope, ptr.ty.elemType(), operand);
298}
299
300fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
301 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
302}
303
304fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
305 const operand = try resolveInst(mod, scope, inst.positionals.operand);
306 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);
309}
310
311fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
312 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
313 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
314 const ret_type = fn_ty.fnReturnType();
315 return mod.constType(scope, inst.base.src, ret_type);
316}
317
318fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
319 const operand = try resolveInst(mod, scope, inst.positionals.operand);
320 switch (operand.ty.zigTypeTag()) {
321 .Void, .NoReturn => return mod.constVoid(scope, operand.src),
322 else => return mod.fail(scope, operand.src, "expression value is ignored", .{}),
323 }
324}
325
326fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
327 const operand = try resolveInst(mod, scope, inst.positionals.operand);
328 switch (operand.ty.zigTypeTag()) {
329 .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}),
330 else => return mod.constVoid(scope, operand.src),
331 }
332}
333
334fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
335 const var_type = try resolveType(mod, scope, inst.positionals.operand);
336 const ptr_type = try mod.singleMutPtrType(scope, inst.base.src, var_type);
337 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
338 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
339}
340
341fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
342 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
343}
344
345fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
346 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
347 const value = try resolveInst(mod, scope, inst.positionals.rhs);
348 return mod.storePtr(scope, inst.base.src, ptr, value);
349}
350
351fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
352 const fn_inst = try resolveInst(mod, scope, inst.positionals.func);
353 const arg_index = inst.positionals.arg_index;
354
355 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
356 .Fn => fn_inst.ty,
357 .BoundFn => {
358 return mod.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{});
359 },
360 else => {
361 return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
362 },
363 };
364
365 // TODO support C-style var args
366 const param_count = fn_ty.fnParamLen();
367 if (arg_index >= param_count) {
368 return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{
369 arg_index,
370 fn_ty,
371 param_count,
372 });
373 }
374
375 // TODO support generic functions
376 const param_type = fn_ty.fnParamType(arg_index);
377 return mod.constType(scope, inst.base.src, param_type);
378}
379
380fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
381 // The bytes references memory inside the ZIR module, which can get deallocated
382 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
383 var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
384 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
385
386 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
387 ty_payload.* = .{ .len = arena_bytes.len };
388
389 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
390 bytes_payload.* = .{ .data = arena_bytes };
391
392 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
393 .ty = Type.initPayload(&ty_payload.base),
394 .val = Value.initPayload(&bytes_payload.base),
395 });
396 return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl);
397}
398
399fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
400 const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name);
401 const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
402 return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
403 try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
404 return mod.constVoid(scope, export_inst.base.src);
405}
406
407fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
408 return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
409}
410
411fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
412 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
413 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
414 const param_index = b.instructions.items.len;
415 const param_count = fn_ty.fnParamLen();
416 if (param_index >= param_count) {
417 return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
418 param_index,
419 param_count,
420 });
421 }
422 const param_type = fn_ty.fnParamType(param_index);
423 return mod.addNoOp(b, inst.base.src, param_type, .arg);
424}
425
426fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
427 const parent_block = scope.cast(Scope.Block).?;
428
429 // Reserve space for a Block instruction so that generated Break instructions can
430 // point to it, even if it doesn't end up getting used because the code ends up being
431 // comptime evaluated.
432 const block_inst = try parent_block.arena.create(Inst.Block);
433 block_inst.* = .{
434 .base = .{
435 .tag = Inst.Block.base_tag,
436 .ty = undefined, // Set after analysis.
437 .src = inst.base.src,
438 },
439 .body = undefined,
440 };
441
442 var child_block: Scope.Block = .{
443 .parent = parent_block,
444 .func = parent_block.func,
445 .decl = parent_block.decl,
446 .instructions = .{},
447 .arena = parent_block.arena,
448 // TODO @as here is working around a miscompilation compiler bug :(
449 .label = @as(?Scope.Block.Label, Scope.Block.Label{
450 .zir_block = inst,
451 .results = .{},
452 .block_inst = block_inst,
453 }),
454 };
455 const label = &child_block.label.?;
456
457 defer child_block.instructions.deinit(mod.gpa);
458 defer label.results.deinit(mod.gpa);
459
460 try analyzeBody(mod, &child_block.base, inst.positionals.body);
461
462 // Blocks must terminate with noreturn instruction.
463 assert(child_block.instructions.items.len != 0);
464 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
465
466 // Need to set the type and emit the Block instruction. This allows machine code generation
467 // to emit a jump instruction to after the block when it encounters the break.
468 try parent_block.instructions.append(mod.gpa, &block_inst.base);
469 block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);
470 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
471 return &block_inst.base;
472}
473
474fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
475 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
476 return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
477}
478
479fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
480 const operand = try resolveInst(mod, scope, inst.positionals.operand);
481 const block = inst.positionals.block;
482 return analyzeBreak(mod, scope, inst.base.src, block, operand);
483}
484
485fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
486 const block = inst.positionals.block;
487 const void_inst = try mod.constVoid(scope, inst.base.src);
488 return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
489}
490
491fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
492 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
493 return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
494}
495
496fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
497 const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
498 return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);
499}
500
501fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
502 return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
503}
504
505fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
506 const decl = try analyzeDeclVal(mod, scope, inst);
507 const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl);
508 return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
509}
510
511fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
512 const decl = inst.positionals.decl;
513 const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl);
514 return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
515}
516
517fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
518 const func = try resolveInst(mod, scope, inst.positionals.func);
519 if (func.ty.zigTypeTag() != .Fn)
520 return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
521
522 const cc = func.ty.fnCallingConvention();
523 if (cc == .Naked) {
524 // TODO add error note: declared here
525 return mod.fail(
526 scope,
527 inst.positionals.func.src,
528 "unable to call function with naked calling convention",
529 .{},
530 );
531 }
532 const call_params_len = inst.positionals.args.len;
533 const fn_params_len = func.ty.fnParamLen();
534 if (func.ty.fnIsVarArgs()) {
535 if (call_params_len < fn_params_len) {
536 // TODO add error note: declared here
537 return mod.fail(
538 scope,
539 inst.positionals.func.src,
540 "expected at least {} arguments, found {}",
541 .{ fn_params_len, call_params_len },
542 );
543 }
544 return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
545 } else if (fn_params_len != call_params_len) {
546 // TODO add error note: declared here
547 return mod.fail(
548 scope,
549 inst.positionals.func.src,
550 "expected {} arguments, found {}",
551 .{ fn_params_len, call_params_len },
552 );
553 }
554
555 if (inst.kw_args.modifier == .compile_time) {
556 return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
557 }
558 if (inst.kw_args.modifier != .auto) {
559 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
560 }
561
562 // TODO handle function calls of generic functions
563
564 const fn_param_types = try mod.gpa.alloc(Type, fn_params_len);
565 defer mod.gpa.free(fn_param_types);
566 func.ty.fnParamTypes(fn_param_types);
567
568 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
569 for (inst.positionals.args) |src_arg, i| {
570 const uncasted_arg = try resolveInst(mod, scope, src_arg);
571 casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg);
572 }
573
574 const ret_type = func.ty.fnReturnType();
575
576 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
577 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
578}
579
580fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
581 const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);
582 const fn_zir = blk: {
583 var fn_arena = std.heap.ArenaAllocator.init(mod.gpa);
584 errdefer fn_arena.deinit();
585
586 const fn_zir = try scope.arena().create(Module.Fn.ZIR);
587 fn_zir.* = .{
588 .body = .{
589 .instructions = fn_inst.positionals.body.instructions,
590 },
591 .arena = fn_arena.state,
592 };
593 break :blk fn_zir;
594 };
595 const new_func = try scope.arena().create(Module.Fn);
596 new_func.* = .{
597 .analysis = .{ .queued = fn_zir },
598 .owner_decl = scope.decl().?,
599 };
600 const fn_payload = try scope.arena().create(Value.Payload.Function);
601 fn_payload.* = .{ .func = new_func };
602 return mod.constInst(scope, fn_inst.base.src, .{
603 .ty = fn_type,
604 .val = Value.initPayload(&fn_payload.base),
605 });
606}
607
608fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
609 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
610}
611
612fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
613 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
614
615 // Hot path for some common function types.
616 if (fntype.positionals.param_types.len == 0) {
617 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
618 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
619 }
620
621 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
622 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
623 }
624
625 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
626 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
627 }
628
629 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
630 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
631 }
632 }
633
634 const arena = scope.arena();
635 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
636 for (fntype.positionals.param_types) |param_type, i| {
637 param_types[i] = try resolveType(mod, scope, param_type);
638 }
639
640 const payload = try arena.create(Type.Payload.Function);
641 payload.* = .{
642 .cc = fntype.kw_args.cc,
643 .return_type = return_type,
644 .param_types = param_types,
645 };
646 return mod.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
647}
648
649fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
650 return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
651}
652
653fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
654 const dest_type = try resolveType(mod, scope, as.positionals.lhs);
655 const new_inst = try resolveInst(mod, scope, as.positionals.rhs);
656 return mod.coerce(scope, dest_type, new_inst);
657}
658
659fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
660 const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand);
661 if (ptr.ty.zigTypeTag() != .Pointer) {
662 return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
663 }
664 // TODO handle known-pointer-address
665 const b = try mod.requireRuntimeBlock(scope, ptrtoint.base.src);
666 const ty = Type.initTag(.usize);
667 return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
668}
669
670fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
671 const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr);
672 const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name);
673
674 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
675 .Pointer => object_ptr.ty.elemType(),
676 else => return mod.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
677 };
678 switch (elem_ty.zigTypeTag()) {
679 .Array => {
680 if (mem.eql(u8, field_name, "len")) {
681 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
682 len_payload.* = .{ .int = elem_ty.arrayLen() };
683
684 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
685 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
686
687 return mod.constInst(scope, fieldptr.base.src, .{
688 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
689 .val = Value.initPayload(&ref_payload.base),
690 });
691 } else {
692 return mod.fail(
693 scope,
694 fieldptr.positionals.field_name.src,
695 "no member named '{}' in '{}'",
696 .{ field_name, elem_ty },
697 );
698 }
699 },
700 else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
701 }
702}
703
704fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
705 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
706 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
707
708 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
709 .ComptimeInt => true,
710 .Int => false,
711 else => return mod.fail(
712 scope,
713 inst.positionals.lhs.src,
714 "expected integer type, found '{}'",
715 .{
716 dest_type,
717 },
718 ),
719 };
720
721 switch (operand.ty.zigTypeTag()) {
722 .ComptimeInt, .Int => {},
723 else => return mod.fail(
724 scope,
725 inst.positionals.rhs.src,
726 "expected integer type, found '{}'",
727 .{operand.ty},
728 ),
729 }
730
731 if (operand.value() != null) {
732 return mod.coerce(scope, dest_type, operand);
733 } else if (dest_is_comptime_int) {
734 return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
735 }
736
737 return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
738}
739
740fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
741 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
742 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
743 return mod.bitcast(scope, dest_type, operand);
744}
745
746fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
747 const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
748 const operand = try resolveInst(mod, scope, inst.positionals.rhs);
749
750 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
751 .ComptimeFloat => true,
752 .Float => false,
753 else => return mod.fail(
754 scope,
755 inst.positionals.lhs.src,
756 "expected float type, found '{}'",
757 .{
758 dest_type,
759 },
760 ),
761 };
762
763 switch (operand.ty.zigTypeTag()) {
764 .ComptimeFloat, .Float, .ComptimeInt => {},
765 else => return mod.fail(
766 scope,
767 inst.positionals.rhs.src,
768 "expected float type, found '{}'",
769 .{operand.ty},
770 ),
771 }
772
773 if (operand.value() != null) {
774 return mod.coerce(scope, dest_type, operand);
775 } else if (dest_is_comptime_float) {
776 return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
777 }
778
779 return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
780}
781
782fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
783 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
784 const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
785 const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
786
787 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
788 if (array_ptr.value()) |array_ptr_val| {
789 if (elem_index.value()) |index_val| {
790 // Both array pointer and index are compile-time known.
791 const index_u64 = index_val.toUnsignedInt();
792 // @intCast here because it would have been impossible to construct a value that
793 // required a larger index.
794 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
795
796 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
797 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
798
799 return mod.constInst(scope, inst.base.src, .{
800 .ty = Type.initPayload(&type_payload.base),
801 .val = elem_ptr,
802 });
803 }
804 }
805 }
806
807 return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
808}
809
810fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
811 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
812}
813
814fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
815 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{});
816}
817
818fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
819 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
820}
821
822fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
823 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
824}
825
826fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
827 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{});
828}
829
830fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
831 const tracy = trace(@src());
832 defer tracy.end();
833
834 const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
835 const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
836
837 const instructions = &[_]*Inst{ lhs, rhs };
838 const resolved_type = try mod.resolvePeerTypes(scope, instructions);
839 const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
840 const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
841
842 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
843 resolved_type.elemType()
844 else
845 resolved_type;
846
847 const scalar_tag = scalar_type.zigTypeTag();
848
849 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
850 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
851 return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
852 lhs.ty.arrayLen(),
853 rhs.ty.arrayLen(),
854 });
855 }
856 return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
857 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
858 return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
859 lhs.ty,
860 rhs.ty,
861 });
862 }
863
864 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
865 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
866
867 if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
868 return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
869 }
870
871 if (casted_lhs.value()) |lhs_val| {
872 if (casted_rhs.value()) |rhs_val| {
873 return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
874 }
875 }
876
877 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
878 const ir_tag = switch (inst.base.tag) {
879 .add => Inst.Tag.add,
880 .sub => Inst.Tag.sub,
881 else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}),
882 };
883
884 return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
885}
886
887/// Analyzes operands that are known at comptime
888fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
889 // incase rhs is 0, simply return lhs without doing any calculations
890 // TODO Once division is implemented we should throw an error when dividing by 0.
891 if (rhs_val.compareWithZero(.eq)) {
892 return mod.constInst(scope, inst.base.src, .{
893 .ty = res_type,
894 .val = lhs_val,
895 });
896 }
897 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
898
899 const value = try switch (inst.base.tag) {
900 .add => blk: {
901 const val = if (is_int)
902 Module.intAdd(scope.arena(), lhs_val, rhs_val)
903 else
904 mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);
905 break :blk val;
906 },
907 .sub => blk: {
908 const val = if (is_int)
909 Module.intSub(scope.arena(), lhs_val, rhs_val)
910 else
911 mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);
912 break :blk val;
913 },
914 else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}),
915 };
916
917 return mod.constInst(scope, inst.base.src, .{
918 .ty = res_type,
919 .val = value,
920 });
921}
922
923fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
924 const ptr = try resolveInst(mod, scope, deref.positionals.operand);
925 return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src);
926}
927
928fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
929 const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
930 const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
931 const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;
932
933 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
934 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
935 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
936
937 for (inputs) |*elem, i| {
938 elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]);
939 }
940 for (clobbers) |*elem, i| {
941 elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]);
942 }
943 for (args) |*elem, i| {
944 const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);
945 elem.* = try mod.coerce(scope, Type.initTag(.usize), arg);
946 }
947
948 const b = try mod.requireRuntimeBlock(scope, assembly.base.src);
949 const inst = try b.arena.create(Inst.Assembly);
950 inst.* = .{
951 .base = .{
952 .tag = .assembly,
953 .ty = return_type,
954 .src = assembly.base.src,
955 },
956 .asm_source = asm_source,
957 .is_volatile = assembly.kw_args.@"volatile",
958 .output = output,
959 .inputs = inputs,
960 .clobbers = clobbers,
961 .args = args,
962 };
963 try b.instructions.append(mod.gpa, &inst.base);
964 return &inst.base;
965}
966
967fn analyzeInstCmp(
968 mod: *Module,
969 scope: *Scope,
970 inst: *zir.Inst.BinOp,
971 op: std.math.CompareOperator,
972) InnerError!*Inst {
973 const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
974 const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
975
976 const is_equality_cmp = switch (op) {
977 .eq, .neq => true,
978 else => false,
979 };
980 const lhs_ty_tag = lhs.ty.zigTypeTag();
981 const rhs_ty_tag = rhs.ty.zigTypeTag();
982 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
983 // null == null, null != null
984 return mod.constBool(scope, inst.base.src, op == .eq);
985 } else if (is_equality_cmp and
986 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
987 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
988 {
989 // comparing null with optionals
990 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
991 if (opt_operand.value()) |opt_val| {
992 const is_null = opt_val.isNull();
993 return mod.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
994 }
995 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
996 const inst_tag: Inst.Tag = switch (op) {
997 .eq => .isnull,
998 .neq => .isnonnull,
999 else => unreachable,
1000 };
1001 return mod.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
1002 } else if (is_equality_cmp and
1003 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
1004 {
1005 return mod.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
1006 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
1007 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
1008 return mod.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
1009 } else if (is_equality_cmp and
1010 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
1011 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
1012 {
1013 return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
1014 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
1015 if (!is_equality_cmp) {
1016 return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
1017 }
1018 return mod.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
1019 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
1020 // This operation allows any combination of integer and float types, regardless of the
1021 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
1022 // numeric types.
1023 return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
1024 }
1025 return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
1026}
1027
1028fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1029 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1030 return mod.constType(scope, inst.base.src, operand.ty);
1031}
1032
1033fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1034 const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand);
1035 const bool_type = Type.initTag(.bool);
1036 const operand = try mod.coerce(scope, bool_type, uncasted_operand);
1037 if (try mod.resolveDefinedValue(scope, operand)) |val| {
1038 return mod.constBool(scope, inst.base.src, !val.toBool());
1039 }
1040 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1041 return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
1042}
1043
1044fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
1045 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1046 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
1047}
1048
1049fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
1050 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
1051 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
1052
1053 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
1054 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
1055 try analyzeBody(mod, scope, body.*);
1056 return mod.constVoid(scope, inst.base.src);
1057 }
1058
1059 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1060
1061 var true_block: Scope.Block = .{
1062 .parent = parent_block,
1063 .func = parent_block.func,
1064 .decl = parent_block.decl,
1065 .instructions = .{},
1066 .arena = parent_block.arena,
1067 };
1068 defer true_block.instructions.deinit(mod.gpa);
1069 try analyzeBody(mod, &true_block.base, inst.positionals.then_body);
1070
1071 var false_block: Scope.Block = .{
1072 .parent = parent_block,
1073 .func = parent_block.func,
1074 .decl = parent_block.decl,
1075 .instructions = .{},
1076 .arena = parent_block.arena,
1077 };
1078 defer false_block.instructions.deinit(mod.gpa);
1079 try analyzeBody(mod, &false_block.base, inst.positionals.else_body);
1080
1081 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
1082 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
1083 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
1084}
1085
1086fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1087 return mod.analyzeUnreach(scope, unreach.base.src);
1088}
1089
1090fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1091 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
1092 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1093 if (mod.wantSafety(scope)) {
1094 // TODO Once we have a panic function to call, call it here instead of this.
1095 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
1096 }
1097 return mod.analyzeUnreach(scope, unreach.base.src);
1098}
1099
1100fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1101 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1102 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1103 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
1104}
1105
1106fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
1107 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1108 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
1109}
1110
1111fn floatOpAllowed(tag: zir.Inst.Tag) bool {
1112 // extend this swich as additional operators are implemented
1113 return switch (tag) {
1114 .add, .sub => true,
1115 else => false,
1116 };
1117}
1118
1119fn analyzeBreak(
1120 mod: *Module,
1121 scope: *Scope,
1122 src: usize,
1123 zir_block: *zir.Inst.Block,
1124 operand: *Inst,
1125) InnerError!*Inst {
1126 var opt_block = scope.cast(Scope.Block);
1127 while (opt_block) |block| {
1128 if (block.label) |*label| {
1129 if (label.zir_block == zir_block) {
1130 try label.results.append(mod.gpa, operand);
1131 const b = try mod.requireRuntimeBlock(scope, src);
1132 return mod.addBr(b, src, label.block_inst, operand);
1133 }
1134 }
1135 opt_block = block.parent;
1136 } else unreachable;
1137}
1138
1139fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
1140 const decl_name = inst.positionals.name;
1141 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
1142 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
1143 return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
1144
1145 const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl);
1146
1147 return decl;
1148}
1149
1150fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1151 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1152 const ty = try mod.singleConstPtrType(scope, inst.base.src, elem_type);
1153 return mod.constType(scope, inst.base.src, ty);
1154}
1155
1156fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1157 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1158 const ty = try mod.singleMutPtrType(scope, inst.base.src, elem_type);
1159 return mod.constType(scope, inst.base.src, ty);
1160}
src/all_types.hpp+1-1
...@@ -4110,7 +4110,7 @@ struct IrInstSrcCheckSwitchProngs {...@@ -4110,7 +4110,7 @@ struct IrInstSrcCheckSwitchProngs {
4110 IrInstSrc *target_value;4110 IrInstSrc *target_value;
4111 IrInstSrcCheckSwitchProngsRange *ranges;4111 IrInstSrcCheckSwitchProngsRange *ranges;
4112 size_t range_count;4112 size_t range_count;
4113 bool have_else_prong;4113 AstNode* else_prong;
4114 bool have_underscore_prong;4114 bool have_underscore_prong;
4115};4115};
41164116
src/analyze.cpp+14
...@@ -1490,6 +1490,20 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV...@@ -1490,6 +1490,20 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
1490}1490}
14911491
1492ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {1492ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
1493 Error err;
1494 // Hot path for simple identifiers, to avoid unnecessary memory allocations.
1495 if (node->type == NodeTypeSymbol) {
1496 Buf *variable_name = node->data.symbol_expr.symbol;
1497 if (buf_eql_str(variable_name, "_"))
1498 goto abort_hot_path;
1499 ZigType *primitive_type;
1500 if ((err = get_primitive_type(g, variable_name, &primitive_type))) {
1501 goto abort_hot_path;
1502 } else {
1503 return primitive_type;
1504 }
1505abort_hot_path:;
1506 }
1493 ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,1507 ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,
1494 nullptr, UndefBad);1508 nullptr, UndefBad);
1495 if (type_is_invalid(result->type))1509 if (type_is_invalid(result->type))
src/ir.cpp+58-16
...@@ -4300,14 +4300,14 @@ static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode...@@ -4300,14 +4300,14 @@ static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode
43004300
4301static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,4301static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
4302 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,4302 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
4303 bool have_else_prong, bool have_underscore_prong)4303 AstNode* else_prong, bool have_underscore_prong)
4304{4304{
4305 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(4305 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
4306 irb, scope, source_node);4306 irb, scope, source_node);
4307 instruction->target_value = target_value;4307 instruction->target_value = target_value;
4308 instruction->ranges = ranges;4308 instruction->ranges = ranges;
4309 instruction->range_count = range_count;4309 instruction->range_count = range_count;
4310 instruction->have_else_prong = have_else_prong;4310 instruction->else_prong = else_prong;
4311 instruction->have_underscore_prong = have_underscore_prong;4311 instruction->have_underscore_prong = have_underscore_prong;
43124312
4313 ir_ref_instruction(target_value, irb->current_basic_block);4313 ir_ref_instruction(target_value, irb->current_basic_block);
...@@ -9347,7 +9347,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n...@@ -9347,7 +9347,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
9347 }9347 }
93489348
9349 IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,9349 IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
9350 check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr);9350 check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
93519351
9352 IrInstSrc *br_instruction;9352 IrInstSrc *br_instruction;
9353 if (cases.length == 0) {9353 if (cases.length == 0) {
...@@ -20604,17 +20604,25 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20604,17 +20604,25 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20604 return ira->codegen->invalid_inst_gen;20604 return ira->codegen->invalid_inst_gen;
20605 }20605 }
2060620606
20607 ZigType *expected_return_type = result_loc->value->type->data.pointer.child_type;
20608
20607 IrInstGen *dummy_value = ir_const(ira, source_instr, return_type);20609 IrInstGen *dummy_value = ir_const(ira, source_instr, return_type);
20608 dummy_value->value->special = ConstValSpecialRuntime;20610 dummy_value->value->special = ConstValSpecialRuntime;
20609 IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr,20611 IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr,
20610 dummy_value, result_loc->value->type->data.pointer.child_type);20612 dummy_value, expected_return_type);
20611 if (type_is_invalid(dummy_result->value->type))20613 if (type_is_invalid(dummy_result->value->type)) {
20614 if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) &&
20615 expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet)
20616 {
20617 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
20618 ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));
20619 }
20612 return ira->codegen->invalid_inst_gen;20620 return ira->codegen->invalid_inst_gen;
20613 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
20614 if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
20615 res_child_type = return_type;
20616 }20621 }
20617 if (!handle_is_ptr(ira->codegen, res_child_type)) {20622 if (expected_return_type == ira->codegen->builtin_types.entry_anytype) {
20623 expected_return_type = return_type;
20624 }
20625 if (!handle_is_ptr(ira->codegen, expected_return_type)) {
20618 ir_reset_result(call_result_loc);20626 ir_reset_result(call_result_loc);
20619 result_loc = nullptr;20627 result_loc = nullptr;
20620 }20628 }
...@@ -28828,7 +28836,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28828,7 +28836,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28828 buf_ptr(enum_field->name)));28836 buf_ptr(enum_field->name)));
28829 }28837 }
28830 }28838 }
28831 } else if (!instruction->have_else_prong) {28839 } else if (instruction->else_prong == nullptr) {
28832 if (switch_type->data.enumeration.non_exhaustive) {28840 if (switch_type->data.enumeration.non_exhaustive) {
28833 ir_add_error(ira, &instruction->base.base,28841 ir_add_error(ira, &instruction->base.base,
28834 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));28842 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
...@@ -28843,6 +28851,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28843,6 +28851,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28843 buf_ptr(enum_field->name)));28851 buf_ptr(enum_field->name)));
28844 }28852 }
28845 }28853 }
28854 } else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) {
28855 ir_add_error_node(ira, instruction->else_prong,
28856 buf_sprintf("unreachable else prong, all cases already handled"));
28857 return ira->codegen->invalid_inst_gen;
28846 }28858 }
28847 } else if (switch_type->id == ZigTypeIdErrorSet) {28859 } else if (switch_type->id == ZigTypeIdErrorSet) {
28848 if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) {28860 if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) {
...@@ -28889,7 +28901,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28889,7 +28901,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28889 }28901 }
28890 field_prev_uses[start_index] = start_value->base.source_node;28902 field_prev_uses[start_index] = start_value->base.source_node;
28891 }28903 }
28892 if (!instruction->have_else_prong) {28904 if (instruction->else_prong == nullptr) {
28893 if (type_is_global_error_set(switch_type)) {28905 if (type_is_global_error_set(switch_type)) {
28894 ir_add_error(ira, &instruction->base.base,28906 ir_add_error(ira, &instruction->base.base,
28895 buf_sprintf("else prong required when switching on type 'anyerror'"));28907 buf_sprintf("else prong required when switching on type 'anyerror'"));
...@@ -28951,16 +28963,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28951,16 +28963,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28951 return ira->codegen->invalid_inst_gen;28963 return ira->codegen->invalid_inst_gen;
28952 }28964 }
28953 }28965 }
28954 if (!instruction->have_else_prong) {28966
28955 BigInt min_val;28967 BigInt min_val;
28956 eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);28968 eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
28957 BigInt max_val;28969 BigInt max_val;
28958 eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);28970 eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
28959 if (!rangeset_spans(&rs, &min_val, &max_val)) {28971 bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val);
28972 if (!handles_all_cases && instruction->else_prong == nullptr) {
28960 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));28973 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
28961 return ira->codegen->invalid_inst_gen;28974 return ira->codegen->invalid_inst_gen;
28975 } else if(handles_all_cases && instruction->else_prong != nullptr) {
28976 ir_add_error_node(ira, instruction->else_prong,
28977 buf_sprintf("unreachable else prong, all cases already handled"));
28978 return ira->codegen->invalid_inst_gen;
28962 }28979 }
28963 }
28964 } else if (switch_type->id == ZigTypeIdBool) {28980 } else if (switch_type->id == ZigTypeIdBool) {
28965 int seenTrue = 0;28981 int seenTrue = 0;
28966 int seenFalse = 0;28982 int seenFalse = 0;
...@@ -28990,11 +29006,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28990,11 +29006,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28990 return ira->codegen->invalid_inst_gen;29006 return ira->codegen->invalid_inst_gen;
28991 }29007 }
28992 }29008 }
28993 if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {29009 if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) {
28994 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));29010 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
28995 return ira->codegen->invalid_inst_gen;29011 return ira->codegen->invalid_inst_gen;
28996 }29012 }
28997 } else if (!instruction->have_else_prong) {29013
29014 if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) {
29015 ir_add_error_node(ira, instruction->else_prong,
29016 buf_sprintf("unreachable else prong, all cases already handled"));
29017 return ira->codegen->invalid_inst_gen;
29018 }
29019 } else if (instruction->else_prong == nullptr) {
28998 ir_add_error(ira, &instruction->base.base,29020 ir_add_error(ira, &instruction->base.base,
28999 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));29021 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
29000 return ira->codegen->invalid_inst_gen;29022 return ira->codegen->invalid_inst_gen;
...@@ -29077,6 +29099,19 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig...@@ -29077,6 +29099,19 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
29077 ZigType *result_type;29099 ZigType *result_type;
29078 uint32_t old_align_bytes;29100 uint32_t old_align_bytes;
2907929101
29102 ZigType *actual_ptr = target_type;
29103 if (actual_ptr->id == ZigTypeIdOptional) {
29104 actual_ptr = actual_ptr->data.maybe.child_type;
29105 } else if (is_slice(actual_ptr)) {
29106 actual_ptr = actual_ptr->data.structure.fields[slice_ptr_index]->type_entry;
29107 }
29108
29109 if (safety_check_on && !type_has_bits(ira->codegen, actual_ptr)) {
29110 ir_add_error(ira, &target->base,
29111 buf_sprintf("cannot adjust alignment of zero sized type '%s'", buf_ptr(&target_type->name)));
29112 return ira->codegen->invalid_inst_gen;
29113 }
29114
29080 if (target_type->id == ZigTypeIdPointer) {29115 if (target_type->id == ZigTypeIdPointer) {
29081 result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);29116 result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
29082 if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))29117 if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))
...@@ -30894,6 +30929,13 @@ static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndEx...@@ -30894,6 +30929,13 @@ static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndEx
30894 IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value,30929 IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value,
30895 instruction->result_loc->allow_write_through_const);30930 instruction->result_loc->allow_write_through_const);
30896 if (type_is_invalid(store_ptr->value->type)) {30931 if (type_is_invalid(store_ptr->value->type)) {
30932 if (instruction->result_loc->id == ResultLocIdReturn &&
30933 (value->value->type->id == ZigTypeIdErrorUnion || value->value->type->id == ZigTypeIdErrorSet) &&
30934 ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet)
30935 {
30936 add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
30937 ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));
30938 }
30897 return ira->codegen->invalid_inst_gen;30939 return ira->codegen->invalid_inst_gen;
30898 }30940 }
30899 }30941 }
src/ir_print.cpp+1-1
...@@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr...@@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
2175 fprintf(irp->f, "...");2175 fprintf(irp->f, "...");
2176 ir_print_other_inst_src(irp, instruction->ranges[i].end);2176 ir_print_other_inst_src(irp, instruction->ranges[i].end);
2177 }2177 }
2178 const char *have_else_str = instruction->have_else_prong ? "yes" : "no";2178 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
2179 fprintf(irp->f, ")else:%s", have_else_str);2179 fprintf(irp->f, ")else:%s", have_else_str);
2180}2180}
21812181
src/link.cpp+4
...@@ -2107,6 +2107,10 @@ static void construct_linker_job_wasm(LinkJob *lj) {...@@ -2107,6 +2107,10 @@ static void construct_linker_job_wasm(LinkJob *lj) {
2107 lj->args.append("-z");2107 lj->args.append("-z");
2108 lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size)));2108 lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size)));
21092109
2110 // put stack before globals so that stack overflow results in segfault immediately before corrupting globals
2111 // see https://github.com/ziglang/zig/issues/4496
2112 lj->args.append("--stack-first");
2113
2110 if (g->out_type != OutTypeExe) {2114 if (g->out_type != OutTypeExe) {
2111 lj->args.append("--no-entry"); // So lld doesn't look for _start.2115 lj->args.append("--no-entry"); // So lld doesn't look for _start.
21122116
src/zig_clang.cpp+8
...@@ -2823,6 +2823,14 @@ struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(...@@ -2823,6 +2823,14 @@ struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(
2823 return bitcast(casted->getBeginLoc());2823 return bitcast(casted->getBeginLoc());
2824}2824}
28252825
2826
2827enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(
2828 const struct ZigClangUnaryExprOrTypeTraitExpr *self)
2829{
2830 auto casted = reinterpret_cast<const clang::UnaryExprOrTypeTraitExpr *>(self);
2831 return (ZigClangUnaryExprOrTypeTrait_Kind)casted->getKind();
2832}
2833
2826const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *self) {2834const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *self) {
2827 auto casted = reinterpret_cast<const clang::DoStmt *>(self);2835 auto casted = reinterpret_cast<const clang::DoStmt *>(self);
2828 return reinterpret_cast<const struct ZigClangStmt *>(casted->getBody());2836 return reinterpret_cast<const struct ZigClangStmt *>(casted->getBody());
src/zig_clang.h+9
...@@ -901,6 +901,14 @@ enum ZigClangExpr_ConstExprUsage {...@@ -901,6 +901,14 @@ enum ZigClangExpr_ConstExprUsage {
901 ZigClangExpr_EvaluateForMangling,901 ZigClangExpr_EvaluateForMangling,
902};902};
903903
904enum ZigClangUnaryExprOrTypeTrait_Kind {
905 ZigClangUnaryExprOrTypeTrait_KindSizeOf,
906 ZigClangUnaryExprOrTypeTrait_KindAlignOf,
907 ZigClangUnaryExprOrTypeTrait_KindVecStep,
908 ZigClangUnaryExprOrTypeTrait_KindOpenMPRequiredSimdAlign,
909 ZigClangUnaryExprOrTypeTrait_KindPreferredAlignOf,
910};
911
904ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,912ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,
905 struct ZigClangSourceLocation Loc);913 struct ZigClangSourceLocation Loc);
906ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,914ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,
...@@ -1190,6 +1198,7 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getIdx(const...@@ -1190,6 +1198,7 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getIdx(const
11901198
1191ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(const struct ZigClangUnaryExprOrTypeTraitExpr *);1199ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(const struct ZigClangUnaryExprOrTypeTraitExpr *);
1192ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *);1200ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *);
1201ZIG_EXTERN_C enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(const struct ZigClangUnaryExprOrTypeTraitExpr *);
11931202
1194ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *);1203ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *);
1195ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *);1204ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *);
test/compile_errors.zig+144
...@@ -2,6 +2,32 @@ const tests = @import("tests.zig");...@@ -2,6 +2,32 @@ 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("@alignCast of zero sized types",
6 \\export fn foo() void {
7 \\ const a: *void = undefined;
8 \\ _ = @alignCast(2, a);
9 \\}
10 \\export fn bar() void {
11 \\ const a: ?*void = undefined;
12 \\ _ = @alignCast(2, a);
13 \\}
14 \\export fn baz() void {
15 \\ const a: []void = undefined;
16 \\ _ = @alignCast(2, a);
17 \\}
18 \\export fn qux() void {
19 \\ const a = struct {
20 \\ fn a(comptime b: u32) void {}
21 \\ }.a;
22 \\ _ = @alignCast(2, a);
23 \\}
24 , &[_][]const u8{
25 "tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void'",
26 "tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void'",
27 "tmp.zig:11:23: error: cannot adjust alignment of zero sized type '[]void'",
28 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
29 });
30
5 cases.addTest("invalid pointer with @Type",31 cases.addTest("invalid pointer with @Type",
6 \\export fn entry() void {32 \\export fn entry() void {
7 \\ _ = @Type(.{ .Pointer = .{33 \\ _ = @Type(.{ .Pointer = .{
...@@ -18,6 +44,28 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -18,6 +44,28 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
18 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",44 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
19 });45 });
2046
47 cases.addTest("helpful return type error message",
48 \\export fn foo() u32 {
49 \\ return error.Ohno;
50 \\}
51 \\fn bar() !u32 {
52 \\ return error.Ohno;
53 \\}
54 \\export fn baz() void {
55 \\ try bar();
56 \\}
57 \\export fn quux() u32 {
58 \\ return bar();
59 \\}
60 , &[_][]const u8{
61 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
62 "tmp.zig:1:17: note: function cannot return an error",
63 "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",
64 "tmp.zig:7:17: note: function cannot return an error",
65 "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
66 "tmp.zig:10:18: note: function cannot return an error",
67 });
68
21 cases.addTest("int/float conversion to comptime_int/float",69 cases.addTest("int/float conversion to comptime_int/float",
22 \\export fn foo() void {70 \\export fn foo() void {
23 \\ var a: f32 = 2;71 \\ var a: f32 = 2;
...@@ -509,6 +557,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -509,6 +557,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
509 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",557 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
510 });558 });
511559
560 cases.add("switch expression - unreachable else prong (bool)",
561 \\fn foo(x: bool) void {
562 \\ switch (x) {
563 \\ true => {},
564 \\ false => {},
565 \\ else => {},
566 \\ }
567 \\}
568 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
569 , &[_][]const u8{
570 "tmp.zig:5:9: error: unreachable else prong, all cases already handled",
571 });
572
573 cases.add("switch expression - unreachable else prong (u1)",
574 \\fn foo(x: u1) void {
575 \\ switch (x) {
576 \\ 0 => {},
577 \\ 1 => {},
578 \\ else => {},
579 \\ }
580 \\}
581 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
582 , &[_][]const u8{
583 "tmp.zig:5:9: error: unreachable else prong, all cases already handled",
584 });
585
586 cases.add("switch expression - unreachable else prong (u2)",
587 \\fn foo(x: u2) void {
588 \\ switch (x) {
589 \\ 0 => {},
590 \\ 1 => {},
591 \\ 2 => {},
592 \\ 3 => {},
593 \\ else => {},
594 \\ }
595 \\}
596 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
597 , &[_][]const u8{
598 "tmp.zig:7:9: error: unreachable else prong, all cases already handled",
599 });
600
601 cases.add("switch expression - unreachable else prong (range u8)",
602 \\fn foo(x: u8) void {
603 \\ switch (x) {
604 \\ 0 => {},
605 \\ 1 => {},
606 \\ 2 => {},
607 \\ 3 => {},
608 \\ 4...255 => {},
609 \\ else => {},
610 \\ }
611 \\}
612 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
613 , &[_][]const u8{
614 "tmp.zig:8:9: error: unreachable else prong, all cases already handled",
615 });
616
617 cases.add("switch expression - unreachable else prong (range i8)",
618 \\fn foo(x: i8) void {
619 \\ switch (x) {
620 \\ -128...0 => {},
621 \\ 1 => {},
622 \\ 2 => {},
623 \\ 3 => {},
624 \\ 4...127 => {},
625 \\ else => {},
626 \\ }
627 \\}
628 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
629 , &[_][]const u8{
630 "tmp.zig:8:9: error: unreachable else prong, all cases already handled",
631 });
632
633 cases.add("switch expression - unreachable else prong (enum)",
634 \\const TestEnum = enum{ T1, T2 };
635 \\
636 \\fn err(x: u8) TestEnum {
637 \\ switch (x) {
638 \\ 0 => return TestEnum.T1,
639 \\ else => return TestEnum.T2,
640 \\ }
641 \\}
642 \\
643 \\fn foo(x: u8) void {
644 \\ switch (err(x)) {
645 \\ TestEnum.T1 => {},
646 \\ TestEnum.T2 => {},
647 \\ else => {},
648 \\ }
649 \\}
650 \\
651 \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
652 , &[_][]const u8{
653 "tmp.zig:14:9: error: unreachable else prong, all cases already handled",
654 });
655
512 cases.addTest("@export with empty name string",656 cases.addTest("@export with empty name string",
513 \\pub export fn entry() void { }657 \\pub export fn entry() void { }
514 \\comptime {658 \\comptime {
test/stage1/behavior/bugs/1111.zig-1
...@@ -7,6 +7,5 @@ test "issue 1111 fixed" {...@@ -7,6 +7,5 @@ test "issue 1111 fixed" {
77
8 switch (v) {8 switch (v) {
9 Foo.Bar => return,9 Foo.Bar => return,
10 else => return,
11 }10 }
12}11}
test/stage2/compare_output.zig+105
...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{...@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
7 .os_tag = .linux,7 .os_tag = .linux,
8};8};
99
10const linux_riscv64 = std.zig.CrossTarget{
11 .cpu_arch = .riscv64,
12 .os_tag = .linux,
13};
14
10pub fn addCases(ctx: *TestContext) !void {15pub fn addCases(ctx: *TestContext) !void {
11 if (std.Target.current.os.tag != .linux or16 if (std.Target.current.os.tag != .linux or
12 std.Target.current.cpu.arch != .x86_64)17 std.Target.current.cpu.arch != .x86_64)
...@@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {
118 \\123 \\
119 );124 );
120 }125 }
126
127 {
128 var case = ctx.exe("hello world", linux_riscv64);
129 // Regular old hello world
130 case.addCompareOutput(
131 \\export fn _start() noreturn {
132 \\ print();
133 \\
134 \\ exit();
135 \\}
136 \\
137 \\fn print() void {
138 \\ asm volatile ("ecall"
139 \\ :
140 \\ : [number] "{a7}" (64),
141 \\ [arg1] "{a0}" (1),
142 \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
143 \\ [arg3] "{a2}" ("Hello, World!\n".len)
144 \\ : "rcx", "r11", "memory"
145 \\ );
146 \\ return;
147 \\}
148 \\
149 \\fn exit() noreturn {
150 \\ asm volatile ("ecall"
151 \\ :
152 \\ : [number] "{a7}" (94),
153 \\ [arg1] "{a0}" (0)
154 \\ : "rcx", "r11", "memory"
155 \\ );
156 \\ unreachable;
157 \\}
158 ,
159 "Hello, World!\n",
160 );
161 }
121162
122 {163 {
123 var case = ctx.exe("adding numbers at comptime", linux_x64);164 var case = ctx.exe("adding numbers at comptime", linux_x64);
...@@ -333,5 +374,69 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -333,5 +374,69 @@ pub fn addCases(ctx: *TestContext) !void {
333 ,374 ,
334 "",375 "",
335 );376 );
377
378 // Now we test integer return values.
379 case.addCompareOutput(
380 \\export fn _start() noreturn {
381 \\ assert(add(3, 4) == 7);
382 \\ assert(add(20, 10) == 30);
383 \\
384 \\ exit();
385 \\}
386 \\
387 \\fn add(a: u32, b: u32) u32 {
388 \\ return a + b;
389 \\}
390 \\
391 \\pub fn assert(ok: bool) void {
392 \\ if (!ok) unreachable; // assertion failure
393 \\}
394 \\
395 \\fn exit() noreturn {
396 \\ asm volatile ("syscall"
397 \\ :
398 \\ : [number] "{rax}" (231),
399 \\ [arg1] "{rdi}" (0)
400 \\ : "rcx", "r11", "memory"
401 \\ );
402 \\ unreachable;
403 \\}
404 ,
405 "",
406 );
407
408 // Local mutable variables.
409 case.addCompareOutput(
410 \\export fn _start() noreturn {
411 \\ assert(add(3, 4) == 7);
412 \\ assert(add(20, 10) == 30);
413 \\
414 \\ exit();
415 \\}
416 \\
417 \\fn add(a: u32, b: u32) u32 {
418 \\ var x: u32 = undefined;
419 \\ x = 0;
420 \\ x += a;
421 \\ x += b;
422 \\ return x;
423 \\}
424 \\
425 \\pub fn assert(ok: bool) void {
426 \\ if (!ok) unreachable; // assertion failure
427 \\}
428 \\
429 \\fn exit() noreturn {
430 \\ asm volatile ("syscall"
431 \\ :
432 \\ : [number] "{rax}" (231),
433 \\ [arg1] "{rdi}" (0)
434 \\ : "rcx", "r11", "memory"
435 \\ );
436 \\ unreachable;
437 \\}
438 ,
439 "",
440 );
336 }441 }
337}442}
test/translate_c.zig+20
...@@ -3,6 +3,16 @@ const std = @import("std");...@@ -3,6 +3,16 @@ 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("alignof",
7 \\int main() {
8 \\ int a = _Alignof(int);
9 \\}
10 , &[_][]const u8{
11 \\pub export fn main() c_int {
12 \\ var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));
13 \\}
14 });
15
6 cases.add("initializer list macro",16 cases.add("initializer list macro",
7 \\typedef struct Color {17 \\typedef struct Color {
8 \\ unsigned char r;18 \\ unsigned char r;
...@@ -2715,6 +2725,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2715,6 +2725,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2715 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));2725 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
2716 });2726 });
27172727
2728 cases.add("macro with cast to unsigned short, long, and long long",
2729 \\#define CURLAUTH_BASIC_BUT_USHORT ((unsigned short) 1)
2730 \\#define CURLAUTH_BASIC ((unsigned long) 1)
2731 \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
2732 , &[_][]const u8{
2733 \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1));
2734 \\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1));
2735 \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1));
2736 });
2737
2718 cases.add("macro conditional operator",2738 cases.add("macro conditional operator",
2719 \\#define FOO a ? b : c2739 \\#define FOO a ? b : c
2720 , &[_][]const u8{2740 , &[_][]const u8{