diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index e7db130..c5643a7 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -1,6 +1,7 @@ const compiler = @import("../compiler.zig"); const LLVM = compiler.LLVM; +pub extern fn NativityLLVMInitializeAll() void; pub extern fn NativityLLVMCreateContext() *LLVM.Context; pub extern fn NativityLLVMCreateModule(module_name_ptr: [*]const u8, module_name_len: usize, context: *LLVM.Context) *LLVM.Module; pub extern fn NativityLLVMCreateBuilder(context: *LLVM.Context) *LLVM.Builder; diff --git a/bootstrap/compiler.zig b/bootstrap/compiler.zig index 165b8b3..86c8e78 100644 --- a/bootstrap/compiler.zig +++ b/bootstrap/compiler.zig @@ -3069,6 +3069,7 @@ const Job = packed struct(u64) { llvm_optimize, llvm_emit_object, llvm_notify_object_done, + compile_c_source_file, }; }; @@ -3322,6 +3323,7 @@ const Unit = struct { main_source_file_path: []const u8, executable_path: []const u8, object_path: []const u8, + c_source_files: []const []const u8, target: Target, optimization: Optimization, generate_debug_information: bool, @@ -3336,34 +3338,50 @@ const Unit = struct { .descriptor = descriptor, }; - switch (unit.descriptor.target.arch) { - inline else => |a| { - const arch = @field(LLVM, @tagName(a)); - arch.initializeTarget(); - arch.initializeTargetInfo(); - arch.initializeTargetMC(); - arch.initializeAsmPrinter(); - arch.initializeAsmParser(); - }, + if (descriptor.c_source_files.len > 0) { + LLVM.initializeAll(); + } else { + switch (unit.descriptor.target.arch) { + inline else => |a| { + const arch = @field(LLVM, @tagName(a)); + arch.initializeTarget(); + arch.initializeTargetInfo(); + arch.initializeTargetMC(); + arch.initializeAsmPrinter(); + arch.initializeAsmParser(); + }, + } } - const main_source_file_absolute = instance.path_from_cwd(instance.arena, unit.descriptor.main_source_file_path); const new_file_index = add_file(main_source_file_absolute, &.{}); - instance.threads[0].task_system.program_state = .analysis; - instance.threads[0].add_thread_work(Job{ + var last_assigned_thread_index: u32 = 0; + instance.threads[last_assigned_thread_index].task_system.program_state = .analysis; + instance.threads[last_assigned_thread_index].add_thread_work(Job{ .offset = new_file_index, .count = 1, .id = .analyze_file, }); - control_thread(unit); + last_assigned_thread_index += 1; + + for (descriptor.c_source_files, 0..) |_, index| { + const thread_index = last_assigned_thread_index % instance.threads.len; + instance.threads[thread_index].add_thread_work(Job{ + .offset = @intCast(index), + .count = 1, + .id = .compile_c_source_file, + }); + last_assigned_thread_index += 1; + } + + control_thread(unit, last_assigned_thread_index); return unit; } }; -fn control_thread(unit: *Unit) void { - var last_assigned_thread_index: u32 = 1; +fn control_thread(unit: *Unit, lati: u32) void { + var last_assigned_thread_index: u32 = lati; var first_ir_done = false; var total_is_done: bool = false; var iterations_without_work_done: u32 = 0; @@ -3613,6 +3631,7 @@ fn command_exe(arguments: []const []const u8) void { .main_source_file_path = main_source_file_path, .object_path = object_path, .executable_path = executable_path, + .c_source_files = &.{}, .optimization = optimization, .generate_debug_information = generate_debug_information, .codegen_backend = .{ @@ -6650,6 +6669,7 @@ fn get_array_type(thread: *Thread, descriptor: Type.Array.Descriptor) *Type { pub const LLVM = struct { const bindings = @import("backend/llvm_bindings.zig"); + pub const initializeAll = bindings.NativityLLVMInitializeAll; pub const x86_64 = struct { pub const initializeTarget = bindings.LLVMInitializeX86Target; pub const initializeTargetInfo = bindings.LLVMInitializeX86TargetInfo; diff --git a/build/test_runner.zig b/build/test_runner.zig index 6f1c606..662fc09 100644 --- a/build/test_runner.zig +++ b/build/test_runner.zig @@ -31,6 +31,113 @@ fn collectDirectoryDirEntries(allocator: Allocator, path: []const u8) ![]const [ const bootstrap_relative_path = "zig-out/bin/nat"; +const Run = struct{ + compilation_run: usize = 0, + compilation_failure: usize = 0, + test_run: usize = 0, + test_failure: usize = 0, + + fn add(run: *Run, other: Run) void { + run.compilation_run += other.compilation_run; + run.compilation_failure += other.compilation_failure; + run.test_run += other.test_run; + run.test_failure += other.test_failure; + } +}; + +fn compiler_run(allocator: Allocator, args: struct{ + test_name: []const u8, + repetitions: usize, + extra_arguments: []const []const u8, + source_file_path: []const u8, + compiler_path: []const u8, + is_test: bool, + self_hosted: bool, +}) !Run { + std.debug.print("{s} [repetitions={}] {s}", .{args.test_name, args.repetitions, if (args.repetitions > 1) "\n\n" else ""}); + var run = Run{}; + + for (0..args.repetitions) |_| { + const base_argv: []const []const u8 = &.{ args.compiler_path, if (args.is_test) "test" else "exe", "-main_source_file", args.source_file_path }; + const argv = try std.mem.concat(allocator, []const u8, &.{base_argv, args.extra_arguments}); + // if (std.mem.eql(u8, args.compiler_path, "nat/compiler_lightly_optimize_for_speed")) @breakpoint(); + const compile_run = try std.process.Child.run(.{ + .allocator = allocator, + // TODO: delete -main_source_file? + .argv = argv, + .max_output_bytes = std.math.maxInt(u64), + }); + run.compilation_run += 1; + + const compilation_result: TestError!bool = switch (compile_run.term) { + .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, + .Signal => error.signaled, + .Stopped => error.stopped, + .Unknown => error.unknown, + }; + + const compilation_success = compilation_result catch b: { + run.compilation_failure += 1; + break :b false; + }; + + std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); + if (compile_run.stdout.len > 0) { + std.debug.print("STDOUT:\n\n{s}\n\n", .{compile_run.stdout}); + } + if (compile_run.stderr.len > 0) { + std.debug.print("STDERR:\n\n{s}\n\n", .{compile_run.stderr}); + } + + if (compilation_success and !args.self_hosted) { + const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", args.test_name }); + const test_run = try std.process.Child.run(.{ + .allocator = allocator, + .argv = &.{test_path}, + .max_output_bytes = std.math.maxInt(u64), + }); + run.test_run += 1; + const test_result: TestError!bool = switch (test_run.term) { + .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, + .Signal => error.signaled, + .Stopped => error.stopped, + .Unknown => error.unknown, + }; + + const test_success = test_result catch b: { + break :b false; + }; + run.test_failure += @intFromBool(!test_success); + + std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); + if (test_run.stdout.len > 0) { + std.debug.print("STDOUT:\n\n{s}\n\n", .{test_run.stdout}); + } + if (test_run.stderr.len > 0) { + std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr}); + } + } else { + std.debug.print("\n", .{}); + } + } + + return run; +} + +fn group_start(group: []const u8, test_count: usize) void { + std.debug.print("\n[{s} START ({} tests queued)]\n\n", .{group, test_count}); +} + +fn group_end(group: []const u8, test_count: usize, run: Run) !void { + std.debug.print("\n{s} COMPILATIONS: {}. FAILED: {}\n", .{ group, test_count, run.compilation_failure }); + std.debug.print("{s} TESTS: {}. RAN: {}. FAILED: {}\n", .{ group, test_count, run.test_run, run.test_failure }); + std.debug.print("\n[{s} END]\n\n", .{group}); + + if (run.compilation_failure > 0 or run.test_failure > 0) { + return error.fail; + } +} + fn runStandalone(allocator: Allocator, args: struct { directory_path: []const u8, group_name: []const u8, @@ -42,88 +149,25 @@ fn runStandalone(allocator: Allocator, args: struct { const test_names = try collectDirectoryDirEntries(allocator, args.directory_path); std.debug.assert(args.repetitions > 0); - const total_compilation_count = test_names.len; - var ran_compilation_count: usize = 0; - var failed_compilation_count: usize = 0; + var total_run = Run{}; - var ran_test_count: usize = 0; - var failed_test_count: usize = 0; - const total_test_count = test_names.len; - - std.debug.print("\n[{s} START]\n\n", .{args.group_name}); + group_start(args.group_name, test_names.len * args.repetitions); for (test_names) |test_name| { - std.debug.print("{s} [repetitions={}]{s}", .{test_name, args.repetitions, if (args.repetitions > 1) "\n\n" else ""}); - for (0..args.repetitions) |_| { - const source_file_path = try std.mem.concat(allocator, u8, &.{ args.directory_path, "/", test_name, "/main.nat" }); - const argv: []const []const u8 = &.{ args.compiler_path, if (args.is_test) "test" else "exe", "-main_source_file", source_file_path }; - // if (std.mem.eql(u8, args.compiler_path, "nat/compiler_lightly_optimize_for_speed")) @breakpoint(); - const compile_run = try std.process.Child.run(.{ - .allocator = allocator, - // TODO: delete -main_source_file? - .argv = argv, - .max_output_bytes = std.math.maxInt(u64), - }); - ran_compilation_count += 1; - - const compilation_result: TestError!bool = switch (compile_run.term) { - .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, - .Signal => error.signaled, - .Stopped => error.stopped, - .Unknown => error.unknown, - }; - - const compilation_success = compilation_result catch b: { - failed_compilation_count += 1; - break :b false; - }; - - std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); - if (compile_run.stdout.len > 0) { - std.debug.print("STDOUT:\n\n{s}\n\n", .{compile_run.stdout}); - } - if (compile_run.stderr.len > 0) { - std.debug.print("STDERR:\n\n{s}\n\n", .{compile_run.stderr}); - } - - if (compilation_success and !args.self_hosted) { - const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", test_name }); - const test_run = try std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{test_path}, - .max_output_bytes = std.math.maxInt(u64), - }); - ran_test_count += 1; - const test_result: TestError!bool = switch (test_run.term) { - .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, - .Signal => error.signaled, - .Stopped => error.stopped, - .Unknown => error.unknown, - }; - - const test_success = test_result catch b: { - failed_test_count += 1; - break :b false; - }; - std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); - if (test_run.stdout.len > 0) { - std.debug.print("STDOUT:\n\n{s}\n\n", .{test_run.stdout}); - } - if (test_run.stderr.len > 0) { - std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr}); - } - } else { - std.debug.print("\n", .{}); - } - } + const source_file_path = try std.mem.concat(allocator, u8, &.{ args.directory_path, "/", test_name, "/main.nat" }); + const run = try compiler_run(allocator, .{ + .compiler_path = args.compiler_path, + .source_file_path = source_file_path, + .test_name = test_name, + .repetitions = args.repetitions, + .extra_arguments = &.{}, + .is_test = args.is_test, + .self_hosted = args.self_hosted, + }); + total_run.add(run); } - std.debug.print("\n{s} COMPILATIONS: {}. FAILED: {}\n", .{ args.group_name, total_compilation_count, failed_compilation_count }); - std.debug.print("{s} TESTS: {}. RAN: {}. FAILED: {}\n", .{ args.group_name, total_test_count, ran_test_count, failed_test_count }); - - if (failed_compilation_count > 0 or failed_test_count > 0) { - return error.fail; - } + try group_end(args.group_name, test_names.len * args.repetitions, total_run); } fn runBuildTests(allocator: Allocator, args: struct { @@ -583,6 +627,22 @@ fn run_test_suite(allocator: Allocator, args: struct { return errors; } +fn c_abi_tests(allocator: Allocator) !void { + const test_count = 1; + const group = "C ABI"; + group_start(group, test_count); + const run = try compiler_run(allocator, .{ + .test_name = "c_abi", + .repetitions = 1, + .extra_arguments = &.{}, + .source_file_path = "retest/c_abi/main.nat", + .compiler_path = bootstrap_relative_path, + .is_test = false, + .self_hosted = false, + }); + try group_end(group, test_count, run); +} + pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); const allocator = arena.allocator(); @@ -596,6 +656,8 @@ pub fn main() !void { .repetitions = 1, }); + try c_abi_tests(allocator); + // var errors = run_test_suite(allocator, .{ // .self_hosted = false, // .compiler_path = bootstrap_relative_path, diff --git a/retest/c_abi/main.nat b/retest/c_abi/main.nat new file mode 100644 index 0000000..d1b9c93 --- /dev/null +++ b/retest/c_abi/main.nat @@ -0,0 +1,4 @@ +fn[cc(.c)] main[export]() s32 +{ + return 0; +} diff --git a/src/llvm/llvm.cpp b/src/llvm/llvm.cpp index fdb7b93..6705e9d 100644 --- a/src/llvm/llvm.cpp +++ b/src/llvm/llvm.cpp @@ -25,6 +25,15 @@ using namespace llvm; using llvm::orc::ThreadSafeContext; using llvm::orc::ThreadSafeModule; +extern "C" void NativityLLVMInitializeAll() +{ + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + InitializeAllAsmPrinters(); +} + extern "C" LLVMContext* NativityLLVMCreateContext() { auto* context = new LLVMContext(); @@ -940,14 +949,6 @@ extern "C" void NativityLLVMCallSetCallingConvention(CallBase& call_instruction, call_instruction.setCallingConv(calling_convention); } -extern "C" void NativityLLVMInitializeCodeGeneration() -{ - InitializeAllTargetInfos(); - InitializeAllTargets(); - InitializeAllTargetMCs(); - InitializeAllAsmParsers(); - InitializeAllAsmPrinters(); -} extern "C" const Target* NativityLLVMGetTarget(const char* target_triple_ptr, size_t target_triple_len, const char** message_ptr, size_t* message_len) {