* std.ArrayList gains `moveToUnmanaged` and dead code `ArrayListUnmanaged.appendWrite` is deleted. * emit_h state is attached to Module rather than Compilation. * remove the implementation of emit-h because it did not properly integrate with incremental compilation. I will re-implement it in a follow-up commit. * Compilation: use the .codegen_failure tag rather than .dependency_failure tag for when `bin_file.updateDecl` fails. C backend: * Use a CValue tagged union instead of strings for C values. * Cleanly separate state into Object and DeclGen: - Object is present only when generating a .c file - DeclGen is present for both generating a .c and .h * Move some functions into their respective Object/DeclGen namespace. * Forward decls are managed by the incremental compilation frontend; C backend no longer renders function signatures based on callsites. For simplicity, all functions always get forward decls. * Constants are managed by the incremental compilation frontend. C backend no longer has a "constants" section. * Participate in incremental compilation. Each Decl gets an ArrayList for its generated C code and it is updated when the Decl is updated. During flush(), all these are joined together in the output file. * The new CValue tagged union is used to clean up using of assigning to locals without an additional pointer local. * Fix bug with bitcast of non-pointers making the memcpy destination immutable.
373 lines
11 KiB
Zig
373 lines
11 KiB
Zig
const std = @import("std");
|
|
const TestContext = @import("../../src/test.zig").TestContext;
|
|
|
|
// These tests should work with all platforms, but we're using linux_x64 for
|
|
// now for consistency. Will be expanded eventually.
|
|
const linux_x64 = std.zig.CrossTarget{
|
|
.cpu_arch = .x86_64,
|
|
.os_tag = .linux,
|
|
};
|
|
|
|
pub fn addCases(ctx: *TestContext) !void {
|
|
{
|
|
var case = ctx.exeFromCompiledC("hello world with updates", .{});
|
|
|
|
// Regular old hello world
|
|
case.addCompareOutput(
|
|
\\extern fn puts(s: [*:0]const u8) c_int;
|
|
\\export fn main() c_int {
|
|
\\ _ = puts("hello world!");
|
|
\\ return 0;
|
|
\\}
|
|
, "hello world!" ++ std.cstr.line_sep);
|
|
|
|
// Now change the message only
|
|
case.addCompareOutput(
|
|
\\extern fn puts(s: [*:0]const u8) c_int;
|
|
\\export fn main() c_int {
|
|
\\ _ = puts("yo");
|
|
\\ return 0;
|
|
\\}
|
|
, "yo" ++ std.cstr.line_sep);
|
|
}
|
|
|
|
{
|
|
var case = ctx.exeFromCompiledC("alloc and retptr", .{});
|
|
|
|
case.addCompareOutput(
|
|
\\fn add(a: i32, b: i32) i32 {
|
|
\\ return a + b;
|
|
\\}
|
|
\\
|
|
\\fn addIndirect(a: i32, b: i32) i32 {
|
|
\\ return add(a, b);
|
|
\\}
|
|
\\
|
|
\\export fn main() c_int {
|
|
\\ return addIndirect(1, 2) - 3;
|
|
\\}
|
|
, "");
|
|
}
|
|
|
|
{
|
|
var case = ctx.exeFromCompiledC("inferred local const and var", .{});
|
|
|
|
case.addCompareOutput(
|
|
\\fn add(a: i32, b: i32) i32 {
|
|
\\ return a + b;
|
|
\\}
|
|
\\
|
|
\\export fn main() c_int {
|
|
\\ const x = add(1, 2);
|
|
\\ var y = add(3, 0);
|
|
\\ y -= x;
|
|
\\ return y;
|
|
\\}
|
|
, "");
|
|
}
|
|
{
|
|
var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{});
|
|
|
|
case.addCompareOutput(
|
|
\\export fn main() i32 {
|
|
\\ @setEvalBranchQuota(1001);
|
|
\\ const y = rec(1001);
|
|
\\ return y - 1;
|
|
\\}
|
|
\\
|
|
\\inline fn rec(n: usize) usize {
|
|
\\ if (n <= 1) return n;
|
|
\\ return rec(n - 1);
|
|
\\}
|
|
, "");
|
|
}
|
|
ctx.c("empty start function", linux_x64,
|
|
\\export fn _start() noreturn {
|
|
\\ unreachable;
|
|
\\}
|
|
,
|
|
\\zig_noreturn void _start(void) {
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.h("simple header", linux_x64,
|
|
\\export fn start() void{}
|
|
,
|
|
\\void start(void);
|
|
\\
|
|
);
|
|
ctx.c("less empty start function", linux_x64,
|
|
\\fn main() noreturn {
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
\\export fn _start() noreturn {
|
|
\\ main();
|
|
\\}
|
|
,
|
|
\\static zig_noreturn void main(void);
|
|
\\
|
|
\\static zig_noreturn void main(void) {
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ main();
|
|
\\}
|
|
\\
|
|
);
|
|
// TODO: implement return values
|
|
// TODO: figure out a way to prevent asm constants from being generated
|
|
ctx.c("inline asm", linux_x64,
|
|
\\fn exitGood() noreturn {
|
|
\\ asm volatile ("syscall"
|
|
\\ :
|
|
\\ : [number] "{rax}" (231),
|
|
\\ [arg1] "{rdi}" (0)
|
|
\\ );
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
\\export fn _start() noreturn {
|
|
\\ exitGood();
|
|
\\}
|
|
,
|
|
\\static zig_noreturn void exitGood(void);
|
|
\\
|
|
\\static uint8_t exitGood__anon_0[6] = "{rax}";
|
|
\\static uint8_t exitGood__anon_1[6] = "{rdi}";
|
|
\\static uint8_t exitGood__anon_2[8] = "syscall";
|
|
\\
|
|
\\static zig_noreturn void exitGood(void) {
|
|
\\ register uintptr_t rax_constant __asm__("rax") = 231;
|
|
\\ register uintptr_t rdi_constant __asm__("rdi") = 0;
|
|
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ exitGood();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.c("exit with parameter", linux_x64,
|
|
\\export fn _start() noreturn {
|
|
\\ exit(0);
|
|
\\}
|
|
\\
|
|
\\fn exit(code: usize) noreturn {
|
|
\\ asm volatile ("syscall"
|
|
\\ :
|
|
\\ : [number] "{rax}" (231),
|
|
\\ [arg1] "{rdi}" (code)
|
|
\\ );
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
,
|
|
\\static zig_noreturn void exit(uintptr_t arg0);
|
|
\\
|
|
\\static uint8_t exit__anon_0[6] = "{rax}";
|
|
\\static uint8_t exit__anon_1[6] = "{rdi}";
|
|
\\static uint8_t exit__anon_2[8] = "syscall";
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ exit(0);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exit(uintptr_t arg0) {
|
|
\\ register uintptr_t rax_constant __asm__("rax") = 231;
|
|
\\ register uintptr_t rdi_constant __asm__("rdi") = arg0;
|
|
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.c("exit with u8 parameter", linux_x64,
|
|
\\export fn _start() noreturn {
|
|
\\ exit(0);
|
|
\\}
|
|
\\
|
|
\\fn exit(code: u8) noreturn {
|
|
\\ asm volatile ("syscall"
|
|
\\ :
|
|
\\ : [number] "{rax}" (231),
|
|
\\ [arg1] "{rdi}" (code)
|
|
\\ );
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
,
|
|
\\static zig_noreturn void exit(uint8_t arg0);
|
|
\\
|
|
\\static uint8_t exit__anon_0[6] = "{rax}";
|
|
\\static uint8_t exit__anon_1[6] = "{rdi}";
|
|
\\static uint8_t exit__anon_2[8] = "syscall";
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ exit(0);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exit(uint8_t arg0) {
|
|
\\ uintptr_t const __temp_0 = (uintptr_t)arg0;
|
|
\\ register uintptr_t rax_constant __asm__("rax") = 231;
|
|
\\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
|
|
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.c("exit with u8 arithmetic", linux_x64,
|
|
\\export fn _start() noreturn {
|
|
\\ exitMath(1);
|
|
\\}
|
|
\\
|
|
\\fn exitMath(a: u8) noreturn {
|
|
\\ exit(0 + a - a);
|
|
\\}
|
|
\\
|
|
\\fn exit(code: u8) noreturn {
|
|
\\ asm volatile ("syscall"
|
|
\\ :
|
|
\\ : [number] "{rax}" (231),
|
|
\\ [arg1] "{rdi}" (code)
|
|
\\ );
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
,
|
|
\\static zig_noreturn void exitMath(uint8_t arg0);
|
|
\\static zig_noreturn void exit(uint8_t arg0);
|
|
\\
|
|
\\static uint8_t exit__anon_0[6] = "{rax}";
|
|
\\static uint8_t exit__anon_1[6] = "{rdi}";
|
|
\\static uint8_t exit__anon_2[8] = "syscall";
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ exitMath(1);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exitMath(uint8_t arg0) {
|
|
\\ uint8_t const __temp_0 = 0 + arg0;
|
|
\\ uint8_t const __temp_1 = __temp_0 - arg0;
|
|
\\ exit(__temp_1);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exit(uint8_t arg0) {
|
|
\\ uintptr_t const __temp_0 = (uintptr_t)arg0;
|
|
\\ register uintptr_t rax_constant __asm__("rax") = 231;
|
|
\\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
|
|
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.c("exit with u8 arithmetic inverted", linux_x64,
|
|
\\export fn _start() noreturn {
|
|
\\ exitMath(1);
|
|
\\}
|
|
\\
|
|
\\fn exitMath(a: u8) noreturn {
|
|
\\ exit(a + 0 - a);
|
|
\\}
|
|
\\
|
|
\\fn exit(code: u8) noreturn {
|
|
\\ asm volatile ("syscall"
|
|
\\ :
|
|
\\ : [number] "{rax}" (231),
|
|
\\ [arg1] "{rdi}" (code)
|
|
\\ );
|
|
\\ unreachable;
|
|
\\}
|
|
\\
|
|
,
|
|
\\static zig_noreturn void exitMath(uint8_t arg0);
|
|
\\static zig_noreturn void exit(uint8_t arg0);
|
|
\\
|
|
\\static uint8_t exit__anon_0[6] = "{rax}";
|
|
\\static uint8_t exit__anon_1[6] = "{rdi}";
|
|
\\static uint8_t exit__anon_2[8] = "syscall";
|
|
\\
|
|
\\zig_noreturn void _start(void) {
|
|
\\ exitMath(1);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exitMath(uint8_t arg0) {
|
|
\\ uint8_t const __temp_0 = arg0 + 0;
|
|
\\ uint8_t const __temp_1 = __temp_0 - arg0;
|
|
\\ exit(__temp_1);
|
|
\\}
|
|
\\
|
|
\\static zig_noreturn void exit(uint8_t arg0) {
|
|
\\ uintptr_t const __temp_0 = (uintptr_t)arg0;
|
|
\\ register uintptr_t rax_constant __asm__("rax") = 231;
|
|
\\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
|
|
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
|
\\ zig_breakpoint();
|
|
\\ zig_unreachable();
|
|
\\}
|
|
\\
|
|
);
|
|
ctx.h("header with single param function", linux_x64,
|
|
\\export fn start(a: u8) void{}
|
|
,
|
|
\\void start(uint8_t arg0);
|
|
\\
|
|
);
|
|
ctx.h("header with multiple param function", linux_x64,
|
|
\\export fn start(a: u8, b: u8, c: u8) void{}
|
|
,
|
|
\\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2);
|
|
\\
|
|
);
|
|
ctx.h("header with u32 param function", linux_x64,
|
|
\\export fn start(a: u32) void{}
|
|
,
|
|
\\void start(uint32_t arg0);
|
|
\\
|
|
);
|
|
ctx.h("header with usize param function", linux_x64,
|
|
\\export fn start(a: usize) void{}
|
|
,
|
|
\\void start(uintptr_t arg0);
|
|
\\
|
|
);
|
|
ctx.h("header with bool param function", linux_x64,
|
|
\\export fn start(a: bool) void{}
|
|
,
|
|
\\void start(bool arg0);
|
|
\\
|
|
);
|
|
ctx.h("header with noreturn function", linux_x64,
|
|
\\export fn start() noreturn {
|
|
\\ unreachable;
|
|
\\}
|
|
,
|
|
\\zig_noreturn void start(void);
|
|
\\
|
|
);
|
|
ctx.h("header with multiple functions", linux_x64,
|
|
\\export fn a() void{}
|
|
\\export fn b() void{}
|
|
\\export fn c() void{}
|
|
,
|
|
\\void a(void);
|
|
\\void b(void);
|
|
\\void c(void);
|
|
\\
|
|
);
|
|
ctx.h("header with multiple includes", linux_x64,
|
|
\\export fn start(a: u32, b: usize) void{}
|
|
,
|
|
\\void start(uint32_t arg0, uintptr_t arg1);
|
|
\\
|
|
);
|
|
}
|