Merge pull request #236 from birth-software/start-preparing-for-c

Start preparing for C compilation
This commit is contained in:
David 2024-06-09 08:40:56 -06:00 committed by GitHub
commit 5347b1f0e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 188 additions and 100 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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,

4
retest/c_abi/main.nat Normal file
View File

@ -0,0 +1,4 @@
fn[cc(.c)] main[export]() s32
{
return 0;
}

View File

@ -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)
{