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 {
7777 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
7878 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);
8083 exe.addBuildOption(bool, "enable_tracy", tracy != null);
8184 if (tracy) |tracy_path| {
8285 const client_cpp = fs.path.join(
......@@ -104,6 +107,12 @@ pub fn build(b: *Builder) !void {
104107 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
105108 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
107116 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
108117 test_stage2_step.dependOn(&test_stage2.step);
109118 test_step.dependOn(test_stage2_step);
ci/azure/pipelines.yml+1-1
......@@ -41,7 +41,7 @@ jobs:
4141
4242 steps:
4343 - 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")
4545 .\sfx.exe -y -o\
4646 del sfx.exe
4747 displayName: Download/Extract/Install MSYS2
doc/langref.html.in+2-1
......@@ -2,6 +2,7 @@
22<html lang="en">
33 <head>
44 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
56 <title>Documentation - The Zig Programming Language</title>
67 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/>
78 <style>
......@@ -7473,7 +7474,7 @@ export fn @"A function name that is a complete sentence."() void {}
74737474 <p>
74747475 When looking at the resulting object, you can see the symbol is used verbatim:
74757476 </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>
74777478 {#see_also|Exporting a C Library#}
74787479 {#header_close#}
74797480
lib/std/array_list.zig+86
......@@ -108,6 +108,33 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
108108 mem.copy(T, self.items[i .. i + items.len], items);
109109 }
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
111138 /// Extend the list by 1 element. Allocates more memory as necessary.
112139 pub fn append(self: *Self, item: T) !void {
113140 const new_item_ptr = try self.addOne();
......@@ -189,6 +216,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
189216 mem.set(T, self.items[old_len..self.items.len], value);
190217 }
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
192228 /// Adjust the list's length to `new_len`.
193229 /// Does not initialize added items if any.
194230 pub fn resize(self: *Self, new_len: usize) !void {
......@@ -366,6 +402,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
366402 mem.copy(T, self.items[i .. i + items.len], items);
367403 }
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
369414 /// Extend the list by 1 element. Allocates more memory as necessary.
370415 pub fn append(self: *Self, allocator: *Allocator, item: T) !void {
371416 const new_item_ptr = try self.addOne(allocator);
......@@ -437,6 +482,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
437482 mem.set(T, self.items[old_len..self.items.len], value);
438483 }
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
440494 /// Adjust the list's length to `new_len`.
441495 /// Does not initialize added items if any.
442496 pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
......@@ -714,6 +768,38 @@ test "std.ArrayList.insertSlice" {
714768 testing.expect(list.items[0] == 1);
715769}
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
717803const Item = struct {
718804 integer: i32,
719805 sub_items: ArrayList(Item),
lib/std/build.zig+41-24
......@@ -430,9 +430,9 @@ pub const Builder = struct {
430430 const entry = self.user_input_options.getEntry(name) orelse return null;
431431 entry.value.used = true;
432432 switch (type_id) {
433 TypeId.Bool => switch (entry.value.value) {
434 UserValue.Flag => return true,
435 UserValue.Scalar => |s| {
433 .Bool => switch (entry.value.value) {
434 .Flag => return true,
435 .Scalar => |s| {
436436 if (mem.eql(u8, s, "true")) {
437437 return true;
438438 } else if (mem.eql(u8, s, "false")) {
......@@ -443,21 +443,21 @@ pub const Builder = struct {
443443 return null;
444444 }
445445 },
446 UserValue.List => {
446 .List => {
447447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
448448 self.markInvalidUserInput();
449449 return null;
450450 },
451451 },
452 TypeId.Int => panic("TODO integer options to build script", .{}),
453 TypeId.Float => panic("TODO float options to build script", .{}),
454 TypeId.Enum => switch (entry.value.value) {
455 UserValue.Flag => {
452 .Int => panic("TODO integer options to build script", .{}),
453 .Float => panic("TODO float options to build script", .{}),
454 .Enum => switch (entry.value.value) {
455 .Flag => {
456456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
457457 self.markInvalidUserInput();
458458 return null;
459459 },
460 UserValue.Scalar => |s| {
460 .Scalar => |s| {
461461 if (std.meta.stringToEnum(T, s)) |enum_lit| {
462462 return enum_lit;
463463 } else {
......@@ -466,33 +466,35 @@ pub const Builder = struct {
466466 return null;
467467 }
468468 },
469 UserValue.List => {
469 .List => {
470470 warn("Expected -D{} to be a string, but received a list.\n", .{name});
471471 self.markInvalidUserInput();
472472 return null;
473473 },
474474 },
475 TypeId.String => switch (entry.value.value) {
476 UserValue.Flag => {
475 .String => switch (entry.value.value) {
476 .Flag => {
477477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
478478 self.markInvalidUserInput();
479479 return null;
480480 },
481 UserValue.List => {
481 .List => {
482482 warn("Expected -D{} to be a string, but received a list.\n", .{name});
483483 self.markInvalidUserInput();
484484 return null;
485485 },
486 UserValue.Scalar => |s| return s,
486 .Scalar => |s| return s,
487487 },
488 TypeId.List => switch (entry.value.value) {
489 UserValue.Flag => {
488 .List => switch (entry.value.value) {
489 .Flag => {
490490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
491491 self.markInvalidUserInput();
492492 return null;
493493 },
494 UserValue.Scalar => |s| return &[_][]const u8{s},
495 UserValue.List => |lst| return lst.span(),
494 .Scalar => |s| {
495 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
496 },
497 .List => |lst| return lst.span(),
496498 },
497499 }
498500 }
......@@ -1151,6 +1153,7 @@ pub const LibExeObjStep = struct {
11511153 bundle_compiler_rt: bool,
11521154 disable_stack_probing: bool,
11531155 disable_sanitize_c: bool,
1156 rdynamic: bool,
11541157 c_std: Builder.CStd,
11551158 override_lib_dir: ?[]const u8,
11561159 main_pkg_path: ?[]const u8,
......@@ -1311,6 +1314,7 @@ pub const LibExeObjStep = struct {
13111314 .bundle_compiler_rt = false,
13121315 .disable_stack_probing = false,
13131316 .disable_sanitize_c = false,
1317 .rdynamic = false,
13141318 .output_dir = null,
13151319 .single_threaded = false,
13161320 .installed_path = null,
......@@ -1704,13 +1708,23 @@ pub const LibExeObjStep = struct {
17041708
17051709 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
17061710 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 }
17071721 switch (@typeInfo(T)) {
17081722 .Enum => |enum_info| {
1709 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
1723 out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
17101724 inline for (enum_info.fields) |field| {
17111725 out.print(" {},\n", .{field.name}) catch unreachable;
17121726 }
1713 out.print("}};\n", .{}) catch unreachable;
1727 out.writeAll("};\n") catch unreachable;
17141728 },
17151729 else => {},
17161730 }
......@@ -1843,10 +1857,10 @@ pub const LibExeObjStep = struct {
18431857 zig_args.append(builder.zig_exe) catch unreachable;
18441858
18451859 const cmd = switch (self.kind) {
1846 Kind.Lib => "build-lib",
1847 Kind.Exe => "build-exe",
1848 Kind.Obj => "build-obj",
1849 Kind.Test => "test",
1860 .Lib => "build-lib",
1861 .Exe => "build-exe",
1862 .Obj => "build-obj",
1863 .Test => "test",
18501864 };
18511865 zig_args.append(cmd) catch unreachable;
18521866
......@@ -1994,6 +2008,9 @@ pub const LibExeObjStep = struct {
19942008 if (self.disable_sanitize_c) {
19952009 try zig_args.append("-fno-sanitize-c");
19962010 }
2011 if (self.rdynamic) {
2012 try zig_args.append("-rdynamic");
2013 }
19972014
19982015 if (self.code_model != .default) {
19992016 try zig_args.append("-code-model");
lib/std/build/emit_raw.zig+5-3
......@@ -46,9 +46,10 @@ const BinaryElfOutput = struct {
4646 .segments = ArrayList(*BinaryElfSegment).init(allocator),
4747 .sections = ArrayList(*BinaryElfSection).init(allocator),
4848 };
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| {
5253 if (sectionValidForOutput(section)) {
5354 const newSection = try allocator.create(BinaryElfSection);
5455
......@@ -61,7 +62,8 @@ const BinaryElfOutput = struct {
6162 }
6263 }
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| {
6567 if (phdr.p_type == elf.PT_LOAD) {
6668 const newSegment = try allocator.create(BinaryElfSegment);
6769
lib/std/build/run.zig+18
......@@ -4,6 +4,7 @@ const build = std.build;
44const Step = build.Step;
55const Builder = build.Builder;
66const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
78const fs = std.fs;
89const mem = std.mem;
910const process = std.process;
......@@ -42,6 +43,10 @@ pub const RunStep = struct {
4243
4344 pub const Arg = union(enum) {
4445 Artifact: *LibExeObjStep,
46 WriteFile: struct {
47 step: *WriteFileStep,
48 file_name: []const u8,
49 },
4550 Bytes: []u8,
4651 };
4752
......@@ -62,6 +67,16 @@ pub const RunStep = struct {
6267 self.step.dependOn(&artifact.step);
6368 }
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
6580 pub fn addArg(self: *RunStep, arg: []const u8) void {
6681 self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable;
6782 }
......@@ -142,6 +157,9 @@ pub const RunStep = struct {
142157 for (self.argv.span()) |arg| {
143158 switch (arg) {
144159 Arg.Bytes => |bytes| try argv_list.append(bytes),
160 Arg.WriteFile => |file| {
161 try argv_list.append(file.step.getOutputPath(file.file_name));
162 },
145163 Arg.Artifact => |artifact| {
146164 if (artifact.target.isWindows()) {
147165 // 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;
88pub const parse = @import("c/parse.zig").parse;
99pub const ast = @import("c/ast.zig");
1010
11test "" {
12 _ = tokenizer;
13}
14
1115pub usingnamespace @import("os/bits.zig");
1216
1317pub usingnamespace switch (std.Target.current.os.tag) {
lib/std/c/tokenizer.zig+76-115
......@@ -1,19 +1,10 @@
11const std = @import("std");
22const 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
124pub const Token = struct {
135 id: Id,
146 start: usize,
157 end: usize,
16 source: *Source,
178
189 pub const Id = union(enum) {
1910 Invalid,
......@@ -251,31 +242,6 @@ pub const Token = struct {
251242 }
252243 };
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
279245 // TODO extensions
280246 pub const keywords = std.ComptimeStringMap(Id, .{
281247 .{ "auto", .Keyword_auto },
......@@ -355,26 +321,26 @@ pub const Token = struct {
355321 }
356322
357323 pub const NumSuffix = enum {
358 None,
359 F,
360 L,
361 U,
362 LU,
363 LL,
364 LLU,
324 none,
325 f,
326 l,
327 u,
328 lu,
329 ll,
330 llu,
365331 };
366332
367333 pub const StrKind = enum {
368 None,
369 Wide,
370 Utf8,
371 Utf16,
372 Utf32,
334 none,
335 wide,
336 utf_8,
337 utf_16,
338 utf_32,
373339 };
374340};
375341
376342pub const Tokenizer = struct {
377 source: *Source,
343 buffer: []const u8,
378344 index: usize = 0,
379345 prev_tok_id: @TagType(Token.Id) = .Invalid,
380346 pp_directive: bool = false,
......@@ -385,7 +351,6 @@ pub const Tokenizer = struct {
385351 .id = .Eof,
386352 .start = self.index,
387353 .end = undefined,
388 .source = self.source,
389354 };
390355 var state: enum {
391356 Start,
......@@ -446,8 +411,8 @@ pub const Tokenizer = struct {
446411 } = .Start;
447412 var string = false;
448413 var counter: u32 = 0;
449 while (self.index < self.source.buffer.len) : (self.index += 1) {
450 const c = self.source.buffer[self.index];
414 while (self.index < self.buffer.len) : (self.index += 1) {
415 const c = self.buffer[self.index];
451416 switch (state) {
452417 .Start => switch (c) {
453418 '\n' => {
......@@ -460,11 +425,11 @@ pub const Tokenizer = struct {
460425 state = .Cr;
461426 },
462427 '"' => {
463 result.id = .{ .StringLiteral = .None };
428 result.id = .{ .StringLiteral = .none };
464429 state = .StringLiteral;
465430 },
466431 '\'' => {
467 result.id = .{ .CharLiteral = .None };
432 result.id = .{ .CharLiteral = .none };
468433 state = .CharLiteralStart;
469434 },
470435 'u' => {
......@@ -641,11 +606,11 @@ pub const Tokenizer = struct {
641606 state = .u8;
642607 },
643608 '\'' => {
644 result.id = .{ .CharLiteral = .Utf16 };
609 result.id = .{ .CharLiteral = .utf_16 };
645610 state = .CharLiteralStart;
646611 },
647612 '\"' => {
648 result.id = .{ .StringLiteral = .Utf16 };
613 result.id = .{ .StringLiteral = .utf_16 };
649614 state = .StringLiteral;
650615 },
651616 else => {
......@@ -655,7 +620,7 @@ pub const Tokenizer = struct {
655620 },
656621 .u8 => switch (c) {
657622 '\"' => {
658 result.id = .{ .StringLiteral = .Utf8 };
623 result.id = .{ .StringLiteral = .utf_8 };
659624 state = .StringLiteral;
660625 },
661626 else => {
......@@ -665,11 +630,11 @@ pub const Tokenizer = struct {
665630 },
666631 .U => switch (c) {
667632 '\'' => {
668 result.id = .{ .CharLiteral = .Utf32 };
633 result.id = .{ .CharLiteral = .utf_32 };
669634 state = .CharLiteralStart;
670635 },
671636 '\"' => {
672 result.id = .{ .StringLiteral = .Utf32 };
637 result.id = .{ .StringLiteral = .utf_32 };
673638 state = .StringLiteral;
674639 },
675640 else => {
......@@ -679,11 +644,11 @@ pub const Tokenizer = struct {
679644 },
680645 .L => switch (c) {
681646 '\'' => {
682 result.id = .{ .CharLiteral = .Wide };
647 result.id = .{ .CharLiteral = .wide };
683648 state = .CharLiteralStart;
684649 },
685650 '\"' => {
686 result.id = .{ .StringLiteral = .Wide };
651 result.id = .{ .StringLiteral = .wide };
687652 state = .StringLiteral;
688653 },
689654 else => {
......@@ -808,7 +773,7 @@ pub const Tokenizer = struct {
808773 .Identifier => switch (c) {
809774 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
810775 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;
812777 if (self.prev_tok_id == .Hash)
813778 self.pp_directive = true;
814779 break;
......@@ -1137,7 +1102,7 @@ pub const Tokenizer = struct {
11371102 state = .IntegerSuffixL;
11381103 },
11391104 else => {
1140 result.id = .{ .IntegerLiteral = .None };
1105 result.id = .{ .IntegerLiteral = .none };
11411106 break;
11421107 },
11431108 },
......@@ -1146,7 +1111,7 @@ pub const Tokenizer = struct {
11461111 state = .IntegerSuffixUL;
11471112 },
11481113 else => {
1149 result.id = .{ .IntegerLiteral = .U };
1114 result.id = .{ .IntegerLiteral = .u };
11501115 break;
11511116 },
11521117 },
......@@ -1155,34 +1120,34 @@ pub const Tokenizer = struct {
11551120 state = .IntegerSuffixLL;
11561121 },
11571122 'u', 'U' => {
1158 result.id = .{ .IntegerLiteral = .LU };
1123 result.id = .{ .IntegerLiteral = .lu };
11591124 self.index += 1;
11601125 break;
11611126 },
11621127 else => {
1163 result.id = .{ .IntegerLiteral = .L };
1128 result.id = .{ .IntegerLiteral = .l };
11641129 break;
11651130 },
11661131 },
11671132 .IntegerSuffixLL => switch (c) {
11681133 'u', 'U' => {
1169 result.id = .{ .IntegerLiteral = .LLU };
1134 result.id = .{ .IntegerLiteral = .llu };
11701135 self.index += 1;
11711136 break;
11721137 },
11731138 else => {
1174 result.id = .{ .IntegerLiteral = .LL };
1139 result.id = .{ .IntegerLiteral = .ll };
11751140 break;
11761141 },
11771142 },
11781143 .IntegerSuffixUL => switch (c) {
11791144 'l', 'L' => {
1180 result.id = .{ .IntegerLiteral = .LLU };
1145 result.id = .{ .IntegerLiteral = .llu };
11811146 self.index += 1;
11821147 break;
11831148 },
11841149 else => {
1185 result.id = .{ .IntegerLiteral = .LU };
1150 result.id = .{ .IntegerLiteral = .lu };
11861151 break;
11871152 },
11881153 },
......@@ -1230,26 +1195,26 @@ pub const Tokenizer = struct {
12301195 },
12311196 .FloatSuffix => switch (c) {
12321197 'l', 'L' => {
1233 result.id = .{ .FloatLiteral = .L };
1198 result.id = .{ .FloatLiteral = .l };
12341199 self.index += 1;
12351200 break;
12361201 },
12371202 'f', 'F' => {
1238 result.id = .{ .FloatLiteral = .F };
1203 result.id = .{ .FloatLiteral = .f };
12391204 self.index += 1;
12401205 break;
12411206 },
12421207 else => {
1243 result.id = .{ .FloatLiteral = .None };
1208 result.id = .{ .FloatLiteral = .none };
12441209 break;
12451210 },
12461211 },
12471212 }
1248 } else if (self.index == self.source.buffer.len) {
1213 } else if (self.index == self.buffer.len) {
12491214 switch (state) {
12501215 .Start => {},
12511216 .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;
12531218 },
12541219
12551220 .Cr,
......@@ -1270,11 +1235,11 @@ pub const Tokenizer = struct {
12701235 .MacroString,
12711236 => 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
12751240 .FloatFraction,
12761241 .FloatFractionHex,
1277 => result.id = .{ .FloatLiteral = .None },
1242 => result.id = .{ .FloatLiteral = .none },
12781243
12791244 .IntegerLiteralOct,
12801245 .IntegerLiteralBinary,
......@@ -1282,13 +1247,13 @@ pub const Tokenizer = struct {
12821247 .IntegerLiteral,
12831248 .IntegerSuffix,
12841249 .Zero,
1285 => result.id = .{ .IntegerLiteral = .None },
1286 .IntegerSuffixU => result.id = .{ .IntegerLiteral = .U },
1287 .IntegerSuffixL => result.id = .{ .IntegerLiteral = .L },
1288 .IntegerSuffixLL => result.id = .{ .IntegerLiteral = .LL },
1289 .IntegerSuffixUL => result.id = .{ .IntegerLiteral = .LU },
1250 => result.id = .{ .IntegerLiteral = .none },
1251 .IntegerSuffixU => result.id = .{ .IntegerLiteral = .u },
1252 .IntegerSuffixL => result.id = .{ .IntegerLiteral = .l },
1253 .IntegerSuffixLL => result.id = .{ .IntegerLiteral = .ll },
1254 .IntegerSuffixUL => result.id = .{ .IntegerLiteral = .lu },
12901255
1291 .FloatSuffix => result.id = .{ .FloatLiteral = .None },
1256 .FloatSuffix => result.id = .{ .FloatLiteral = .none },
12921257 .Equal => result.id = .Equal,
12931258 .Bang => result.id = .Bang,
12941259 .Minus => result.id = .Minus,
......@@ -1466,7 +1431,7 @@ test "preprocessor keywords" {
14661431 .Hash,
14671432 .Identifier,
14681433 .AngleBracketLeft,
1469 .{ .IntegerLiteral = .None },
1434 .{ .IntegerLiteral = .none },
14701435 .Nl,
14711436 .Hash,
14721437 .Keyword_ifdef,
......@@ -1499,18 +1464,18 @@ test "line continuation" {
14991464 .Identifier,
15001465 .Identifier,
15011466 .Nl,
1502 .{ .StringLiteral = .None },
1467 .{ .StringLiteral = .none },
15031468 .Nl,
15041469 .Hash,
15051470 .Keyword_define,
1506 .{ .StringLiteral = .None },
1471 .{ .StringLiteral = .none },
15071472 .Nl,
1508 .{ .StringLiteral = .None },
1473 .{ .StringLiteral = .none },
15091474 .Nl,
15101475 .Hash,
15111476 .Keyword_define,
1512 .{ .StringLiteral = .None },
1513 .{ .StringLiteral = .None },
1477 .{ .StringLiteral = .none },
1478 .{ .StringLiteral = .none },
15141479 });
15151480}
15161481
......@@ -1527,23 +1492,23 @@ test "string prefix" {
15271492 \\L'foo'
15281493 \\
15291494 , &[_]Token.Id{
1530 .{ .StringLiteral = .None },
1495 .{ .StringLiteral = .none },
15311496 .Nl,
1532 .{ .StringLiteral = .Utf16 },
1497 .{ .StringLiteral = .utf_16 },
15331498 .Nl,
1534 .{ .StringLiteral = .Utf8 },
1499 .{ .StringLiteral = .utf_8 },
15351500 .Nl,
1536 .{ .StringLiteral = .Utf32 },
1501 .{ .StringLiteral = .utf_32 },
15371502 .Nl,
1538 .{ .StringLiteral = .Wide },
1503 .{ .StringLiteral = .wide },
15391504 .Nl,
1540 .{ .CharLiteral = .None },
1505 .{ .CharLiteral = .none },
15411506 .Nl,
1542 .{ .CharLiteral = .Utf16 },
1507 .{ .CharLiteral = .utf_16 },
15431508 .Nl,
1544 .{ .CharLiteral = .Utf32 },
1509 .{ .CharLiteral = .utf_32 },
15451510 .Nl,
1546 .{ .CharLiteral = .Wide },
1511 .{ .CharLiteral = .wide },
15471512 .Nl,
15481513 });
15491514}
......@@ -1555,33 +1520,29 @@ test "num suffixes" {
15551520 \\ 1u 1ul 1ull 1
15561521 \\
15571522 , &[_]Token.Id{
1558 .{ .FloatLiteral = .F },
1559 .{ .FloatLiteral = .L },
1560 .{ .FloatLiteral = .None },
1561 .{ .FloatLiteral = .None },
1562 .{ .FloatLiteral = .None },
1523 .{ .FloatLiteral = .f },
1524 .{ .FloatLiteral = .l },
1525 .{ .FloatLiteral = .none },
1526 .{ .FloatLiteral = .none },
1527 .{ .FloatLiteral = .none },
15631528 .Nl,
1564 .{ .IntegerLiteral = .L },
1565 .{ .IntegerLiteral = .LU },
1566 .{ .IntegerLiteral = .LL },
1567 .{ .IntegerLiteral = .LLU },
1568 .{ .IntegerLiteral = .None },
1529 .{ .IntegerLiteral = .l },
1530 .{ .IntegerLiteral = .lu },
1531 .{ .IntegerLiteral = .ll },
1532 .{ .IntegerLiteral = .llu },
1533 .{ .IntegerLiteral = .none },
15691534 .Nl,
1570 .{ .IntegerLiteral = .U },
1571 .{ .IntegerLiteral = .LU },
1572 .{ .IntegerLiteral = .LLU },
1573 .{ .IntegerLiteral = .None },
1535 .{ .IntegerLiteral = .u },
1536 .{ .IntegerLiteral = .lu },
1537 .{ .IntegerLiteral = .llu },
1538 .{ .IntegerLiteral = .none },
15741539 .Nl,
15751540 });
15761541}
15771542
15781543fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void {
15791544 var tokenizer = Tokenizer{
1580 .source = &Source{
1581 .buffer = source,
1582 .file_name = undefined,
1583 .tokens = undefined,
1584 },
1545 .buffer = source,
15851546 };
15861547 for (expected_tokens) |expected_token_id| {
15871548 const token = tokenizer.next();
lib/std/child_process.zig+9-13
......@@ -364,6 +364,7 @@ pub const ChildProcess = struct {
364364 error.FileTooBig => unreachable,
365365 error.DeviceBusy => unreachable,
366366 error.FileLocksNotSupported => unreachable,
367 error.BadPathName => unreachable, // Windows-only
367368 else => |e| return e,
368369 }
369370 else
......@@ -480,25 +481,20 @@ pub const ChildProcess = struct {
480481
481482 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
484484 const nul_handle = if (any_ignore)
485 windows.CreateFile(
486 "NUL",
487 windows.GENERIC_READ,
488 windows.FILE_SHARE_READ,
489 null,
490 windows.OPEN_EXISTING,
491 windows.FILE_ATTRIBUTE_NORMAL,
492 null,
493 ) catch |err| switch (err) {
494 error.SharingViolation => unreachable, // not possible for "NUL"
485 windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
486 .dir = std.fs.cwd().fd,
487 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
488 .share_access = windows.FILE_SHARE_READ,
489 .creation = windows.OPEN_EXISTING,
490 .io_mode = .blocking,
491 }) catch |err| switch (err) {
495492 error.PathAlreadyExists => unreachable, // not possible for "NUL"
496493 error.PipeBusy => unreachable, // not possible for "NUL"
497 error.InvalidUtf8 => unreachable, // not possible for "NUL"
498 error.BadPathName => unreachable, // not possible for "NUL"
499494 error.FileNotFound => unreachable, // not possible for "NUL"
500495 error.AccessDenied => unreachable, // not possible for "NUL"
501496 error.NameTooLong => unreachable, // not possible for "NUL"
497 error.WouldBlock => unreachable, // not possible for "NUL"
502498 else => |e| return e,
503499 }
504500 else
lib/std/crypto/blake2.zig+224-27
......@@ -1,8 +1,7 @@
11const mem = @import("../mem.zig");
2const math = @import("../math.zig");
3const endian = @import("../endian.zig");
4const debug = @import("../debug.zig");
52const builtin = @import("builtin");
3const debug = @import("../debug.zig");
4const math = @import("../math.zig");
65const htest = @import("test.zig");
76
87const RoundParam = struct {
......@@ -31,7 +30,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
3130pub const Blake2s224 = Blake2s(224);
3231pub const Blake2s256 = Blake2s(256);
3332
34fn Blake2s(comptime out_len: usize) type {
33pub fn Blake2s(comptime out_len: usize) type {
3534 return struct {
3635 const Self = @This();
3736 pub const block_length = 64;
......@@ -67,10 +66,17 @@ fn Blake2s(comptime out_len: usize) type {
6766 buf: [64]u8,
6867 buf_len: u8,
6968
69 key: []const u8,
70
7071 pub fn init() Self {
72 return init_keyed("");
73 }
74
75 pub fn init_keyed(key: []const u8) Self {
7176 debug.assert(8 <= out_len and out_len <= 512);
7277
7378 var s: Self = undefined;
79 s.key = key;
7480 s.reset();
7581 return s;
7682 }
......@@ -78,14 +84,24 @@ fn Blake2s(comptime out_len: usize) type {
7884 pub fn reset(d: *Self) void {
7985 mem.copy(u32, d.h[0..], iv[0..]);
8086
81 // No key plus default parameters
82 d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3);
87 // default parameters
88 d.h[0] ^= 0x01010000 ^ @truncate(u32, d.key.len << 8) ^ @intCast(u32, out_len >> 3);
8389 d.t = 0;
8490 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 }
8597 }
8698
8799 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);
89105 d.update(b);
90106 d.final(out);
91107 }
......@@ -94,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type {
94110 var off: usize = 0;
95111
96112 // 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) {
98114 off += 64 - d.buf_len;
99115 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
100116 d.t += 64;
......@@ -103,7 +119,7 @@ fn Blake2s(comptime out_len: usize) type {
103119 }
104120
105121 // Full middle blocks.
106 while (off + 64 <= b.len) : (off += 64) {
122 while (off + 64 < b.len) : (off += 64) {
107123 d.t += 64;
108124 d.round(b[off .. off + 64], false);
109125 }
......@@ -123,7 +139,7 @@ fn Blake2s(comptime out_len: usize) type {
123139 const rr = d.h[0 .. out_len / 32];
124140
125141 for (rr) |s, j| {
126 mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
142 mem.writeIntSliceLittle(u32, out[4 * j ..], s);
127143 }
128144 }
129145
......@@ -188,6 +204,9 @@ test "blake2s224 single" {
188204
189205 const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";
190206 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);
191210}
192211
193212test "blake2s224 streaming" {
......@@ -212,6 +231,37 @@ test "blake2s224 streaming" {
212231 h.update("c");
213232 h.final(out[0..]);
214233 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 }
215265}
216266
217267test "blake2s256 single" {
......@@ -223,6 +273,9 @@ test "blake2s256 single" {
223273
224274 const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";
225275 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);
226279}
227280
228281test "blake2s256 streaming" {
......@@ -247,15 +300,60 @@ test "blake2s256 streaming" {
247300 h.update("c");
248301 h.final(out[0..]);
249302 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..]);
250316}
251317
252test "blake2s256 aligned final" {
253 var block = [_]u8{0} ** Blake2s256.block_length;
254 var out: [Blake2s256.digest_length]u8 = undefined;
318test "blake2s256 keyed" {
319 var out: [32]u8 = undefined;
320
321 const h1 = "10f918da4d74fab3302e48a5d67d03804b1ec95372a62a0f33b7c9fa28ba1ae6";
322 const key = "secret_key";
255323
256 var h = Blake2s256.init();
257 h.update(&block);
324 Blake2s256.hash_keyed(key, "a" ** 64 ++ "b" ** 64, &out);
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);
258336 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 }
259357}
260358
261359/////////////////////
......@@ -264,7 +362,7 @@ test "blake2s256 aligned final" {
264362pub const Blake2b384 = Blake2b(384);
265363pub const Blake2b512 = Blake2b(512);
266364
267fn Blake2b(comptime out_len: usize) type {
365pub fn Blake2b(comptime out_len: usize) type {
268366 return struct {
269367 const Self = @This();
270368 pub const block_length = 128;
......@@ -302,10 +400,17 @@ fn Blake2b(comptime out_len: usize) type {
302400 buf: [128]u8,
303401 buf_len: u8,
304402
403 key: []const u8,
404
305405 pub fn init() Self {
406 return init_keyed("");
407 }
408
409 pub fn init_keyed(key: []const u8) Self {
306410 debug.assert(8 <= out_len and out_len <= 512);
307411
308412 var s: Self = undefined;
413 s.key = key;
309414 s.reset();
310415 return s;
311416 }
......@@ -313,14 +418,24 @@ fn Blake2b(comptime out_len: usize) type {
313418 pub fn reset(d: *Self) void {
314419 mem.copy(u64, d.h[0..], iv[0..]);
315420
316 // No key plus default parameters
317 d.h[0] ^= 0x01010000 ^ (out_len >> 3);
421 // default parameters
422 d.h[0] ^= 0x01010000 ^ (d.key.len << 8) ^ (out_len >> 3);
318423 d.t = 0;
319424 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 }
320431 }
321432
322433 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);
324439 d.update(b);
325440 d.final(out);
326441 }
......@@ -329,7 +444,7 @@ fn Blake2b(comptime out_len: usize) type {
329444 var off: usize = 0;
330445
331446 // 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) {
333448 off += 128 - d.buf_len;
334449 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
335450 d.t += 128;
......@@ -338,7 +453,7 @@ fn Blake2b(comptime out_len: usize) type {
338453 }
339454
340455 // Full middle blocks.
341 while (off + 128 <= b.len) : (off += 128) {
456 while (off + 128 < b.len) : (off += 128) {
342457 d.t += 128;
343458 d.round(b[off .. off + 128], false);
344459 }
......@@ -356,7 +471,7 @@ fn Blake2b(comptime out_len: usize) type {
356471 const rr = d.h[0 .. out_len / 64];
357472
358473 for (rr) |s, j| {
359 mem.writeIntLittle(u64, out[8 * j ..][0..8], s);
474 mem.writeIntSliceLittle(u64, out[8 * j ..], s);
360475 }
361476 }
362477
......@@ -421,6 +536,9 @@ test "blake2b384 single" {
421536
422537 const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";
423538 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);
424542}
425543
426544test "blake2b384 streaming" {
......@@ -445,6 +563,37 @@ test "blake2b384 streaming" {
445563 h.update("c");
446564 h.final(out[0..]);
447565 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 }
448597}
449598
450599test "blake2b512 single" {
......@@ -456,6 +605,9 @@ test "blake2b512 single" {
456605
457606 const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";
458607 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);
459611}
460612
461613test "blake2b512 streaming" {
......@@ -480,13 +632,58 @@ test "blake2b512 streaming" {
480632 h.update("c");
481633 h.final(out[0..]);
482634 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..]);
483648}
484649
485test "blake2b512 aligned final" {
486 var block = [_]u8{0} ** Blake2b512.block_length;
487 var out: [Blake2b512.digest_length]u8 = undefined;
650test "blake2b512 keyed" {
651 var out: [64]u8 = undefined;
488652
489 var h = Blake2b512.init();
490 h.update(&block);
653 const h1 = "8a978060ccaf582f388f37454363071ac9a67e3a704585fd879fb8a419a447e389c7c6de790faa20a7a7dccf197de736bc5b40b98a930b36df5bee7555750c4d";
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);
491668 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 }
492689}
lib/std/debug/leb128.zig+44
......@@ -159,6 +159,50 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
159159 return buf.pos;
160160}
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
162206// tests
163207fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
164208 var reader = std.io.fixedBufferStream(encoded);
lib/std/dwarf.zig+1-1
......@@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");
99
1010const ArrayList = std.ArrayList;
1111
12usingnamespace @import("dwarf_bits.zig");
12pub usingnamespace @import("dwarf_bits.zig");
1313
1414const PcRange = struct {
1515 start: u64,
lib/std/dwarf_bits.zig+17
......@@ -680,3 +680,20 @@ pub const LANG_HP_Basic91 = 0x8004;
680680pub const LANG_HP_Pascal91 = 0x8005;
681681pub const LANG_HP_IMacro = 0x8006;
682682pub 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 {
341341 shentsize: u16,
342342 shnum: u16,
343343 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 }
344358};
345359
346360pub fn readHeader(file: File) !Header {
......@@ -378,144 +392,137 @@ pub fn readHeader(file: File) !Header {
378392 });
379393}
380394
381/// All integers are native endian.
382pub const AllHeaders = struct {
383 header: Header,
384 section_headers: []Elf64_Shdr,
385 program_headers: []Elf64_Phdr,
386 allocator: *mem.Allocator,
387};
388
389pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
390 var hdrs: AllHeaders = .{
391 .allocator = allocator,
392 .header = try readHeader(file),
393 .section_headers = undefined,
394 .program_headers = undefined,
395 };
396 const is_64 = hdrs.header.is_64;
397 const need_bswap = hdrs.header.endian != std.builtin.endian;
398
399 hdrs.section_headers = try allocator.alloc(Elf64_Shdr, hdrs.header.shnum);
400 errdefer allocator.free(hdrs.section_headers);
401
402 hdrs.program_headers = try allocator.alloc(Elf64_Phdr, hdrs.header.phnum);
403 errdefer allocator.free(hdrs.program_headers);
404
405 // If the ELF file is 64-bit and same-endianness, then all we have to do is
406 // yeet the bytes into memory.
407 // If only the endianness is different, they can be simply byte swapped.
408 if (is_64) {
409 const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers);
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);
395pub const ProgramHeaderIterator = struct {
396 elf_header: Header,
397 file: File,
398 index: usize = 0,
399
400 pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr {
401 if (self.index >= self.elf_header.phnum) return null;
402 defer self.index += 1;
403
404 if (self.elf_header.is_64) {
405 var phdr: Elf64_Phdr = undefined;
406 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
407 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
408
409 // ELF endianness matches native endianness.
410 if (self.elf_header.endian == std.builtin.endian) return phdr;
411
412 // Convert fields to native endianness.
413 return Elf64_Phdr{
414 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
415 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
416 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
417 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
418 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
419 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
420 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
421 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
422 };
423 }
413424
414 if (need_bswap) {
415 for (hdrs.section_headers) |*shdr| {
416 shdr.* = .{
417 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
418 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
419 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
420 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
421 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
422 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
423 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
424 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
425 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
426 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
427 };
428 }
429 for (hdrs.program_headers) |*phdr| {
430 phdr.* = .{
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 }
425 var phdr: Elf32_Phdr = undefined;
426 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
427 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
428
429 // ELF endianness does NOT match native endianness.
430 if (self.elf_header.endian != std.builtin.endian) {
431 // Convert fields to native endianness.
432 phdr = .{
433 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
434 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
435 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
436 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
437 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
438 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
439 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
440 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
441 };
441442 }
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 };
444455 }
456};
445457
446 const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);
447 defer allocator.free(shdrs_32);
448
449 const phdrs_32 = try allocator.alloc(Elf32_Phdr, hdrs.header.phnum);
450 defer allocator.free(phdrs_32);
451
452 const shdr_buf = std.mem.sliceAsBytes(shdrs_32);
453 const phdr_buf = std.mem.sliceAsBytes(phdrs_32);
454 try preadNoEof(file, shdr_buf, hdrs.header.shoff);
455 try preadNoEof(file, phdr_buf, hdrs.header.phoff);
456
457 if (need_bswap) {
458 for (hdrs.section_headers) |*shdr, i| {
459 const o = shdrs_32[i];
460 shdr.* = .{
461 .sh_name = @byteSwap(@TypeOf(o.sh_name), o.sh_name),
462 .sh_type = @byteSwap(@TypeOf(o.sh_type), o.sh_type),
463 .sh_flags = @byteSwap(@TypeOf(o.sh_flags), o.sh_flags),
464 .sh_addr = @byteSwap(@TypeOf(o.sh_addr), o.sh_addr),
465 .sh_offset = @byteSwap(@TypeOf(o.sh_offset), o.sh_offset),
466 .sh_size = @byteSwap(@TypeOf(o.sh_size), o.sh_size),
467 .sh_link = @byteSwap(@TypeOf(o.sh_link), o.sh_link),
468 .sh_info = @byteSwap(@TypeOf(o.sh_info), o.sh_info),
469 .sh_addralign = @byteSwap(@TypeOf(o.sh_addralign), o.sh_addralign),
470 .sh_entsize = @byteSwap(@TypeOf(o.sh_entsize), o.sh_entsize),
471 };
472 }
473 for (hdrs.program_headers) |*phdr, i| {
474 const o = phdrs_32[i];
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),
458pub const SectionHeaderIterator = struct {
459 elf_header: Header,
460 file: File,
461 index: usize = 0,
462
463 pub fn next(self: *SectionHeaderIterator) !?Elf64_Shdr {
464 if (self.index >= self.elf_header.shnum) return null;
465 defer self.index += 1;
466
467 if (self.elf_header.is_64) {
468 var shdr: Elf64_Shdr = undefined;
469 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(shdr)) * self.index;
470 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
471
472 // ELF endianness matches native endianness.
473 if (self.elf_header.endian == std.builtin.endian) return shdr;
474
475 // Convert fields to native endianness.
476 return Elf64_Shdr{
477 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
478 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
479 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
480 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
481 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
482 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
483 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
484 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
485 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
486 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
484487 };
485488 }
486 } else {
487 for (hdrs.section_headers) |*shdr, i| {
488 const o = shdrs_32[i];
489 shdr.* = .{
490 .sh_name = o.sh_name,
491 .sh_type = o.sh_type,
492 .sh_flags = o.sh_flags,
493 .sh_addr = o.sh_addr,
494 .sh_offset = o.sh_offset,
495 .sh_size = o.sh_size,
496 .sh_link = o.sh_link,
497 .sh_info = o.sh_info,
498 .sh_addralign = o.sh_addralign,
499 .sh_entsize = o.sh_entsize,
500 };
501 }
502 for (hdrs.program_headers) |*phdr, i| {
503 const o = phdrs_32[i];
504 phdr.* = .{
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,
489
490 var shdr: Elf32_Shdr = undefined;
491 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
492 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
493
494 // ELF endianness does NOT match native endianness.
495 if (self.elf_header.endian != std.builtin.endian) {
496 // Convert fields to native endianness.
497 shdr = .{
498 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
499 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
500 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
501 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
502 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
503 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
504 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
505 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
506 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
507 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
513508 };
514509 }
515 }
516510
517 return hdrs;
518}
511 // Convert 32-bit header to 64-bit.
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
520527pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
521528 if (is_64) {
......@@ -538,7 +545,7 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
538545}
539546
540547fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
541 var i: u64 = 0;
548 var i: usize = 0;
542549 while (i < buf.len) {
543550 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
544551 error.SystemResources => return error.SystemResources,
lib/std/fifo.zig+8-2
......@@ -224,6 +224,7 @@ pub fn LinearFifo(
224224 pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {
225225 return .{ .context = self };
226226 }
227
227228 /// Deprecated: `use reader`
228229 pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) {
229230 return .{ .context = self };
......@@ -315,6 +316,11 @@ pub fn LinearFifo(
315316 return bytes.len;
316317 }
317318
319 pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) {
320 return .{ .context = self };
321 }
322
323 /// Deprecated: `use writer`
318324 pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
319325 return .{ .context = self };
320326 }
......@@ -426,14 +432,14 @@ test "LinearFifo(u8, .Dynamic)" {
426432 fifo.shrink(0);
427433
428434 {
429 try fifo.outStream().print("{}, {}!", .{ "Hello", "World" });
435 try fifo.writer().print("{}, {}!", .{ "Hello", "World" });
430436 var result: [30]u8 = undefined;
431437 testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
432438 testing.expectEqual(@as(usize, 0), fifo.readableLength());
433439 }
434440
435441 {
436 try fifo.outStream().writeAll("This is a test");
442 try fifo.writer().writeAll("This is a test");
437443 var result: [30]u8 = undefined;
438444 testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
439445 testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
lib/std/fmt.zig+2
......@@ -88,6 +88,8 @@ pub fn format(
8888 if (args.len > ArgSetType.bit_count) {
8989 @compileError("32 arguments max are supported per format call");
9090 }
91 if (args.len == 0)
92 return writer.writeAll(fmt);
9193
9294 const State = enum {
9395 Start,
lib/std/fs.zig+37-23
......@@ -84,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
8484 try crypto.randomBytes(rand_buf[0..]);
8585 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, .{})) {
8888 return rename(tmp_path, new_path);
8989 } else |err| switch (err) {
9090 error.PathAlreadyExists => continue,
......@@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
225225/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
226226pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
227227 assert(path.isAbsoluteWindowsW(absolute_path_w));
228 const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
229 os.windows.CloseHandle(handle);
228 return os.mkdirW(absolute_path_w, default_new_dir_mode);
230229}
231230
232231pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");
......@@ -881,8 +880,7 @@ pub const Dir = struct {
881880 }
882881
883882 pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
884 const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
885 os.windows.CloseHandle(handle);
883 try os.mkdiratW(self.fd, sub_path, default_new_dir_mode);
886884 }
887885
888886 /// Calls makeDir recursively to make an entire path. Returns success if the path
......@@ -1119,7 +1117,7 @@ pub const Dir = struct {
11191117 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
11201118 if (builtin.os.tag == .windows) {
11211119 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());
11231121 } else if (builtin.os.tag == .wasi) {
11241122 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
11251123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
......@@ -1153,7 +1151,7 @@ pub const Dir = struct {
11531151 }
11541152
11551153 /// 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 {
11571155 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
11581156 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
11591157 else => |e| return e,
......@@ -1182,7 +1180,7 @@ pub const Dir = struct {
11821180 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
11831181 if (builtin.os.tag == .windows) {
11841182 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());
11861184 } else if (builtin.os.tag == .wasi) {
11871185 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
11881186 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
......@@ -1204,7 +1202,7 @@ pub const Dir = struct {
12041202
12051203 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
12061204 /// 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 {
12081206 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
12091207 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
12101208 else => |e| return e,
......@@ -1263,11 +1261,11 @@ pub const Dir = struct {
12631261 /// are null-terminated, WTF16 encoded.
12641262 pub fn symLinkW(
12651263 self: Dir,
1266 target_path_w: [:0]const u16,
1267 sym_link_path_w: [:0]const u16,
1264 target_path_w: []const u16,
1265 sym_link_path_w: []const u16,
12681266 flags: SymLinkFlags,
12691267 ) !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);
12711269 }
12721270
12731271 /// Read value of a symbolic link.
......@@ -1278,7 +1276,8 @@ pub const Dir = struct {
12781276 return self.readLinkWasi(sub_path, buffer);
12791277 }
12801278 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);
12821281 }
12831282 const sub_path_c = try os.toPosixPath(sub_path);
12841283 return self.readLinkZ(&sub_path_c, buffer);
......@@ -1295,15 +1294,15 @@ pub const Dir = struct {
12951294 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
12961295 if (builtin.os.tag == .windows) {
12971296 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);
12991298 }
13001299 return os.readlinkatZ(self.fd, sub_path_c, buffer);
13011300 }
13021301
13031302 /// Windows-only. Same as `readLink` except the pathname parameter
13041303 /// is null-terminated, WTF16 encoded.
1305 pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {
1306 return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);
1304 pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
1305 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
13071306 }
13081307
13091308 /// On success, caller owns returned buffer.
......@@ -1813,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
18131812 assert(path.isAbsolute(target_path));
18141813 assert(path.isAbsolute(sym_link_path));
18151814 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);
18171818 }
18181819 return os.symlink(target_path, sym_link_path);
18191820}
......@@ -1822,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
18221823/// Note that this function will by default try creating a symbolic link to a file. If you would
18231824/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
18241825/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
1825pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {
1826 assert(path.isAbsoluteWindowsW(target_path_w));
1827 assert(path.isAbsoluteWindowsW(sym_link_path_w));
1828 return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);
1826pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
1827 assert(path.isAbsoluteWindowsWTF16(target_path_w));
1828 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
1829 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
18291830}
18301831
18311832/// 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
18361837 if (builtin.os.tag == .windows) {
18371838 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
18381839 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);
18401841 }
18411842 return os.symlinkZ(target_path_c, sym_link_path_c);
18421843}
......@@ -1938,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
19381939 return walker;
19391940}
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
19431957pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
19441958 if (builtin.os.tag == .linux) {
lib/std/fs/file.zig+14-1
......@@ -47,7 +47,20 @@ pub const File = struct {
4747 else => 0o666,
4848 };
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
5265 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 {
374374 defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
375375 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
376376
377 const dir_handle = try windows.CreateFileW(
378 dirname_utf16le.ptr,
379 windows.FILE_LIST_DIRECTORY,
380 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
381 null,
382 windows.OPEN_EXISTING,
383 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
384 null,
385 );
377 const dir_handle = try windows.OpenFile(dirname_utf16le, .{
378 .dir = std.fs.cwd().fd,
379 .access_mask = windows.FILE_LIST_DIRECTORY,
380 .creation = windows.FILE_OPEN,
381 .io_mode = .blocking,
382 .open_dir = true,
383 });
386384 var dir_handle_consumed = false;
387385 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)
5656pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
5757 switch (strat) {
5858 .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.
6259 for (key) |element| {
6360 hash(hasher, element, .Shallow);
6461 }
......@@ -75,30 +72,34 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo
7572/// Strategy is provided to determine if pointers should be followed or not.
7673pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
7774 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
7881 switch (@typeInfo(Key)) {
7982 .NoReturn,
8083 .Opaque,
8184 .Undefined,
8285 .Void,
8386 .Null,
84 .BoundFn,
8587 .ComptimeFloat,
8688 .ComptimeInt,
8789 .Type,
8890 .EnumLiteral,
8991 .Frame,
92 .Float,
9093 => @compileError("cannot hash this type"),
9194
9295 // Help the optimizer see that hashing an int is easy by inlining!
9396 // TODO Check if the situation is better after #561 is resolved.
9497 .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
9899 .Bool => hash(hasher, @boolToInt(key), strat),
99100 .Enum => hash(hasher, @enumToInt(key), strat),
100101 .ErrorSet => hash(hasher, @errorToInt(key), strat),
101 .AnyFrame, .Fn => hash(hasher, @ptrToInt(key), strat),
102 .AnyFrame, .BoundFn, .Fn => hash(hasher, @ptrToInt(key), strat),
102103
103104 .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 {
121122 },
122123
123124 .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.
127125 inline for (info.fields) |field| {
128126 // We reuse the hash of the previous field as the seed for the
129127 // next one so that they're dependant.
......@@ -266,12 +264,12 @@ test "hash slice deep" {
266264test "hash struct deep" {
267265 const Foo = struct {
268266 a: u32,
269 b: f64,
267 b: u16,
270268 c: *bool,
271269
272270 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 {
275273 const ptr = try allocator.create(bool);
276274 ptr.* = c_;
277275 return Self{ .a = a_, .b = b_, .c = ptr };
......@@ -279,9 +277,9 @@ test "hash struct deep" {
279277 };
280278
281279 const allocator = std.testing.allocator;
282 const foo = try Foo.init(allocator, 123, 1.0, true);
283 const bar = try Foo.init(allocator, 123, 1.0, true);
284 const baz = try Foo.init(allocator, 123, 1.0, false);
280 const foo = try Foo.init(allocator, 123, 10, true);
281 const bar = try Foo.init(allocator, 123, 10, true);
282 const baz = try Foo.init(allocator, 123, 10, false);
285283 defer allocator.destroy(foo.c);
286284 defer allocator.destroy(bar.c);
287285 defer allocator.destroy(baz.c);
......@@ -338,12 +336,12 @@ test "testHash struct" {
338336test "testHash union" {
339337 const Foo = union(enum) {
340338 A: u32,
341 B: f32,
339 B: bool,
342340 C: u32,
343341 };
344342
345343 const a = Foo{ .A = 18 };
346 var b = Foo{ .B = 12.34 };
344 var b = Foo{ .B = true };
347345 const c = Foo{ .C = 18 };
348346 testing.expect(testHash(a) == testHash(a));
349347 testing.expect(testHash(a) != testHash(b));
lib/std/hash_map.zig+21-8
......@@ -5,6 +5,7 @@ const testing = std.testing;
55const math = std.math;
66const mem = std.mem;
77const meta = std.meta;
8const trait = meta.trait;
89const autoHash = std.hash.autoHash;
910const Wyhash = std.hash.Wyhash;
1011const Allocator = mem.Allocator;
......@@ -195,6 +196,10 @@ pub fn HashMap(
195196 return self.unmanaged.getEntry(key);
196197 }
197198
199 pub fn getIndex(self: Self, key: K) ?usize {
200 return self.unmanaged.getIndex(key);
201 }
202
198203 pub fn get(self: Self, key: K) ?V {
199204 return self.unmanaged.get(key);
200205 }
......@@ -478,17 +483,21 @@ pub fn HashMapUnmanaged(
478483 }
479484
480485 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 {
481491 const header = self.index_header orelse {
482492 // Linear scan.
483493 const h = if (store_hash) hash(key) else {};
484 for (self.entries.items) |*item| {
494 for (self.entries.items) |*item, i| {
485495 if (item.hash == h and eql(key, item.key)) {
486 return item;
496 return i;
487497 }
488498 }
489499 return null;
490500 };
491
492501 switch (header.capacityIndexType()) {
493502 .u8 => return self.getInternal(key, header, u8),
494503 .u16 => return self.getInternal(key, header, u16),
......@@ -710,7 +719,7 @@ pub fn HashMapUnmanaged(
710719 unreachable;
711720 }
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 {
714723 const indexes = header.indexes(I);
715724 const h = hash(key);
716725 const start_index = header.constrainIndex(h);
......@@ -724,7 +733,7 @@ pub fn HashMapUnmanaged(
724733 const entry = &self.entries.items[index.entry_index];
725734 const hash_match = if (store_hash) h == entry.hash else true;
726735 if (hash_match and eql(key, entry.key))
727 return entry;
736 return index.entry_index;
728737 }
729738 return null;
730739 }
......@@ -1023,9 +1032,13 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
10231032pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
10241033 return struct {
10251034 fn hash(key: K) u32 {
1026 var hasher = Wyhash.init(0);
1027 autoHash(&hasher, key);
1028 return @truncate(u32, hasher.final());
1035 if (comptime trait.hasUniqueRepresentation(K)) {
1036 return @truncate(u32, Wyhash.hash(0, std.mem.asBytes(&key)));
1037 } else {
1038 var hasher = Wyhash.init(0);
1039 autoHash(&hasher, key);
1040 return @truncate(u32, hasher.final());
1041 }
10291042 }
10301043 }.hash;
10311044}
lib/std/math/big/int.zig+1-1
......@@ -99,7 +99,7 @@ pub const Mutable = struct {
9999 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
100100 return .{
101101 .allocator = allocator,
102 .limbs = limbs,
102 .limbs = self.limbs,
103103 .metadata = if (self.positive)
104104 self.len & ~Managed.sign_bit
105105 else
lib/std/math/big/int_test.zig+22
......@@ -2,6 +2,7 @@ const std = @import("../../std.zig");
22const mem = std.mem;
33const testing = std.testing;
44const Managed = std.math.big.int.Managed;
5const Mutable = std.math.big.int.Mutable;
56const Limb = std.math.big.Limb;
67const DoubleLimb = std.math.big.DoubleLimb;
78const maxInt = std.math.maxInt;
......@@ -1453,3 +1454,24 @@ test "big.int gcd one large" {
14531454
14541455 testing.expect((try r.to(u64)) == 1);
14551456}
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" {
20302030 testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 }));
20312031}
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
20332106/// Converts a little-endian integer to host endianness.
20342107pub fn littleToNative(comptime T: type, x: T) T {
20352108 return switch (builtin.endian) {
lib/std/meta/trait.zig+68
......@@ -429,3 +429,71 @@ test "std.meta.trait.hasFunctions" {
429429 testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" }));
430430 testing.expect(!hasFunctions(TestStruct2, tuple));
431431}
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");
1414
1515pub const Address = extern union {
1616 any: os.sockaddr,
17 in: os.sockaddr_in,
18 in6: os.sockaddr_in6,
17 in: Ip4Address,
18 in6: Ip6Address,
1919 un: if (has_unix_sockets) os.sockaddr_un else void,
2020
2121 // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512
......@@ -76,19 +76,227 @@ pub const Address = extern union {
7676 }
7777 }
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
79287 /// Parse a given IPv6 address string into an Address.
80288 /// Assumes the Scope ID of the address is fully numeric.
81289 /// For non-numeric addresses, see `resolveIp6`.
82 pub fn parseIp6(buf: []const u8, port: u16) !Address {
83 var result = Address{
84 .in6 = os.sockaddr_in6{
290 pub fn parse(buf: []const u8, port: u16) !Ip6Address {
291 var result = Ip6Address{
292 .sa = os.sockaddr_in6{
85293 .scope_id = 0,
86294 .port = mem.nativeToBig(u16, port),
87295 .flowinfo = 0,
88296 .addr = undefined,
89297 },
90298 };
91 var ip_slice = result.in6.addr[0..];
299 var ip_slice = result.sa.addr[0..];
92300
93301 var tail: [16]u8 = undefined;
94302
......@@ -101,10 +309,10 @@ pub const Address = extern union {
101309 if (scope_id) {
102310 if (c >= '0' and c <= '9') {
103311 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)) {
105313 return error.Overflow;
106314 }
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)) {
108316 return error.Overflow;
109317 }
110318 } else {
......@@ -141,10 +349,10 @@ pub const Address = extern union {
141349 return error.InvalidIpv4Mapping;
142350 }
143351 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 {
145353 return error.InvalidIpv4Mapping;
146 }).in.addr;
147 ip_slice = result.in6.addr[0..];
354 }).sa.addr;
355 ip_slice = result.sa.addr[0..];
148356 ip_slice[10] = 0xff;
149357 ip_slice[11] = 0xff;
150358
......@@ -180,22 +388,22 @@ pub const Address = extern union {
180388 index += 1;
181389 ip_slice[index] = @truncate(u8, x);
182390 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]);
184392 return result;
185393 }
186394 }
187395
188 pub fn resolveIp6(buf: []const u8, port: u16) !Address {
396 pub fn resolve(buf: []const u8, port: u16) !Ip6Address {
189397 // TODO: Unify the implementations of resolveIp6 and parseIp6.
190 var result = Address{
191 .in6 = os.sockaddr_in6{
398 var result = Ip6Address{
399 .sa = os.sockaddr_in6{
192400 .scope_id = 0,
193401 .port = mem.nativeToBig(u16, port),
194402 .flowinfo = 0,
195403 .addr = undefined,
196404 },
197405 };
198 var ip_slice = result.in6.addr[0..];
406 var ip_slice = result.sa.addr[0..];
199407
200408 var tail: [16]u8 = undefined;
201409
......@@ -256,10 +464,10 @@ pub const Address = extern union {
256464 return error.InvalidIpv4Mapping;
257465 }
258466 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 {
260468 return error.InvalidIpv4Mapping;
261 }).in.addr;
262 ip_slice = result.in6.addr[0..];
469 }).sa.addr;
470 ip_slice = result.sa.addr[0..];
263471 ip_slice[10] = 0xff;
264472 ip_slice[11] = 0xff;
265473
......@@ -299,7 +507,7 @@ pub const Address = extern union {
299507 };
300508 }
301509
302 result.in6.scope_id = resolved_scope_id;
510 result.sa.scope_id = resolved_scope_id;
303511
304512 if (index == 14) {
305513 ip_slice[14] = @truncate(u8, x >> 8);
......@@ -310,63 +518,14 @@ pub const Address = extern union {
310518 index += 1;
311519 ip_slice[index] = @truncate(u8, x);
312520 index += 1;
313 mem.copy(u8, result.in6.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;
521 mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
352522 return result;
353523 }
354
355 return error.Incomplete;
356524 }
357525
358 pub fn initIp4(addr: [4]u8, port: u16) Address {
359 return Address{
360 .in = os.sockaddr_in{
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{
526 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
527 return Ip6Address{
528 .sa = os.sockaddr_in6{
370529 .addr = addr,
371530 .port = mem.nativeToBig(u16, port),
372531 .flowinfo = flowinfo,
......@@ -375,147 +534,71 @@ pub const Address = extern union {
375534 };
376535 }
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
393537 /// Returns the port in native endian.
394538 /// Asserts that the address is ip4 or ip6.
395 pub fn getPort(self: Address) u16 {
396 const big_endian_port = switch (self.any.family) {
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);
539 pub fn getPort(self: Ip6Address) u16 {
540 return mem.bigToNative(u16, self.sa.port);
402541 }
403542
404543 /// `port` is native-endian.
405544 /// Asserts that the address is ip4 or ip6.
406 pub fn setPort(self: *Address, port: u16) void {
407 const ptr = switch (self.any.family) {
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 }
545 pub fn setPort(self: *Ip6Address, port: u16) void {
546 self.sa.port = mem.nativeToBig(u16, port);
424547 }
425548
426549 pub fn format(
427 self: Address,
550 self: Ip6Address,
428551 comptime fmt: []const u8,
429552 options: std.fmt.FormatOptions,
430553 out_stream: anytype,
431554 ) !void {
432 switch (self.any.family) {
433 os.AF_INET => {
434 const port = mem.bigToNative(u16, self.in.port);
435 const bytes = @ptrCast(*const [4]u8, &self.in.addr);
436 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
437 bytes[0],
438 bytes[1],
439 bytes[2],
440 bytes[3],
441 port,
442 });
443 },
444 os.AF_INET6 => {
445 const port = mem.bigToNative(u16, self.in6.port);
446 if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
447 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
448 self.in6.addr[12],
449 self.in6.addr[13],
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 }
555 const port = mem.bigToNative(u16, self.sa.port);
556 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
557 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
558 self.sa.addr[12],
559 self.sa.addr[13],
560 self.sa.addr[14],
561 self.sa.addr[15],
562 port,
563 });
564 return;
565 }
566 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.sa.addr);
567 const native_endian_parts = switch (builtin.endian) {
568 .Big => big_endian_parts.*,
569 .Little => blk: {
570 var buf: [8]u16 = undefined;
571 for (big_endian_parts) |part, i| {
572 buf[i] = mem.bigToNative(u16, part);
482573 }
483 try std.fmt.format(out_stream, "]:{}", .{port});
574 break :blk buf;
484575 },
485 os.AF_UNIX => {
486 if (!has_unix_sockets) {
487 unreachable;
576 };
577 try out_stream.writeAll("[");
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;
488585 }
489
490 try std.fmt.format(out_stream, "{}", .{&self.un.path});
491 },
492 else => unreachable,
586 continue;
587 }
588 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
589 if (i != native_endian_parts.len - 1) {
590 try out_stream.writeAll(":");
591 }
493592 }
593 try std.fmt.format(out_stream, "]:{}", .{port});
494594 }
495595
496 pub fn eql(a: Address, b: Address) bool {
497 const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
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 }
596 pub fn getOsSockLen(self: Ip6Address) os.socklen_t {
597 return @sizeOf(os.sockaddr_in6);
516598 }
517599};
518600
601
519602pub fn connectUnixSocket(path: []const u8) !fs.File {
520603 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
521604 const sockfd = try os.socket(
......@@ -777,7 +860,7 @@ fn linuxLookupName(
777860 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));
778861 var da6 = os.sockaddr_in6{
779862 .family = os.AF_INET6,
780 .scope_id = addr.addr.in6.scope_id,
863 .scope_id = addr.addr.in6.sa.scope_id,
781864 .port = 65535,
782865 .flowinfo = 0,
783866 .addr = [1]u8{0} ** 16,
......@@ -795,7 +878,7 @@ fn linuxLookupName(
795878 var salen: os.socklen_t = undefined;
796879 var dalen: os.socklen_t = undefined;
797880 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);
799882 da = @ptrCast(*os.sockaddr, &da6);
800883 dalen = @sizeOf(os.sockaddr_in6);
801884 sa = @ptrCast(*os.sockaddr, &sa6);
......@@ -803,8 +886,8 @@ fn linuxLookupName(
803886 } else {
804887 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
805888 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);
807 da4.addr = addr.addr.in.addr;
889 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.sa.addr);
890 da4.addr = addr.addr.in.sa.addr;
808891 da = @ptrCast(*os.sockaddr, &da4);
809892 dalen = @sizeOf(os.sockaddr_in);
810893 sa = @ptrCast(*os.sockaddr, &sa4);
lib/std/os.zig+176-106
......@@ -1041,6 +1041,9 @@ pub const OpenError = error{
10411041
10421042 /// The underlying filesystem does not support file locks
10431043 FileLocksNotSupported,
1044
1045 BadPathName,
1046 InvalidUtf8,
10441047} || UnexpectedError;
10451048
10461049/// 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
10921095 }
10931096}
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
10951131/// Windows-only. The path parameter is
10961132/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
10971133/// 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 {
1099 @compileError("TODO implement openW for windows");
1134/// TODO currently, this function does not handle all flag combinations
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 };
11001144}
11011145
11021146/// Open and possibly create a file. Keeps trying if it gets interrupted.
11031147/// `file_path` is relative to the open directory handle `dir_fd`.
11041148/// See also `openatC`.
1105/// TODO support windows
11061149pub 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 }
11071157 const file_path_c = try toPosixPath(file_path);
11081158 return openatZ(dir_fd, &file_path_c, flags, mode);
11091159}
......@@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
11451195/// Open and possibly create a file. Keeps trying if it gets interrupted.
11461196/// `file_path` is relative to the open directory handle `dir_fd`.
11471197/// See also `openat`.
1148/// TODO support windows
11491198pub 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 }
11501203 while (true) {
11511204 const rc = system.openat(dir_fd, file_path, flags, mode);
11521205 switch (errno(rc)) {
......@@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
11771230 }
11781231}
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
11801247pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
11811248 while (true) {
11821249 switch (errno(system.dup2(old_fd, new_fd))) {
......@@ -1683,7 +1750,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
16831750 @compileError("unlink is not supported in WASI; use unlinkat instead");
16841751 } else if (builtin.os.tag == .windows) {
16851752 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());
16871754 } else {
16881755 const file_path_c = try toPosixPath(file_path);
16891756 return unlinkZ(&file_path_c);
......@@ -1696,7 +1763,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");
16961763pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
16971764 if (builtin.os.tag == .windows) {
16981765 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());
17001767 }
17011768 switch (errno(system.unlink(file_path))) {
17021769 0 => return,
......@@ -1717,6 +1784,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
17171784 }
17181785}
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
17201792pub const UnlinkatError = UnlinkError || error{
17211793 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
17221794 DirNotEmpty,
......@@ -1727,7 +1799,7 @@ pub const UnlinkatError = UnlinkError || error{
17271799pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
17281800 if (builtin.os.tag == .windows) {
17291801 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);
17311803 } else if (builtin.os.tag == .wasi) {
17321804 return unlinkatWasi(dirfd, file_path, flags);
17331805 } else {
......@@ -1774,7 +1846,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
17741846pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
17751847 if (builtin.os.tag == .windows) {
17761848 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);
17781850 }
17791851 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
17801852 0 => return,
......@@ -1800,67 +1872,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
18001872}
18011873
18021874/// 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 {
1804 const w = windows;
1805
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 }
1875pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
1876 const remove_dir = (flags & AT_REMOVEDIR) != 0;
1877 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
18641878}
18651879
18661880const RenameError = error{
......@@ -2087,7 +2101,7 @@ pub fn renameatW(
20872101pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
20882102 if (builtin.os.tag == .windows) {
20892103 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);
20912105 } else if (builtin.os.tag == .wasi) {
20922106 return mkdiratWasi(dir_fd, sub_dir_path, mode);
20932107 } else {
......@@ -2145,8 +2159,19 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
21452159 }
21462160}
21472161
2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
2162pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
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 };
21502175 windows.CloseHandle(sub_dir_handle);
21512176}
21522177
......@@ -2175,9 +2200,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21752200 if (builtin.os.tag == .wasi) {
21762201 @compileError("mkdir is not supported in WASI; use mkdirat instead");
21772202 } else if (builtin.os.tag == .windows) {
2178 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
2179 windows.CloseHandle(sub_dir_handle);
2180 return;
2203 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2204 return mkdirW(dir_path_w.span(), mode);
21812205 } else {
21822206 const dir_path_c = try toPosixPath(dir_path);
21832207 return mkdirZ(&dir_path_c, mode);
......@@ -2188,9 +2212,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21882212pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
21892213 if (builtin.os.tag == .windows) {
21902214 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2191 const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null);
2192 windows.CloseHandle(sub_dir_handle);
2193 return;
2215 return mkdirW(dir_path_w.span(), mode);
21942216 }
21952217 switch (errno(system.mkdir(dir_path, mode))) {
21962218 0 => return,
......@@ -2211,6 +2233,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
22112233 }
22122234}
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
22142253pub const DeleteDirError = error{
22152254 AccessDenied,
22162255 FileBusy,
......@@ -2231,7 +2270,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
22312270 @compileError("rmdir is not supported in WASI; use unlinkat instead");
22322271 } else if (builtin.os.tag == .windows) {
22332272 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());
22352274 } else {
22362275 const dir_path_c = try toPosixPath(dir_path);
22372276 return rmdirZ(&dir_path_c);
......@@ -2244,7 +2283,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");
22442283pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
22452284 if (builtin.os.tag == .windows) {
22462285 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());
22482287 }
22492288 switch (errno(system.rmdir(dir_path))) {
22502289 0 => return,
......@@ -2265,6 +2304,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
22652304 }
22662305}
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
22682315pub const ChangeCurDirError = error{
22692316 AccessDenied,
22702317 FileSystem,
......@@ -2354,7 +2401,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
23542401 if (builtin.os.tag == .wasi) {
23552402 @compileError("readlink is not supported in WASI; use readlinkat instead");
23562403 } 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);
23582406 } else {
23592407 const file_path_c = try toPosixPath(file_path);
23602408 return readlinkZ(&file_path_c, out_buffer);
......@@ -2363,17 +2411,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
23632411
23642412pub 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.
23672415/// See also `readlinkZ`.
2368pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2369 return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);
2416pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2417 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
23702418}
23712419
23722420/// Same as `readlink` except `file_path` is null-terminated.
23732421pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
23742422 if (builtin.os.tag == .windows) {
23752423 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);
23772425 }
23782426 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
23792427 switch (errno(rc)) {
......@@ -2399,7 +2447,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
23992447 return readlinkatWasi(dirfd, file_path, out_buffer);
24002448 }
24012449 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);
24032452 }
24042453 const file_path_c = try toPosixPath(file_path);
24052454 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
24292478
24302479/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
24312480/// See also `readlinkat`.
2432pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2433 return windows.ReadLinkW(dirfd, file_path, out_buffer);
2481pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2482 return windows.ReadLink(dirfd, file_path, out_buffer);
24342483}
24352484
24362485/// 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
24382487pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
24392488 if (builtin.os.tag == .windows) {
24402489 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);
24422491 }
24432492 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
24442493 switch (errno(rc)) {
......@@ -3959,7 +4008,7 @@ pub const RealPathError = error{
39594008pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
39604009 if (builtin.os.tag == .windows) {
39614010 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
3962 return realpathW(pathname_w.span().ptr, out_buffer);
4011 return realpathW(pathname_w.span(), out_buffer);
39634012 }
39644013 if (builtin.os.tag == .wasi) {
39654014 @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");
39744023pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
39754024 if (builtin.os.tag == .windows) {
39764025 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
3977 return realpathW(pathname_w.span().ptr, out_buffer);
4026 return realpathW(pathname_w.span(), out_buffer);
39784027 }
39794028 if (builtin.os.tag == .linux and !builtin.link_libc) {
39804029 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
40104059 return mem.spanZ(result_path);
40114060}
40124061
4013/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
4014/// TODO use ntdll for better semantics
4015pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4016 const h_file = try windows.CreateFileW(
4017 pathname,
4018 windows.GENERIC_READ,
4019 windows.FILE_SHARE_READ,
4020 null,
4021 windows.OPEN_EXISTING,
4022 windows.FILE_FLAG_BACKUP_SEMANTICS,
4023 null,
4024 );
4025 defer windows.CloseHandle(h_file);
4062/// Same as `realpath` except `pathname` is UTF16LE-encoded.
4063/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
4064pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4065 const w = windows;
4066
4067 const dir = std.fs.cwd().fd;
4068 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
4069 const share_access = w.FILE_SHARE_READ;
4070 const creation = w.FILE_OPEN;
4071 const h_file = blk: {
4072 const res = w.OpenFile(pathname, .{
4073 .dir = dir,
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;
4028 const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
4097 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
4098 const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
40294099
40304100 // Windows returns \\?\ prepended to the path.
40314101 // 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;
237237pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
238238pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
239239pub 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;
33const testing = std.testing;
44const expect = testing.expect;
55const expectEqual = testing.expectEqual;
6const expectError = testing.expectError;
67const io = std.io;
78const fs = std.fs;
89const mem = std.mem;
......@@ -19,6 +20,95 @@ const tmpDir = std.testing.tmpDir;
1920const Dir = std.fs.Dir;
2021const 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
22112test "symlink with relative paths" {
23113 if (builtin.os.tag == .wasi) return error.SkipZigTest;
24114
......@@ -27,7 +117,7 @@ test "symlink with relative paths" {
27117 try cwd.writeFile("file.txt", "nonsense");
28118
29119 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);
31121 } else {
32122 try os.symlink("file.txt", "symlinked");
33123 }
......@@ -85,7 +175,7 @@ test "readlinkat" {
85175
86176 // create a symbolic link
87177 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);
89179 } else {
90180 try os.symlinkat("file.txt", tmp.dir.fd, "link");
91181 }
lib/std/os/windows.zig+129-285
......@@ -25,76 +25,11 @@ pub usingnamespace @import("windows/bits.zig");
2525
2626pub 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
9328pub const OpenError = error{
9429 IsDir,
30 NotDir,
9531 FileNotFound,
9632 NoDevice,
97 SharingViolation,
9833 AccessDenied,
9934 PipeBusy,
10035 PathAlreadyExists,
......@@ -111,15 +46,21 @@ pub const OpenFileOptions = struct {
11146 share_access_nonblocking: bool = false,
11247 creation: ULONG,
11348 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,
11455};
11556
11657/// TODO when share_access_nonblocking is false, this implementation uses
11758/// untinterruptible sleep() to block. This is not the final iteration of the API.
11859pub 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) {
12061 return error.IsDir;
12162 }
122 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) {
63 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) {
12364 return error.IsDir;
12465 }
12566
......@@ -142,11 +83,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
14283 .SecurityQualityOfService = null,
14384 };
14485 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
14691 var delay: usize = 1;
14792 while (true) {
148 var flags: ULONG = undefined;
149 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
15093 const rc = ntdll.NtCreateFile(
15194 &result,
15295 options.access_mask,
......@@ -156,7 +99,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
15699 FILE_ATTRIBUTE_NORMAL,
157100 options.share_access,
158101 options.creation,
159 FILE_NON_DIRECTORY_FILE | blocking_flag,
102 flags,
160103 null,
161104 0,
162105 );
......@@ -184,6 +127,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
184127 .OBJECT_PATH_SYNTAX_BAD => unreachable,
185128 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
186129 .FILE_IS_A_DIRECTORY => return error.IsDir,
130 .NOT_A_DIRECTORY => return error.NotDir,
187131 else => return unexpectedStatus(rc),
188132 }
189133 }
......@@ -215,30 +159,61 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
215159 }
216160}
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.
218168pub fn DeviceIoControl(
219169 h: HANDLE,
220 ioControlCode: DWORD,
170 ioControlCode: ULONG,
221171 in: ?[]const u8,
222172 out: ?[]u8,
223 overlapped: ?*OVERLAPPED,
224) !DWORD {
225 var bytes: DWORD = undefined;
226 if (kernel32.DeviceIoControl(
227 h,
228 ioControlCode,
229 if (in) |i| i.ptr else null,
230 if (in) |i| @intCast(u32, i.len) else 0,
231 if (out) |o| o.ptr else null,
232 if (out) |o| @intCast(u32, o.len) else 0,
233 &bytes,
234 overlapped,
235 ) == 0) {
236 switch (kernel32.GetLastError()) {
237 .IO_PENDING => if (overlapped == null) unreachable,
238 else => |err| return unexpectedError(err),
173) DeviceIoControlError!void {
174 // Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
175 const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
176
177 var io: IO_STATUS_BLOCK = undefined;
178 const in_ptr = if (in) |i| i.ptr else null;
179 const in_len = if (in) |i| @intCast(ULONG, i.len) else 0;
180 const out_ptr = if (out) |o| o.ptr else null;
181 const out_len = if (out) |o| @intCast(ULONG, o.len) else 0;
182
183 const rc = blk: {
184 if (is_fsctl) {
185 break :blk ntdll.NtFsControlFile(
186 h,
187 null,
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 );
239210 }
211 };
212 switch (rc) {
213 .SUCCESS => {},
214 .INVALID_PARAMETER => unreachable,
215 else => return unexpectedStatus(rc),
240216 }
241 return bytes;
242217}
243218
244219pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
......@@ -607,27 +582,14 @@ pub const CreateSymbolicLinkError = error{
607582 PathAlreadyExists,
608583 FileNotFound,
609584 NameTooLong,
610 InvalidUtf8,
611 BadPathName,
612585 NoDevice,
613586 Unexpected,
614587};
615588
616589pub fn CreateSymbolicLink(
617590 dir: ?HANDLE,
618 sym_link_path: []const u8,
619 target_path: []const u8,
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,
591 sym_link_path: []const u16,
592 target_path: []const u16,
631593 is_directory: bool,
632594) CreateSymbolicLinkError!void {
633595 const SYMLINK_DATA = extern struct {
......@@ -641,71 +603,19 @@ pub fn CreateSymbolicLinkW(
641603 Flags: ULONG,
642604 };
643605
644 var symlink_handle: HANDLE = undefined;
645 if (is_directory) {
646 const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {
647 error.Overflow => return error.NameTooLong,
648 };
649 var nt_name = UNICODE_STRING{
650 .Length = sym_link_len_bytes,
651 .MaximumLength = sym_link_len_bytes,
652 .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),
653 };
654
655 if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {
656 // Windows does not recognize this, but it does work with empty string.
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 }
606 const symlink_handle = OpenFile(sym_link_path, .{
607 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
608 .dir = dir,
609 .creation = FILE_CREATE,
610 .io_mode = .blocking,
611 .open_dir = is_directory,
612 }) catch |err| switch (err) {
613 error.IsDir => return error.PathAlreadyExists,
614 error.NotDir => unreachable,
615 error.WouldBlock => unreachable,
616 error.PipeBusy => unreachable,
617 else => |e| return e,
618 };
709619 defer CloseHandle(symlink_handle);
710620
711621 // prepare reparse data buffer
......@@ -727,8 +637,7 @@ pub fn CreateSymbolicLinkW(
727637 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);
728638 const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;
729639 @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);
730 // TODO replace with NtDeviceIoControl
731 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
640 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
732641}
733642
734643pub const ReadLinkError = error{
......@@ -737,44 +646,32 @@ pub const ReadLinkError = error{
737646 Unexpected,
738647 NameTooLong,
739648 UnsupportedReparsePointType,
740 InvalidUtf8,
741 BadPathName,
742649};
743650
744pub fn ReadLink(
745 dir: ?HANDLE,
746 sub_path: []const u8,
747 out_buffer: []u8,
748) ReadLinkError![]u8 {
749 const sub_path_w = try sliceToPrefixedFileW(sub_path);
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) {
651pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
652 // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
653 // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
654 // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
655 // to open the symlink there and then.
656 const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) {
755657 error.Overflow => return error.NameTooLong,
756658 };
757659 var nt_name = UNICODE_STRING{
758660 .Length = path_len_bytes,
759661 .MaximumLength = path_len_bytes,
760 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
662 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
761663 };
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
768664 var attr = OBJECT_ATTRIBUTES{
769665 .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,
771667 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
772668 .ObjectName = &nt_name,
773669 .SecurityDescriptor = null,
774670 .SecurityQualityOfService = null,
775671 };
776 var io: IO_STATUS_BLOCK = undefined;
777672 var result_handle: HANDLE = undefined;
673 var io: IO_STATUS_BLOCK = undefined;
674
778675 const rc = ntdll.NtCreateFile(
779676 &result_handle,
780677 FILE_READ_ATTRIBUTES,
......@@ -806,7 +703,7 @@ pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) Rea
806703 defer CloseHandle(result_handle);
807704
808705 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
811708 const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
812709 switch (reparse_struct.ReparseTag) {
......@@ -848,135 +745,83 @@ pub const DeleteFileError = error{
848745 NameTooLong,
849746 FileBusy,
850747 Unexpected,
748 NotDir,
749 IsDir,
851750};
852751
853pub fn DeleteFile(filename: []const u8) DeleteFileError!void {
854 const filename_w = try sliceToPrefixedFileW(filename);
855 return DeleteFileW(filename_w.span().ptr);
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,
752pub const DeleteFileOptions = struct {
753 dir: ?HANDLE,
754 remove_dir: bool = false,
897755};
898756
899/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
900pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
901 const pathname_w = try sliceToPrefixedFileW(pathname);
902 return CreateDirectoryW(dir, pathname_w.span().ptr, sa);
903}
757pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
758 const create_options_flags: ULONG = if (options.remove_dir)
759 FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
760 else
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.
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 };
763 const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
914764 var nt_name = UNICODE_STRING{
915765 .Length = path_len_bytes,
916766 .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)),
918769 };
919770
920771 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
921772 // Windows does not recognize this, but it does work with empty string.
922773 nt_name.Length = 0;
923774 }
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
925780 var attr = OBJECT_ATTRIBUTES{
926781 .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,
928783 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
929784 .ObjectName = &nt_name,
930 .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
785 .SecurityDescriptor = null,
931786 .SecurityQualityOfService = null,
932787 };
933788 var io: IO_STATUS_BLOCK = undefined;
934 var result_handle: HANDLE = undefined;
935 const rc = ntdll.NtCreateFile(
936 &result_handle,
937 GENERIC_READ | SYNCHRONIZE,
789 var tmp_handle: HANDLE = undefined;
790 var rc = ntdll.NtCreateFile(
791 &tmp_handle,
792 SYNCHRONIZE | DELETE,
938793 &attr,
939794 &io,
940795 null,
941 FILE_ATTRIBUTE_NORMAL,
942 FILE_SHARE_READ,
943 FILE_CREATE,
944 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
796 0,
797 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
798 FILE_OPEN,
799 create_options_flags,
945800 null,
946801 0,
947802 );
948803 switch (rc) {
949 .SUCCESS => return result_handle,
804 .SUCCESS => return CloseHandle(tmp_handle),
950805 .OBJECT_NAME_INVALID => unreachable,
951806 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
952 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
953 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
954807 .INVALID_PARAMETER => unreachable,
955 .ACCESS_DENIED => return error.AccessDenied,
956 .OBJECT_PATH_SYNTAX_BAD => unreachable,
957 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
808 .FILE_IS_A_DIRECTORY => return error.IsDir,
809 .NOT_A_DIRECTORY => return error.NotDir,
958810 else => return unexpectedStatus(rc),
959811 }
960812}
961813
962pub const RemoveDirectoryError = error{
963 FileNotFound,
964 DirNotEmpty,
965 Unexpected,
966 NotDir,
967};
814pub const MoveFileError = error{Unexpected};
968815
969pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {
970 const dir_path_w = try sliceToPrefixedFileW(dir_path);
971 return RemoveDirectoryW(dir_path_w.span().ptr);
816pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
817 const old_path_w = try sliceToPrefixedFileW(old_path);
818 const new_path_w = try sliceToPrefixedFileW(new_path);
819 return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
972820}
973821
974pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void {
975 if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {
822pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
823 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
976824 switch (kernel32.GetLastError()) {
977 .PATH_NOT_FOUND => return error.FileNotFound,
978 .DIR_NOT_EMPTY => return error.DirNotEmpty,
979 .DIRECTORY => return error.NotDir,
980825 else => |err| return unexpectedError(err),
981826 }
982827 }
......@@ -1463,8 +1308,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
14631308}
14641309
14651310/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1466/// it will get NT-style prefix `\??\` prepended automatically. For prepending
1467/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
1311/// it will get NT-style prefix `\??\` prepended automatically.
14681312pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
14691313 // TODO https://github.com/ziglang/zig/issues/2765
14701314 var path_space: PathSpace = undefined;
lib/std/os/windows/ntdll.zig+12
......@@ -54,6 +54,18 @@ pub extern "NtDll" fn NtDeviceIoControlFile(
5454 OutputBuffer: ?PVOID,
5555 OutputBufferLength: ULONG,
5656) 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;
5769pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS;
5870pub extern "NtDll" fn RtlDosPathNameToNtPathName_U(
5971 DosPathName: [*:0]const u16,
lib/std/special/compiler_rt.zig+1
......@@ -92,6 +92,7 @@ comptime {
9292 @export(@import("compiler_rt/floatunsidf.zig").__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });
9393 @export(@import("compiler_rt/floatundidf.zig").__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });
9494
95 @export(@import("compiler_rt/floatditf.zig").__floatditf, .{ .name = "__floatditf", .linkage = linkage });
9596 @export(@import("compiler_rt/floattitf.zig").__floattitf, .{ .name = "__floattitf", .linkage = linkage });
9697 @export(@import("compiler_rt/floattidf.zig").__floattidf, .{ .name = "__floattidf", .linkage = linkage });
9798 @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)" {
171171 expectEqual(a10, a10);
172172}
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
174227/// This function is intended to be used only in tests. When the two slices are not
175228/// equal, prints diagnostics to stderr to show exactly how they are not equal,
176229/// then aborts.
lib/std/zig.zig+16
......@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
4343 return .{ .line = line, .column = column };
4444}
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
4662/// Returns the standard file system basename of a binary generated by the Zig compiler.
4763pub fn binNameAlloc(
4864 allocator: *std.mem.Allocator,
lib/std/zig/ast.zig+6-2
......@@ -1299,6 +1299,10 @@ pub const Node = struct {
12991299 });
13001300 }
13011301
1302 pub fn body(self: *const FnProto) ?*Node {
1303 return self.getTrailer("body_node");
1304 }
1305
13021306 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
13031307 const trailers_start = @alignCast(
13041308 @alignOf(ParamDecl),
......@@ -1381,7 +1385,7 @@ pub const Node = struct {
13811385 .Invalid => {},
13821386 }
13831387
1384 if (self.getTrailer("body_node")) |body_node| {
1388 if (self.body()) |body_node| {
13851389 if (i < 1) return body_node;
13861390 i -= 1;
13871391 }
......@@ -1397,7 +1401,7 @@ pub const Node = struct {
13971401 }
13981402
13991403 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();
14011405 switch (self.return_type) {
14021406 .Explicit, .InferErrorSet => |node| return node.lastToken(),
14031407 .Invalid => |tok| return tok,
lib/std/zig/parse.zig+10-1
......@@ -201,7 +201,16 @@ const Parser = struct {
201201 p.findNextContainerMember();
202202 const next = p.token_ids[p.tok_i];
203203 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 },
205214 else => {
206215 if (next == .RBrace) {
207216 if (!top_level) break;
lib/std/zig/parser_test.zig+8
......@@ -293,6 +293,14 @@ test "zig fmt: decl between fields" {
293293 });
294294}
295295
296test "zig fmt: eof after missing comma" {
297 try testError(
298 \\foo()
299 , &[_]Error{
300 .ExpectedToken,
301 });
302}
303
296304test "zig fmt: errdefer with payload" {
297305 try testCanonical(
298306 \\pub fn main() anyerror!void {
lib/std/zig/string_literal.zig-1
......@@ -104,7 +104,6 @@ pub fn parse(
104104 return error.InvalidCharacter;
105105 },
106106 },
107 else => unreachable,
108107 }
109108 }
110109 unreachable;
src-self-hosted/Module.zig+196-1281
......@@ -6,6 +6,7 @@ const Value = @import("value.zig").Value;
66const Type = @import("type.zig").Type;
77const TypedValue = @import("TypedValue.zig");
88const assert = std.debug.assert;
9const log = std.log;
910const BigIntConst = std.math.big.int.Const;
1011const BigIntMutable = std.math.big.int.Mutable;
1112const Target = std.Target;
......@@ -20,6 +21,7 @@ const ast = std.zig.ast;
2021const trace = @import("tracy.zig").trace;
2122const liveness = @import("liveness.zig");
2223const astgen = @import("astgen.zig");
24const zir_sema = @import("zir_sema.zig");
2325
2426/// General-purpose allocator. Used for both temporary and long-term storage.
2527gpa: *Allocator,
......@@ -46,7 +48,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
4648/// Maps fully qualified namespaced names to the Decl struct for them.
4749decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
4850
49optimize_mode: std.builtin.Mode,
5051link_error_flags: link.File.ErrorFlags = .{},
5152
5253work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
......@@ -75,6 +76,8 @@ next_anon_name_index: usize = 0,
7576/// contains Decls that need to be deleted if they end up having no references to them.
7677deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
7778
79/// Owned by Module.
80root_name: []u8,
7881keep_source_files_loaded: bool,
7982
8083pub const InnerError = error{ OutOfMemory, AnalysisFail };
......@@ -86,6 +89,9 @@ const WorkItem = union(enum) {
8689 /// It may have already be analyzed, or it may have been determined
8790 /// to be outdated; in this case perform semantic analysis again.
8891 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,
8995};
9096
9197pub const Export = struct {
......@@ -173,6 +179,13 @@ pub const Decl = struct {
173179 /// This is populated regardless of semantic analysis and code generation.
174180 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
176189 contents_hash: std.zig.SrcHash,
177190
178191 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
......@@ -233,7 +246,7 @@ pub const Decl = struct {
233246
234247 pub fn dump(self: *Decl) void {
235248 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
236 std.debug.warn("{}:{}:{} name={} status={}", .{
249 std.debug.print("{}:{}:{} name={} status={}", .{
237250 self.scope.sub_file_path,
238251 loc.line + 1,
239252 loc.column + 1,
......@@ -241,12 +254,12 @@ pub const Decl = struct {
241254 @tagName(self.analysis),
242255 });
243256 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 });
245258 }
246 std.debug.warn("\n", .{});
259 std.debug.print("\n", .{});
247260 }
248261
249 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
262 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
250263 switch (self.typed_value) {
251264 .most_recent => |*x| return x,
252265 .never_succeeded => return null,
......@@ -384,18 +397,6 @@ pub const Scope = struct {
384397 };
385398 }
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
399400 /// Asserts the scope has a parent which is a ZIRModule or File and
400401 /// returns the sub_file_path field.
401402 pub fn subFilePath(base: *Scope) []const u8 {
......@@ -551,7 +552,7 @@ pub const Scope = struct {
551552
552553 pub fn dumpSrc(self: *File, src: usize) void {
553554 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 });
555556 }
556557
557558 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
......@@ -653,7 +654,7 @@ pub const Scope = struct {
653654
654655 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
655656 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 });
657658 }
658659
659660 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
......@@ -784,6 +785,7 @@ pub const AllErrors = struct {
784785
785786pub const InitOptions = struct {
786787 target: std.Target,
788 root_name: []const u8,
787789 root_pkg: *Package,
788790 output_mode: std.builtin.OutputMode,
789791 bin_file_dir: ?std.fs.Dir = null,
......@@ -795,12 +797,18 @@ pub const InitOptions = struct {
795797};
796798
797799pub 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
798803 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,
800807 .target = options.target,
801808 .output_mode = options.output_mode,
802809 .link_mode = options.link_mode orelse .Static,
803810 .object_format = options.object_format orelse options.target.getObjectFormat(),
811 .optimize_mode = options.optimize_mode,
804812 });
805813 errdefer bin_file.destroy();
806814
......@@ -832,12 +840,12 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
832840
833841 return Module{
834842 .gpa = gpa,
843 .root_name = root_name,
835844 .root_pkg = options.root_pkg,
836845 .root_scope = root_scope,
837846 .bin_file_dir = bin_file_dir,
838847 .bin_file_path = options.bin_file_path,
839848 .bin_file = bin_file,
840 .optimize_mode = options.optimize_mode,
841849 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
842850 .keep_source_files_loaded = options.keep_source_files_loaded,
843851 };
......@@ -846,6 +854,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
846854pub fn deinit(self: *Module) void {
847855 self.bin_file.destroy();
848856 const gpa = self.gpa;
857 self.gpa.free(self.root_name);
849858 self.deletion_set.deinit(gpa);
850859 self.work_queue.deinit();
851860
......@@ -887,13 +896,18 @@ pub fn deinit(self: *Module) void {
887896
888897fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
889898 for (export_list) |exp| {
899 gpa.free(exp.options.name);
890900 gpa.destroy(exp);
891901 }
892902 gpa.free(export_list);
893903}
894904
895905pub 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;
897911}
898912
899913/// Detect changes to source files, perform semantic analysis, and update the output files.
......@@ -941,7 +955,6 @@ pub fn update(self: *Module) !void {
941955 }
942956
943957 self.link_error_flags = self.bin_file.errorFlags();
944 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
945958
946959 // If there are any errors, we anticipate the source files being loaded
947960 // 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 {
10551068 error.AnalysisFail => {
10561069 decl.analysis = .dependency_failure;
10571070 },
1058 error.CGenFailure => {
1059 // Error is handled by CBE, don't try adding it again
1060 },
10611071 else => {
10621072 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1063 const result = self.failed_decls.getOrPutAssumeCapacity(decl);
1064 if (result.found_existing) {
1065 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
1066 } else {
1067 result.entry.value = try ErrorMsg.create(
1068 self.gpa,
1069 decl.src(),
1070 "unable to codegen: {}",
1071 .{@errorName(err)},
1072 );
1073 }
1073 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1074 self.gpa,
1075 decl.src(),
1076 "unable to codegen: {}",
1077 .{@errorName(err)},
1078 ));
10741079 decl.analysis = .codegen_failure_retryable;
10751080 },
10761081 };
......@@ -1082,10 +1087,22 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
10821087 error.AnalysisFail => continue,
10831088 };
10841089 },
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 },
10851102 };
10861103}
10871104
1088fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1105pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
10891106 const tracy = trace(@src());
10901107 defer tracy.end();
10911108
......@@ -1099,12 +1116,10 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
10991116 .codegen_failure_retryable,
11001117 => return error.AnalysisFail,
11011118
1102 .complete, .outdated => blk: {
1103 if (decl.generation == self.generation) {
1104 assert(decl.analysis == .complete);
1105 return;
1106 }
1107 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1119 .complete => return,
1120
1121 .outdated => blk: {
1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});
11081123
11091124 // The exports this Decl performs will be re-discovered, so we remove them here
11101125 // prior to re-analysis.
......@@ -1129,7 +1144,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
11291144 };
11301145
11311146 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])
11331148 else
11341149 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
11351150 error.OutOfMemory => return error.OutOfMemory,
......@@ -1205,7 +1220,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12051220 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
12061221
12071222 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, .{
12091224 .ty = Type.initTag(.type),
12101225 .val = Value.initTag(.type_type),
12111226 });
......@@ -1244,11 +1259,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12441259 };
12451260
12461261 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, .{
12481263 .return_type = return_type_inst,
12491264 .param_types = param_types,
12501265 }, .{});
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
12531268 // We need the memory for the Type to go into the arena for the Decl
12541269 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
......@@ -1264,7 +1279,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12641279 };
12651280 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, .{
12681283 .instructions = fn_type_scope.instructions.items,
12691284 });
12701285 const new_func = try decl_arena.allocator.create(Fn);
......@@ -1317,7 +1332,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13171332 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
13181333 {
13191334 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);
13211336 }
13221337
13231338 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
......@@ -1387,19 +1402,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13871402 }
13881403}
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
14031405fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
14041406 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
14051407 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 {
14921494}
14931495
14941496fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1497 const tracy = trace(@src());
1498 defer tracy.end();
1499
14951500 // We may be analyzing it for the first time, or this may be
14961501 // an incremental update. This code handles both cases.
14971502 const tree = try self.getAstTree(root_scope);
......@@ -1533,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15331538 if (!srcHashEql(decl.contents_hash, contents_hash)) {
15341539 try self.markOutdatedDecl(decl);
15351540 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 });
15361545 }
15371546 }
15381547 } else {
......@@ -1551,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15511560 // Handle explicitly deleted decls from the source code. Not to be confused
15521561 // with when we delete decls because they are no longer referenced.
15531562 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});
15551564 try self.deleteDecl(entry.key);
15561565 }
15571566}
......@@ -1580,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
15801589 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
15811590 if (self.decl_table.get(name_hash)) |decl| {
15821591 deleted_decls.removeAssertDiscard(decl);
1583 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
15841592 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
15851593 try self.markOutdatedDecl(decl);
15861594 decl.contents_hash = src_decl.contents_hash;
......@@ -1600,12 +1608,12 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
16001608 }
16011609 }
16021610 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);
16041612 }
16051613 // Handle explicitly deleted decls from the source code. Not to be confused
16061614 // with when we delete decls because they are no longer referenced.
16071615 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});
16091617 try self.deleteDecl(entry.key);
16101618 }
16111619}
......@@ -1617,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
16171625 // not be present in the set, and this does nothing.
16181626 decl.scope.removeDecl(decl);
16191627
1620 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});
16211629 const name_hash = decl.fullyQualifiedNameHash();
16221630 self.decl_table.removeAssertDiscard(name_hash);
16231631 // 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 {
16791687 entry.value.destroy(self.gpa);
16801688 }
16811689 _ = self.symbol_exports.remove(exp.options.name);
1690 self.gpa.free(exp.options.name);
16821691 self.gpa.destroy(exp);
16831692 }
16841693 self.gpa.free(kv.value);
......@@ -1703,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
17031712 const fn_zir = func.analysis.queued;
17041713 defer fn_zir.arena.promote(self.gpa).deinit();
17051714 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
17101719 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
17111720 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});
17131722}
17141723
17151724fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1716 //std.debug.warn("mark {} outdated\n", .{decl.name});
1725 log.debug(.module, "mark {} outdated\n", .{decl.name});
17171726 try self.work_queue.writeItem(.{ .analyze_decl = decl });
17181727 if (self.failed_decls.remove(decl)) |entry| {
17191728 entry.value.destroy(self.gpa);
......@@ -1758,131 +1767,18 @@ fn createNewDecl(
17581767 return new_decl;
17591768}
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
18651770/// 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 {
18671772 return scope.cast(Scope.Block) orelse
18681773 return self.fail(scope, src, "instruction illegal outside function body", .{});
18691774}
18701775
1871fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
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 {
1776pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
18811777 return (try self.resolveDefinedValue(scope, base)) orelse
18821778 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
18831779}
18841780
1885fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1781pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
18861782 if (base.value()) |val| {
18871783 if (val.isUndef()) {
18881784 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 {
18921788 return null;
18931789}
18941790
1895fn resolveConstString(self: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
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 {
1791pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
19121792 try self.ensureDeclAnalyzed(exported_decl);
19131793 const typed_value = exported_decl.typed_value.most_recent.typed_value;
19141794 switch (typed_value.ty.zigTypeTag()) {
......@@ -1922,6 +1802,9 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19221802 const new_export = try self.gpa.create(Export);
19231803 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
19251808 const owner_decl = scope.decl().?;
19261809
19271810 new_export.* = .{
......@@ -1934,7 +1817,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19341817 };
19351818
19361819 // 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);
19381821 if (!eo_gop.found_existing) {
19391822 eo_gop.entry.value = &[0]*Export{};
19401823 }
......@@ -1943,7 +1826,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19431826 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
19441827
19451828 // 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);
19471830 if (!de_gop.found_existing) {
19481831 de_gop.entry.value = &[0]*Export{};
19491832 }
......@@ -1980,7 +1863,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19801863 };
19811864}
19821865
1983fn addNoOp(
1866pub fn addNoOp(
19841867 self: *Module,
19851868 block: *Scope.Block,
19861869 src: usize,
......@@ -1999,7 +1882,7 @@ fn addNoOp(
19991882 return &inst.base;
20001883}
20011884
2002fn addUnOp(
1885pub fn addUnOp(
20031886 self: *Module,
20041887 block: *Scope.Block,
20051888 src: usize,
......@@ -2020,7 +1903,7 @@ fn addUnOp(
20201903 return &inst.base;
20211904}
20221905
2023fn addBinOp(
1906pub fn addBinOp(
20241907 self: *Module,
20251908 block: *Scope.Block,
20261909 src: usize,
......@@ -2043,7 +1926,7 @@ fn addBinOp(
20431926 return &inst.base;
20441927}
20451928
2046fn addBr(
1929pub fn addBr(
20471930 self: *Module,
20481931 scope_block: *Scope.Block,
20491932 src: usize,
......@@ -2064,7 +1947,7 @@ fn addBr(
20641947 return &inst.base;
20651948}
20661949
2067fn addCondBr(
1950pub fn addCondBr(
20681951 self: *Module,
20691952 block: *Scope.Block,
20701953 src: usize,
......@@ -2087,7 +1970,7 @@ fn addCondBr(
20871970 return &inst.base;
20881971}
20891972
2090fn addCall(
1973pub fn addCall(
20911974 self: *Module,
20921975 block: *Scope.Block,
20931976 src: usize,
......@@ -2109,138 +1992,7 @@ fn addCall(
21091992 return &inst.base;
21101993}
21111994
2112pub fn addZIRInstSpecial(
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 {
1995pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
22441996 const const_inst = try scope.arena().create(Inst.Constant);
22451997 const_inst.* = .{
22461998 .base = .{
......@@ -2253,42 +2005,42 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)
22532005 return &const_inst.base;
22542006}
22552007
2256fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2008pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
22572009 return self.constInst(scope, src, .{
22582010 .ty = Type.initTag(.type),
22592011 .val = try ty.toValue(scope.arena()),
22602012 });
22612013}
22622014
2263fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
2015pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
22642016 return self.constInst(scope, src, .{
22652017 .ty = Type.initTag(.void),
2266 .val = Value.initTag(.the_one_possible_value),
2018 .val = Value.initTag(.void_value),
22672019 });
22682020}
22692021
2270fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2022pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
22712023 return self.constInst(scope, src, .{
22722024 .ty = Type.initTag(.noreturn),
2273 .val = Value.initTag(.the_one_possible_value),
2025 .val = Value.initTag(.unreachable_value),
22742026 });
22752027}
22762028
2277fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2029pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
22782030 return self.constInst(scope, src, .{
22792031 .ty = ty,
22802032 .val = Value.initTag(.undef),
22812033 });
22822034}
22832035
2284fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2036pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
22852037 return self.constInst(scope, src, .{
22862038 .ty = Type.initTag(.bool),
22872039 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
22882040 });
22892041}
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 {
22922044 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
22932045 int_payload.* = .{ .int = int };
22942046
......@@ -2298,7 +2050,7 @@ fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64
22982050 });
22992051}
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 {
23022054 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
23032055 int_payload.* = .{ .int = int };
23042056
......@@ -2308,7 +2060,7 @@ fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64)
23082060 });
23092061}
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 {
23122064 const val_payload = if (big_int.positive) blk: {
23132065 if (big_int.to(u64)) |x| {
23142066 return self.constIntUnsigned(scope, src, ty, x);
......@@ -2337,191 +2089,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI
23372089 });
23382090}
23392091
2340fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
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(
2092pub fn createAnonymousDecl(
25252093 self: *Module,
25262094 scope: *Scope,
25272095 decl_arena: *std.heap.ArenaAllocator,
......@@ -2567,151 +2135,7 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De
25672135 return self.decl_table.get(name_hash);
25682136}
25692137
2570fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) 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 {
2138pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
27152139 const scope_decl = scope.decl().?;
27162140 try self.declareDeclDependency(scope_decl, decl);
27172141 self.ensureDeclAnalyzed(decl) catch |err| {
......@@ -2739,410 +2163,7 @@ fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerEr
27392163 });
27402164}
27412165
2742fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) 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 {
2166pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
31462167 const elem_ty = switch (ptr.ty.zigTypeTag()) {
31472168 .Pointer => ptr.ty.elemType(),
31482169 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
31542175 });
31552176 }
31562177
3157 return self.fail(scope, src, "TODO implement runtime deref", .{});
3158}
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);
2178 const b = try self.requireRuntimeBlock(scope, src);
2179 return self.addUnOp(b, src, elem_ty, .load, ptr);
32742180}
32752181
3276fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
3277 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);
3278 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
3279
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);
2182pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
2183 const decl = self.lookupDeclName(scope, decl_name) orelse
2184 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
2185 return self.analyzeDeclRef(scope, src, decl);
33112186}
33122187
3313fn wantSafety(self: *Module, scope: *Scope) bool {
3314 return switch (self.optimize_mode) {
2188pub fn wantSafety(self: *Module, scope: *Scope) bool {
2189 // TODO take into account scope's safety overrides
2190 return switch (self.optimizeMode()) {
33152191 .Debug => true,
33162192 .ReleaseSafe => true,
33172193 .ReleaseFast => false,
......@@ -3319,33 +2195,12 @@ fn wantSafety(self: *Module, scope: *Scope) bool {
33192195 };
33202196}
33212197
3322fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
3323 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
3324 if (self.wantSafety(scope)) {
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 }
2198pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
2199 const b = try self.requireRuntimeBlock(scope, src);
2200 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
33462201}
33472202
3348fn analyzeIsNull(
2203pub fn analyzeIsNull(
33492204 self: *Module,
33502205 scope: *Scope,
33512206 src: usize,
......@@ -3356,7 +2211,7 @@ fn analyzeIsNull(
33562211}
33572212
33582213/// Asserts that lhs and rhs types are both numeric.
3359fn cmpNumeric(
2214pub fn cmpNumeric(
33602215 self: *Module,
33612216 scope: *Scope,
33622217 src: usize,
......@@ -3539,7 +2394,7 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
35392394 }
35402395}
35412396
3542fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
2397pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
35432398 if (instructions.len == 0)
35442399 return Type.initTag(.noreturn);
35452400
......@@ -3579,7 +2434,7 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
35792434 return prev_inst.ty;
35802435}
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 {
35832438 // If the types are the same, we can return the operand.
35842439 if (dest_type.eql(inst.ty))
35852440 return inst;
......@@ -3599,7 +2454,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
35992454
36002455 // *[N]T to []T
36012456 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()))
36032458 {
36042459 const array_type = inst.ty.elemType();
36052460 const dst_elem_type = dest_type.elemType();
......@@ -3675,7 +2530,23 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
36752530 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
36762531}
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 {
36792550 if (inst.value()) |val| {
36802551 // Keep the comptime Value representation; take the new type.
36812552 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 {
38222693 return @bitCast(u128, a) == @bitCast(u128, b);
38232694}
38242695
3825fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2696pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
38262697 // TODO is this a performance issue? maybe we should try the operation without
38272698 // resorting to BigInt first.
38282699 var lhs_space: Value.BigIntSpace = undefined;
......@@ -3850,7 +2721,7 @@ fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
38502721 return Value.initPayload(val_payload);
38512722}
38522723
3853fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2724pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
38542725 // TODO is this a performance issue? maybe we should try the operation without
38552726 // resorting to BigInt first.
38562727 var lhs_space: Value.BigIntSpace = undefined;
......@@ -3878,7 +2749,7 @@ fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
38782749 return Value.initPayload(val_payload);
38792750}
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 {
38822753 var bit_count = switch (float_type.tag()) {
38832754 .comptime_float => 128,
38842755 else => float_type.floatBits(self.target()),
......@@ -3887,7 +2758,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
38872758 const allocator = scope.arena();
38882759 const val_payload = switch (bit_count) {
38892760 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", .{});
38912762 },
38922763 32 => blk: {
38932764 const lhs_val = lhs.toFloat(f32);
......@@ -3904,7 +2775,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39042775 break :blk &val_payload.base;
39052776 },
39062777 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", .{});
39082779 },
39092780 else => unreachable,
39102781 };
......@@ -3912,7 +2783,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39122783 return Value.initPayload(val_payload);
39132784}
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 {
39162787 var bit_count = switch (float_type.tag()) {
39172788 .comptime_float => 128,
39182789 else => float_type.floatBits(self.target()),
......@@ -3921,7 +2792,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39212792 const allocator = scope.arena();
39222793 const val_payload = switch (bit_count) {
39232794 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", .{});
39252796 },
39262797 32 => blk: {
39272798 const lhs_val = lhs.toFloat(f32);
......@@ -3938,10 +2809,54 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39382809 break :blk &val_payload.base;
39392810 },
39402811 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", .{});
39422813 },
39432814 else => unreachable,
39442815 };
39452816
39462817 return Value.initPayload(val_payload);
39472818}
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 @@
11pub const Table = std.StringHashMap(*Package);
22
3/// This should be used for file operations.
34root_src_dir: std.fs.Dir,
4/// Relative to `root_src_dir`.
5root_src_path: []const u8,
5/// This is for metadata purposes, for example putting into debug information.
6root_src_dir_path: []u8,
7/// Relative to `root_src_dir` and `root_src_dir_path`.
8root_src_path: []u8,
69table: Table,
710
811/// No references to `root_src_dir` and `root_src_path` are kept.
......@@ -18,8 +21,11 @@ pub fn create(
1821 errdefer allocator.destroy(ptr);
1922 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
2023 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);
2126 ptr.* = .{
2227 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),
28 .root_src_dir_path = root_src_dir_path,
2329 .root_src_path = root_src_path_dupe,
2430 .table = Table.init(allocator),
2531 };
......@@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {
3036 const allocator = self.table.allocator;
3137 self.root_src_dir.close();
3238 allocator.free(self.root_src_path);
39 allocator.free(self.root_src_dir_path);
3340 {
3441 var it = self.table.iterator();
3542 while (it.next()) |kv| {
......@@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {
4148}
4249
4350pub fn add(self: *Package, name: []const u8, package: *Package) !void {
51 try self.table.ensureCapacity(self.table.items().len + 1);
4452 const name_dupe = try mem.dupe(self.table.allocator, u8, name);
45 errdefer self.table.allocator.deinit(name_dupe);
46 const entry = try self.table.put(name_dupe, package);
47 assert(entry == null);
53 self.table.putAssumeCapacityNoClobber(name_dupe, package);
4854}
4955
5056const std = @import("std");
src-self-hosted/astgen.zig+287-102
......@@ -17,6 +17,9 @@ pub const ResultLoc = union(enum) {
1717 discard,
1818 /// The expression has an inferred type, and it will be evaluated as an rvalue.
1919 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,
2023 /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
2124 ty: *zir.Inst,
2225 /// The expression must store its result into this typed pointer.
......@@ -33,7 +36,7 @@ pub const ResultLoc = union(enum) {
3336
3437pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
3538 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, .{
3740 .ty = Type.initTag(.type),
3841 .val = Value.initTag(.type_type),
3942 });
......@@ -46,18 +49,45 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
4649 switch (node.tag) {
4750 .VarDecl => unreachable, // Handled in `blockExpr`.
4851 .Assign => unreachable, // Handled in `blockExpr`.
49
50 .Add => return arithmetic(mod, scope, rl, node.castTag(.Add).?, .add),
51 .Sub => return arithmetic(mod, scope, rl, node.castTag(.Sub).?, .sub),
52
53 .BangEqual => return cmp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
54 .EqualEqual => return cmp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
55 .GreaterThan => return cmp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
56 .GreaterOrEqual => return cmp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
57 .LessThan => return cmp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
58 .LessOrEqual => return cmp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
59
60 .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),
52 .AssignBitAnd => unreachable, // Handled in `blockExpr`.
53 .AssignBitOr => unreachable, // Handled in `blockExpr`.
54 .AssignBitShiftLeft => unreachable, // Handled in `blockExpr`.
55 .AssignBitShiftRight => unreachable, // Handled in `blockExpr`.
56 .AssignBitXor => unreachable, // Handled in `blockExpr`.
57 .AssignDiv => unreachable, // Handled in `blockExpr`.
58 .AssignSub => unreachable, // Handled in `blockExpr`.
59 .AssignSubWrap => unreachable, // Handled in `blockExpr`.
60 .AssignMod => unreachable, // Handled in `blockExpr`.
61 .AssignAdd => unreachable, // Handled in `blockExpr`.
62 .AssignAddWrap => unreachable, // Handled in `blockExpr`.
63 .AssignMul => unreachable, // Handled in `blockExpr`.
64 .AssignMulWrap => unreachable, // Handled in `blockExpr`.
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).?),
6191 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
6292 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
6393 .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
90120
91121 var scope = parent_scope;
92122 for (block_node.statements()) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;
124 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
93125 switch (statement.tag) {
94126 .VarDecl => {
95127 const var_decl_node = statement.castTag(.VarDecl).?;
......@@ -99,10 +131,25 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
99131 const ass = statement.castTag(.Assign).?;
100132 try assign(mod, scope, ass);
101133 },
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
102148 else => {
103149 const possibly_unused_result = try expr(mod, scope, .none, statement);
104 const src = scope.tree().token_locs[statement.firstToken()].start;
105 _ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result);
150 if (!possibly_unused_result.tag.isNoReturn()) {
151 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
152 }
106153 },
107154 }
108155 }
......@@ -133,7 +180,7 @@ fn varDecl(
133180 if (nodeMayNeedMemoryLocation(init_node)) {
134181 if (node.getTrailer("type_node")) |type_node| {
135182 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);
137184 const result_loc: ResultLoc = .{ .ptr = alloc };
138185 const init_inst = try expr(mod, scope, result_loc, init_node);
139186 const sub_scope = try block_arena.create(Scope.LocalVal);
......@@ -145,7 +192,7 @@ fn varDecl(
145192 };
146193 return &sub_scope.base;
147194 } else {
148 const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred);
195 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
149196 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
150197 const init_inst = try expr(mod, scope, result_loc, init_node);
151198 const sub_scope = try block_arena.create(Scope.LocalVal);
......@@ -176,7 +223,7 @@ fn varDecl(
176223 .Keyword_var => {
177224 if (node.getTrailer("type_node")) |type_node| {
178225 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);
180227 const result_loc: ResultLoc = .{ .ptr = alloc };
181228 const init_inst = try expr(mod, scope, result_loc, init_node);
182229 const sub_scope = try block_arena.create(Scope.LocalPtr);
......@@ -188,7 +235,7 @@ fn varDecl(
188235 };
189236 return &sub_scope.base;
190237 } else {
191 const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred);
238 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
192239 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
193240 const init_inst = try expr(mod, scope, result_loc, init_node);
194241 const sub_scope = try block_arena.create(Scope.LocalPtr);
......@@ -207,28 +254,44 @@ fn varDecl(
207254
208255fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
209256 if (infix_node.lhs.castTag(.Identifier)) |ident| {
210 const tree = scope.tree();
211 const ident_name = try identifierTokenString(mod, scope, ident.token);
257 // This intentionally does not support @"_" syntax.
258 const ident_name = scope.tree().tokenSlice(ident.token);
212259 if (std.mem.eql(u8, ident_name, "_")) {
213260 _ = try expr(mod, scope, .discard, infix_node.rhs);
214261 return;
215 } else {
216 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
217262 }
218 } else {
219 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
220263 }
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);
221284}
222285
223286fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
224287 const tree = scope.tree();
225288 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, .{
227290 .ty = Type.initTag(.type),
228291 .val = Value.initTag(.bool_type),
229292 });
230293 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);
232295}
233296
234297/// Identifier token -> String (allocated in scope.arena())
......@@ -257,7 +320,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke
257320
258321 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 }, .{});
261324}
262325
263326fn 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!
268331 const lhs = try expr(mod, scope, .none, node.lhs);
269332 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 }, .{});
272 return mod.addZIRUnOp(scope, src, .deref, pointer);
334 const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
335 return addZIRUnOp(mod, scope, src, .deref, pointer);
273336}
274337
275338fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
276339 const tree = scope.tree();
277340 const src = tree.token_locs[node.rtoken].start;
278341 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);
280343}
281344
282fn cmp(
345fn simpleBinOp(
283346 mod: *Module,
284347 scope: *Scope,
285348 rl: ResultLoc,
286349 infix_node: *ast.Node.SimpleInfixOp,
287 cmp_inst_tag: zir.Inst.Tag,
350 op_inst_tag: zir.Inst.Tag,
288351) InnerError!*zir.Inst {
289352 const tree = scope.tree();
290353 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 {
305355 const lhs = try expr(mod, scope, .none, infix_node.lhs);
306356 const rhs = try expr(mod, scope, .none, infix_node.rhs);
307357
308 const tree = scope.tree();
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);
358 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
312359 return rlWrap(mod, scope, rl, result);
313360}
314361
......@@ -331,19 +378,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
331378
332379 const tree = scope.tree();
333380 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, .{
335382 .ty = Type.initTag(.type),
336383 .val = Value.initTag(.bool_type),
337384 });
338385 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, .{
341388 .condition = cond,
342389 .then_body = undefined, // populated below
343390 .else_body = undefined, // populated below
344391 }, .{});
345392
346 const block = try mod.addZIRInstBlock(scope, if_src, .{
393 const block = try addZIRInstBlock(mod, scope, if_src, .{
347394 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
348395 });
349396 var then_scope: Scope.GenZIR = .{
......@@ -359,14 +406,14 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
359406 // proper type inference requires peer type resolution on the if's
360407 // branches.
361408 const branch_rl: ResultLoc = switch (rl) {
362 .discard, .none, .ty, .ptr => rl,
409 .discard, .none, .ty, .ptr, .lvalue => rl,
363410 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
364411 };
365412
366413 const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
367414 if (!then_result.tag.isNoReturn()) {
368415 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, .{
370417 .block = block,
371418 .operand = then_result,
372419 }, .{});
......@@ -387,7 +434,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
387434 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
388435 if (!else_result.tag.isNoReturn()) {
389436 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, .{
391438 .block = block,
392439 .operand = else_result,
393440 }, .{});
......@@ -396,7 +443,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
396443 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
397444 // by directly allocating the body for this one instruction.
398445 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, .{
400447 .block = block,
401448 }, .{});
402449 }
......@@ -412,20 +459,20 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
412459 const src = tree.token_locs[cfe.ltoken].start;
413460 if (cfe.getRHS()) |rhs_node| {
414461 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);
416463 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);
418465 } 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);
420467 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);
422469 }
423470 } else {
424 return mod.addZIRNoOp(scope, src, .returnvoid);
471 return addZIRNoOp(mod, scope, src, .returnvoid);
425472 }
426473}
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 {
429476 const tracy = trace(@src());
430477 defer tracy.end();
431478
......@@ -437,7 +484,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
437484 }
438485
439486 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);
441489 }
442490
443491 if (ident_name.len >= 2) integer: {
......@@ -461,16 +509,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
461509 else => {
462510 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
463511 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
464 return mod.addZIRInstConst(scope, src, .{
512 const result = try addZIRInstConst(mod, scope, src, .{
465513 .ty = Type.initTag(.comptime_int),
466514 .val = Value.initPayload(&int_type_payload.base),
467515 });
516 return rlWrap(mod, scope, rl, result);
468517 },
469518 };
470 return mod.addZIRInstConst(scope, src, .{
519 const result = try addZIRInstConst(mod, scope, src, .{
471520 .ty = Type.initTag(.type),
472521 .val = val,
473522 });
523 return rlWrap(mod, scope, rl, result);
474524 }
475525 }
476526
......@@ -481,14 +531,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481531 .local_val => {
482532 const local_val = s.cast(Scope.LocalVal).?;
483533 if (mem.eql(u8, local_val.name, ident_name)) {
484 return local_val.inst;
534 return rlWrap(mod, scope, rl, local_val.inst);
485535 }
486536 s = local_val.parent;
487537 },
488538 .local_ptr => {
489539 const local_ptr = s.cast(Scope.LocalPtr).?;
490540 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 }
492547 }
493548 s = local_ptr.parent;
494549 },
......@@ -498,7 +553,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
498553 }
499554
500555 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);
502559 }
503560
504561 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
520577 };
521578
522579 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 }, .{});
524581}
525582
526583fn 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
545602 const int_payload = try arena.create(Value.Payload.Int_u64);
546603 int_payload.* = .{ .int = small_int };
547604 const src = tree.token_locs[int_lit.token].start;
548 return mod.addZIRInstConst(scope, src, .{
605 return addZIRInstConst(mod, scope, src, .{
549606 .ty = Type.initTag(.comptime_int),
550607 .val = Value.initPayload(&int_payload.base),
551608 });
......@@ -568,7 +625,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne
568625 const float_payload = try arena.create(Value.Payload.Float_128);
569626 float_payload.* = .{ .val = val };
570627 const src = tree.token_locs[float_lit.token].start;
571 return mod.addZIRInstConst(scope, src, .{
628 return addZIRInstConst(mod, scope, src, .{
572629 .ty = Type.initTag(.comptime_float),
573630 .val = Value.initPayload(&float_payload.base),
574631 });
......@@ -578,7 +635,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro
578635 const arena = scope.arena();
579636 const tree = scope.tree();
580637 const src = tree.token_locs[node.token].start;
581 return mod.addZIRInstConst(scope, src, .{
638 return addZIRInstConst(mod, scope, src, .{
582639 .ty = Type.initTag(.@"undefined"),
583640 .val = Value.initTag(.undef),
584641 });
......@@ -588,7 +645,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
588645 const arena = scope.arena();
589646 const tree = scope.tree();
590647 const src = tree.token_locs[node.token].start;
591 return mod.addZIRInstConst(scope, src, .{
648 return addZIRInstConst(mod, scope, src, .{
592649 .ty = Type.initTag(.bool),
593650 .val = switch (tree.token_ids[node.token]) {
594651 .Keyword_true => Value.initTag(.bool_true),
......@@ -602,7 +659,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
602659 const arena = scope.arena();
603660 const tree = scope.tree();
604661 const src = tree.token_locs[node.token].start;
605 return mod.addZIRInstConst(scope, src, .{
662 return addZIRInstConst(mod, scope, src, .{
606663 .ty = Type.initTag(.@"null"),
607664 .val = Value.initTag(.null_value),
608665 });
......@@ -620,7 +677,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
620677
621678 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, .{
624681 .ty = Type.initTag(.type),
625682 .val = Value.initTag(.const_slice_u8_type),
626683 });
......@@ -632,11 +689,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
632689 args[i] = try expr(mod, scope, .none, input.expr);
633690 }
634691
635 const return_type = try mod.addZIRInstConst(scope, src, .{
692 const return_type = try addZIRInstConst(mod, scope, src, .{
636693 .ty = Type.initTag(.type),
637694 .val = Value.initTag(.void_type),
638695 });
639 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
696 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
640697 .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
641698 .return_type = return_type,
642699 }, .{
......@@ -666,14 +723,14 @@ fn simpleCast(
666723 try ensureBuiltinParamCount(mod, scope, call, 2);
667724 const tree = scope.tree();
668725 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, .{
670727 .ty = Type.initTag(.type),
671728 .val = Value.initTag(.type_type),
672729 });
673730 const params = call.params();
674731 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
675732 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);
677734 return rlWrap(mod, scope, rl, result);
678735}
679736
......@@ -682,7 +739,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError
682739 const operand = try expr(mod, scope, .none, call.params()[0]);
683740 const tree = scope.tree();
684741 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);
686743}
687744
688745fn 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
695752 .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
696753 .discard => {
697754 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);
699756 return result;
700757 },
758 .lvalue => {
759 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
760 return addZIRUnOp(mod, scope, result.src, .ref, result);
761 },
701762 .ty => |result_ty| {
702763 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);
704765 },
705766 .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);
707768 return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
708769 },
709770 .bitcasted_ptr => |bitcasted_ptr| {
......@@ -715,7 +776,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
715776 return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
716777 },
717778 .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, .{
719780 .dest_type = dest_type,
720781 .block = block_ptr,
721782 }, .{});
......@@ -728,7 +789,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
728789 try ensureBuiltinParamCount(mod, scope, call, 2);
729790 const tree = scope.tree();
730791 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, .{
732793 .ty = Type.initTag(.type),
733794 .val = Value.initTag(.type_type),
734795 });
......@@ -737,21 +798,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
737798 switch (rl) {
738799 .none => {
739800 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);
741802 },
742803 .discard => {
743804 const operand = try expr(mod, scope, .none, params[1]);
744 const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);
745 _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
805 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
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);
746812 return result;
747813 },
748814 .ty => |result_ty| {
749815 const result = try expr(mod, scope, .none, params[1]);
750 const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result);
751 return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted);
816 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
817 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
752818 },
753819 .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);
755821 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
756822 },
757823 .bitcasted_ptr => |bitcasted_ptr| {
......@@ -799,7 +865,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
799865 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
800866 for (param_nodes) |param_node, i| {
801867 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, .{
803869 .func = lhs,
804870 .arg_index = i,
805871 }, .{});
......@@ -807,7 +873,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
807873 }
808874
809875 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, .{
811877 .func = lhs,
812878 .args = args,
813879 }, .{});
......@@ -818,7 +884,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
818884fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
819885 const tree = scope.tree();
820886 const src = tree.token_locs[unreach_node.token].start;
821 return mod.addZIRNoOp(scope, src, .@"unreachable");
887 return addZIRNoOp(mod, scope, src, .@"unreachable");
822888}
823889
824890fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
......@@ -1000,19 +1066,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
10001066 .none => return result,
10011067 .discard => {
10021068 // 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);
10041070 return result;
10051071 },
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),
10071077 .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, .{
10091079 .ptr = ptr_inst,
10101080 .value = result,
10111081 }, .{});
1012 _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{
1013 .ptr = ptr_inst,
1014 .value = casted_result,
1015 }, .{});
1082 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
10161083 return casted_result;
10171084 },
10181085 .bitcasted_ptr => |bitcasted_ptr| {
......@@ -1026,3 +1093,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
10261093 },
10271094 }
10281095}
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 {
828828 EvaluateForMangling,
829829};
830830
831pub const ZigClangUnaryExprOrTypeTrait_Kind = extern enum {
832 SizeOf,
833 AlignOf,
834 VecStep,
835 OpenMPRequiredSimdAlign,
836 PreferredAlignOf,
837};
838
831839pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
832840pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
833841pub 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
12251233
12261234pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
12271235pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
1236pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getKind(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangUnaryExprOrTypeTrait_Kind;
12281237
12291238pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
12301239pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
src-self-hosted/codegen.zig+928-211
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const mem = std.mem;
3const math = std.math;
34const assert = std.debug.assert;
45const ir = @import("ir.zig");
56const Type = @import("type.zig").Type;
......@@ -11,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;
1112const Target = std.Target;
1213const Allocator = mem.Allocator;
1314const 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
1521/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
1622pub const BlockData = struct {
......@@ -43,64 +49,66 @@ pub fn generateSymbol(
4349 src: usize,
4450 typed_value: TypedValue,
4551 code: *std.ArrayList(u8),
52 dbg_line: *std.ArrayList(u8),
4653) GenerateSymbolError!Result {
4754 const tracy = trace(@src());
4855 defer tracy.end();
4956
5057 switch (typed_value.ty.zigTypeTag()) {
5158 .Fn => {
52 switch (bin_file.options.target.cpu.arch) {
53 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
54 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
55 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
56 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
57 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
58 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
59 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
60 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
61 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
62 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
63 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
64 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
65 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
66 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
67 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
68 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
69 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
70 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
71 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
72 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
73 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
74 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
75 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
76 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
77 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
78 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
79 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
80 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
81 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
82 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
83 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
84 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
85 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
86 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
87 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
88 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
89 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
90 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
91 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
92 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
93 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
94 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
95 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
96 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
97 .shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),
98 .lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),
99 .wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),
100 .wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),
101 .renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),
102 .renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),
103 .ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),
59 switch (bin_file.base.options.target.cpu.arch) {
60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
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."),
104112 }
105113 },
106114 .Array => {
......@@ -112,7 +120,7 @@ pub fn generateSymbol(
112120 switch (try generateSymbol(bin_file, src, .{
113121 .ty = typed_value.ty.elemType(),
114122 .val = sentinel,
115 }, code)) {
123 }, code, dbg_line)) {
116124 .appended => return Result{ .appended = {} },
117125 .externally_managed => |slice| {
118126 code.appendSliceAssumeCapacity(slice);
......@@ -141,7 +149,7 @@ pub fn generateSymbol(
141149 // TODO handle the dependency of this symbol on the decl's vaddr.
142150 // If the decl changes vaddr, then this symbol needs to get regenerated.
143151 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();
145153 switch (bin_file.ptr_width) {
146154 .p32 => {
147155 try code.resize(4);
......@@ -164,7 +172,7 @@ pub fn generateSymbol(
164172 };
165173 },
166174 .Int => {
167 const info = typed_value.ty.intInfo(bin_file.options.target);
175 const info = typed_value.ty.intInfo(bin_file.base.options.target);
168176 if (info.bits == 8 and !info.signed) {
169177 const x = typed_value.val.toUnsignedInt();
170178 try code.append(@intCast(u8, x));
......@@ -204,10 +212,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
204212 target: *const std.Target,
205213 mod_fn: *const Module.Fn,
206214 code: *std.ArrayList(u8),
215 dbg_line: *std.ArrayList(u8),
207216 err_msg: ?*ErrorMsg,
208217 args: []MCValue,
218 ret_mcv: MCValue,
219 fn_type: Type,
209220 arg_index: usize,
210221 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
212238 /// Whenever there is a runtime branch, we push a Branch onto this stack,
213239 /// 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 {
225251 unreach,
226252 /// No more references to this value remain.
227253 dead,
254 /// The value is undefined.
255 undef,
228256 /// 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.
229258 immediate: u64,
230259 /// 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.
231261 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,
232264 /// The value is in a target-specific register.
233265 register: Register,
234266 /// 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.
235268 memory: u64,
236269 /// 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,
238274 /// The value is in the compare flags assuming an unsigned operation,
239275 /// with this operator applied on top of it.
240 compare_flags_unsigned: std.math.CompareOperator,
276 compare_flags_unsigned: math.CompareOperator,
241277 /// The value is in the compare flags assuming a signed operation,
242278 /// with this operator applied on top of it.
243 compare_flags_signed: std.math.CompareOperator,
279 compare_flags_signed: math.CompareOperator,
244280
245281 fn isMemory(mcv: MCValue) bool {
246282 return switch (mcv) {
......@@ -267,6 +303,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
267303 .memory,
268304 .compare_flags_unsigned,
269305 .compare_flags_signed,
306 .ptr_stack_offset,
307 .ptr_embedded_in_code,
308 .undef,
270309 => false,
271310
272311 .register,
......@@ -279,10 +318,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
279318 const Branch = struct {
280319 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
281320 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
282 free_registers: FreeRegInt = std.math.maxInt(FreeRegInt),
321 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
283322
284323 /// Maps offset to what is stored there.
285 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
324 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
286325 /// Offset from the stack base, representing the end of the stack frame.
287326 max_end_stack: u32 = 0,
288327 /// 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 {
292331 fn markRegUsed(self: *Branch, reg: Register) void {
293332 if (FreeRegInt == u0) return;
294333 const index = reg.allocIndex() orelse return;
295 const ShiftInt = std.math.Log2Int(FreeRegInt);
334 const ShiftInt = math.Log2Int(FreeRegInt);
296335 const shift = @intCast(ShiftInt, index);
297336 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
298337 }
......@@ -300,11 +339,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
300339 fn markRegFree(self: *Branch, reg: Register) void {
301340 if (FreeRegInt == u0) return;
302341 const index = reg.allocIndex() orelse return;
303 const ShiftInt = std.math.Log2Int(FreeRegInt);
342 const ShiftInt = math.Log2Int(FreeRegInt);
304343 const shift = @intCast(ShiftInt, index);
305344 self.free_registers |= @as(FreeRegInt, 1) << shift;
306345 }
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
308360 fn deinit(self: *Branch, gpa: *Allocator) void {
309361 self.inst_table.deinit(gpa);
310362 self.registers.deinit(gpa);
......@@ -329,15 +381,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
329381 src: usize,
330382 typed_value: TypedValue,
331383 code: *std.ArrayList(u8),
384 dbg_line: *std.ArrayList(u8),
332385 ) GenerateSymbolError!Result {
333386 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
334387
335388 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
342390 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
343391 defer {
......@@ -348,24 +396,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
348396 const branch = try branch_stack.addOne();
349397 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
351415 var function = Self{
352416 .gpa = bin_file.allocator,
353 .target = &bin_file.options.target,
417 .target = &bin_file.base.options.target,
354418 .bin_file = bin_file,
355419 .mod_fn = module_fn,
356420 .code = code,
421 .dbg_line = dbg_line,
357422 .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,
359426 .arg_index = 0,
360427 .branch_stack = &branch_stack,
361428 .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,
362434 };
435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
363436
364 const cc = fn_type.fnCallingConvention();
365 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
437 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
366438 error.CodegenFail => return Result{ .fail = function.err_msg.? },
367439 else => |e| return e,
368440 };
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
370448 function.gen() catch |err| switch (err) {
371449 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -380,28 +458,81 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
380458 }
381459
382460 fn gen(self: *Self) !void {
383 try self.code.ensureCapacity(self.code.items.len + 11);
384
385 // push rbp
386 // mov rbp, rsp
387 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
388
389 // sub rsp, x
390 const stack_end = self.branch_stack.items[0].max_end_stack;
391 if (stack_end > std.math.maxInt(i32)) {
392 return self.fail(self.src, "too much stack used in call parameters", .{});
393 } else if (stack_end > std.math.maxInt(i8)) {
394 // 48 83 ec xx sub rsp,0x10
395 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
396 const x = @intCast(u32, stack_end);
397 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
398 } else if (stack_end != 0) {
399 // 48 81 ec xx xx xx xx sub rsp,0x80
400 const x = @intCast(u8, stack_end);
401 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
402 }
461 switch (arch) {
462 .x86_64 => {
463 try self.code.ensureCapacity(self.code.items.len + 11);
464
465 const cc = self.fn_type.fnCallingConvention();
466 if (cc != .Naked) {
467 // We want to subtract the aligned stack frame size from rsp here, but we don't
468 // yet know how big it will be, so we leave room for a 4-byte stack size.
469 // TODO During semantic analysis, check if there are no function calls. If there
470 // are none, here we can omit the part where we subtract and then add rsp.
471 self.code.appendSliceAssumeCapacity(&[_]u8{
472 0x55, // push rbp
473 0x48, 0x89, 0xe5, // mov rbp, rsp
474 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
475 });
476 const reloc_index = self.code.items.len;
477 self.code.items.len += 4;
478
479 try self.dbgSetPrologueEnd();
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);
405536 }
406537
407538 fn genBody(self: *Self, body: ir.Body) InnerError!void {
......@@ -418,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
418549 }
419550 }
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
421584 fn processDeath(self: *Self, inst: *ir.Inst) void {
422585 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
423586 const entry = branch.inst_table.getEntry(inst) orelse return;
......@@ -425,8 +588,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
425588 entry.value = .dead;
426589 switch (prev_value) {
427590 .register => |reg| {
428 _ = branch.registers.remove(reg);
429 branch.markRegFree(reg);
591 const canon_reg = toCanonicalReg(reg);
592 _ = branch.registers.remove(canon_reg);
593 branch.markRegFree(canon_reg);
430594 },
431595 else => {}, // TODO process stack allocation death
432596 }
......@@ -435,6 +599,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
435599 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
436600 switch (inst.tag) {
437601 .add => return self.genAdd(inst.castTag(.add).?),
602 .alloc => return self.genAlloc(inst.castTag(.alloc).?),
438603 .arg => return self.genArg(inst.castTag(.arg).?),
439604 .assembly => return self.genAsm(inst.castTag(.assembly).?),
440605 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
......@@ -451,19 +616,91 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
451616 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
452617 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
453618 .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).?),
454622 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
455623 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
624 .load => return self.genLoad(inst.castTag(.load).?),
625 .not => return self.genNot(inst.castTag(.not).?),
456626 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
627 .ref => return self.genRef(inst.castTag(.ref).?),
457628 .ret => return self.genRet(inst.castTag(.ret).?),
458629 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
630 .store => return self.genStore(inst.castTag(.store).?),
459631 .sub => return self.genSub(inst.castTag(.sub).?),
460632 .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).?),
464633 }
465634 }
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
467704 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
468705 // No side effects, so if it's unreferenced, do nothing.
469706 if (inst.base.isUnused())
......@@ -542,6 +779,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
542779 }
543780 }
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
545863 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
546864 // No side effects, so if it's unreferenced, do nothing.
547865 if (inst.base.isUnused())
......@@ -609,7 +927,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
609927 // and as a register.
610928 switch (src_mcv) {
611929 .immediate => |imm| {
612 if (imm > std.math.maxInt(u31)) {
930 if (imm > math.maxInt(u31)) {
613931 src_mcv = try self.copyToNewRegister(src_inst);
614932 }
615933 },
......@@ -624,13 +942,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
624942 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
625943 switch (dst_mcv) {
626944 .none => unreachable,
945 .undef => unreachable,
627946 .dead, .unreach, .immediate => unreachable,
628947 .compare_flags_unsigned => unreachable,
629948 .compare_flags_signed => unreachable,
949 .ptr_stack_offset => unreachable,
950 .ptr_embedded_in_code => unreachable,
630951 .register => |dst_reg| {
631952 switch (src_mcv) {
632953 .none => unreachable,
954 .undef => try self.genSetReg(src, dst_reg, .undef),
633955 .dead, .unreach => unreachable,
956 .ptr_stack_offset => unreachable,
957 .ptr_embedded_in_code => unreachable,
634958 .register => |src_reg| {
635959 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
636960 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 {
638962 .immediate => |imm| {
639963 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
640964 // 81 /opx id
641 if (imm32 <= std.math.maxInt(u7)) {
965 if (imm32 <= math.maxInt(u7)) {
642966 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
643967 self.code.appendSliceAssumeCapacity(&[_]u8{
644968 0x83,
......@@ -699,26 +1023,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
6991023 .i386, .x86_64 => {
7001024 try self.code.append(0xcc); // int3
7011025 },
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 },
7021033 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
7031034 }
7041035 return .none;
7051036 }
7061037
7071038 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
708 const fn_ty = inst.func.ty;
709 const cc = fn_ty.fnCallingConvention();
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);
1039 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
1040 defer info.deinit(self);
7161041
7171042 switch (arch) {
7181043 .x86_64 => {
719 for (mc_args) |mc_arg, arg_i| {
1044 for (info.args) |mc_arg, arg_i| {
7201045 const arg = inst.args[arg_i];
7211046 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.
7221049 switch (mc_arg) {
7231050 .none => continue,
7241051 .register => |reg| {
......@@ -730,6 +1057,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
7301057 // mov qword ptr [rsp + stack_offset], x
7311058 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
7321059 },
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,
7331067 .immediate => unreachable,
7341068 .unreach => unreachable,
7351069 .dead => unreachable,
......@@ -758,30 +1092,86 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
7581092 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
7591093 }
7601094 },
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 },
7611120 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
7621121 }
7631122
764 const return_type = fn_ty.fnReturnType();
765 switch (return_type.zigTypeTag()) {
766 .Void => return MCValue{ .none = {} },
767 .NoReturn => return MCValue{ .unreach = {} },
768 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
1123 return info.return_value;
1124 }
1125
1126 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
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", .{}),
7691150 }
7701151 }
7711152
7721153 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
773 if (mcv != .none) {
774 return self.fail(src, "TODO implement return with non-void operand", .{});
775 }
1154 const ret_ty = self.fn_type.fnReturnType();
1155 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
7761156 switch (arch) {
7771157 .i386 => {
7781158 try self.code.append(0xc3); // ret
7791159 },
7801160 .x86_64 => {
781 try self.code.appendSlice(&[_]u8{
782 0x5d, // pop rbp
783 0xc3, // ret
784 });
1161 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
1162 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
1163 // which is available if the jump is 127 bytes or less forward.
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));
7851175 },
7861176 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
7871177 }
......@@ -797,7 +1187,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
7971187 return self.ret(inst.base.src, .none);
7981188 }
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 {
8011191 // No side effects, so if it's unreferenced, do nothing.
8021192 if (inst.base.isUnused())
8031193 return MCValue.dead;
......@@ -830,6 +1220,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
8301220 }
8311221 }
8321222
1223 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1224 try self.dbgAdvancePCAndLine(inst.base.src);
1225 return MCValue.none;
1226 }
1227
8331228 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
8341229 switch (arch) {
8351230 .x86_64 => {
......@@ -865,7 +1260,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
8651260 // test reg, 1
8661261 // TODO detect al, ax, eax
8671262 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 });
8691266 self.code.appendSliceAssumeCapacity(&[_]u8{
8701267 0xf6,
8711268 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
......@@ -921,7 +1318,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9211318 switch (reloc) {
9221319 .rel32 => |pos| {
9231320 const amt = self.code.items.len - (pos + 4);
924 const s32_amt = std.math.cast(i32, amt) catch
1321 // 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
9251327 return self.fail(src, "unable to perform relocation: jump too far", .{});
9261328 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
9271329 },
......@@ -963,36 +1365,72 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9631365 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
9641366 if (!inst.is_volatile and inst.base.isUnused())
9651367 return MCValue.dead;
966 if (arch != .x86_64 and arch != .i386) {
967 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
968 }
969 for (inst.inputs) |input, i| {
970 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
971 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
972 }
973 const reg_name = input[1 .. input.len - 1];
974 const reg = parseRegName(reg_name) orelse
975 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
976 const arg = try self.resolveInst(inst.args[i]);
977 try self.genSetReg(inst.base.src, reg, arg);
978 }
1368 switch (arch) {
1369 .riscv64 => {
1370 for (inst.inputs) |input, i| {
1371 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1372 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1373 }
1374 const reg_name = input[1 .. input.len - 1];
1375 const reg = parseRegName(reg_name) orelse
1376 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1377 const arg = try self.resolveInst(inst.args[i]);
1378 try self.genSetReg(inst.base.src, reg, arg);
1379 }
9791380
980 if (mem.eql(u8, inst.asm_source, "syscall")) {
981 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
982 } else {
983 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
984 }
1381 if (mem.eql(u8, inst.asm_source, "ecall")) {
1382 const full = @bitCast(u32, instructions.CallBreak{
1383 .mode = @enumToInt(instructions.CallBreak.Mode.ecall),
1384 });
9851385
986 if (inst.output) |output| {
987 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
988 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
989 }
990 const reg_name = output[2 .. output.len - 1];
991 const reg = parseRegName(reg_name) orelse
992 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
993 return MCValue{ .register = reg };
994 } else {
995 return MCValue.none;
1386 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), full);
1387 } else {
1388 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
1389 }
1390
1391 if (inst.output) |output| {
1392 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1393 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1394 }
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", .{}),
9961434 }
9971435 }
9981436
......@@ -1024,15 +1462,211 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10241462 }
10251463 }
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 {
10281479 switch (arch) {
10291480 .x86_64 => switch (mcv) {
10301481 .dead => unreachable,
1031 .none => unreachable,
1032 .unreach => unreachable,
1482 .ptr_stack_offset => 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 },
10331665 .compare_flags_unsigned => |op| {
10341666 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 });
10361670 const opcode: u8 = switch (op) {
10371671 .gte => 0x93,
10381672 .gt => 0x97,
......@@ -1048,9 +1682,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10481682 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
10491683 },
10501684 .immediate => |x| {
1051 if (reg.size() != 64) {
1052 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1053 }
10541685 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
10551686 // register is the fastest way to zero a register.
10561687 if (x == 0) {
......@@ -1071,7 +1702,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10711702 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
10721703 return;
10731704 }
1074 if (x <= std.math.maxInt(u32)) {
1705 if (x <= math.maxInt(u32)) {
10751706 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
10761707 //
10771708 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
......@@ -1103,16 +1734,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11031734 //
11041735 // In this case, the encoding of the REX byte is 0b0100100B
11051736 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() });
11071738 self.code.items.len += 9;
11081739 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
11091740 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
11101741 mem.writeIntLittle(u64, imm_ptr, x);
11111742 },
11121743 .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 }
11161744 // We need the offset from RIP in a signed i32 twos complement.
11171745 // The instruction is 7 bytes long and RIP points to the next instruction.
11181746 try self.code.ensureCapacity(self.code.items.len + 7);
......@@ -1120,7 +1748,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11201748 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
11211749 // bits as five.
11221750 // 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() });
11241752 self.code.items.len += 6;
11251753 const rip = self.code.items.len;
11261754 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
......@@ -1131,9 +1759,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11311759 mem.writeIntLittle(i32, imm_ptr, offset);
11321760 },
11331761 .register => |src_reg| {
1134 if (reg.size() != 64) {
1135 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1136 }
1762 // If the registers are the same, nothing to do.
1763 if (src_reg.id() == reg.id())
1764 return;
1765
11371766 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
11381767 // This is thus three bytes: REX 0x8B R/M.
11391768 // If the destination is extended, the R field must be 1.
......@@ -1141,15 +1770,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11411770 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
11421771 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
11431772 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() });
11451774 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
11461775 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
11471776 },
11481777 .memory => |x| {
1149 if (reg.size() != 64) {
1150 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1151 }
1152 if (x <= std.math.maxInt(u32)) {
1778 if (x <= math.maxInt(u32)) {
11531779 // Moving from memory to a register is a variant of `8B /r`.
11541780 // Since we're using 64-bit moves, we require a REX.
11551781 // 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 {
11581784 // 0b00RRR100, where RRR is the lower three bits of the register ID.
11591785 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
11601786 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() });
11621788 self.code.appendSliceAssumeCapacity(&[_]u8{
11631789 0x8B,
11641790 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
......@@ -1186,7 +1812,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11861812 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
11871813 // this instruction.
11881814 const id3 = @truncate(u3, reg.id());
1189 std.debug.assert(id3 != 4 and id3 != 5);
1815 assert(id3 != 4 and id3 != 5);
11901816
11911817 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
11921818 try self.genSetReg(src, reg, MCValue{ .immediate = x });
......@@ -1201,17 +1827,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12011827 //
12021828 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
12031829 // 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() });
12051831 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
12061832 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
12071833 }
12081834 }
12091835 },
1210 .stack_offset => |off| {
1211 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1836 .stack_offset => |unadjusted_off| {
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 }
12121858 },
12131859 },
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}),
12151861 }
12161862 }
12171863
......@@ -1247,24 +1893,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12471893 }
12481894 }
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
12681896 /// If the MCValue is an immediate, and it does not fit within this type,
12691897 /// we put it in a register.
12701898 /// A potential opportunity for future optimization here would be keeping track
......@@ -1282,7 +1910,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12821910 .is_signed = false,
12831911 },
12841912 });
1285 if (imm >= std.math.maxInt(U)) {
1913 if (imm >= math.maxInt(U)) {
12861914 return self.copyToNewRegister(inst);
12871915 }
12881916 },
......@@ -1292,6 +1920,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12921920 }
12931921
12941922 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1923 if (typed_value.val.isUndef())
1924 return MCValue.undef;
12951925 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
12961926 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
12971927 switch (typed_value.ty.zigTypeTag()) {
......@@ -1320,19 +1950,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13201950 }
13211951 }
13221952
1323 fn resolveParameters(
1324 self: *Self,
1325 src: usize,
1326 cc: std.builtin.CallingConvention,
1327 param_types: []const Type,
1328 results: []MCValue,
1329 ) !u32 {
1953 const CallMCValues = struct {
1954 args: []MCValue,
1955 return_value: MCValue,
1956 stack_byte_count: u32,
1957 stack_align: u32,
1958
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
13301982 switch (arch) {
13311983 .x86_64 => {
13321984 switch (cc) {
13331985 .Naked => {
1334 assert(results.len == 0);
1335 return 0;
1986 assert(result.args.len == 0);
1987 result.return_value = .{ .unreach = {} };
1988 result.stack_byte_count = 0;
1989 result.stack_align = 1;
1990 return result;
13361991 },
13371992 .Unspecified, .C => {
13381993 var next_int_reg: usize = 0;
......@@ -1341,24 +1996,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13411996 for (param_types) |ty, i| {
13421997 switch (ty.zigTypeTag()) {
13431998 .Bool, .Int => {
1999 const param_size = @intCast(u32, ty.abiSize(self.target.*));
13442000 if (next_int_reg >= c_abi_int_param_regs.len) {
1345 results[i] = .{ .stack_offset = next_stack_offset };
1346 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
2001 result.args[i] = .{ .stack_offset = next_stack_offset };
2002 next_stack_offset += param_size;
13472003 } 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 };
13492009 next_int_reg += 1;
13502010 }
13512011 },
13522012 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
13532013 }
13542014 }
1355 return next_stack_offset;
2015 result.stack_byte_count = next_stack_offset;
2016 result.stack_align = 16;
13562017 },
13572018 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
13582019 }
13592020 },
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}),
13612023 }
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 };
13622052 }
13632053
13642054 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 {
13712061 usingnamespace switch (arch) {
13722062 .i386 => @import("codegen/x86.zig"),
13732063 .x86_64 => @import("codegen/x86_64.zig"),
2064 .riscv64 => @import("codegen/riscv64.zig"),
13742065 else => struct {
13752066 pub const Register = enum {
13762067 dummy,
......@@ -1387,7 +2078,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13872078 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
13882079
13892080 fn parseRegName(name: []const u8) ?Register {
2081 if (@hasDecl(Register, "parseRegName")) {
2082 return Register.parseRegName(name);
2083 }
13902084 return std.meta.stringToEnum(Register, name);
13912085 }
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 }
13922109 };
13932110}
src-self-hosted/codegen/c.zig+11-5
......@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {
8989 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
9090 const instructions = func.analysis.success.instructions;
9191 if (instructions.len > 0) {
92 try writer.writeAll("\n");
9293 for (instructions) |inst| {
93 try writer.writeAll("\n ");
9494 switch (inst.tag) {
9595 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
9696 .call => try genCall(file, inst.castTag(.call).?, decl),
9797 .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),
99100 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100101 }
101102 }
102 try writer.writeAll("\n");
103103 }
104104
105105 try writer.writeAll("}\n\n");
......@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !
112112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
113113 const writer = file.main.writer();
114114 const header = file.header.writer();
115 try writer.writeAll(" ");
115116 if (inst.func.castTag(.constant)) |func_inst| {
116117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
117118 const target = func_val.func.owner_decl;
......@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
126127 try renderFunctionSignature(file, header, target);
127128 try header.writeAll(";\n");
128129 }
129 try writer.print("{}();", .{tname});
130 try writer.print("{}();\n", .{tname});
130131 } else {
131132 return file.fail(decl.src(), "TODO non-function call target?", .{});
132133 }
......@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
138139 }
139140}
140141
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
143 // TODO emit #line directive here with line number and filename
144}
145
141146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
142147 const writer = file.main.writer();
148 try writer.writeAll(" ");
143149 for (as.inputs) |i, index| {
144150 if (i[0] == '{' and i[i.len - 1] == '}') {
145151 const reg = i[1 .. i.len - 1];
......@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
187193 }
188194 }
189195 }
190 try writer.writeAll(");");
196 try writer.writeAll(");\n");
191197}
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) {
8181 else => null,
8282 };
8383 }
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 }
84104};
85105
86106// zig fmt: on
......@@ -88,3 +108,4 @@ pub const Register = enum(u8) {
88108/// These registers belong to the called function.
89109pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
90110pub 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;
44const Module = @import("Module.zig");
55const assert = std.debug.assert;
66const codegen = @import("codegen.zig");
7const ast = std.zig.ast;
78
89/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
910/// of instructions that correspond to the ZIR text format.
......@@ -47,6 +48,7 @@ pub const Inst = struct {
4748
4849 pub const Tag = enum {
4950 add,
51 alloc,
5052 arg,
5153 assembly,
5254 bitcast,
......@@ -63,28 +65,34 @@ pub const Inst = struct {
6365 cmp_neq,
6466 condbr,
6567 constant,
68 dbg_stmt,
6669 isnonnull,
6770 isnull,
71 /// Read a value from a pointer.
72 load,
6873 ptrtoint,
74 ref,
6975 ret,
7076 retvoid,
77 /// Write a value to a pointer. LHS is pointer, RHS is value.
78 store,
7179 sub,
7280 unreach,
7381 not,
7482 floatcast,
7583 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.
8085 pub fn Type(tag: Tag) type {
8186 return switch (tag) {
87 .alloc,
8288 .retvoid,
8389 .unreach,
8490 .arg,
8591 .breakpoint,
92 .dbg_stmt,
8693 => NoOp,
8794
95 .ref,
8896 .ret,
8997 .bitcast,
9098 .not,
......@@ -93,6 +101,7 @@ pub const Inst = struct {
93101 .ptrtoint,
94102 .floatcast,
95103 .intcast,
104 .load,
96105 => UnOp,
97106
98107 .add,
......@@ -103,6 +112,7 @@ pub const Inst = struct {
103112 .cmp_gte,
104113 .cmp_gt,
105114 .cmp_neq,
115 .store,
106116 => BinOp,
107117
108118 .assembly => Assembly,
......@@ -157,8 +167,7 @@ pub const Inst = struct {
157167
158168 /// Returns `null` if runtime-known.
159169 pub fn value(base: *Inst) ?Value {
160 if (base.ty.onePossibleValue())
161 return Value.initTag(.the_one_possible_value);
170 if (base.ty.onePossibleValue()) |opv| return opv;
162171
163172 const inst = base.cast(Constant) orelse return null;
164173 return inst.val;
src-self-hosted/link.zig+1063-280
......@@ -8,6 +8,15 @@ const fs = std.fs;
88const elf = std.elf;
99const codegen = @import("codegen.zig");
1010const 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
1221const default_entry_addr = 0x8000000;
1322
......@@ -16,6 +25,9 @@ pub const Options = struct {
1625 output_mode: std.builtin.OutputMode,
1726 link_mode: std.builtin.LinkMode,
1827 object_format: std.builtin.ObjectFormat,
28 optimize_mode: std.builtin.Mode,
29 root_name: []const u8,
30 root_pkg: *const Package,
1931 /// Used for calculating how much space to reserve for symbols in case the binary file
2032 /// does not already have a symbol table.
2133 symbol_count_hint: u64 = 32,
......@@ -24,96 +36,27 @@ pub const Options = struct {
2436 program_code_size_hint: u64 = 256 * 1024,
2537};
2638
27/// Attempts incremental linking, if the file already exists.
28/// If incremental linking fails, falls back to truncating the file and rewriting it.
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,
39pub const File = struct {
40 tag: Tag,
3541 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.
56pub fn writeFilePath(
57 allocator: *Allocator,
58 dir: fs.Dir,
59 sub_path: []const u8,
60 module: Module,
61 errors: *std.ArrayList(Module.ErrorMsg),
62) !void {
63 const options: Options = .{
64 .target = module.target,
65 .output_mode = module.output_mode,
66 .link_mode = module.link_mode,
67 .object_format = module.object_format,
68 .symbol_count_hint = module.decls.items.len,
69 };
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 });
43 /// Attempts incremental linking, if the file already exists. If
44 /// incremental linking fails, falls back to truncating the file and
45 /// rewriting it. A malicious file is detected as incremental link failure
46 /// and does not cause Illegal Behavior. This operation is not atomic.
47 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
48 switch (options.object_format) {
49 .unknown => unreachable,
50 .coff => return error.TODOImplementCoff,
51 .elf => return Elf.openPath(allocator, dir, sub_path, options),
52 .macho => return error.TODOImplementMacho,
53 .wasm => return error.TODOImplementWasm,
54 .c => return C.openPath(allocator, dir, sub_path, options),
55 .hex => return error.TODOImplementHex,
56 .raw => return error.TODOImplementRaw,
57 }
8458 }
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,
11760 pub fn cast(base: *File, comptime T: type) ?*T {
11861 if (base.tag != T.base_tag)
11962 return null;
......@@ -123,86 +66,82 @@ pub const File = struct {
12366
12467 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
12568 switch (base.tag) {
126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
127 .C => {},
128 else => unreachable,
69 .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
70 .c => {},
12971 }
13072 }
13173
13274 pub fn makeExecutable(base: *File) !void {
13375 switch (base.tag) {
134 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
135 else => unreachable,
76 .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
77 .c => unreachable,
13678 }
13779 }
13880
13981 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
14082 switch (base.tag) {
141 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
142 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
143 else => unreachable,
83 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
84 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
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 => {},
14492 }
14593 }
14694
14795 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
14896 switch (base.tag) {
149 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
150 .C => {},
151 else => unreachable,
97 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
98 .c => {},
15299 }
153100 }
154101
155102 pub fn deinit(base: *File) void {
156103 switch (base.tag) {
157 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
158 .C => @fieldParentPtr(C, "base", base).deinit(),
159 else => unreachable,
104 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
105 .c => @fieldParentPtr(C, "base", base).deinit(),
160106 }
161107 }
162108
163109 pub fn destroy(base: *File) void {
164110 switch (base.tag) {
165 .Elf => {
111 .elf => {
166112 const parent = @fieldParentPtr(Elf, "base", base);
167113 parent.deinit();
168114 parent.allocator.destroy(parent);
169115 },
170 .C => {
116 .c => {
171117 const parent = @fieldParentPtr(C, "base", base);
172118 parent.deinit();
173119 parent.allocator.destroy(parent);
174120 },
175 else => unreachable,
176121 }
177122 }
178123
179124 pub fn flush(base: *File) !void {
125 const tracy = trace(@src());
126 defer tracy.end();
127
180128 try switch (base.tag) {
181 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
182 .C => @fieldParentPtr(C, "base", base).flush(),
183 else => unreachable,
129 .elf => @fieldParentPtr(Elf, "base", base).flush(),
130 .c => @fieldParentPtr(C, "base", base).flush(),
184131 };
185132 }
186133
187134 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188135 switch (base.tag) {
189 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 else => unreachable,
136 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
137 .c => unreachable,
191138 }
192139 }
193140
194141 pub fn errorFlags(base: *File) ErrorFlags {
195142 return switch (base.tag) {
196 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .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,
143 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
144 .c => return .{ .no_entry_point_found = false },
206145 };
207146 }
208147
......@@ -214,14 +153,14 @@ pub const File = struct {
214153 exports: []const *Module.Export,
215154 ) !void {
216155 switch (base.tag) {
217 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
218 .C => return {},
156 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
157 .c => return {},
219158 }
220159 }
221160
222161 pub const Tag = enum {
223 Elf,
224 C,
162 elf,
163 c,
225164 };
226165
227166 pub const ErrorFlags = struct {
......@@ -229,24 +168,49 @@ pub const File = struct {
229168 };
230169
231170 pub const C = struct {
232 pub const base_tag: Tag = .C;
233 base: File = File{ .tag = base_tag },
171 pub const base_tag: Tag = .c;
172
173 base: File,
234174
235175 allocator: *Allocator,
236176 header: std.ArrayList(u8),
237177 constants: std.ArrayList(u8),
238178 main: std.ArrayList(u8),
239179 file: ?fs.File,
240 options: Options,
241180 called: std.StringHashMap(void),
242181 need_stddef: bool = false,
243182 need_stdint: bool = false,
244183 need_noreturn: bool = false,
245184 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
247211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
248212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
249 return error.CGenFailure;
213 return error.AnalysisFail;
250214 }
251215
252216 pub fn deinit(self: *File.C) void {
......@@ -260,7 +224,7 @@ pub const File = struct {
260224
261225 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
262226 c_codegen.generate(self, decl) catch |err| {
263 if (err == error.CGenFailure) {
227 if (err == error.AnalysisFail) {
264228 try module.failed_decls.put(module.gpa, decl, self.error_msg);
265229 }
266230 return err;
......@@ -301,13 +265,13 @@ pub const File = struct {
301265 };
302266
303267 pub const Elf = struct {
304 pub const base_tag: Tag = .Elf;
305 base: File = File{ .tag = base_tag },
268 pub const base_tag: Tag = .elf;
269
270 base: File,
306271
307272 allocator: *Allocator,
308273 file: ?fs.File,
309274 owns_file_handle: bool,
310 options: Options,
311275 ptr_width: enum { p32, p64 },
312276
313277 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
......@@ -326,12 +290,20 @@ pub const File = struct {
326290 phdr_got_index: ?u16 = null,
327291 entry_addr: ?u64 = null,
328292
293 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
329294 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
330295 shstrtab_index: ?u16 = null,
331296
332297 text_section_index: ?u16 = null,
333298 symtab_section_index: ?u16 = null,
334299 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
336308 /// The same order as in the file. ELF requires global symbols to all be after the
337309 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
......@@ -352,7 +324,12 @@ pub const File = struct {
352324 phdr_table_dirty: bool = false,
353325 shdr_table_dirty: bool = false,
354326 shstrtab_dirty: bool = false,
327 debug_strtab_dirty: bool = false,
355328 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
357334 error_flags: ErrorFlags = ErrorFlags{},
358335
......@@ -374,6 +351,12 @@ pub const File = struct {
374351 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
375352 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
377360 /// `alloc_num / alloc_den` is the factor of padding when allocating.
378361 const alloc_num = 4;
379362 const alloc_den = 3;
......@@ -437,16 +420,139 @@ pub const File = struct {
437420 sym_index: ?u32 = null,
438421 };
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
440544 pub fn deinit(self: *Elf) void {
441545 self.sections.deinit(self.allocator);
442546 self.program_headers.deinit(self.allocator);
443547 self.shstrtab.deinit(self.allocator);
548 self.debug_strtab.deinit(self.allocator);
444549 self.local_symbols.deinit(self.allocator);
445550 self.global_symbols.deinit(self.allocator);
446551 self.global_symbol_free_list.deinit(self.allocator);
447552 self.local_symbol_free_list.deinit(self.allocator);
448553 self.offset_table_free_list.deinit(self.allocator);
449554 self.text_block_free_list.deinit(self.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);
450556 self.offset_table.deinit(self.allocator);
451557 if (self.owns_file_handle) {
452558 if (self.file) |f| f.close();
......@@ -467,13 +573,21 @@ pub const File = struct {
467573 self.file = try dir.createFile(sub_path, .{
468574 .truncate = false,
469575 .read = true,
470 .mode = determineMode(self.options),
576 .mode = determineMode(self.base.options),
471577 });
472578 }
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
474588 /// Returns end pos of collision, if any.
475589 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;
477591 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
478592 if (start < ehdr_size)
479593 return ehdr_size;
......@@ -518,6 +632,8 @@ pub const File = struct {
518632 }
519633
520634 fn allocatedSize(self: *Elf, start: u64) u64 {
635 if (start == 0)
636 return 0;
521637 var min_pos: u64 = std.math.maxInt(u64);
522638 if (self.shdr_table_offset) |off| {
523639 if (off > start and off < min_pos) min_pos = off;
......@@ -544,6 +660,7 @@ pub const File = struct {
544660 return start;
545661 }
546662
663 /// TODO Improve this to use a table.
547664 fn makeString(self: *Elf, bytes: []const u8) !u32 {
548665 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
549666 const result = self.shstrtab.items.len;
......@@ -552,6 +669,15 @@ pub const File = struct {
552669 return @intCast(u32, result);
553670 }
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
555681 fn getString(self: *Elf, str_off: u32) []const u8 {
556682 assert(str_off < self.shstrtab.items.len);
557683 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
......@@ -570,16 +696,13 @@ pub const File = struct {
570696 .p32 => true,
571697 .p64 => false,
572698 };
573 const ptr_size: u8 = switch (self.ptr_width) {
574 .p32 => 4,
575 .p64 => 8,
576 };
699 const ptr_size: u8 = self.ptrWidthBytes();
577700 if (self.phdr_load_re_index == null) {
578701 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;
580703 const p_align = 0x1000;
581704 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 });
583706 try self.program_headers.append(self.allocator, .{
584707 .p_type = elf.PT_LOAD,
585708 .p_offset = off,
......@@ -595,12 +718,12 @@ pub const File = struct {
595718 }
596719 if (self.phdr_got_index == null) {
597720 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;
599722 // We really only need ptr alignment but since we are using PROGBITS, linux requires
600723 // page align.
601724 const p_align = 0x1000;
602725 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 });
604727 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
605728 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
606729 // else in virtual memory.
......@@ -622,7 +745,7 @@ pub const File = struct {
622745 assert(self.shstrtab.items.len == 0);
623746 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
624747 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 });
626749 try self.sections.append(self.allocator, .{
627750 .sh_name = try self.makeString(".shstrtab"),
628751 .sh_type = elf.SHT_STRTAB,
......@@ -678,9 +801,9 @@ pub const File = struct {
678801 self.symtab_section_index = @intCast(u16, self.sections.items.len);
679802 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
680803 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;
682805 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
685808 try self.sections.append(self.allocator, .{
686809 .sh_name = try self.makeString(".symtab"),
......@@ -698,6 +821,124 @@ pub const File = struct {
698821 self.shdr_table_dirty = true;
699822 try self.writeSymbol(0);
700823 }
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 }
701942 const shsize: u64 = switch (self.ptr_width) {
702943 .p32 => @sizeOf(elf.Elf32_Shdr),
703944 .p64 => @sizeOf(elf.Elf64_Shdr),
......@@ -733,12 +974,307 @@ pub const File = struct {
733974
734975 /// Commit pending changes and write headers.
735976 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
738985 // Unfortunately these have to be buffered and done at the end because ELF does not allow
739986 // mixing local and global symbols within a symbol table.
740987 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
7421278 if (self.phdr_table_dirty) {
7431279 const phsize: u64 = switch (self.ptr_width) {
7441280 .p32 => @sizeOf(elf.Elf32_Phdr),
......@@ -796,7 +1332,7 @@ pub const File = struct {
7961332 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
7971333 }
7981334 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
8011337 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
8021338 if (!self.shdr_table_dirty) {
......@@ -806,6 +1342,27 @@ pub const File = struct {
8061342 self.shstrtab_dirty = false;
8071343 }
8081344 }
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 }
8091366 if (self.shdr_table_dirty) {
8101367 const shsize: u64 = switch (self.ptr_width) {
8111368 .p32 => @sizeOf(elf.Elf32_Shdr),
......@@ -842,7 +1399,7 @@ pub const File = struct {
8421399
8431400 for (buf) |*shdr, i| {
8441401 shdr.* = self.sections.items[i];
845 std.log.debug(.link, "writing section {}\n", .{shdr.*});
1402 log.debug(.link, "writing section {}\n", .{shdr.*});
8461403 if (foreign_endian) {
8471404 bswapAllFields(elf.Elf64_Shdr, shdr);
8481405 }
......@@ -852,8 +1409,8 @@ pub const File = struct {
8521409 }
8531410 self.shdr_table_dirty = false;
8541411 }
855 if (self.entry_addr == null and self.options.output_mode == .Exe) {
856 std.log.debug(.link, "no_entry_point_found = true\n", .{});
1412 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1413 log.debug(.link, "no_entry_point_found = true\n", .{});
8571414 self.error_flags.no_entry_point_found = true;
8581415 } else {
8591416 self.error_flags.no_entry_point_found = false;
......@@ -861,14 +1418,27 @@ pub const File = struct {
8611418 }
8621419
8631420 // 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);
8641425 assert(!self.phdr_table_dirty);
8651426 assert(!self.shdr_table_dirty);
8661427 assert(!self.shstrtab_dirty);
1428 assert(!self.debug_strtab_dirty);
8671429 assert(!self.offset_table_count_dirty);
8681430 const syms_sect = &self.sections.items[self.symtab_section_index.?];
8691431 assert(syms_sect.sh_info == self.local_symbols.items.len);
8701432 }
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
8721442 fn writeElfHeader(self: *Elf) !void {
8731443 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
8741444
......@@ -882,7 +1452,7 @@ pub const File = struct {
8821452 };
8831453 index += 1;
8841454
885 const endian = self.options.target.cpu.arch.endian();
1455 const endian = self.base.options.target.cpu.arch.endian();
8861456 hdr_buf[index] = switch (endian) {
8871457 .Little => elf.ELFDATA2LSB,
8881458 .Big => elf.ELFDATA2MSB,
......@@ -900,10 +1470,10 @@ pub const File = struct {
9001470
9011471 assert(index == 16);
9021472
903 const elf_type = switch (self.options.output_mode) {
1473 const elf_type = switch (self.base.options.output_mode) {
9041474 .Exe => elf.ET.EXEC,
9051475 .Obj => elf.ET.REL,
906 .Lib => switch (self.options.link_mode) {
1476 .Lib => switch (self.base.options.link_mode) {
9071477 .Static => elf.ET.REL,
9081478 .Dynamic => elf.ET.DYN,
9091479 },
......@@ -911,7 +1481,7 @@ pub const File = struct {
9111481 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
9121482 index += 2;
9131483
914 const machine = self.options.target.cpu.arch.toElfMachine();
1484 const machine = self.base.options.target.cpu.arch.toElfMachine();
9151485 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
9161486 index += 2;
9171487
......@@ -1129,6 +1699,15 @@ pub const File = struct {
11291699 phdr.p_memsz = needed_size;
11301700 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
11321711 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
11331712 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
11341713 }
......@@ -1160,17 +1739,14 @@ pub const File = struct {
11601739 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
11611740 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.
11641742 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);
11661743 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
11691745 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 });
11711747 decl.link.local_sym_index = i;
11721748 } 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 });
11741750 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
11751751 _ = self.local_symbols.addOneAssumeCapacity();
11761752 }
......@@ -1197,23 +1773,107 @@ pub const File = struct {
11971773 }
11981774
11991775 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.
12001777 self.freeTextBlock(&decl.link);
12011778 if (decl.link.local_sym_index != 0) {
1202 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1203 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};
1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_index) catch {};
12041781
12051782 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
12061783
12071784 decl.link.local_sym_index = 0;
12081785 }
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 }
12091807 }
12101808
12111809 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1810 const tracy = trace(@src());
1811 defer tracy.end();
1812
12121813 var code_buffer = std.ArrayList(u8).init(self.allocator);
12131814 defer code_buffer.deinit();
12141815
1816 var dbg_line_buffer = std.ArrayList(u8).init(self.allocator);
1817 defer dbg_line_buffer.deinit();
1818
12151819 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) {
12171877 .externally_managed => |x| x,
12181878 .appended => code_buffer.items,
12191879 .fail => |em| {
......@@ -1223,12 +1883,9 @@ pub const File = struct {
12231883 },
12241884 };
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()) {
1229 .Fn => elf.STT_FUNC,
1230 else => elf.STT_OBJECT,
1231 };
1888 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
12321889
12331890 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
12341891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
......@@ -1238,11 +1895,11 @@ pub const File = struct {
12381895 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
12391896 if (need_realloc) {
12401897 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 });
12421899 if (vaddr != local_sym.st_value) {
12431900 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", .{});
12461903 self.offset_table.items[decl.link.offset_table_index] = vaddr;
12471904 try self.writeOffsetTableEntry(decl.link.offset_table_index);
12481905 }
......@@ -1260,7 +1917,7 @@ pub const File = struct {
12601917 const decl_name = mem.spanZ(decl.name);
12611918 const name_str_index = try self.makeString(decl_name);
12621919 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 });
12641921 errdefer self.freeTextBlock(&decl.link);
12651922
12661923 local_sym.* = .{
......@@ -1281,6 +1938,94 @@ pub const File = struct {
12811938 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
12821939 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
12842029 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
12852030 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
12862031 return self.updateDeclExports(module, decl, decl_exports);
......@@ -1293,10 +2038,10 @@ pub const File = struct {
12932038 decl: *const Module.Decl,
12942039 exports: []const *Module.Export,
12952040 ) !void {
1296 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1297 // them, so that deleting exports is guaranteed to succeed.
2041 const tracy = trace(@src());
2042 defer tracy.end();
2043
12982044 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);
13002045 const typed_value = decl.typed_value.most_recent.typed_value;
13012046 if (decl.link.local_sym_index == 0) return;
13022047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
......@@ -1361,16 +2106,38 @@ pub const File = struct {
13612106 }
13622107 }
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
13642131 pub fn deleteExport(self: *Elf, exp: Export) void {
13652132 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 {};
13672134 self.global_symbols.items[sym_index].st_info = 0;
13682135 }
13692136
13702137 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();
13722139 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()) {
13742141 32 => {
13752142 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
13762143 if (foreign_endian) {
......@@ -1390,15 +2157,15 @@ pub const File = struct {
13902157 }
13912158
13922159 fn writeSectHeader(self: *Elf, index: usize) !void {
1393 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1394 const offset = self.sections.items[index].sh_offset;
1395 switch (self.options.target.cpu.arch.ptrBitWidth()) {
2160 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
2161 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
13962162 32 => {
13972163 var shdr: [1]elf.Elf32_Shdr = undefined;
13982164 shdr[0] = sectHeaderTo32(self.sections.items[index]);
13992165 if (foreign_endian) {
14002166 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
14012167 }
2168 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
14022169 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
14032170 },
14042171 64 => {
......@@ -1406,6 +2173,7 @@ pub const File = struct {
14062173 if (foreign_endian) {
14072174 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
14082175 }
2176 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
14092177 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
14102178 },
14112179 else => return error.UnsupportedArchitecture,
......@@ -1415,10 +2183,7 @@ pub const File = struct {
14152183 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
14162184 const shdr = &self.sections.items[self.got_section_index.?];
14172185 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1418 const entry_size: u16 = switch (self.ptr_width) {
1419 .p32 => 4,
1420 .p64 => 8,
1421 };
2186 const entry_size: u16 = self.ptrWidthBytes();
14222187 if (self.offset_table_count_dirty) {
14232188 // TODO Also detect virtual address collisions.
14242189 const allocated_size = self.allocatedSize(shdr.sh_offset);
......@@ -1440,7 +2205,7 @@ pub const File = struct {
14402205
14412206 self.offset_table_count_dirty = false;
14422207 }
1443 const endian = self.options.target.cpu.arch.endian();
2208 const endian = self.base.options.target.cpu.arch.endian();
14442209 const off = shdr.sh_offset + @as(u64, entry_size) * index;
14452210 switch (self.ptr_width) {
14462211 .p32 => {
......@@ -1482,7 +2247,7 @@ pub const File = struct {
14822247 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
14832248 self.shdr_table_dirty = true; // TODO look into only writing one section
14842249 }
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();
14862251 switch (self.ptr_width) {
14872252 .p32 => {
14882253 var sym = [1]elf.Elf32_Sym{
......@@ -1518,7 +2283,7 @@ pub const File = struct {
15182283 .p32 => @sizeOf(elf.Elf32_Sym),
15192284 .p64 => @sizeOf(elf.Elf64_Sym),
15202285 };
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();
15222287 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
15232288 switch (self.ptr_width) {
15242289 .p32 => {
......@@ -1561,106 +2326,124 @@ pub const File = struct {
15612326 },
15622327 }
15632328 }
1564 };
1565};
15662329
1567/// Truncates the existing file contents and overwrites the contents.
1568/// Returns an error if `file` is not already open with +read +write +seek abilities.
1569pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1570 switch (options.output_mode) {
1571 .Exe => {},
1572 .Obj => {},
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 }
2330 fn ptrWidthBytes(self: Elf) u8 {
2331 return switch (self.ptr_width) {
2332 .p32 => 4,
2333 .p64 => 8,
2334 };
2335 }
15852336
1586 var self: File.Elf = .{
1587 .allocator = allocator,
1588 .file = file,
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}
2337 /// The reloc offset for the virtual address of a function in its Line Number Program.
2338 /// Size is a virtual address integer.
2339 const dbg_line_vaddr_reloc_index = 3;
16282340
1629/// Returns error.IncrFailed if incremental update could not be performed.
1630fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1631 switch (options.output_mode) {
1632 .Exe => {},
1633 .Obj => {},
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();
2341 /// The reloc offset for the line offset of a function from the previous function's line.
2342 /// It's a fixed-size 4-byte ULEB128.
2343 fn getRelocDbgLineOff(self: Elf) usize {
2344 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
2345 }
16582346
1659 // TODO implement reading the elf file
1660 return error.IncrFailed;
1661 //try self.populateMissingMetadata();
1662 //return self;
1663}
2347 fn getRelocDbgFileIndex(self: Elf) usize {
2348 return self.getRelocDbgLineOff() + 5;
2349 }
2350
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
16652448/// Saturating multiplication
16662449fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
src-self-hosted/main.zig+12-13
......@@ -10,9 +10,7 @@ const Module = @import("Module.zig");
1010const link = @import("link.zig");
1111const Package = @import("Package.zig");
1212const zir = @import("zir.zig");
13
14// TODO Improve async I/O enough that we feel comfortable doing this.
15//pub const io_mode = .evented;
13const build_options = @import("build_options");
1614
1715pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
1816
......@@ -47,18 +45,16 @@ pub fn log(
4745 if (@enumToInt(level) > @enumToInt(std.log.level))
4846 return;
4947
50 const scope_prefix = "(" ++ switch (scope) {
51 // Uncomment to hide logs
52 //.compiler,
53 .module,
54 .liveness,
55 .link,
56 => return,
48 const scope_name = @tagName(scope);
49 const ok = comptime for (build_options.log_scopes) |log_scope| {
50 if (mem.eql(u8, log_scope, scope_name))
51 break true;
52 } else false;
5753
58 else => @tagName(scope),
59 } ++ "): ";
54 if (!ok)
55 return;
6056
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
57 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
6258
6359 // Print the message to stderr, silently ignoring any errors
6460 std.debug.print(prefix ++ format, args);
......@@ -94,6 +90,8 @@ pub fn main() !void {
9490 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
9591 } else if (mem.eql(u8, cmd, "version")) {
9692 // 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".
9795 std.debug.print("TODO version command not implemented yet\n", .{});
9896 return error.Unimplemented;
9997 } else if (mem.eql(u8, cmd, "zen")) {
......@@ -492,6 +490,7 @@ fn buildOutputType(
492490 defer root_pkg.destroy();
493491
494492 var module = try Module.init(gpa, .{
493 .root_name = root_name,
495494 .target = target_info.target,
496495 .output_mode = output_mode,
497496 .root_pkg = root_pkg,
src-self-hosted/test.zig+67-15
......@@ -4,6 +4,11 @@ const Module = @import("Module.zig");
44const Allocator = std.mem.Allocator;
55const zir = @import("zir.zig");
66const 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
813const cheader = @embedFile("cbe.h");
914
......@@ -401,8 +406,6 @@ pub const TestContext = struct {
401406 const root_node = try progress.start("tests", self.cases.items.len);
402407 defer root_node.end();
403408
404 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
405
406409 for (self.cases.items) |case| {
407410 std.testing.base_allocator_instance.reset();
408411
......@@ -415,13 +418,19 @@ pub const TestContext = struct {
415418 progress.initial_delay_ns = 0;
416419 progress.refresh_rate_ns = 0;
417420
418 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
419 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
421 try self.runOneCase(std.testing.allocator, &prg_node, case);
420422 try std.testing.allocator_instance.validate();
421423 }
422424 }
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
425434 var tmp = std.testing.tmpDir(.{});
426435 defer tmp.cleanup();
427436
......@@ -429,10 +438,10 @@ pub const TestContext = struct {
429438 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
430439 defer root_pkg.destroy();
431440
432 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
433 defer allocator.free(bin_name);
441 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
434442
435443 var module = try Module.init(allocator, .{
444 .root_name = "test_case",
436445 .target = target,
437446 // TODO: support tests for object file building, and library builds
438447 // and linking. This will require a rework to support multi-file
......@@ -484,8 +493,7 @@ pub const TestContext = struct {
484493 // incremental updates
485494 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
486495 defer file.close();
487 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
488 defer allocator.free(out);
496 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
489497
490498 if (expected_output.len != out.len) {
491499 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 {
532540 var test_node = update_node.start("assert", null);
533541 test_node.activate();
534542 defer test_node.end();
535 var handled_errors = try allocator.alloc(bool, e.len);
536 defer allocator.free(handled_errors);
543 var handled_errors = try arena.alloc(bool, e.len);
537544 for (handled_errors) |*h| {
538545 h.* = false;
539546 }
......@@ -568,14 +575,59 @@ pub const TestContext = struct {
568575 exec_node.activate();
569576 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});
574 defer allocator.free(exe_path);
626 try module.makeBinFileExecutable();
575627
576628 break :x try std.ChildProcess.exec(.{
577629 .allocator = allocator,
578 .argv = &[_][]const u8{exe_path},
630 .argv = argv.items,
579631 .cwd_dir = tmp.dir,
580632 });
581633 };
src-self-hosted/translate_c.zig+245-200
......@@ -8,7 +8,6 @@ const Token = std.zig.Token;
88usingnamespace @import("clang.zig");
99const ctok = std.c.tokenizer;
1010const CToken = std.c.Token;
11const CTokenList = std.c.tokenizer.Source.TokenList;
1211const mem = std.mem;
1312const math = std.math;
1413
......@@ -2864,7 +2863,6 @@ fn transCharLiteral(
28642863 "TODO: support character literal kind {}",
28652864 .{kind},
28662865 ),
2867 else => unreachable,
28682866 };
28692867 if (suppress_as == .no_as) {
28702868 return maybeSuppressResult(rp, scope, result_used, int_lit_node);
......@@ -3070,13 +3068,30 @@ fn transUnaryExprOrTypeTraitExpr(
30703068 stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
30713069 result_used: ResultUsed,
30723070) TransError!*ast.Node {
3071 const loc = ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt);
30733072 const type_node = try transQualType(
30743073 rp,
30753074 ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
3076 ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt),
3075 loc,
30773076 );
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);
30803095 builtin_node.params()[0] = type_node;
30813096 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
30823097 return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
......@@ -4515,7 +4530,7 @@ const CtrlFlow = struct {
45154530 const ltoken = try appendToken(c, kw, kw_text);
45164531 const label_token = if (label) |l| blk: {
45174532 _ = try appendToken(c, .Colon, ":");
4518 break :blk try appendToken(c, .Identifier, l);
4533 break :blk try appendIdentifier(c, l);
45194534 } else null;
45204535 return CtrlFlow{
45214536 .c = c,
......@@ -5197,16 +5212,39 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
51975212 ZigClangErrorMsg_delete(errors.ptr, errors.len);
51985213}
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
52005237fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
52015238 // TODO if we see #undef, delete it from the table
52025239 var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
52035240 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();
52055243 const scope = c.global_scope;
52065244
52075245 while (it.I != it_end.I) : (it.I += 1) {
52085246 const entity = ZigClangPreprocessingRecord_iterator_deref(it);
5209 tok_list.shrink(0);
5247 tok_list.items.len = 0;
52105248 switch (ZigClangPreprocessedEntity_getKind(entity)) {
52115249 .MacroDefinitionKind => {
52125250 const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
......@@ -5224,38 +5262,34 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
52245262 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
52255263 const slice = begin_c[0..mem.len(begin_c)];
52265264
5227 tok_list.shrink(0);
52285265 var tokenizer = std.c.Tokenizer{
5229 .source = &std.c.tokenizer.Source{
5230 .buffer = slice,
5231 .file_name = undefined,
5232 .tokens = undefined,
5233 },
5266 .buffer = slice,
52345267 };
52355268 while (true) {
52365269 const tok = tokenizer.next();
52375270 switch (tok.id) {
52385271 .Nl, .Eof => {
5239 try tok_list.push(tok);
5272 try tok_list.append(tok);
52405273 break;
52415274 },
52425275 .LineComment, .MultiLineComment => continue,
52435276 else => {},
52445277 }
5245 try tok_list.push(tok);
5278 try tok_list.append(tok);
52465279 }
52475280
5248 var tok_it = tok_list.iterator(0);
5249 const first_tok = tok_it.next().?;
5250 assert(mem.eql(u8, slice[first_tok.start..first_tok.end], name));
5281 var tok_it = CTokIterator{
5282 .source = slice,
5283 .list = tok_list.items,
5284 };
5285 assert(mem.eql(u8, tok_it.slice(0), name));
52515286
52525287 var macro_fn = false;
5253 const next = tok_it.peek().?;
5254 switch (next.id) {
5288 switch (tok_it.peek().?) {
52555289 .Identifier => {
52565290 // if it equals itself, ignore. for example, from stdio.h:
52575291 // #define stdin stdin
5258 if (mem.eql(u8, name, slice[next.start..next.end])) {
5292 if (mem.eql(u8, name, tok_it.slice(1))) {
52595293 continue;
52605294 }
52615295 },
......@@ -5266,15 +5300,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
52665300 },
52675301 .LParen => {
52685302 // 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;
52705304 },
52715305 else => {},
52725306 }
52735307
52745308 (if (macro_fn)
5275 transMacroFnDefine(c, &tok_it, slice, mangled_name, begin_loc)
5309 transMacroFnDefine(c, &tok_it, mangled_name, begin_loc)
52765310 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) {
52785312 error.ParseError => continue,
52795313 error.OutOfMemory => |e| return e,
52805314 };
......@@ -5284,7 +5318,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
52845318 }
52855319}
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 {
52885322 const scope = &c.global_scope.base;
52895323
52905324 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
......@@ -5292,15 +5326,15 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
52925326 const name_tok = try appendIdentifier(c, name);
52935327 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);
52965330 const last = it.next().?;
5297 if (last.id != .Eof and last.id != .Nl)
5331 if (last != .Eof and last != .Nl)
52985332 return failDecl(
52995333 c,
53005334 source_loc,
53015335 name,
53025336 "unable to translate C expr: unexpected token .{}",
5303 .{@tagName(last.id)},
5337 .{@tagName(last)},
53045338 );
53055339
53065340 const semicolon_token = try appendToken(c, .Semicolon, ";");
......@@ -5316,7 +5350,7 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
53165350 _ = try c.global_scope.macro_table.put(name, &node.base);
53175351}
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 {
53205354 var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
53215355 defer block_scope.deinit();
53225356 const scope = &block_scope.base;
......@@ -5327,7 +5361,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
53275361 const name_tok = try appendIdentifier(c, name);
53285362 _ = try appendToken(c, .LParen, "(");
53295363
5330 if (it.next().?.id != .LParen) {
5364 if (it.next().? != .LParen) {
53315365 return failDecl(
53325366 c,
53335367 source_loc,
......@@ -5341,8 +5375,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
53415375 defer fn_params.deinit();
53425376
53435377 while (true) {
5344 const param_tok = it.next().?;
5345 if (param_tok.id != .Identifier) {
5378 if (it.next().? != .Identifier) {
53465379 return failDecl(
53475380 c,
53485381 source_loc,
......@@ -5352,7 +5385,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
53525385 );
53535386 }
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));
53565389 const param_name_tok = try appendIdentifier(c, mangled_name);
53575390 _ = try appendToken(c, .Colon, ":");
53585391
......@@ -5370,13 +5403,13 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
53705403 .param_type = .{ .any_type = &any_type.base },
53715404 };
53725405
5373 if (it.peek().?.id != .Comma)
5406 if (it.peek().? != .Comma)
53745407 break;
53755408 _ = it.next();
53765409 _ = try appendToken(c, .Comma, ",");
53775410 }
53785411
5379 if (it.next().?.id != .RParen) {
5412 if (it.next().? != .RParen) {
53805413 return failDecl(
53815414 c,
53825415 source_loc,
......@@ -5391,15 +5424,15 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
53915424 const type_of = try c.createBuiltinCall("@TypeOf", 1);
53925425
53935426 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);
53955428 const last = it.next().?;
5396 if (last.id != .Eof and last.id != .Nl)
5429 if (last != .Eof and last != .Nl)
53975430 return failDecl(
53985431 c,
53995432 source_loc,
54005433 name,
54015434 "unable to translate C expr: unexpected token .{}",
5402 .{@tagName(last.id)},
5435 .{@tagName(last)},
54035436 );
54045437 _ = try appendToken(c, .Semicolon, ";");
54055438 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,
54365469
54375470const ParseError = Error || error{ParseError};
54385471
5439fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5440 const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
5441 switch (it.next().?.id) {
5472fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5473 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
5474 switch (it.next().?) {
54425475 .QuestionMark => {
54435476 // must come immediately after expr
54445477 _ = try appendToken(c, .RParen, ")");
54455478 const if_node = try transCreateNodeIf(c);
54465479 if_node.condition = node;
5447 if_node.body = try parseCPrimaryExpr(c, it, source, source_loc, scope);
5448 if (it.next().?.id != .Colon) {
5449 const first_tok = it.list.at(0);
5480 if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);
5481 if (it.next().? != .Colon) {
54505482 try failDecl(
54515483 c,
54525484 source_loc,
5453 source[first_tok.start..first_tok.end],
5485 it.slice(0),
54545486 "unable to translate C expr: expected ':'",
54555487 .{},
54565488 );
54575489 return error.ParseError;
54585490 }
54595491 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);
54615493 return &if_node.base;
54625494 },
54635495 .Comma => {
......@@ -5480,10 +5512,10 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
54805512 };
54815513 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);
54845516 _ = try appendToken(c, .Semicolon, ";");
5485 if (it.next().?.id != .Comma) {
5486 _ = it.prev();
5517 if (it.next().? != .Comma) {
5518 it.i -= 1;
54875519 break;
54885520 }
54895521 }
......@@ -5494,70 +5526,74 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
54945526 return &block_node.base;
54955527 },
54965528 else => {
5497 _ = it.prev();
5529 it.i -= 1;
54985530 return node;
54995531 },
55005532 }
55015533}
55025534
5503fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
5504 var lit_bytes = source[tok.start..tok.end];
5535fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
5536 var lit_bytes = it.slice(it.i);
55055537
5506 if (tok.id == .IntegerLiteral) {
5507 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
5508 switch (lit_bytes[1]) {
5509 '0'...'7' => {
5510 // Octal
5511 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
5512 },
5513 'X' => {
5514 // Hexadecimal with capital X, valid in C but not in Zig
5515 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
5516 },
5517 else => {},
5538 switch (it.list[it.i].id) {
5539 .IntegerLiteral => |suffix| {
5540 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
5541 switch (lit_bytes[1]) {
5542 '0'...'7' => {
5543 // Octal
5544 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
5545 },
5546 'X' => {
5547 // Hexadecimal with capital X, valid in C but not in Zig
5548 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
5549 },
5550 else => {},
5551 }
55185552 }
5519 }
55205553
5521 if (tok.id.IntegerLiteral == .None) {
5522 return transCreateNodeInt(c, lit_bytes);
5523 }
5554 if (suffix == .none) {
5555 return transCreateNodeInt(c, lit_bytes);
5556 }
55245557
5525 const cast_node = try c.createBuiltinCall("@as", 2);
5526 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.IntegerLiteral) {
5527 .U => "c_uint",
5528 .L => "c_long",
5529 .LU => "c_ulong",
5530 .LL => "c_longlong",
5531 .LLU => "c_ulonglong",
5532 else => unreachable,
5533 });
5534 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (tok.id.IntegerLiteral) {
5535 .U, .L => @as(u8, 1),
5536 .LU, .LL => 2,
5537 .LLU => 3,
5538 else => unreachable,
5539 }];
5540 _ = try appendToken(c, .Comma, ",");
5541 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
5542 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5543 return &cast_node.base;
5544 } else if (tok.id == .FloatLiteral) {
5545 if (lit_bytes[0] == '.')
5546 lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
5547 if (tok.id.FloatLiteral == .None) {
5548 return transCreateNodeFloat(c, lit_bytes);
5549 }
5550 const cast_node = try c.createBuiltinCall("@as", 2);
5551 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.FloatLiteral) {
5552 .F => "f32",
5553 .L => "c_longdouble",
5554 else => unreachable,
5555 });
5556 _ = try appendToken(c, .Comma, ",");
5557 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
5558 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5559 return &cast_node.base;
5560 } else unreachable;
5558 const cast_node = try c.createBuiltinCall("@as", 2);
5559 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5560 .u => "c_uint",
5561 .l => "c_long",
5562 .lu => "c_ulong",
5563 .ll => "c_longlong",
5564 .llu => "c_ulonglong",
5565 else => unreachable,
5566 });
5567 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
5568 .u, .l => @as(u8, 1),
5569 .lu, .ll => 2,
5570 .llu => 3,
5571 else => unreachable,
5572 }];
5573 _ = try appendToken(c, .Comma, ",");
5574 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
5575 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5576 return &cast_node.base;
5577 },
5578 .FloatLiteral => |suffix| {
5579 if (lit_bytes[0] == '.')
5580 lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
5581 if (suffix == .none) {
5582 return transCreateNodeFloat(c, lit_bytes);
5583 }
5584 const cast_node = try c.createBuiltinCall("@as", 2);
5585 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5586 .f => "f32",
5587 .l => "c_longdouble",
5588 else => unreachable,
5589 });
5590 _ = try appendToken(c, .Comma, ",");
5591 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
5592 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5593 return &cast_node.base;
5594 },
5595 else => unreachable,
5596 }
55615597}
55625598
55635599fn 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
57205756 return bytes[0..i];
57215757}
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 {
57245760 const tok = it.next().?;
5725 switch (tok.id) {
5761 const slice = it.slice(it.i);
5762 switch (tok) {
57265763 .CharLiteral => {
5727 const first_tok = it.list.at(0);
5728 if (source[tok.start] != '\'' or source[tok.start + 1] == '\\' or tok.end - tok.start == 3) {
5729 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
5764 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
5765 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
57305766 const node = try c.arena.create(ast.Node.OneToken);
57315767 node.* = .{
57325768 .base = .{ .tag = .CharLiteral },
......@@ -5734,7 +5770,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
57345770 };
57355771 return &node.base;
57365772 } 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]});
57385774 const node = try c.arena.create(ast.Node.OneToken);
57395775 node.* = .{
57405776 .base = .{ .tag = .IntegerLiteral },
......@@ -5744,8 +5780,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
57445780 }
57455781 },
57465782 .StringLiteral => {
5747 const first_tok = it.list.at(0);
5748 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
5783 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
57495784 const node = try c.arena.create(ast.Node.OneToken);
57505785 node.* = .{
57515786 .base = .{ .tag = .StringLiteral },
......@@ -5754,7 +5789,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
57545789 return &node.base;
57555790 },
57565791 .IntegerLiteral, .FloatLiteral => {
5757 return parseCNumLit(c, tok, source, source_loc);
5792 return parseCNumLit(c, it, source_loc);
57585793 },
57595794 // eventually this will be replaced by std.c.parse which will handle these correctly
57605795 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
......@@ -5764,22 +5799,50 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
57645799 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
57655800 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
57665801 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
5767 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "c_char"),
5768 .Keyword_unsigned => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
5802 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
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 },
57695833 .Identifier => {
5770 const mangled_name = scope.getAlias(source[tok.start..tok.end]);
5834 const mangled_name = scope.getAlias(it.slice(it.i));
57715835 return transCreateNodeIdentifier(c, mangled_name);
57725836 },
57735837 .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().?;
57775841 if (next_id != .RParen) {
5778 const first_tok = it.list.at(0);
57795842 try failDecl(
57805843 c,
57815844 source_loc,
5782 source[first_tok.start..first_tok.end],
5845 it.slice(0),
57835846 "unable to translate C expr: expected ')'' instead got: {}",
57845847 .{@tagName(next_id)},
57855848 );
......@@ -5787,7 +5850,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
57875850 }
57885851 var saw_l_paren = false;
57895852 var saw_integer_literal = false;
5790 switch (it.peek().?.id) {
5853 switch (it.peek().?) {
57915854 // (type)(to_cast)
57925855 .LParen => {
57935856 saw_l_paren = true;
......@@ -5805,14 +5868,13 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
58055868 // hack to get zig fmt to render a comma in builtin calls
58065869 _ = 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) {
5811 const first_tok = it.list.at(0);
5873 if (saw_l_paren and it.next().? != .RParen) {
58125874 try failDecl(
58135875 c,
58145876 source_loc,
5815 source[first_tok.start..first_tok.end],
5877 it.slice(0),
58165878 "unable to translate C expr: expected ')''",
58175879 .{},
58185880 );
......@@ -5843,13 +5905,12 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
58435905 return &group_node.base;
58445906 },
58455907 else => {
5846 const first_tok = it.list.at(0);
58475908 try failDecl(
58485909 c,
58495910 source_loc,
5850 source[first_tok.start..first_tok.end],
5911 it.slice(0),
58515912 "unable to translate C expr: unexpected token .{}",
5852 .{@tagName(tok.id)},
5913 .{@tagName(tok)},
58535914 );
58545915 return error.ParseError;
58555916 },
......@@ -5957,61 +6018,52 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
59576018 return &group_node.base;
59586019}
59596020
5960fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
5961 var node = try parseCPrimaryExpr(c, it, source, source_loc, scope);
6021fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6022 var node = try parseCPrimaryExpr(c, it, source_loc, scope);
59626023 while (true) {
5963 const tok = it.next().?;
59646024 var op_token: ast.TokenIndex = undefined;
59656025 var op_id: ast.Node.Tag = undefined;
59666026 var bool_op = false;
5967 switch (tok.id) {
6027 switch (it.next().?) {
59686028 .Period => {
5969 const name_tok = it.next().?;
5970 if (name_tok.id != .Identifier) {
5971 const first_tok = it.list.at(0);
6029 if (it.next().? != .Identifier) {
59726030 try failDecl(
59736031 c,
59746032 source_loc,
5975 source[first_tok.start..first_tok.end],
6033 it.slice(0),
59766034 "unable to translate C expr: expected identifier",
59776035 .{},
59786036 );
59796037 return error.ParseError;
59806038 }
59816039
5982 node = try transCreateNodeFieldAccess(c, node, source[name_tok.start..name_tok.end]);
6040 node = try transCreateNodeFieldAccess(c, node, it.slice(it.i));
59836041 continue;
59846042 },
59856043 .Arrow => {
5986 const name_tok = it.next().?;
5987 if (name_tok.id != .Identifier) {
5988 const first_tok = it.list.at(0);
6044 if (it.next().? != .Identifier) {
59896045 try failDecl(
59906046 c,
59916047 source_loc,
5992 source[first_tok.start..first_tok.end],
6048 it.slice(0),
59936049 "unable to translate C expr: expected identifier",
59946050 .{},
59956051 );
59966052 return error.ParseError;
59976053 }
59986054 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));
60006056 continue;
60016057 },
60026058 .Asterisk => {
6003 if (it.peek().?.id == .RParen) {
6059 if (it.peek().? == .RParen) {
60046060 // type *)
60056061
60066062 // hack to get zig fmt to render a comma in builtin calls
60076063 _ = try appendToken(c, .Comma, ",");
60086064
6009 // * token
6010 _ = it.prev();
60116065 // last token of `node`
6012 const prev_id = it.prev().?.id;
6013 _ = it.next();
6014 _ = it.next();
6066 const prev_id = it.list[it.i - 1].id;
60156067
60166068 if (prev_id == .Keyword_void) {
60176069 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
......@@ -6082,15 +6134,14 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
60826134 },
60836135 .LBracket => {
60846136 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);
60866138 arr_node.rtoken = try appendToken(c, .RBracket, "]");
60876139 node = &arr_node.base;
6088 if (it.next().?.id != .RBracket) {
6089 const first_tok = it.list.at(0);
6140 if (it.next().? != .RBracket) {
60906141 try failDecl(
60916142 c,
60926143 source_loc,
6093 source[first_tok.start..first_tok.end],
6144 it.slice(0),
60946145 "unable to translate C expr: expected ']'",
60956146 .{},
60966147 );
......@@ -6103,23 +6154,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
61036154 var call_params = std.ArrayList(*ast.Node).init(c.gpa);
61046155 defer call_params.deinit();
61056156 while (true) {
6106 const arg = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6157 const arg = try parseCPrefixOpExpr(c, it, source_loc, scope);
61076158 try call_params.append(arg);
6108 const next = it.next().?;
6109 if (next.id == .Comma)
6110 _ = try appendToken(c, .Comma, ",")
6111 else if (next.id == .RParen)
6112 break
6113 else {
6114 const first_tok = it.list.at(0);
6115 try failDecl(
6116 c,
6117 source_loc,
6118 source[first_tok.start..first_tok.end],
6119 "unable to translate C expr: expected ',' or ')'",
6120 .{},
6121 );
6122 return error.ParseError;
6159 switch (it.next().?) {
6160 .Comma => _ = try appendToken(c, .Comma, ","),
6161 .RParen => break,
6162 else => {
6163 try failDecl(
6164 c,
6165 source_loc,
6166 it.slice(0),
6167 "unable to translate C expr: expected ',' or ')'",
6168 .{},
6169 );
6170 return error.ParseError;
6171 },
61236172 }
61246173 }
61256174 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,
61446193 defer init_vals.deinit();
61456194
61466195 while (true) {
6147 const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6196 const val = try parseCPrefixOpExpr(c, it, source_loc, scope);
61486197 try init_vals.append(val);
6149 const next = it.next().?;
6150 if (next.id == .Comma)
6151 _ = try appendToken(c, .Comma, ",")
6152 else if (next.id == .RBrace)
6153 break
6154 else {
6155 const first_tok = it.list.at(0);
6156 try failDecl(
6157 c,
6158 source_loc,
6159 source[first_tok.start..first_tok.end],
6160 "unable to translate C expr: expected ',' or '}}'",
6161 .{},
6162 );
6163 return error.ParseError;
6198 switch (it.next().?) {
6199 .Comma => _ = try appendToken(c, .Comma, ","),
6200 .RBrace => break,
6201 else => {
6202 try failDecl(
6203 c,
6204 source_loc,
6205 it.slice(0),
6206 "unable to translate C expr: expected ',' or '}}'",
6207 .{},
6208 );
6209 return error.ParseError;
6210 },
61646211 }
61656212 }
61666213 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,
62076254 op_id = .ArrayCat;
62086255 op_token = try appendToken(c, .PlusPlus, "++");
62096256
6210 _ = it.prev();
6257 it.i -= 1;
62116258 },
62126259 .Identifier => {
62136260 op_id = .ArrayCat;
62146261 op_token = try appendToken(c, .PlusPlus, "++");
62156262
6216 _ = it.prev();
6263 it.i -= 1;
62176264 },
62186265 else => {
6219 _ = it.prev();
6266 it.i -= 1;
62206267 return node;
62216268 },
62226269 }
62236270 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
62246271 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);
62266273 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
62276274 op_node.* = .{
62286275 .base = .{ .tag = op_id },
......@@ -6234,38 +6281,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
62346281 }
62356282}
62366283
6237fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6238 const op_tok = it.next().?;
6239
6240 switch (op_tok.id) {
6284fn parseCPrefixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
6285 switch (it.next().?) {
62416286 .Bang => {
62426287 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);
62446289 return &node.base;
62456290 },
62466291 .Minus => {
62476292 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);
62496294 return &node.base;
62506295 },
6251 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
6296 .Plus => return try parseCPrefixOpExpr(c, it, source_loc, scope),
62526297 .Tilde => {
62536298 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);
62556300 return &node.base;
62566301 },
62576302 .Asterisk => {
6258 const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6303 const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
62596304 return try transCreateNodePtrDeref(c, node);
62606305 },
62616306 .Ampersand => {
62626307 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);
62646309 return &node.base;
62656310 },
62666311 else => {
6267 _ = it.prev();
6268 return try parseCSuffixOpExpr(c, it, source, source_loc, scope);
6312 it.i -= 1;
6313 return try parseCSuffixOpExpr(c, it, source_loc, scope);
62696314 },
62706315 }
62716316}
src-self-hosted/type.zig+132-16
......@@ -67,6 +67,7 @@ pub const Type = extern union {
6767
6868 .array, .array_u8_sentinel_0 => return .Array,
6969 .single_const_pointer => return .Pointer,
70 .single_mut_pointer => return .Pointer,
7071 .single_const_pointer_to_comptime_int => return .Pointer,
7172 .const_slice_u8 => return .Pointer,
7273 }
......@@ -261,6 +262,15 @@ pub const Type = extern union {
261262 };
262263 return Type{ .ptr_otherwise = &new_payload.base };
263264 },
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 },
264274 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
265275 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
266276 .function => {
......@@ -368,6 +378,12 @@ pub const Type = extern union {
368378 ty = payload.pointee_type;
369379 continue;
370380 },
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 },
371387 .int_signed => {
372388 const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
373389 return out_stream.print("i{}", .{payload.bits});
......@@ -467,6 +483,7 @@ pub const Type = extern union {
467483 .array_u8_sentinel_0,
468484 .array, // TODO check for zero bits
469485 .single_const_pointer,
486 .single_mut_pointer,
470487 .int_signed, // TODO check for zero bits
471488 .int_unsigned, // TODO check for zero bits
472489 => true,
......@@ -493,13 +510,18 @@ pub const Type = extern union {
493510 .u8,
494511 .i8,
495512 .bool,
513 .array_u8_sentinel_0,
514 => return 1,
515
496516 .fn_noreturn_no_args, // represents machine code; not a pointer
497517 .fn_void_no_args, // represents machine code; not a pointer
498518 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
499519 .fn_ccc_void_no_args, // represents machine code; not a pointer
500520 .function, // represents machine code; not a pointer
501 .array_u8_sentinel_0,
502 => return 1,
521 => return switch (target.cpu.arch) {
522 .riscv64 => 2,
523 else => 1,
524 },
503525
504526 .i16, .u16 => return 2,
505527 .i32, .u32 => return 4,
......@@ -510,6 +532,7 @@ pub const Type = extern union {
510532 .single_const_pointer_to_comptime_int,
511533 .const_slice_u8,
512534 .single_const_pointer,
535 .single_mut_pointer,
513536 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
514537
515538 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
......@@ -591,6 +614,7 @@ pub const Type = extern union {
591614 .single_const_pointer_to_comptime_int,
592615 .const_slice_u8,
593616 .single_const_pointer,
617 .single_mut_pointer,
594618 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
595619
596620 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
......@@ -671,6 +695,7 @@ pub const Type = extern union {
671695 => false,
672696
673697 .single_const_pointer,
698 .single_mut_pointer,
674699 .single_const_pointer_to_comptime_int,
675700 => true,
676701 };
......@@ -714,6 +739,7 @@ pub const Type = extern union {
714739 .array,
715740 .array_u8_sentinel_0,
716741 .single_const_pointer,
742 .single_mut_pointer,
717743 .single_const_pointer_to_comptime_int,
718744 .fn_noreturn_no_args,
719745 .fn_void_no_args,
......@@ -728,8 +754,7 @@ pub const Type = extern union {
728754 };
729755 }
730756
731 /// Asserts the type is a pointer type.
732 pub fn pointerIsConst(self: Type) bool {
757 pub fn isConstPtr(self: Type) bool {
733758 return switch (self.tag()) {
734759 .u8,
735760 .i8,
......@@ -773,7 +798,8 @@ pub const Type = extern union {
773798 .function,
774799 .int_unsigned,
775800 .int_signed,
776 => unreachable,
801 .single_mut_pointer,
802 => false,
777803
778804 .single_const_pointer,
779805 .single_const_pointer_to_comptime_int,
......@@ -782,6 +808,58 @@ pub const Type = extern union {
782808 };
783809 }
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
785863 /// Asserts the type is a pointer or array type.
786864 pub fn elemType(self: Type) Type {
787865 return switch (self.tag()) {
......@@ -829,6 +907,7 @@ pub const Type = extern union {
829907
830908 .array => self.cast(Payload.Array).?.elem_type,
831909 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
910 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,
832911 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
833912 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
834913 };
......@@ -876,6 +955,7 @@ pub const Type = extern union {
876955 .fn_ccc_void_no_args,
877956 .function,
878957 .single_const_pointer,
958 .single_mut_pointer,
879959 .single_const_pointer_to_comptime_int,
880960 .const_slice_u8,
881961 .int_unsigned,
......@@ -929,6 +1009,7 @@ pub const Type = extern union {
9291009 .fn_ccc_void_no_args,
9301010 .function,
9311011 .single_const_pointer,
1012 .single_mut_pointer,
9321013 .single_const_pointer_to_comptime_int,
9331014 .const_slice_u8,
9341015 .int_unsigned,
......@@ -970,6 +1051,7 @@ pub const Type = extern union {
9701051 .function,
9711052 .array,
9721053 .single_const_pointer,
1054 .single_mut_pointer,
9731055 .single_const_pointer_to_comptime_int,
9741056 .array_u8_sentinel_0,
9751057 .const_slice_u8,
......@@ -1024,6 +1106,7 @@ pub const Type = extern union {
10241106 .function,
10251107 .array,
10261108 .single_const_pointer,
1109 .single_mut_pointer,
10271110 .single_const_pointer_to_comptime_int,
10281111 .array_u8_sentinel_0,
10291112 .const_slice_u8,
......@@ -1078,6 +1161,7 @@ pub const Type = extern union {
10781161 .function,
10791162 .array,
10801163 .single_const_pointer,
1164 .single_mut_pointer,
10811165 .single_const_pointer_to_comptime_int,
10821166 .array_u8_sentinel_0,
10831167 .const_slice_u8,
......@@ -1130,6 +1214,7 @@ pub const Type = extern union {
11301214 .function,
11311215 .array,
11321216 .single_const_pointer,
1217 .single_mut_pointer,
11331218 .single_const_pointer_to_comptime_int,
11341219 .array_u8_sentinel_0,
11351220 .const_slice_u8,
......@@ -1211,6 +1296,7 @@ pub const Type = extern union {
12111296 .@"undefined",
12121297 .array,
12131298 .single_const_pointer,
1299 .single_mut_pointer,
12141300 .single_const_pointer_to_comptime_int,
12151301 .array_u8_sentinel_0,
12161302 .const_slice_u8,
......@@ -1268,6 +1354,7 @@ pub const Type = extern union {
12681354 .@"undefined",
12691355 .array,
12701356 .single_const_pointer,
1357 .single_mut_pointer,
12711358 .single_const_pointer_to_comptime_int,
12721359 .array_u8_sentinel_0,
12731360 .const_slice_u8,
......@@ -1324,6 +1411,7 @@ pub const Type = extern union {
13241411 .@"undefined",
13251412 .array,
13261413 .single_const_pointer,
1414 .single_mut_pointer,
13271415 .single_const_pointer_to_comptime_int,
13281416 .array_u8_sentinel_0,
13291417 .const_slice_u8,
......@@ -1380,6 +1468,7 @@ pub const Type = extern union {
13801468 .@"undefined",
13811469 .array,
13821470 .single_const_pointer,
1471 .single_mut_pointer,
13831472 .single_const_pointer_to_comptime_int,
13841473 .array_u8_sentinel_0,
13851474 .const_slice_u8,
......@@ -1433,6 +1522,7 @@ pub const Type = extern union {
14331522 .@"undefined",
14341523 .array,
14351524 .single_const_pointer,
1525 .single_mut_pointer,
14361526 .single_const_pointer_to_comptime_int,
14371527 .array_u8_sentinel_0,
14381528 .const_slice_u8,
......@@ -1486,6 +1576,7 @@ pub const Type = extern union {
14861576 .@"undefined",
14871577 .array,
14881578 .single_const_pointer,
1579 .single_mut_pointer,
14891580 .single_const_pointer_to_comptime_int,
14901581 .array_u8_sentinel_0,
14911582 .const_slice_u8,
......@@ -1559,6 +1650,7 @@ pub const Type = extern union {
15591650 .function,
15601651 .array,
15611652 .single_const_pointer,
1653 .single_mut_pointer,
15621654 .single_const_pointer_to_comptime_int,
15631655 .array_u8_sentinel_0,
15641656 .const_slice_u8,
......@@ -1566,7 +1658,7 @@ pub const Type = extern union {
15661658 };
15671659 }
15681660
1569 pub fn onePossibleValue(self: Type) bool {
1661 pub fn onePossibleValue(self: Type) ?Value {
15701662 var ty = self;
15711663 while (true) switch (ty.tag()) {
15721664 .f16,
......@@ -1605,21 +1697,32 @@ pub const Type = extern union {
16051697 .single_const_pointer_to_comptime_int,
16061698 .array_u8_sentinel_0,
16071699 .const_slice_u8,
1608 => return false,
1609
16101700 .c_void,
1611 .void,
1612 .noreturn,
1613 .@"null",
1614 .@"undefined",
1615 => return true,
1701 => return null,
1702
1703 .void => return Value.initTag(.void_value),
1704 .noreturn => return Value.initTag(.unreachable_value),
1705 .@"null" => return Value.initTag(.null_value),
1706 .@"undefined" => return Value.initTag(.undef),
16161707
1617 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
1618 .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,
1708 .int_unsigned => {
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 },
16191722 .array => {
16201723 const array = ty.cast(Payload.Array).?;
16211724 if (array.len == 0)
1622 return true;
1725 return Value.initTag(.empty_array);
16231726 ty = array.elem_type;
16241727 continue;
16251728 },
......@@ -1628,6 +1731,11 @@ pub const Type = extern union {
16281731 ty = ptr.pointee_type;
16291732 continue;
16301733 },
1734 .single_mut_pointer => {
1735 const ptr = ty.cast(Payload.SingleMutPointer).?;
1736 ty = ptr.pointee_type;
1737 continue;
1738 },
16311739 };
16321740 }
16331741
......@@ -1678,6 +1786,7 @@ pub const Type = extern union {
16781786 .int_signed,
16791787 .array,
16801788 .single_const_pointer,
1789 .single_mut_pointer,
16811790 => return false,
16821791 };
16831792 }
......@@ -1734,6 +1843,7 @@ pub const Type = extern union {
17341843 array_u8_sentinel_0,
17351844 array,
17361845 single_const_pointer,
1846 single_mut_pointer,
17371847 int_signed,
17381848 int_unsigned,
17391849 function,
......@@ -1764,6 +1874,12 @@ pub const Type = extern union {
17641874 pointee_type: Type,
17651875 };
17661876
1877 pub const SingleMutPointer = struct {
1878 base: Payload = Payload{ .tag = .single_mut_pointer },
1879
1880 pointee_type: Type,
1881 };
1882
17671883 pub const IntSigned = struct {
17681884 base: Payload = Payload{ .tag = .int_signed },
17691885
src-self-hosted/value.zig+51-24
......@@ -63,7 +63,9 @@ pub const Value = extern union {
6363
6464 undef,
6565 zero,
66 the_one_possible_value, // when the type only has one possible value
66 void_value,
67 unreachable_value,
68 empty_array,
6769 null_value,
6870 bool_true,
6971 bool_false, // See last_no_payload_tag below.
......@@ -164,7 +166,9 @@ pub const Value = extern union {
164166 .const_slice_u8_type,
165167 .undef,
166168 .zero,
167 .the_one_possible_value,
169 .void_value,
170 .unreachable_value,
171 .empty_array,
168172 .null_value,
169173 .bool_true,
170174 .bool_false,
......@@ -285,7 +289,8 @@ pub const Value = extern union {
285289 .null_value => return out_stream.writeAll("null"),
286290 .undef => return out_stream.writeAll("undefined"),
287291 .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"),
289294 .bool_true => return out_stream.writeAll("true"),
290295 .bool_false => return out_stream.writeAll("false"),
291296 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
......@@ -312,6 +317,7 @@ pub const Value = extern union {
312317 try out_stream.print("&[{}] ", .{elem_ptr.index});
313318 val = elem_ptr.array_ptr;
314319 },
320 .empty_array => return out_stream.writeAll(".{}"),
315321 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
316322 .repeated => {
317323 try out_stream.writeAll("(repeated) ");
......@@ -388,7 +394,9 @@ pub const Value = extern union {
388394
389395 .undef,
390396 .zero,
391 .the_one_possible_value,
397 .void_value,
398 .unreachable_value,
399 .empty_array,
392400 .bool_true,
393401 .bool_false,
394402 .null_value,
......@@ -460,15 +468,18 @@ pub const Value = extern union {
460468 .decl_ref,
461469 .elem_ptr,
462470 .bytes,
463 .undef,
464471 .repeated,
465472 .float_16,
466473 .float_32,
467474 .float_64,
468475 .float_128,
476 .void_value,
477 .unreachable_value,
478 .empty_array,
469479 => unreachable,
470480
471 .the_one_possible_value, // An integer with one possible value is always zero.
481 .undef => unreachable,
482
472483 .zero,
473484 .bool_false,
474485 => return BigIntMutable.init(&space.limbs, 0).toConst(),
......@@ -532,16 +543,19 @@ pub const Value = extern union {
532543 .decl_ref,
533544 .elem_ptr,
534545 .bytes,
535 .undef,
536546 .repeated,
537547 .float_16,
538548 .float_32,
539549 .float_64,
540550 .float_128,
551 .void_value,
552 .unreachable_value,
553 .empty_array,
541554 => unreachable,
542555
556 .undef => unreachable,
557
543558 .zero,
544 .the_one_possible_value, // an integer with one possible value is always zero
545559 .bool_false,
546560 => return 0,
547561
......@@ -570,10 +584,9 @@ pub const Value = extern union {
570584 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
571585 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
572586
573 .zero, .the_one_possible_value => 0,
587 .zero => 0,
574588 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
575 // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
576 .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
589 .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
577590
578591 .int_big_positive, .int_big_negative => @panic("big int to f128"),
579592 else => unreachable,
......@@ -637,9 +650,11 @@ pub const Value = extern union {
637650 .float_32,
638651 .float_64,
639652 .float_128,
653 .void_value,
654 .unreachable_value,
655 .empty_array,
640656 => unreachable,
641657
642 .the_one_possible_value, // an integer with one possible value is always zero
643658 .zero,
644659 .bool_false,
645660 => return 0,
......@@ -714,11 +729,13 @@ pub const Value = extern union {
714729 .float_32,
715730 .float_64,
716731 .float_128,
732 .void_value,
733 .unreachable_value,
734 .empty_array,
717735 => unreachable,
718736
719737 .zero,
720738 .undef,
721 .the_one_possible_value, // an integer with one possible value is always zero
722739 .bool_false,
723740 => return true,
724741
......@@ -797,13 +814,13 @@ pub const Value = extern union {
797814 // return Value.initPayload(&res_payload.base).copy(allocator);
798815 },
799816 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) };
801818 if (!self.eql(Value.initPayload(&res_payload.base)))
802819 return error.Overflow;
803820 return Value.initPayload(&res_payload.base).copy(allocator);
804821 },
805822 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) };
807824 if (!self.eql(Value.initPayload(&res_payload.base)))
808825 return error.Overflow;
809826 return Value.initPayload(&res_payload.base).copy(allocator);
......@@ -875,7 +892,9 @@ pub const Value = extern union {
875892 .int_i64,
876893 .int_big_positive,
877894 .int_big_negative,
878 .the_one_possible_value,
895 .empty_array,
896 .void_value,
897 .unreachable_value,
879898 => unreachable,
880899
881900 .zero => false,
......@@ -939,10 +958,12 @@ pub const Value = extern union {
939958 .bytes,
940959 .repeated,
941960 .undef,
961 .void_value,
962 .unreachable_value,
963 .empty_array,
942964 => unreachable,
943965
944966 .zero,
945 .the_one_possible_value, // an integer with one possible value is always zero
946967 .bool_false,
947968 => .eq,
948969
......@@ -964,8 +985,8 @@ pub const Value = extern union {
964985 pub fn order(lhs: Value, rhs: Value) std.math.Order {
965986 const lhs_tag = lhs.tag();
966987 const rhs_tag = rhs.tag();
967 const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
968 const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
988 const lhs_is_zero = lhs_tag == .zero;
989 const rhs_is_zero = rhs_tag == .zero;
969990 if (lhs_is_zero) return rhs.orderAgainstZero().invert();
970991 if (rhs_is_zero) return lhs.orderAgainstZero();
971992
......@@ -1071,9 +1092,11 @@ pub const Value = extern union {
10711092 .float_32,
10721093 .float_64,
10731094 .float_128,
1095 .void_value,
1096 .unreachable_value,
1097 .empty_array,
10741098 => unreachable,
10751099
1076 .the_one_possible_value => Value.initTag(.the_one_possible_value),
10771100 .ref_val => self.cast(Payload.RefVal).?.val,
10781101 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
10791102 .elem_ptr => {
......@@ -1130,7 +1153,6 @@ pub const Value = extern union {
11301153 .single_const_pointer_to_comptime_int_type,
11311154 .const_slice_u8_type,
11321155 .zero,
1133 .the_one_possible_value,
11341156 .bool_true,
11351157 .bool_false,
11361158 .null_value,
......@@ -1147,8 +1169,12 @@ pub const Value = extern union {
11471169 .float_32,
11481170 .float_64,
11491171 .float_128,
1172 .void_value,
1173 .unreachable_value,
11501174 => unreachable,
11511175
1176 .empty_array => unreachable, // out of bounds array index
1177
11521178 .bytes => {
11531179 const int_payload = try allocator.create(Payload.Int_u64);
11541180 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
......@@ -1175,8 +1201,7 @@ pub const Value = extern union {
11751201 return self.tag() == .undef;
11761202 }
11771203
1178 /// Valid for all types. Asserts the value is not undefined.
1179 /// `.the_one_possible_value` is reported as not null.
1204 /// Valid for all types. Asserts the value is not undefined and not unreachable.
11801205 pub fn isNull(self: Value) bool {
11811206 return switch (self.tag()) {
11821207 .ty,
......@@ -1221,7 +1246,7 @@ pub const Value = extern union {
12211246 .single_const_pointer_to_comptime_int_type,
12221247 .const_slice_u8_type,
12231248 .zero,
1224 .the_one_possible_value,
1249 .empty_array,
12251250 .bool_true,
12261251 .bool_false,
12271252 .function,
......@@ -1238,9 +1263,11 @@ pub const Value = extern union {
12381263 .float_32,
12391264 .float_64,
12401265 .float_128,
1266 .void_value,
12411267 => false,
12421268
12431269 .undef => unreachable,
1270 .unreachable_value => unreachable,
12441271 .null_value => true,
12451272 };
12461273 }
src-self-hosted/zir.zig+246-77
......@@ -34,25 +34,63 @@ pub const Inst = struct {
3434
3535 /// These names are used directly as the instruction names in the text format.
3636 pub const Tag = enum {
37 /// Arithmetic addition, asserts no integer overflow.
38 add,
39 /// Twos complement wrapping integer addition.
40 addwrap,
3741 /// 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.
3943 alloc,
4044 /// Same as `alloc` except the type is inferred.
4145 alloc_inferred,
46 /// Array concatenation. `a ++ b`
47 array_cat,
48 /// Array multiplication `a ** b`
49 array_mul,
4250 /// Function parameter value. These must be first in a function's main block,
4351 /// in respective order with the parameters.
4452 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,
4566 /// A typed result location pointer is bitcasted to a new result location pointer.
4667 /// The new result location pointer has an inferred type.
4768 bitcast_result_ptr,
69 /// Bitwise OR. `|`
70 bitor,
4871 /// A labeled block of code, which can return a value.
4972 block,
73 /// Boolean NOT. See also `bitnot`.
74 boolnot,
5075 /// Return a value from a `Block`.
5176 @"break",
5277 breakpoint,
5378 /// Same as `break` but without an operand; the operand is assumed to be the void value.
5479 breakvoid,
80 /// Function call.
5581 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,
5694 /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
5795 /// as type coercion from the new element type to the old element type.
5896 /// LHS is destination element type, RHS is result pointer.
......@@ -65,8 +103,12 @@ pub const Inst = struct {
65103 coerce_to_ptr_elem,
66104 /// Emit an error message and fail compilation.
67105 compileerror,
106 /// Conditional branch. Splits control flow based on a boolean condition value.
107 condbr,
68108 /// Special case, has no textual representation.
69109 @"const",
110 /// Declares the beginning of a statement. Used for debug info.
111 dbg_stmt,
70112 /// Represents a pointer to a global decl by name.
71113 declref,
72114 /// Represents a pointer to a global decl by string name.
......@@ -76,61 +118,108 @@ pub const Inst = struct {
76118 declval,
77119 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
78120 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,
79128 /// Emits a compile error if the operand is not `void`.
80129 ensure_result_used,
81130 /// Emits a compile error if an error is ignored.
82131 ensure_result_non_error,
83 boolnot,
84 /// Obtains a pointer to the return value.
85 ret_ptr,
86 /// Obtains the return type of the in-scope function.
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,
132 /// Export the provided Decl as the provided name in the compilation's output object file.
133 @"export",
134 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
135 /// to the named field.
95136 fieldptr,
96 deref,
97 as,
98 @"asm",
99 @"unreachable",
100 @"return",
101 returnvoid,
137 /// Convert a larger float type to any other float type, possibly causing a loss of precision.
138 floatcast,
139 /// Declare a function body.
102140 @"fn",
141 /// Returns a function type.
103142 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,
105162 /// Given a reference to a function and a parameter index, returns the
106163 /// type of the parameter. TODO what happens when the parameter is `anytype`?
107164 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.
108167 primitive,
109 intcast,
110 bitcast,
111 floatcast,
112 elemptr,
113 add,
168 /// Convert a pointer to a `usize` integer.
169 ptrtoint,
170 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
171 /// stores it in a memory location, and returns a const pointer to it. If the value
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.
114197 sub,
115 cmp_lt,
116 cmp_lte,
117 cmp_eq,
118 cmp_gte,
119 cmp_gt,
120 cmp_neq,
121 condbr,
122 isnull,
123 isnonnull,
198 /// Twos complement wrapping integer subtraction.
199 subwrap,
200 /// Returns the type of a value.
201 typeof,
202 /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
203 /// will assume the correctness of this instruction.
204 unreach_nocheck,
205 /// Asserts control-flow will not reach this instruction. In safety-checked modes,
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
125212 pub fn Type(tag: Tag) type {
126213 return switch (tag) {
127214 .arg,
128215 .breakpoint,
129 .@"unreachable",
216 .dbg_stmt,
130217 .returnvoid,
131218 .alloc_inferred,
132219 .ret_ptr,
133220 .ret_type,
221 .unreach_nocheck,
222 .@"unreachable",
134223 => NoOp,
135224
136225 .boolnot,
......@@ -143,10 +232,28 @@ pub const Inst = struct {
143232 .ensure_result_used,
144233 .ensure_result_non_error,
145234 .bitcast_result_ptr,
235 .ref,
236 .bitcast_lvalue,
237 .typeof,
238 .single_const_ptr_type,
239 .single_mut_ptr_type,
146240 => UnOp,
147241
148242 .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,
149255 .sub,
256 .subwrap,
150257 .cmp_lt,
151258 .cmp_lte,
152259 .cmp_eq,
......@@ -158,6 +265,7 @@ pub const Inst = struct {
158265 .intcast,
159266 .bitcast,
160267 .coerce_result_ptr,
268 .xor,
161269 => BinOp,
162270
163271 .block => Block,
......@@ -172,7 +280,6 @@ pub const Inst = struct {
172280 .coerce_result_block_ptr => CoerceResultBlockPtr,
173281 .compileerror => CompileError,
174282 .@"const" => Const,
175 .store => Store,
176283 .str => Str,
177284 .int => Int,
178285 .inttype => IntType,
......@@ -192,63 +299,83 @@ pub const Inst = struct {
192299 /// Function calls do not count.
193300 pub fn isNoReturn(tag: Tag) bool {
194301 return switch (tag) {
302 .add,
303 .addwrap,
195304 .alloc,
196305 .alloc_inferred,
306 .array_cat,
307 .array_mul,
197308 .arg,
309 .as,
310 .@"asm",
311 .bitand,
312 .bitcast,
313 .bitcast_lvalue,
198314 .bitcast_result_ptr,
315 .bitor,
199316 .block,
317 .boolnot,
200318 .breakpoint,
201319 .call,
320 .cmp_lt,
321 .cmp_lte,
322 .cmp_eq,
323 .cmp_gte,
324 .cmp_gt,
325 .cmp_neq,
202326 .coerce_result_ptr,
203327 .coerce_result_block_ptr,
204328 .coerce_to_ptr_elem,
205329 .@"const",
330 .dbg_stmt,
206331 .declref,
207332 .declref_str,
208333 .declval,
209334 .declval_in_module,
335 .deref,
336 .div,
337 .elemptr,
210338 .ensure_result_used,
211339 .ensure_result_non_error,
212 .ret_ptr,
213 .ret_type,
214 .store,
215 .str,
216 .int,
217 .inttype,
218 .ptrtoint,
340 .@"export",
341 .floatcast,
219342 .fieldptr,
220 .deref,
221 .as,
222 .@"asm",
223343 .@"fn",
224344 .fntype,
225 .@"export",
345 .int,
346 .intcast,
347 .inttype,
348 .isnonnull,
349 .isnull,
350 .mod_rem,
351 .mul,
352 .mulwrap,
226353 .param_type,
227354 .primitive,
228 .intcast,
229 .bitcast,
230 .floatcast,
231 .elemptr,
232 .add,
355 .ptrtoint,
356 .ref,
357 .ret_ptr,
358 .ret_type,
359 .shl,
360 .shr,
361 .single_const_ptr_type,
362 .single_mut_ptr_type,
363 .store,
364 .str,
233365 .sub,
234 .cmp_lt,
235 .cmp_lte,
236 .cmp_eq,
237 .cmp_gte,
238 .cmp_gt,
239 .cmp_neq,
240 .isnull,
241 .isnonnull,
242 .boolnot,
366 .subwrap,
367 .typeof,
368 .xor,
243369 => false,
244370
245 .condbr,
246 .@"unreachable",
247 .@"return",
248 .returnvoid,
249371 .@"break",
250372 .breakvoid,
373 .condbr,
251374 .compileerror,
375 .@"return",
376 .returnvoid,
377 .unreach_nocheck,
378 .@"unreachable",
252379 => true,
253380 };
254381 }
......@@ -430,17 +557,6 @@ pub const Inst = struct {
430557 kw_args: struct {},
431558 };
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
444560 pub const Str = struct {
445561 pub const base_tag = Tag.str;
446562 base: Inst,
......@@ -630,7 +746,7 @@ pub const Inst = struct {
630746 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
631747 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
632748 .@"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) },
634750 };
635751 }
636752 };
......@@ -1486,6 +1602,21 @@ const EmitZIR = struct {
14861602 const decl = decl_ref.decl;
14871603 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
14881604 }
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 }
14891620 switch (typed_value.ty.zigTypeTag()) {
14901621 .Pointer => {
14911622 const ptr_elem_type = typed_value.ty.elemType();
......@@ -1716,15 +1847,19 @@ const EmitZIR = struct {
17161847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
17171848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
17181849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
17191851
17201852 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
17211853 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
17221854 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
17231855 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
17241856 .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
17261860 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
17271861 .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),
17281863 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
17291864 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
17301865 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
......@@ -1736,6 +1871,21 @@ const EmitZIR = struct {
17361871 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
17371872 .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
17391889 .block => blk: {
17401890 const old_inst = inst.castTag(.block).?;
17411891 const new_inst = try self.arena.allocator.create(Inst.Block);
......@@ -1973,6 +2123,25 @@ const EmitZIR = struct {
19732123 };
19742124 return self.emitUnnamedDecl(&inttype_inst.base);
19752125 },
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 },
19762145 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
19772146 },
19782147 }
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 {
41104110 IrInstSrc *target_value;
41114111 IrInstSrcCheckSwitchProngsRange *ranges;
41124112 size_t range_count;
4113 bool have_else_prong;
4113 AstNode* else_prong;
41144114 bool have_underscore_prong;
41154115};
41164116
src/analyze.cpp+14
......@@ -1490,6 +1490,20 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
14901490}
14911491
14921492ZigType *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 }
14931507 ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,
14941508 nullptr, UndefBad);
14951509 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
43004300
43014301static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
43024302 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)
43044304{
43054305 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
43064306 irb, scope, source_node);
43074307 instruction->target_value = target_value;
43084308 instruction->ranges = ranges;
43094309 instruction->range_count = range_count;
4310 instruction->have_else_prong = have_else_prong;
4310 instruction->else_prong = else_prong;
43114311 instruction->have_underscore_prong = have_underscore_prong;
43124312
43134313 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
93479347 }
93489348
93499349 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
93529352 IrInstSrc *br_instruction;
93539353 if (cases.length == 0) {
......@@ -20604,17 +20604,25 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2060420604 return ira->codegen->invalid_inst_gen;
2060520605 }
2060620606
20607 ZigType *expected_return_type = result_loc->value->type->data.pointer.child_type;
20608
2060720609 IrInstGen *dummy_value = ir_const(ira, source_instr, return_type);
2060820610 dummy_value->value->special = ConstValSpecialRuntime;
2060920611 IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr,
20610 dummy_value, result_loc->value->type->data.pointer.child_type);
20611 if (type_is_invalid(dummy_result->value->type))
20612 dummy_value, expected_return_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 }
2061220620 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;
2061620621 }
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)) {
2061820626 ir_reset_result(call_result_loc);
2061920627 result_loc = nullptr;
2062020628 }
......@@ -28828,7 +28836,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2882828836 buf_ptr(enum_field->name)));
2882928837 }
2883028838 }
28831 } else if (!instruction->have_else_prong) {
28839 } else if (instruction->else_prong == nullptr) {
2883228840 if (switch_type->data.enumeration.non_exhaustive) {
2883328841 ir_add_error(ira, &instruction->base.base,
2883428842 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,
2884328851 buf_ptr(enum_field->name)));
2884428852 }
2884528853 }
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;
2884628858 }
2884728859 } else if (switch_type->id == ZigTypeIdErrorSet) {
2884828860 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,
2888928901 }
2889028902 field_prev_uses[start_index] = start_value->base.source_node;
2889128903 }
28892 if (!instruction->have_else_prong) {
28904 if (instruction->else_prong == nullptr) {
2889328905 if (type_is_global_error_set(switch_type)) {
2889428906 ir_add_error(ira, &instruction->base.base,
2889528907 buf_sprintf("else prong required when switching on type 'anyerror'"));
......@@ -28951,16 +28963,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2895128963 return ira->codegen->invalid_inst_gen;
2895228964 }
2895328965 }
28954 if (!instruction->have_else_prong) {
28966
2895528967 BigInt min_val;
2895628968 eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
2895728969 BigInt max_val;
2895828970 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) {
2896028973 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
2896128974 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;
2896228979 }
28963 }
2896428980 } else if (switch_type->id == ZigTypeIdBool) {
2896528981 int seenTrue = 0;
2896628982 int seenFalse = 0;
......@@ -28990,11 +29006,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2899029006 return ira->codegen->invalid_inst_gen;
2899129007 }
2899229008 }
28993 if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
29009 if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) {
2899429010 ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
2899529011 return ira->codegen->invalid_inst_gen;
2899629012 }
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) {
2899829020 ir_add_error(ira, &instruction->base.base,
2899929021 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
2900029022 return ira->codegen->invalid_inst_gen;
......@@ -29077,6 +29099,19 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
2907729099 ZigType *result_type;
2907829100 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
2908029115 if (target_type->id == ZigTypeIdPointer) {
2908129116 result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
2908229117 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
3089430929 IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value,
3089530930 instruction->result_loc->allow_write_through_const);
3089630931 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 }
3089730939 return ira->codegen->invalid_inst_gen;
3089830940 }
3089930941 }
src/ir_print.cpp+1-1
......@@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
21752175 fprintf(irp->f, "...");
21762176 ir_print_other_inst_src(irp, instruction->ranges[i].end);
21772177 }
2178 const char *have_else_str = instruction->have_else_prong ? "yes" : "no";
2178 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
21792179 fprintf(irp->f, ")else:%s", have_else_str);
21802180}
21812181
src/link.cpp+4
......@@ -2107,6 +2107,10 @@ static void construct_linker_job_wasm(LinkJob *lj) {
21072107 lj->args.append("-z");
21082108 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
21102114 if (g->out_type != OutTypeExe) {
21112115 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(
28232823 return bitcast(casted->getBeginLoc());
28242824}
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
28262834const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *self) {
28272835 auto casted = reinterpret_cast<const clang::DoStmt *>(self);
28282836 return reinterpret_cast<const struct ZigClangStmt *>(casted->getBody());
src/zig_clang.h+9
......@@ -901,6 +901,14 @@ enum ZigClangExpr_ConstExprUsage {
901901 ZigClangExpr_EvaluateForMangling,
902902};
903903
904enum ZigClangUnaryExprOrTypeTrait_Kind {
905 ZigClangUnaryExprOrTypeTrait_KindSizeOf,
906 ZigClangUnaryExprOrTypeTrait_KindAlignOf,
907 ZigClangUnaryExprOrTypeTrait_KindVecStep,
908 ZigClangUnaryExprOrTypeTrait_KindOpenMPRequiredSimdAlign,
909 ZigClangUnaryExprOrTypeTrait_KindPreferredAlignOf,
910};
911
904912ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,
905913 struct ZigClangSourceLocation Loc);
906914ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,
......@@ -1190,6 +1198,7 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getIdx(const
11901198
11911199ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(const struct ZigClangUnaryExprOrTypeTraitExpr *);
11921200ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *);
1201ZIG_EXTERN_C enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(const struct ZigClangUnaryExprOrTypeTraitExpr *);
11931202
11941203ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *);
11951204ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *);
test/compile_errors.zig+144
......@@ -2,6 +2,32 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub 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
531 cases.addTest("invalid pointer with @Type",
632 \\export fn entry() void {
733 \\ _ = @Type(.{ .Pointer = .{
......@@ -18,6 +44,28 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1844 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
1945 });
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
2169 cases.addTest("int/float conversion to comptime_int/float",
2270 \\export fn foo() void {
2371 \\ var a: f32 = 2;
......@@ -509,6 +557,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
509557 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
510558 });
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
512656 cases.addTest("@export with empty name string",
513657 \\pub export fn entry() void { }
514658 \\comptime {
test/stage1/behavior/bugs/1111.zig-1
......@@ -7,6 +7,5 @@ test "issue 1111 fixed" {
77
88 switch (v) {
99 Foo.Bar => return,
10 else => return,
1110 }
1211}
test/stage2/compare_output.zig+105
......@@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
77 .os_tag = .linux,
88};
99
10const linux_riscv64 = std.zig.CrossTarget{
11 .cpu_arch = .riscv64,
12 .os_tag = .linux,
13};
14
1015pub fn addCases(ctx: *TestContext) !void {
1116 if (std.Target.current.os.tag != .linux or
1217 std.Target.current.cpu.arch != .x86_64)
......@@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {
118123 \\
119124 );
120125 }
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
122163 {
123164 var case = ctx.exe("adding numbers at comptime", linux_x64);
......@@ -333,5 +374,69 @@ pub fn addCases(ctx: *TestContext) !void {
333374 ,
334375 "",
335376 );
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 );
336441 }
337442}
test/translate_c.zig+20
......@@ -3,6 +3,16 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub 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
616 cases.add("initializer list macro",
717 \\typedef struct Color {
818 \\ unsigned char r;
......@@ -2715,6 +2725,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27152725 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
27162726 });
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
27182738 cases.add("macro conditional operator",
27192739 \\#define FOO a ? b : c
27202740 , &[_][]const u8{