self-hosted: add compare output test for new AST->ZIR code

This commit is contained in:
Andrew Kelley 2020-06-24 21:28:42 -04:00
parent 5aa3f56773
commit 20b4a2cf2c
5 changed files with 369 additions and 297 deletions

View File

@ -43,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
return .{ .line = line, .column = column };
}
/// Returns the standard file system basename of a binary generated by the Zig compiler.
pub fn binNameAlloc(
allocator: *std.mem.Allocator,
root_name: []const u8,
target: std.Target,
output_mode: std.builtin.OutputMode,
link_mode: ?std.builtin.LinkMode,
) error{OutOfMemory}![]u8 {
switch (output_mode) {
.Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),
.Lib => {
const suffix = switch (link_mode orelse .Static) {
.Static => target.staticLibSuffix(),
.Dynamic => target.dynamicLibSuffix(),
};
return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
},
.Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
}
}
test "" {
@import("std").meta.refAllDecls(@This());
}

View File

@ -50,8 +50,7 @@ pub fn log(
const scope_prefix = "(" ++ switch (scope) {
// Uncomment to hide logs
//.compiler,
.link,
=> return,
.link => return,
else => @tagName(scope),
} ++ "): ";
@ -431,21 +430,7 @@ fn buildOutputType(
std.debug.warn("-fno-emit-bin not supported yet", .{});
process.exit(1);
},
.yes_default_path => switch (output_mode) {
.Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
.Lib => blk: {
const suffix = switch (link_mode orelse .Static) {
.Static => target_info.target.staticLibSuffix(),
.Dynamic => target_info.target.dynamicLibSuffix(),
};
break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
target_info.target.libPrefix(),
root_name,
suffix,
});
},
.Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
},
.yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
.yes => |p| p,
};

View File

@ -21,32 +21,7 @@ const ErrorMsg = struct {
};
pub const TestContext = struct {
// TODO: remove these. They are deprecated.
zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
/// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
zir_cases: std.ArrayList(ZIRCase),
// TODO: remove
pub const ZIRCompareOutputCase = struct {
name: []const u8,
src_list: []const []const u8,
expected_stdout_list: []const []const u8,
};
pub const ZIRUpdateType = enum {
/// A transformation update transforms the input ZIR and tests against
/// the expected output
Transformation,
/// An error update attempts to compile bad code, and ensures that it
/// fails to compile, and for the expected reasons
Error,
/// An execution update compiles and runs the input ZIR, feeding in
/// provided input and ensuring that the outputs match what is expected
Execution,
/// A compilation update checks that the ZIR compiles without any issues
Compiles,
};
zir_cases: std.ArrayList(Case),
pub const ZIRUpdate = struct {
/// The input to the current update. We simulate an incremental update
@ -57,58 +32,55 @@ pub const TestContext = struct {
/// you can keep it mostly consistent, with small changes, testing the
/// effects of the incremental compilation.
src: [:0]const u8,
case: union(ZIRUpdateType) {
/// The expected output ZIR
case: union(enum) {
/// A transformation update transforms the input ZIR and tests against
/// the expected output ZIR.
Transformation: [:0]const u8,
/// An error update attempts to compile bad code, and ensures that it
/// fails to compile, and for the expected reasons.
/// A slice containing the expected errors *in sequential order*.
Error: []const ErrorMsg,
/// Input to feed to the program, and expected outputs.
///
/// If stdout, stderr, and exit_code are all null, addZIRCase will
/// discard the test. To test for successful compilation, use a
/// dedicated Compile update instead.
Execution: struct {
stdin: ?[]const u8,
stdout: ?[]const u8,
stderr: ?[]const u8,
exit_code: ?u8,
},
/// A Compiles test checks only that compilation of the given ZIR
/// succeeds. To test outputs, use an Execution test. It is good to
/// use a Compiles test before an Execution, as the overhead should
/// be low (due to incremental compilation) and TODO: provide a way
/// to check changed / new / etc decls in testing mode
/// (usingnamespace a debug info struct with a comptime flag?)
Compiles: void,
/// An execution update compiles and runs the input ZIR, feeding in
/// provided input and ensuring that the stdout match what is expected.
Execution: []const u8,
},
};
/// A ZIRCase consists of a set of *updates*. A update can transform ZIR,
/// A Case consists of a set of *updates*. A update can transform ZIR,
/// compile it, ensure that compilation fails, and more. The same Module is
/// used for each update, so each update's source is treated as a single file
/// being updated by the test harness and incrementally compiled.
pub const ZIRCase = struct {
pub const Case = struct {
name: []const u8,
/// The platform the ZIR targets. For non-native platforms, an emulator
/// such as QEMU is required for tests to complete.
target: std.zig.CrossTarget,
updates: std.ArrayList(ZIRUpdate),
output_mode: std.builtin.OutputMode,
/// Either ".zir" or ".zig"
extension: [4]u8,
/// Adds a subcase in which the module is updated with new ZIR, and the
/// resulting ZIR is validated.
pub fn addTransform(self: *ZIRCase, src: [:0]const u8, result: [:0]const u8) void {
pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
self.updates.append(.{
.src = src,
.case = .{ .Transformation = result },
}) catch unreachable;
}
pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
self.updates.append(.{
.src = src,
.case = .{ .Execution = result },
}) catch unreachable;
}
/// Adds a subcase in which the module is updated with invalid ZIR, and
/// ensures that compilation fails for the expected reasons.
///
/// Errors must be specified in sequential order.
pub fn addError(self: *ZIRCase, src: [:0]const u8, errors: []const []const u8) void {
pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
for (errors) |e, i| {
if (e[0] != ':') {
@ -146,15 +118,65 @@ pub const TestContext = struct {
}
};
pub fn addZIRMulti(
pub fn addExeZIR(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *ZIRCase {
const case = ZIRCase{
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Exe,
.extension = ".zir".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addObjZIR(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Obj,
.extension = ".zir".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addExe(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Exe,
.extension = ".zig".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addObj(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Obj,
.extension = ".zig".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
@ -163,14 +185,21 @@ pub const TestContext = struct {
pub fn addZIRCompareOutput(
ctx: *TestContext,
name: []const u8,
src_list: []const []const u8,
expected_stdout_list: []const []const u8,
src: [:0]const u8,
expected_stdout: []const u8,
) void {
ctx.zir_cmp_output_cases.append(.{
.name = name,
.src_list = src_list,
.expected_stdout_list = expected_stdout_list,
}) catch unreachable;
var c = ctx.addExeZIR(name, .{});
c.addCompareOutput(src, expected_stdout);
}
pub fn addCompareOutput(
ctx: *TestContext,
name: []const u8,
src: [:0]const u8,
expected_stdout: []const u8,
) void {
var c = ctx.addExe(name, .{});
c.addCompareOutput(src, expected_stdout);
}
pub fn addZIRTransform(
@ -180,7 +209,7 @@ pub const TestContext = struct {
src: [:0]const u8,
result: [:0]const u8,
) void {
var c = ctx.addZIRMulti(name, target);
var c = ctx.addObjZIR(name, target);
c.addTransform(src, result);
}
@ -191,20 +220,18 @@ pub const TestContext = struct {
src: [:0]const u8,
expected_errors: []const []const u8,
) void {
var c = ctx.addZIRMulti(name, target);
var c = ctx.addObjZIR(name, target);
c.addError(src, expected_errors);
}
fn init() TestContext {
const allocator = std.heap.page_allocator;
return .{
.zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator),
.zir_cases = std.ArrayList(ZIRCase).init(allocator),
.zir_cases = std.ArrayList(Case).init(allocator),
};
}
fn deinit(self: *TestContext) void {
self.zir_cmp_output_cases.deinit();
for (self.zir_cases.items) |c| {
for (c.updates.items) |u| {
if (u.case == .Error) {
@ -235,34 +262,24 @@ pub const TestContext = struct {
progress.refresh();
const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
try self.runOneZIRCase(std.testing.allocator, &prg_node, case, info.target);
try std.testing.allocator_instance.validate();
}
// TODO: wipe the rest of this function
for (self.zir_cmp_output_cases.items) |case| {
std.testing.base_allocator_instance.reset();
var prg_node = root_node.start(case.name, case.src_list.len);
prg_node.activate();
defer prg_node.end();
// So that we can see which test case failed when the leak checker goes off.
progress.refresh();
try self.runOneZIRCmpOutputCase(std.testing.allocator, &prg_node, case, native_info.target);
try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
try std.testing.allocator_instance.validate();
}
}
fn runOneZIRCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void {
fn runOneCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: Case, target: std.Target) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const tmp_src_path = "test_case.zir";
const root_name = "test_case";
const tmp_src_path = try std.fmt.allocPrint(allocator, "{}{}", .{ root_name, case.extension });
defer allocator.free(tmp_src_path);
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
defer root_pkg.destroy();
const bin_name = try std.zig.binNameAlloc(allocator, root_name, target, case.output_mode, null);
defer allocator.free(bin_name);
var module = try Module.init(allocator, .{
.target = target,
// This is an Executable, as opposed to e.g. a *library*. This does
@ -271,17 +288,17 @@ pub const TestContext = struct {
// TODO: support tests for object file building, and library builds
// and linking. This will require a rework to support multi-file
// tests.
.output_mode = .Obj,
.output_mode = case.output_mode,
// TODO: support testing optimizations
.optimize_mode = .Debug,
.bin_file_dir = tmp.dir,
.bin_file_path = "test_case.o",
.bin_file_path = bin_name,
.root_pkg = root_pkg,
.keep_source_files_loaded = true,
});
defer module.deinit();
for (case.updates.items) |update| {
for (case.updates.items) |update, update_index| {
var update_node = prg_node.start("update", 4);
update_node.activate();
defer update_node.end();
@ -293,6 +310,7 @@ pub const TestContext = struct {
var module_node = update_node.start("parse/analysis/codegen", null);
module_node.activate();
try module.makeBinFileWritable();
try module.update();
module_node.end();
@ -341,78 +359,41 @@ pub const TestContext = struct {
}
}
},
.Execution => |expected_stdout| {
var exec_result = x: {
var exec_node = update_node.start("execute", null);
exec_node.activate();
defer exec_node.end();
else => return error.Unimplemented,
}
}
}
try module.makeBinFileExecutable();
fn runOneZIRCmpOutputCase(
self: *TestContext,
allocator: *Allocator,
prg_node: *std.Progress.Node,
case: ZIRCompareOutputCase,
target: std.Target,
) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
defer allocator.free(exe_path);
const tmp_src_path = "test-case.zir";
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
defer root_pkg.destroy();
var module = try Module.init(allocator, .{
.target = target,
.output_mode = .Exe,
.optimize_mode = .Debug,
.bin_file_dir = tmp.dir,
.bin_file_path = "a.out",
.root_pkg = root_pkg,
});
defer module.deinit();
for (case.src_list) |source, i| {
var src_node = prg_node.start("update", 2);
src_node.activate();
defer src_node.end();
try tmp.dir.writeFile(tmp_src_path, source);
var update_node = src_node.start("parse,analysis,codegen", null);
update_node.activate();
try module.makeBinFileWritable();
try module.update();
update_node.end();
var exec_result = x: {
var exec_node = src_node.start("execute", null);
exec_node.activate();
defer exec_node.end();
try module.makeBinFileExecutable();
break :x try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &[_][]const u8{"./a.out"},
.cwd_dir = tmp.dir,
});
};
defer allocator.free(exec_result.stdout);
defer allocator.free(exec_result.stderr);
switch (exec_result.term) {
.Exited => |code| {
if (code != 0) {
std.debug.warn("elf file exited with code {}\n", .{code});
return error.BinaryBadExitCode;
break :x try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &[_][]const u8{exe_path},
.cwd_dir = tmp.dir,
});
};
defer allocator.free(exec_result.stdout);
defer allocator.free(exec_result.stderr);
switch (exec_result.term) {
.Exited => |code| {
if (code != 0) {
std.debug.warn("elf file exited with code {}\n", .{code});
return error.BinaryBadExitCode;
}
},
else => return error.BinaryCrashed,
}
if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
std.debug.panic(
"update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
.{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
);
}
},
else => return error.BinaryCrashed,
}
const expected_stdout = case.expected_stdout_list[i];
if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
std.debug.panic(
"update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
.{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
);
}
}
}

View File

@ -1,28 +1,121 @@
const std = @import("std");
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
// self-hosted does not yet support PE executable files / COFF object files
// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
const linux_x64 = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
// TODO: re-enable these tests.
// https://github.com/ziglang/zig/issues/1364
if (std.Target.current.os.tag != .linux or
std.Target.current.cpu.arch != .x86_64)
{
// TODO implement self-hosted PE (.exe file) linking
// TODO implement more ZIR so we don't depend on x86_64-linux
return;
}
//// hello world
//try ctx.testCompareOutputLibC(
// \\extern fn puts([*]const u8) void;
// \\pub export fn main() c_int {
// \\ puts("Hello, world!");
// \\ return 0;
// \\}
//, "Hello, world!" ++ std.cstr.line_sep);
//// function calling another function
//try ctx.testCompareOutputLibC(
// \\extern fn puts(s: [*]const u8) void;
// \\pub export fn main() c_int {
// \\ return foo("OK");
// \\}
// \\fn foo(s: [*]const u8) c_int {
// \\ puts(s);
// \\ return 0;
// \\}
//, "OK" ++ std.cstr.line_sep);
{
var case = ctx.addExe("hello world with updates", linux_x64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
\\ [arg3] "{rdx}" (14)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"Hello, World!\n",
);
// Now change the message only
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
\\ [arg3] "{rdx}" (104)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
);
// Now we print it twice.
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
\\ [arg3] "{rdx}" (104)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
\\
);
}
}

View File

@ -86,7 +86,7 @@ pub fn addCases(ctx: *TestContext) void {
);
{
var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);
var case = ctx.addObjZIR("reference cycle with compile error in the cycle", linux_x64);
case.addTransform(
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
@ -207,109 +207,101 @@ pub fn addCases(ctx: *TestContext) void {
return;
}
ctx.addZIRCompareOutput(
"hello world ZIR",
&[_][]const u8{
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@msg = str("Hello, world!\n")
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %rdx = str("{rdx}")
\\ %rsi = str("{rsi}")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_addr = ptrtoint(@msg)
\\
\\ %len_name = str("len")
\\ %msg_len_ptr = fieldptr(@msg, %len_name)
\\ %msg_len = deref(%msg_len_ptr)
\\ %rc_write = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi, %rsi, %rdx],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
\\
\\ %rc_exit = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@9 = str("_start")
\\@11 = export(@9, "start")
},
&[_][]const u8{
\\Hello, world!
\\
},
ctx.addZIRCompareOutput("hello world ZIR",
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@msg = str("Hello, world!\n")
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %rdx = str("{rdx}")
\\ %rsi = str("{rsi}")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_addr = ptrtoint(@msg)
\\
\\ %len_name = str("len")
\\ %msg_len_ptr = fieldptr(@msg, %len_name)
\\ %msg_len = deref(%msg_len_ptr)
\\ %rc_write = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi, %rsi, %rdx],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
\\
\\ %rc_exit = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@9 = str("_start")
\\@11 = export(@9, "start")
,
\\Hello, world!
\\
);
ctx.addZIRCompareOutput(
"function call with no args no return value",
&[_][]const u8{
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@exit0_fnty = fntype([], @noreturn)
\\@exit0 = fn(@exit0_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %rc = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %0 = call(@exit0, [])
\\})
\\@9 = str("_start")
\\@11 = export(@9, "start")
},
&[_][]const u8{""},
);
ctx.addZIRCompareOutput("function call with no args no return value",
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@exit0_fnty = fntype([], @noreturn)
\\@exit0 = fn(@exit0_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %rc = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %0 = call(@exit0, [])
\\})
\\@9 = str("_start")
\\@11 = export(@9, "start")
, "");
}