| author | |
| committer | |
| log | 2bae94280058f23ba44dc3857e2b551f5894e1cb |
| tree | 39483fa7a609f36b58dc19524e0b4467ab473974 |
| parent | b23a87953a7a4030af3d9acf8deacb85162dd275 |
15 files changed, 407 insertions(+), 377 deletions(-)
build.zig+1-2| ... | ... | @@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void { |
| 44 | 44 | try findAndReadConfigH(b); |
| 45 | 45 | |
| 46 | 46 | var test_stage2 = b.addTest("src-self-hosted/test.zig"); |
| 47 | test_stage2.setBuildMode(builtin.Mode.Debug); | |
| 47 | test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness | |
| 48 | 48 | test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig"); |
| 49 | 49 | |
| 50 | 50 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| ... | ... | @@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void { |
| 68 | 68 | var ctx = parseConfigH(b, config_h_text); |
| 69 | 69 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); |
| 70 | 70 | |
| 71 | try configureStage2(b, test_stage2, ctx); | |
| 72 | 71 | try configureStage2(b, exe, ctx); |
| 73 | 72 | |
| 74 | 73 | b.default_step.dependOn(&exe.step); |
lib/std/child_process.zig+11-1| ... | ... | @@ -46,6 +46,12 @@ pub const ChildProcess = struct { |
| 46 | 46 | |
| 47 | 47 | /// Set to change the current working directory when spawning the child process. |
| 48 | 48 | cwd: ?[]const u8, |
| 49 | /// Set to change the current working directory when spawning the child process. | |
| 50 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 | |
| 51 | /// Once that is done, `cwd` will be deprecated in favor of this field. | |
| 52 | /// The directory handle must be opened with the ability to be passed | |
| 53 | /// to a child process (no `O_CLOEXEC` flag on POSIX). | |
| 54 | cwd_dir: ?fs.Dir = null, | |
| 49 | 55 | |
| 50 | 56 | err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, |
| 51 | 57 | |
| ... | ... | @@ -183,6 +189,7 @@ pub const ChildProcess = struct { |
| 183 | 189 | allocator: *mem.Allocator, |
| 184 | 190 | argv: []const []const u8, |
| 185 | 191 | cwd: ?[]const u8 = null, |
| 192 | cwd_dir: ?fs.Dir = null, | |
| 186 | 193 | env_map: ?*const BufMap = null, |
| 187 | 194 | max_output_bytes: usize = 50 * 1024, |
| 188 | 195 | expand_arg0: Arg0Expand = .no_expand, |
| ... | ... | @@ -194,6 +201,7 @@ pub const ChildProcess = struct { |
| 194 | 201 | child.stdout_behavior = .Pipe; |
| 195 | 202 | child.stderr_behavior = .Pipe; |
| 196 | 203 | child.cwd = args.cwd; |
| 204 | child.cwd_dir = args.cwd_dir; | |
| 197 | 205 | child.env_map = args.env_map; |
| 198 | 206 | child.expand_arg0 = args.expand_arg0; |
| 199 | 207 | |
| ... | ... | @@ -414,7 +422,9 @@ pub const ChildProcess = struct { |
| 414 | 422 | os.close(stderr_pipe[1]); |
| 415 | 423 | } |
| 416 | 424 | |
| 417 | if (self.cwd) |cwd| { | |
| 425 | if (self.cwd_dir) |cwd| { | |
| 426 | os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 427 | } else if (self.cwd) |cwd| { | |
| 418 | 428 | os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err); |
| 419 | 429 | } |
| 420 | 430 |
lib/std/fs.zig+22-4| ... | ... | @@ -606,7 +606,8 @@ pub const Dir = struct { |
| 606 | 606 | } else 0; |
| 607 | 607 | |
| 608 | 608 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 609 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) | |
| 609 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 610 | const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read) | |
| 610 | 611 | @as(u32, os.O_RDWR) |
| 611 | 612 | else if (flags.write) |
| 612 | 613 | @as(u32, os.O_WRONLY) |
| ... | ... | @@ -689,7 +690,8 @@ pub const Dir = struct { |
| 689 | 690 | } else 0; |
| 690 | 691 | |
| 691 | 692 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 692 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | | |
| 693 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 694 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC | | |
| 693 | 695 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | |
| 694 | 696 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | |
| 695 | 697 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); |
| ... | ... | @@ -787,6 +789,15 @@ pub const Dir = struct { |
| 787 | 789 | } |
| 788 | 790 | } |
| 789 | 791 | |
| 792 | /// This function performs `makePath`, followed by `openDir`. | |
| 793 | /// If supported by the OS, this operation is atomic. It is not atomic on | |
| 794 | /// all operating systems. | |
| 795 | pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir { | |
| 796 | // TODO improve this implementation on Windows; we can avoid 1 call to NtClose | |
| 797 | try self.makePath(sub_path); | |
| 798 | return self.openDir(sub_path, open_dir_options); | |
| 799 | } | |
| 800 | ||
| 790 | 801 | /// Changes the current working directory to the open directory handle. |
| 791 | 802 | /// This modifies global state and can have surprising effects in multi- |
| 792 | 803 | /// threaded applications. Most applications and especially libraries should |
| ... | ... | @@ -807,6 +818,11 @@ pub const Dir = struct { |
| 807 | 818 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 808 | 819 | /// of the result. It means the `iterate` function can be called. |
| 809 | 820 | iterate: bool = false, |
| 821 | ||
| 822 | /// `true` means the opened directory can be passed to a child process. | |
| 823 | /// `false` means the directory handle is considered to be closed when a child | |
| 824 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 825 | share_with_child_process: bool = false, | |
| 810 | 826 | }; |
| 811 | 827 | |
| 812 | 828 | /// Opens a directory at the given path. The directory is a system resource that remains |
| ... | ... | @@ -832,9 +848,11 @@ pub const Dir = struct { |
| 832 | 848 | return self.openDirW(&sub_path_w, args); |
| 833 | 849 | } else if (!args.iterate) { |
| 834 | 850 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; |
| 835 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); | |
| 851 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 852 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH); | |
| 836 | 853 | } else { |
| 837 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); | |
| 854 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 855 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC); | |
| 838 | 856 | } |
| 839 | 857 | } |
| 840 | 858 |
lib/std/fs/file.zig+10| ... | ... | @@ -69,6 +69,11 @@ pub const File = struct { |
| 69 | 69 | /// It allows the use of `noasync` when calling functions related to opening |
| 70 | 70 | /// the file, reading, and writing. |
| 71 | 71 | always_blocking: bool = false, |
| 72 | ||
| 73 | /// `true` means the opened directory can be passed to a child process. | |
| 74 | /// `false` means the directory handle is considered to be closed when a child | |
| 75 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 76 | share_with_child_process: bool = false, | |
| 72 | 77 | }; |
| 73 | 78 | |
| 74 | 79 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| ... | ... | @@ -107,6 +112,11 @@ pub const File = struct { |
| 107 | 112 | /// For POSIX systems this is the file system mode the file will |
| 108 | 113 | /// be created with. |
| 109 | 114 | mode: Mode = default_mode, |
| 115 | ||
| 116 | /// `true` means the opened directory can be passed to a child process. | |
| 117 | /// `false` means the directory handle is considered to be closed when a child | |
| 118 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 119 | share_with_child_process: bool = false, | |
| 110 | 120 | }; |
| 111 | 121 | |
| 112 | 122 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
lib/std/testing.zig+38| ... | ... | @@ -193,6 +193,44 @@ pub fn expect(ok: bool) void { |
| 193 | 193 | if (!ok) @panic("test failure"); |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | pub const TmpDir = struct { | |
| 197 | dir: std.fs.Dir, | |
| 198 | parent_dir: std.fs.Dir, | |
| 199 | sub_path: [sub_path_len]u8, | |
| 200 | ||
| 201 | const random_bytes_count = 12; | |
| 202 | const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count); | |
| 203 | ||
| 204 | pub fn cleanup(self: *TmpDir) void { | |
| 205 | self.dir.close(); | |
| 206 | self.parent_dir.deleteTree(&self.sub_path) catch {}; | |
| 207 | self.parent_dir.close(); | |
| 208 | self.* = undefined; | |
| 209 | } | |
| 210 | }; | |
| 211 | ||
| 212 | pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { | |
| 213 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | |
| 214 | std.crypto.randomBytes(&random_bytes) catch | |
| 215 | @panic("unable to make tmp dir for testing: unable to get random bytes"); | |
| 216 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | |
| 217 | std.fs.base64_encoder.encode(&sub_path, &random_bytes); | |
| 218 | ||
| 219 | var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch | |
| 220 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir"); | |
| 221 | defer cache_dir.close(); | |
| 222 | var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch | |
| 223 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir"); | |
| 224 | var dir = parent_dir.makeOpenPath(&sub_path, opts) catch | |
| 225 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); | |
| 226 | ||
| 227 | return .{ | |
| 228 | .dir = dir, | |
| 229 | .parent_dir = parent_dir, | |
| 230 | .sub_path = sub_path, | |
| 231 | }; | |
| 232 | } | |
| 233 | ||
| 196 | 234 | test "expectEqual nested array" { |
| 197 | 235 | const a = [2][2]f32{ |
| 198 | 236 | [_]f32{ 1.0, 0.0 }, |
lib/std/zig.zig+17| ... | ... | @@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig"); |
| 9 | 9 | pub const system = @import("zig/system.zig"); |
| 10 | 10 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
| 11 | 11 | |
| 12 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | |
| 13 | var line: usize = 0; | |
| 14 | var column: usize = 0; | |
| 15 | for (source[0..byte_offset]) |byte| { | |
| 16 | switch (byte) { | |
| 17 | '\n' => { | |
| 18 | line += 1; | |
| 19 | column = 0; | |
| 20 | }, | |
| 21 | else => { | |
| 22 | column += 1; | |
| 23 | }, | |
| 24 | } | |
| 25 | } | |
| 26 | return .{ .line = line, .column = column }; | |
| 27 | } | |
| 28 | ||
| 12 | 29 | test "" { |
| 13 | 30 | @import("std").meta.refAllDecls(@This()); |
| 14 | 31 | } |
lib/std/zig/system.zig+6-1| ... | ... | @@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct { |
| 415 | 415 | // over our own shared objects and find a dynamic linker. |
| 416 | 416 | self_exe: { |
| 417 | 417 | const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); |
| 418 | defer allocator.free(lib_paths); | |
| 418 | defer { | |
| 419 | for (lib_paths) |lib_path| { | |
| 420 | allocator.free(lib_path); | |
| 421 | } | |
| 422 | allocator.free(lib_paths); | |
| 423 | } | |
| 419 | 424 | |
| 420 | 425 | var found_ld_info: LdInfo = undefined; |
| 421 | 426 | var found_ld_path: [:0]const u8 = undefined; |
src-self-hosted/ir.zig+11-23| ... | ... | @@ -4,10 +4,11 @@ const Allocator = std.mem.Allocator; |
| 4 | 4 | const Value = @import("value.zig").Value; |
| 5 | 5 | const Type = @import("type.zig").Type; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | const text = @import("ir/text.zig"); | |
| 8 | 7 | const BigInt = std.math.big.Int; |
| 9 | 8 | const Target = std.Target; |
| 10 | 9 | |
| 10 | pub const text = @import("ir/text.zig"); | |
| 11 | ||
| 11 | 12 | /// These are in-memory, analyzed instructions. See `text.Inst` for the representation |
| 12 | 13 | /// of instructions that correspond to the ZIR text format. |
| 13 | 14 | /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, |
| ... | ... | @@ -124,6 +125,10 @@ pub const Module = struct { |
| 124 | 125 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 125 | 126 | allocator.free(self.exports); |
| 126 | 127 | allocator.free(self.errors); |
| 128 | for (self.fns) |f| { | |
| 129 | allocator.free(f.body); | |
| 130 | } | |
| 131 | allocator.free(self.fns); | |
| 127 | 132 | self.arena.deinit(); |
| 128 | 133 | self.* = undefined; |
| 129 | 134 | } |
| ... | ... | @@ -795,7 +800,7 @@ pub fn main() anyerror!void { |
| 795 | 800 | |
| 796 | 801 | if (zir_module.errors.len != 0) { |
| 797 | 802 | for (zir_module.errors) |err_msg| { |
| 798 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 803 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 799 | 804 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 800 | 805 | } |
| 801 | 806 | if (debug_error_trace) return error.ParseFailure; |
| ... | ... | @@ -809,10 +814,10 @@ pub fn main() anyerror!void { |
| 809 | 814 | |
| 810 | 815 | if (analyzed_module.errors.len != 0) { |
| 811 | 816 | for (analyzed_module.errors) |err_msg| { |
| 812 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 817 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 813 | 818 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 814 | 819 | } |
| 815 | if (debug_error_trace) return error.ParseFailure; | |
| 820 | if (debug_error_trace) return error.AnalysisFail; | |
| 816 | 821 | std.process.exit(1); |
| 817 | 822 | } |
| 818 | 823 | |
| ... | ... | @@ -831,30 +836,13 @@ pub fn main() anyerror!void { |
| 831 | 836 | defer result.deinit(allocator); |
| 832 | 837 | if (result.errors.len != 0) { |
| 833 | 838 | for (result.errors) |err_msg| { |
| 834 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 839 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 835 | 840 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 836 | 841 | } |
| 837 | if (debug_error_trace) return error.ParseFailure; | |
| 842 | if (debug_error_trace) return error.LinkFailure; | |
| 838 | 843 | std.process.exit(1); |
| 839 | 844 | } |
| 840 | 845 | } |
| 841 | 846 | |
| 842 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | |
| 843 | var line: usize = 0; | |
| 844 | var column: usize = 0; | |
| 845 | for (source[0..byte_offset]) |byte| { | |
| 846 | switch (byte) { | |
| 847 | '\n' => { | |
| 848 | line += 1; | |
| 849 | column = 0; | |
| 850 | }, | |
| 851 | else => { | |
| 852 | column += 1; | |
| 853 | }, | |
| 854 | } | |
| 855 | } | |
| 856 | return .{ .line = line, .column = column }; | |
| 857 | } | |
| 858 | ||
| 859 | 847 | // Performance optimization ideas: |
| 860 | 848 | // * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions |
src-self-hosted/ir/text.zig+25-23| ... | ... | @@ -532,9 +532,10 @@ const Parser = struct { |
| 532 | 532 | else => |byte| return self.failByte(byte), |
| 533 | 533 | }; |
| 534 | 534 | |
| 535 | return Inst.Fn.Body{ | |
| 536 | .instructions = body_context.instructions.toOwnedSlice(), | |
| 537 | }; | |
| 535 | // Move the instructions to the arena | |
| 536 | const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); | |
| 537 | mem.copy(*Inst, instrs, body_context.instructions.items); | |
| 538 | return Inst.Fn.Body{ .instructions = instrs }; | |
| 538 | 539 | } |
| 539 | 540 | |
| 540 | 541 | fn parseStringLiteral(self: *Parser) ![]u8 { |
| ... | ... | @@ -588,26 +589,27 @@ const Parser = struct { |
| 588 | 589 | |
| 589 | 590 | fn parseRoot(self: *Parser) !void { |
| 590 | 591 | // The IR format is designed so that it can be tokenized and parsed at the same time. |
| 591 | while (true) : (self.i += 1) switch (self.source[self.i]) { | |
| 592 | ';' => _ = try skipToAndOver(self, '\n'), | |
| 593 | '@' => { | |
| 594 | self.i += 1; | |
| 595 | const ident = try skipToAndOver(self, ' '); | |
| 596 | skipSpace(self); | |
| 597 | try requireEatBytes(self, "="); | |
| 598 | skipSpace(self); | |
| 599 | const inst = try parseInstruction(self, null); | |
| 600 | const ident_index = self.decls.items.len; | |
| 601 | if (try self.global_name_map.put(ident, ident_index)) |_| { | |
| 602 | return self.fail("redefinition of identifier '{}'", .{ident}); | |
| 603 | } | |
| 604 | try self.decls.append(inst); | |
| 605 | continue; | |
| 606 | }, | |
| 607 | ' ', '\n' => continue, | |
| 608 | 0 => break, | |
| 609 | else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), | |
| 610 | }; | |
| 592 | while (true) { | |
| 593 | switch (self.source[self.i]) { | |
| 594 | ';' => _ = try skipToAndOver(self, '\n'), | |
| 595 | '@' => { | |
| 596 | self.i += 1; | |
| 597 | const ident = try skipToAndOver(self, ' '); | |
| 598 | skipSpace(self); | |
| 599 | try requireEatBytes(self, "="); | |
| 600 | skipSpace(self); | |
| 601 | const inst = try parseInstruction(self, null); | |
| 602 | const ident_index = self.decls.items.len; | |
| 603 | if (try self.global_name_map.put(ident, ident_index)) |_| { | |
| 604 | return self.fail("redefinition of identifier '{}'", .{ident}); | |
| 605 | } | |
| 606 | try self.decls.append(inst); | |
| 607 | }, | |
| 608 | ' ', '\n' => self.i += 1, | |
| 609 | 0 => break, | |
| 610 | else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), | |
| 611 | } | |
| 612 | } | |
| 611 | 613 | } |
| 612 | 614 | |
| 613 | 615 | fn eatByte(self: *Parser, byte: u8) bool { |
src-self-hosted/test.zig+131-200| ... | ... | @@ -1,237 +1,168 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | |
| 3 | const Target = std.Target; | |
| 4 | const Compilation = @import("compilation.zig").Compilation; | |
| 5 | const introspect = @import("introspect.zig"); | |
| 6 | const testing = std.testing; | |
| 7 | const errmsg = @import("errmsg.zig"); | |
| 8 | const ZigCompiler = @import("compilation.zig").ZigCompiler; | |
| 2 | const link = @import("link.zig"); | |
| 3 | const ir = @import("ir.zig"); | |
| 4 | const Allocator = std.mem.Allocator; | |
| 9 | 5 | |
| 10 | var ctx: TestContext = undefined; | |
| 6 | var global_ctx: TestContext = undefined; | |
| 11 | 7 | |
| 12 | test "stage2" { | |
| 13 | // TODO provide a way to run tests in evented I/O mode | |
| 14 | if (!std.io.is_async) return error.SkipZigTest; | |
| 8 | test "self-hosted" { | |
| 9 | try global_ctx.init(); | |
| 10 | defer global_ctx.deinit(); | |
| 15 | 11 | |
| 16 | // TODO https://github.com/ziglang/zig/issues/1364 | |
| 17 | // TODO https://github.com/ziglang/zig/issues/3117 | |
| 18 | if (true) return error.SkipZigTest; | |
| 12 | try @import("stage2_tests").addCases(&global_ctx); | |
| 19 | 13 | |
| 20 | try ctx.init(); | |
| 21 | defer ctx.deinit(); | |
| 22 | ||
| 23 | try @import("stage2_tests").addCases(&ctx); | |
| 24 | ||
| 25 | try ctx.run(); | |
| 14 | try global_ctx.run(); | |
| 26 | 15 | } |
| 27 | 16 | |
| 28 | const file1 = "1.zig"; | |
| 29 | // TODO https://github.com/ziglang/zig/issues/3783 | |
| 30 | const allocator = std.heap.page_allocator; | |
| 31 | ||
| 32 | 17 | pub const TestContext = struct { |
| 33 | zig_compiler: ZigCompiler, | |
| 34 | zig_lib_dir: []u8, | |
| 35 | file_index: std.atomic.Int(usize), | |
| 36 | group: std.event.Group(anyerror!void), | |
| 37 | any_err: anyerror!void, | |
| 38 | ||
| 39 | const tmp_dir_name = "stage2_test_tmp"; | |
| 18 | zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase), | |
| 19 | ||
| 20 | pub const ZIRCompareOutputCase = struct { | |
| 21 | name: []const u8, | |
| 22 | src: [:0]const u8, | |
| 23 | expected_stdout: []const u8, | |
| 24 | }; | |
| 25 | ||
| 26 | pub fn addZIRCompareOutput( | |
| 27 | ctx: *TestContext, | |
| 28 | name: []const u8, | |
| 29 | src: [:0]const u8, | |
| 30 | expected_stdout: []const u8, | |
| 31 | ) void { | |
| 32 | ctx.zir_cmp_output_cases.append(.{ | |
| 33 | .name = name, | |
| 34 | .src = src, | |
| 35 | .expected_stdout = expected_stdout, | |
| 36 | }) catch unreachable; | |
| 37 | } | |
| 40 | 38 | |
| 41 | 39 | fn init(self: *TestContext) !void { |
| 42 | self.* = TestContext{ | |
| 43 | .any_err = {}, | |
| 44 | .zig_compiler = undefined, | |
| 45 | .zig_lib_dir = undefined, | |
| 46 | .group = undefined, | |
| 47 | .file_index = std.atomic.Int(usize).init(0), | |
| 40 | self.* = .{ | |
| 41 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator), | |
| 48 | 42 | }; |
| 49 | ||
| 50 | self.zig_compiler = try ZigCompiler.init(allocator); | |
| 51 | errdefer self.zig_compiler.deinit(); | |
| 52 | ||
| 53 | self.group = std.event.Group(anyerror!void).init(allocator); | |
| 54 | errdefer self.group.wait() catch {}; | |
| 55 | ||
| 56 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); | |
| 57 | errdefer allocator.free(self.zig_lib_dir); | |
| 58 | ||
| 59 | try std.fs.cwd().makePath(tmp_dir_name); | |
| 60 | errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {}; | |
| 61 | 43 | } |
| 62 | 44 | |
| 63 | 45 | fn deinit(self: *TestContext) void { |
| 64 | std.fs.cwd().deleteTree(tmp_dir_name) catch {}; | |
| 65 | allocator.free(self.zig_lib_dir); | |
| 66 | self.zig_compiler.deinit(); | |
| 46 | self.zir_cmp_output_cases.deinit(); | |
| 47 | self.* = undefined; | |
| 67 | 48 | } |
| 68 | 49 | |
| 69 | 50 | fn run(self: *TestContext) !void { |
| 70 | std.event.Loop.startCpuBoundOperation(); | |
| 71 | self.any_err = self.group.wait(); | |
| 72 | return self.any_err; | |
| 51 | var progress = std.Progress{}; | |
| 52 | const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len); | |
| 53 | defer root_node.end(); | |
| 54 | ||
| 55 | const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{}); | |
| 56 | ||
| 57 | for (self.zir_cmp_output_cases.items) |case| { | |
| 58 | std.testing.base_allocator_instance.reset(); | |
| 59 | try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target); | |
| 60 | try std.testing.allocator_instance.validate(); | |
| 61 | } | |
| 73 | 62 | } |
| 74 | 63 | |
| 75 | fn testCompileError( | |
| 64 | fn runOneZIRCmpOutputCase( | |
| 76 | 65 | self: *TestContext, |
| 77 | source: []const u8, | |
| 78 | path: []const u8, | |
| 79 | line: usize, | |
| 80 | column: usize, | |
| 81 | msg: []const u8, | |
| 66 | allocator: *Allocator, | |
| 67 | root_node: *std.Progress.Node, | |
| 68 | case: ZIRCompareOutputCase, | |
| 69 | target: std.Target, | |
| 82 | 70 | ) !void { |
| 83 | var file_index_buf: [20]u8 = undefined; | |
| 84 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); | |
| 85 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); | |
| 71 | var tmp = std.testing.tmpDir(.{ .share_with_child_process = true }); | |
| 72 | defer tmp.cleanup(); | |
| 86 | 73 | |
| 87 | if (std.fs.path.dirname(file1_path)) |dirname| { | |
| 88 | try std.fs.cwd().makePath(dirname); | |
| 89 | } | |
| 74 | var prg_node = root_node.start(case.name, 4); | |
| 75 | prg_node.activate(); | |
| 76 | defer prg_node.end(); | |
| 90 | 77 | |
| 91 | try std.fs.cwd().writeFile(file1_path, source); | |
| 78 | var zir_module = x: { | |
| 79 | var parse_node = prg_node.start("parse", null); | |
| 80 | parse_node.activate(); | |
| 81 | defer parse_node.end(); | |
| 92 | 82 | |
| 93 | var comp = try Compilation.create( | |
| 94 | &self.zig_compiler, | |
| 95 | "test", | |
| 96 | file1_path, | |
| 97 | .Native, | |
| 98 | .Obj, | |
| 99 | .Debug, | |
| 100 | true, // is_static | |
| 101 | self.zig_lib_dir, | |
| 102 | ); | |
| 103 | errdefer comp.destroy(); | |
| 104 | ||
| 105 | comp.start(); | |
| 83 | break :x try ir.text.parse(allocator, case.src); | |
| 84 | }; | |
| 85 | defer zir_module.deinit(allocator); | |
| 86 | if (zir_module.errors.len != 0) { | |
| 87 | debugPrintErrors(case.src, zir_module.errors); | |
| 88 | return error.ParseFailure; | |
| 89 | } | |
| 106 | 90 | |
| 107 | try self.group.call(getModuleEvent, comp, source, path, line, column, msg); | |
| 108 | } | |
| 91 | var analyzed_module = x: { | |
| 92 | var analyze_node = prg_node.start("analyze", null); | |
| 93 | analyze_node.activate(); | |
| 94 | defer analyze_node.end(); | |
| 109 | 95 | |
| 110 | fn testCompareOutputLibC( | |
| 111 | self: *TestContext, | |
| 112 | source: []const u8, | |
| 113 | expected_output: []const u8, | |
| 114 | ) !void { | |
| 115 | var file_index_buf: [20]u8 = undefined; | |
| 116 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); | |
| 117 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); | |
| 96 | break :x try ir.analyze(allocator, zir_module, target); | |
| 97 | }; | |
| 98 | defer analyzed_module.deinit(allocator); | |
| 99 | if (analyzed_module.errors.len != 0) { | |
| 100 | debugPrintErrors(case.src, analyzed_module.errors); | |
| 101 | return error.ParseFailure; | |
| 102 | } | |
| 118 | 103 | |
| 119 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); | |
| 120 | if (std.fs.path.dirname(file1_path)) |dirname| { | |
| 121 | try std.fs.cwd().makePath(dirname); | |
| 104 | var link_result = x: { | |
| 105 | var link_node = prg_node.start("link", null); | |
| 106 | link_node.activate(); | |
| 107 | defer link_node.end(); | |
| 108 | ||
| 109 | break :x try link.updateExecutableFilePath( | |
| 110 | allocator, | |
| 111 | analyzed_module, | |
| 112 | tmp.dir, | |
| 113 | "a.out", | |
| 114 | ); | |
| 115 | }; | |
| 116 | defer link_result.deinit(allocator); | |
| 117 | if (link_result.errors.len != 0) { | |
| 118 | debugPrintErrors(case.src, link_result.errors); | |
| 119 | return error.LinkFailure; | |
| 122 | 120 | } |
| 123 | 121 | |
| 124 | try std.fs.cwd().writeFile(file1_path, source); | |
| 125 | ||
| 126 | var comp = try Compilation.create( | |
| 127 | &self.zig_compiler, | |
| 128 | "test", | |
| 129 | file1_path, | |
| 130 | .Native, | |
| 131 | .Exe, | |
| 132 | .Debug, | |
| 133 | false, | |
| 134 | self.zig_lib_dir, | |
| 135 | ); | |
| 136 | errdefer comp.destroy(); | |
| 137 | ||
| 138 | _ = try comp.addLinkLib("c", true); | |
| 139 | comp.link_out_file = output_file; | |
| 140 | comp.start(); | |
| 141 | ||
| 142 | try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); | |
| 143 | } | |
| 122 | var exec_result = x: { | |
| 123 | var exec_node = prg_node.start("execute", null); | |
| 124 | exec_node.activate(); | |
| 125 | defer exec_node.end(); | |
| 144 | 126 | |
| 145 | async fn getModuleEventSuccess( | |
| 146 | comp: *Compilation, | |
| 147 | exe_file: []const u8, | |
| 148 | expected_output: []const u8, | |
| 149 | ) anyerror!void { | |
| 150 | defer comp.destroy(); | |
| 151 | const build_event = comp.events.get(); | |
| 152 | ||
| 153 | switch (build_event) { | |
| 154 | .Ok => { | |
| 155 | const argv = [_][]const u8{exe_file}; | |
| 156 | // TODO use event loop | |
| 157 | const child = try std.ChildProcess.exec(.{ | |
| 158 | .allocator = allocator, | |
| 159 | .argv = argv, | |
| 160 | .max_output_bytes = 1024 * 1024, | |
| 161 | }); | |
| 162 | switch (child.term) { | |
| 163 | .Exited => |code| { | |
| 164 | if (code != 0) { | |
| 165 | return error.BadReturnCode; | |
| 166 | } | |
| 167 | }, | |
| 168 | else => { | |
| 169 | return error.Crashed; | |
| 170 | }, | |
| 171 | } | |
| 172 | if (!mem.eql(u8, child.stdout, expected_output)) { | |
| 173 | return error.OutputMismatch; | |
| 174 | } | |
| 175 | }, | |
| 176 | .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err, | |
| 177 | .Fail => |msgs| { | |
| 178 | const stderr = std.io.getStdErr(); | |
| 179 | try stderr.write("build incorrectly failed:\n"); | |
| 180 | for (msgs) |msg| { | |
| 181 | defer msg.destroy(); | |
| 182 | try msg.printToFile(stderr, .Auto); | |
| 127 | break :x try std.ChildProcess.exec(.{ | |
| 128 | .allocator = allocator, | |
| 129 | .argv = &[_][]const u8{"./a.out"}, | |
| 130 | .cwd_dir = tmp.dir, | |
| 131 | }); | |
| 132 | }; | |
| 133 | defer allocator.free(exec_result.stdout); | |
| 134 | defer allocator.free(exec_result.stderr); | |
| 135 | switch (exec_result.term) { | |
| 136 | .Exited => |code| { | |
| 137 | if (code != 0) { | |
| 138 | std.debug.warn("elf file exited with code {}\n", .{code}); | |
| 139 | return error.BinaryBadExitCode; | |
| 183 | 140 | } |
| 184 | 141 | }, |
| 142 | else => return error.BinaryCrashed, | |
| 185 | 143 | } |
| 144 | std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout); | |
| 186 | 145 | } |
| 146 | }; | |
| 187 | 147 | |
| 188 | async fn getModuleEvent( | |
| 189 | comp: *Compilation, | |
| 190 | source: []const u8, | |
| 191 | path: []const u8, | |
| 192 | line: usize, | |
| 193 | column: usize, | |
| 194 | text: []const u8, | |
| 195 | ) anyerror!void { | |
| 196 | defer comp.destroy(); | |
| 197 | const build_event = comp.events.get(); | |
| 198 | ||
| 199 | switch (build_event) { | |
| 200 | .Ok => { | |
| 201 | @panic("build incorrectly succeeded"); | |
| 202 | }, | |
| 203 | .Error => |err| { | |
| 204 | @panic("build incorrectly failed"); | |
| 205 | }, | |
| 206 | .Fail => |msgs| { | |
| 207 | testing.expect(msgs.len != 0); | |
| 208 | for (msgs) |msg| { | |
| 209 | if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) { | |
| 210 | const span = msg.getSpan(); | |
| 211 | const first_token = msg.getTree().tokens.at(span.first); | |
| 212 | const last_token = msg.getTree().tokens.at(span.first); | |
| 213 | const start_loc = msg.getTree().tokenLocationPtr(0, first_token); | |
| 214 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { | |
| 215 | return; | |
| 216 | } | |
| 217 | } | |
| 218 | } | |
| 219 | std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{ | |
| 220 | source, | |
| 221 | path, | |
| 222 | line, | |
| 223 | column, | |
| 224 | text, | |
| 225 | }); | |
| 226 | std.debug.warn("\n====found:========\n", .{}); | |
| 227 | const stderr = std.io.getStdErr(); | |
| 228 | for (msgs) |msg| { | |
| 229 | defer msg.destroy(); | |
| 230 | try msg.printToFile(stderr, errmsg.Color.Auto); | |
| 231 | } | |
| 232 | std.debug.warn("============\n", .{}); | |
| 233 | return error.TestFailed; | |
| 234 | }, | |
| 148 | fn debugPrintErrors(src: []const u8, errors: var) void { | |
| 149 | std.debug.warn("\n", .{}); | |
| 150 | var nl = true; | |
| 151 | var line: usize = 1; | |
| 152 | for (src) |byte| { | |
| 153 | if (nl) { | |
| 154 | std.debug.warn("{: >3}| ", .{line}); | |
| 155 | nl = false; | |
| 235 | 156 | } |
| 157 | if (byte == '\n') { | |
| 158 | nl = true; | |
| 159 | line += 1; | |
| 160 | } | |
| 161 | std.debug.warn("{c}", .{byte}); | |
| 236 | 162 | } |
| 237 | }; | |
| 163 | std.debug.warn("\n", .{}); | |
| 164 | for (errors) |err_msg| { | |
| 165 | const loc = std.zig.findLineColumn(src, err_msg.byte_offset); | |
| 166 | std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg }); | |
| 167 | } | |
| 168 | } |
test/stage2/compare_output.zig+22-19| ... | ... | @@ -2,24 +2,27 @@ const std = @import("std"); |
| 2 | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // hello world | |
| 6 | try ctx.testCompareOutputLibC( | |
| 7 | \\extern fn puts([*]const u8) void; | |
| 8 | \\pub export fn main() c_int { | |
| 9 | \\ puts("Hello, world!"); | |
| 10 | \\ return 0; | |
| 11 | \\} | |
| 12 | , "Hello, world!" ++ std.cstr.line_sep); | |
| 5 | // TODO: re-enable these tests. | |
| 6 | // https://github.com/ziglang/zig/issues/1364 | |
| 13 | 7 | |
| 14 | // function calling another function | |
| 15 | try ctx.testCompareOutputLibC( | |
| 16 | \\extern fn puts(s: [*]const u8) void; | |
| 17 | \\pub export fn main() c_int { | |
| 18 | \\ return foo("OK"); | |
| 19 | \\} | |
| 20 | \\fn foo(s: [*]const u8) c_int { | |
| 21 | \\ puts(s); | |
| 22 | \\ return 0; | |
| 23 | \\} | |
| 24 | , "OK" ++ std.cstr.line_sep); | |
| 8 | //// hello world | |
| 9 | //try ctx.testCompareOutputLibC( | |
| 10 | // \\extern fn puts([*]const u8) void; | |
| 11 | // \\pub export fn main() c_int { | |
| 12 | // \\ puts("Hello, world!"); | |
| 13 | // \\ return 0; | |
| 14 | // \\} | |
| 15 | //, "Hello, world!" ++ std.cstr.line_sep); | |
| 16 | ||
| 17 | //// function calling another function | |
| 18 | //try ctx.testCompareOutputLibC( | |
| 19 | // \\extern fn puts(s: [*]const u8) void; | |
| 20 | // \\pub export fn main() c_int { | |
| 21 | // \\ return foo("OK"); | |
| 22 | // \\} | |
| 23 | // \\fn foo(s: [*]const u8) c_int { | |
| 24 | // \\ puts(s); | |
| 25 | // \\ return 0; | |
| 26 | // \\} | |
| 27 | //, "OK" ++ std.cstr.line_sep); | |
| 25 | 28 | } |
test/stage2/compile_errors.zig+53-50| ... | ... | @@ -1,54 +1,57 @@ |
| 1 | 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(ctx: *TestContext) !void { |
| 4 | try ctx.testCompileError( | |
| 5 | \\export fn entry() void {} | |
| 6 | \\export fn entry() void {} | |
| 7 | , "1.zig", 2, 8, "exported symbol collision: 'entry'"); | |
| 8 | ||
| 9 | try ctx.testCompileError( | |
| 10 | \\fn() void {} | |
| 11 | , "1.zig", 1, 1, "missing function name"); | |
| 12 | ||
| 13 | try ctx.testCompileError( | |
| 14 | \\comptime { | |
| 15 | \\ return; | |
| 16 | \\} | |
| 17 | , "1.zig", 2, 5, "return expression outside function definition"); | |
| 18 | ||
| 19 | try ctx.testCompileError( | |
| 20 | \\export fn entry() void { | |
| 21 | \\ defer return; | |
| 22 | \\} | |
| 23 | , "1.zig", 2, 11, "cannot return from defer expression"); | |
| 24 | ||
| 25 | try ctx.testCompileError( | |
| 26 | \\export fn entry() c_int { | |
| 27 | \\ return 36893488147419103232; | |
| 28 | \\} | |
| 29 | , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); | |
| 30 | ||
| 31 | try ctx.testCompileError( | |
| 32 | \\comptime { | |
| 33 | \\ var a: *align(4) align(4) i32 = 0; | |
| 34 | \\} | |
| 35 | , "1.zig", 2, 22, "Extra align qualifier"); | |
| 36 | ||
| 37 | try ctx.testCompileError( | |
| 38 | \\comptime { | |
| 39 | \\ var b: *const const i32 = 0; | |
| 40 | \\} | |
| 41 | , "1.zig", 2, 19, "Extra align qualifier"); | |
| 42 | ||
| 43 | try ctx.testCompileError( | |
| 44 | \\comptime { | |
| 45 | \\ var c: *volatile volatile i32 = 0; | |
| 46 | \\} | |
| 47 | , "1.zig", 2, 22, "Extra align qualifier"); | |
| 48 | ||
| 49 | try ctx.testCompileError( | |
| 50 | \\comptime { | |
| 51 | \\ var d: *allowzero allowzero i32 = 0; | |
| 52 | \\} | |
| 53 | , "1.zig", 2, 23, "Extra align qualifier"); | |
| 4 | // TODO: re-enable these tests. | |
| 5 | // https://github.com/ziglang/zig/issues/1364 | |
| 6 | ||
| 7 | //try ctx.testCompileError( | |
| 8 | // \\export fn entry() void {} | |
| 9 | // \\export fn entry() void {} | |
| 10 | //, "1.zig", 2, 8, "exported symbol collision: 'entry'"); | |
| 11 | ||
| 12 | //try ctx.testCompileError( | |
| 13 | // \\fn() void {} | |
| 14 | //, "1.zig", 1, 1, "missing function name"); | |
| 15 | ||
| 16 | //try ctx.testCompileError( | |
| 17 | // \\comptime { | |
| 18 | // \\ return; | |
| 19 | // \\} | |
| 20 | //, "1.zig", 2, 5, "return expression outside function definition"); | |
| 21 | ||
| 22 | //try ctx.testCompileError( | |
| 23 | // \\export fn entry() void { | |
| 24 | // \\ defer return; | |
| 25 | // \\} | |
| 26 | //, "1.zig", 2, 11, "cannot return from defer expression"); | |
| 27 | ||
| 28 | //try ctx.testCompileError( | |
| 29 | // \\export fn entry() c_int { | |
| 30 | // \\ return 36893488147419103232; | |
| 31 | // \\} | |
| 32 | //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); | |
| 33 | ||
| 34 | //try ctx.testCompileError( | |
| 35 | // \\comptime { | |
| 36 | // \\ var a: *align(4) align(4) i32 = 0; | |
| 37 | // \\} | |
| 38 | //, "1.zig", 2, 22, "Extra align qualifier"); | |
| 39 | ||
| 40 | //try ctx.testCompileError( | |
| 41 | // \\comptime { | |
| 42 | // \\ var b: *const const i32 = 0; | |
| 43 | // \\} | |
| 44 | //, "1.zig", 2, 19, "Extra align qualifier"); | |
| 45 | ||
| 46 | //try ctx.testCompileError( | |
| 47 | // \\comptime { | |
| 48 | // \\ var c: *volatile volatile i32 = 0; | |
| 49 | // \\} | |
| 50 | //, "1.zig", 2, 22, "Extra align qualifier"); | |
| 51 | ||
| 52 | //try ctx.testCompileError( | |
| 53 | // \\comptime { | |
| 54 | // \\ var d: *allowzero allowzero i32 = 0; | |
| 55 | // \\} | |
| 56 | //, "1.zig", 2, 23, "Extra align qualifier"); | |
| 54 | 57 | } |
test/stage2/ir.zig deleted-54| ... | ... | @@ -1,54 +0,0 @@ |
| 1 | test "hello world IR" { | |
| 2 | exeCmp( | |
| 3 | \\@0 = str("Hello, world!\n") | |
| 4 | \\@1 = primitive(void) | |
| 5 | \\@2 = primitive(usize) | |
| 6 | \\@3 = fntype([], @1, cc=Naked) | |
| 7 | \\@4 = int(0) | |
| 8 | \\@5 = int(1) | |
| 9 | \\@6 = int(231) | |
| 10 | \\@7 = str("len") | |
| 11 | \\ | |
| 12 | \\@8 = fn(@3, { | |
| 13 | \\ %0 = as(@2, @5) ; SYS_write | |
| 14 | \\ %1 = as(@2, @5) ; STDOUT_FILENO | |
| 15 | \\ %2 = ptrtoint(@0) ; msg ptr | |
| 16 | \\ %3 = fieldptr(@0, @7) ; msg len ptr | |
| 17 | \\ %4 = deref(%3) ; msg len | |
| 18 | \\ %sysoutreg = str("={rax}") | |
| 19 | \\ %rax = str("{rax}") | |
| 20 | \\ %rdi = str("{rdi}") | |
| 21 | \\ %rsi = str("{rsi}") | |
| 22 | \\ %rdx = str("{rdx}") | |
| 23 | \\ %rcx = str("rcx") | |
| 24 | \\ %r11 = str("r11") | |
| 25 | \\ %memory = str("memory") | |
| 26 | \\ %syscall = str("syscall") | |
| 27 | \\ %5 = asm(%syscall, @2, | |
| 28 | \\ volatile=1, | |
| 29 | \\ output=%sysoutreg, | |
| 30 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 31 | \\ clobbers=[%rcx, %r11, %memory], | |
| 32 | \\ args=[%0, %1, %2, %4]) | |
| 33 | \\ | |
| 34 | \\ %6 = as(@2, @6) ;SYS_exit_group | |
| 35 | \\ %7 = as(@2, @4) ;exit code | |
| 36 | \\ %8 = asm(%syscall, @2, | |
| 37 | \\ volatile=1, | |
| 38 | \\ output=%sysoutreg, | |
| 39 | \\ inputs=[%rax, %rdi], | |
| 40 | \\ clobbers=[%rcx, %r11, %memory], | |
| 41 | \\ args=[%6, %7]) | |
| 42 | \\ | |
| 43 | \\ %9 = unreachable() | |
| 44 | \\}) | |
| 45 | \\ | |
| 46 | \\@9 = str("_start") | |
| 47 | \\@10 = export(@9, @8) | |
| 48 | , | |
| 49 | \\Hello, world! | |
| 50 | \\ | |
| 51 | ); | |
| 52 | } | |
| 53 | ||
| 54 | fn exeCmp(src: []const u8, expected_stdout: []const u8) void {} |
test/stage2/test.zig+1| ... | ... | @@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | pub fn addCases(ctx: *TestContext) !void { |
| 4 | 4 | try @import("compile_errors.zig").addCases(ctx); |
| 5 | 5 | try @import("compare_output.zig").addCases(ctx); |
| 6 | @import("zir.zig").addCases(ctx); | |
| 6 | 7 | } |
test/stage2/zir.zig created+59| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | |
| 2 | ||
| 3 | pub fn addCases(ctx: *TestContext) void { | |
| 4 | if (@import("std").Target.current.os.tag == .windows) { | |
| 5 | // TODO implement self-hosted PE (.exe file) linking | |
| 6 | return; | |
| 7 | } | |
| 8 | ||
| 9 | ctx.addZIRCompareOutput("hello world ZIR", | |
| 10 | \\@0 = str("Hello, world!\n") | |
| 11 | \\@1 = primitive(noreturn) | |
| 12 | \\@2 = primitive(usize) | |
| 13 | \\@3 = fntype([], @1, cc=Naked) | |
| 14 | \\@4 = int(0) | |
| 15 | \\@5 = int(1) | |
| 16 | \\@6 = int(231) | |
| 17 | \\@7 = str("len") | |
| 18 | \\ | |
| 19 | \\@8 = fn(@3, { | |
| 20 | \\ %0 = as(@2, @5) ; SYS_write | |
| 21 | \\ %1 = as(@2, @5) ; STDOUT_FILENO | |
| 22 | \\ %2 = ptrtoint(@0) ; msg ptr | |
| 23 | \\ %3 = fieldptr(@0, @7) ; msg len ptr | |
| 24 | \\ %4 = deref(%3) ; msg len | |
| 25 | \\ %sysoutreg = str("={rax}") | |
| 26 | \\ %rax = str("{rax}") | |
| 27 | \\ %rdi = str("{rdi}") | |
| 28 | \\ %rsi = str("{rsi}") | |
| 29 | \\ %rdx = str("{rdx}") | |
| 30 | \\ %rcx = str("rcx") | |
| 31 | \\ %r11 = str("r11") | |
| 32 | \\ %memory = str("memory") | |
| 33 | \\ %syscall = str("syscall") | |
| 34 | \\ %5 = asm(%syscall, @2, | |
| 35 | \\ volatile=1, | |
| 36 | \\ output=%sysoutreg, | |
| 37 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 38 | \\ clobbers=[%rcx, %r11, %memory], | |
| 39 | \\ args=[%0, %1, %2, %4]) | |
| 40 | \\ | |
| 41 | \\ %6 = as(@2, @6) ;SYS_exit_group | |
| 42 | \\ %7 = as(@2, @4) ;exit code | |
| 43 | \\ %8 = asm(%syscall, @2, | |
| 44 | \\ volatile=1, | |
| 45 | \\ output=%sysoutreg, | |
| 46 | \\ inputs=[%rax, %rdi], | |
| 47 | \\ clobbers=[%rcx, %r11, %memory], | |
| 48 | \\ args=[%6, %7]) | |
| 49 | \\ | |
| 50 | \\ %9 = unreachable() | |
| 51 | \\}) | |
| 52 | \\ | |
| 53 | \\@9 = str("_start") | |
| 54 | \\@10 = export(@9, @8) | |
| 55 | , | |
| 56 | \\Hello, world! | |
| 57 | \\ | |
| 58 | ); | |
| 59 | } |