zig/test/stage2/zir.zig
2020-10-31 12:21:49 +02:00

309 lines
9.6 KiB
Zig

const std = @import("std");
const TestContext = @import("../../src/test.zig").TestContext;
// self-hosted does not yet support PE executable files / COFF object files
// or mach-o files. So we do the ZIR transform 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 {
ctx.transformZIR("referencing decls which appear later in the file", linux_x64,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %11 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = declref("9__anon_0")
\\@9__anon_0 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid() ; deaths=0b1000000000000000
\\})
\\
);
ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@fnty = fntype([], @void, cc=C)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@entry = fn(@fnty, {
\\ %a = str("\x32\x08\x01\x0a")
\\ %a_ref = ref(%a)
\\ %eptr0 = elemptr(%a_ref, @0)
\\ %eptr1 = elemptr(%a_ref, @1)
\\ %eptr2 = elemptr(%a_ref, @2)
\\ %eptr3 = elemptr(%a_ref, @3)
\\ %v0 = deref(%eptr0)
\\ %v1 = deref(%eptr1)
\\ %v2 = deref(%eptr2)
\\ %v3 = deref(%eptr3)
\\ %x0 = add(%v0, %v1)
\\ %x1 = add(%v2, %v3)
\\ %result = add(%x0, %x1)
\\
\\ %expected = int(69)
\\ %ok = cmp_eq(%result, %expected)
\\ %10 = condbr(%ok, {
\\ %11 = returnvoid()
\\ }, {
\\ %12 = breakpoint()
\\ })
\\})
\\
\\@9 = str("entry")
\\@11 = export(@9, "entry")
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid() ; deaths=0b1000000000000000
\\})
\\@entry__anon_1 = str("2\x08\x01\n")
\\@9 = declref("9__anon_0")
\\@9__anon_0 = str("entry")
\\@unnamed$11 = str("entry")
\\@unnamed$12 = export(@unnamed$11, "entry")
\\
);
{
var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64);
case.addTransform(
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = declref("9__anon_0")
\\@9__anon_0 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
\\ %1 = returnvoid() ; deaths=0b1000000000000000
\\})
\\@unnamed$8 = fntype([], @void, cc=C)
\\@a = fn(@unnamed$8, {
\\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
\\ %1 = returnvoid() ; deaths=0b1000000000000000
\\})
\\@unnamed$10 = fntype([], @void, cc=C)
\\@b = fn(@unnamed$10, {
\\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
\\ %1 = returnvoid() ; deaths=0b1000000000000000
\\})
\\
);
// Now we introduce a compile error
case.addError(
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %9 = compileerror("message")
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
,
&[_][]const u8{
":18:21: error: message",
},
);
// Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
// referencing either of them. This tests that the cycle is detected, and the error
// goes away.
case.addTransform(
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %0 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %9 = compileerror("message")
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = declref("9__anon_2")
\\@9__anon_2 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid() ; deaths=0b1000000000000000
\\})
\\
);
}
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;
}
ctx.compareOutputZIR("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.compareOutputZIR("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")
, "");
}