From 0d40031d491a51245c1a6756b4f9ee40457ae4c5 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 23 Oct 2024 16:50:44 -0600 Subject: [PATCH] LLVM emit object --- bootstrap/include/nest/base.h | 34 ++++++++- bootstrap/include/nest/llvm.h | 5 +- bootstrap/include/std/base.h | 1 + bootstrap/include/std/os.h | 48 +++++++----- bootstrap/nest/llvm.cpp | 73 +++++++++--------- bootstrap/nest/main.c | 136 ++++++++++------------------------ bootstrap/runner/runner.c | 60 +++------------ bootstrap/std/os.c | 17 +++++ 8 files changed, 165 insertions(+), 209 deletions(-) diff --git a/bootstrap/include/nest/base.h b/bootstrap/include/nest/base.h index 2548b75..e07adea 100644 --- a/bootstrap/include/nest/base.h +++ b/bootstrap/include/nest/base.h @@ -4,11 +4,41 @@ typedef enum CompilerBackend : u8 { - COMPILER_BACKEND_NEST = 'm', - // COMPILER_BACKEND_LLVM, + COMPILER_BACKEND_NEST, + COMPILER_BACKEND_LLVM, COMPILER_BACKEND_COUNT, } CompilerBackend; +fn String compiler_backend_to_string(CompilerBackend backend) +{ + switch (backend) + { + case COMPILER_BACKEND_NEST: + return strlit("n"); + case COMPILER_BACKEND_LLVM: + return strlit("l"); + case COMPILER_BACKEND_COUNT: + unreachable(); + } +} + +fn CompilerBackend string_to_compiler_backend(String string) +{ + CompilerBackend result = COMPILER_BACKEND_COUNT; + + for (u32 i = 0; i < COMPILER_BACKEND_COUNT; i += 1) + { + auto candidate = (CompilerBackend)i; + if (s_equal(compiler_backend_to_string(candidate), string)) + { + result = candidate; + break; + } + } + + return result; +} + typedef enum CpuArchitecture : u8 { CPU_ARCH_X86_64, diff --git a/bootstrap/include/nest/llvm.h b/bootstrap/include/nest/llvm.h index 4897672..6e3f6b8 100644 --- a/bootstrap/include/nest/llvm.h +++ b/bootstrap/include/nest/llvm.h @@ -2,7 +2,4 @@ #include -#ifdef __cplusplus -extern "C" -#endif -void llvm_codegen(CodegenOptions options); +EXPORT void llvm_codegen(CodegenOptions options, String object_path); diff --git a/bootstrap/include/std/base.h b/bootstrap/include/std/base.h index ee04e6e..999bc04 100644 --- a/bootstrap/include/std/base.h +++ b/bootstrap/include/std/base.h @@ -118,6 +118,7 @@ FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.poin #define breakpoint() __builtin_debugtrap() #define failed_execution() trap() +EXPORT void print(const char* format, ...); #define trap() bad_exit("Trap reached", __FILE__, __LINE__) #define array_length(arr) sizeof(arr) / sizeof((arr)[0]) diff --git a/bootstrap/include/std/os.h b/bootstrap/include/std/os.h index 514b520..faba3e4 100644 --- a/bootstrap/include/std/os.h +++ b/bootstrap/include/std/os.h @@ -41,6 +41,13 @@ STRUCT(Arena) u8 reserved[4 * 8]; }; +STRUCT(FileWriteOptions) +{ + String path; + String content; + u8 executable; +}; + #if __APPLE__ const global_variable u64 page_size = KB(16); #else @@ -52,32 +59,33 @@ global_variable u64 minimum_granularity = page_size; global_variable u64 default_size = GB(4); EXPORT void print(const char* format, ...); -void run_command(Arena* arena, CStringSlice arguments, char* envp[]); -String file_read(Arena* arena, String path); +EXPORT void run_command(Arena* arena, CStringSlice arguments, char* envp[]); +EXPORT String file_read(Arena* arena, String path); +EXPORT void file_write(FileWriteOptions options); -String path_dir(String string); -String path_base(String string); -String path_no_extension(String string); +EXPORT String path_dir(String string); +EXPORT String path_base(String string); +EXPORT String path_no_extension(String string); -Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size); -Arena* arena_init_default(u64 initial_size); -String arena_join_string(Arena* arena, Slice(String) pieces); -u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment); -void arena_reset(Arena* arena); +EXPORT Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size); +EXPORT Arena* arena_init_default(u64 initial_size); +EXPORT String arena_join_string(Arena* arena, Slice(String) pieces); +EXPORT u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment); +EXPORT void arena_reset(Arena* arena); #define arena_allocate(arena, T, count) (T*)(arena_allocate_bytes(arena, sizeof(T) * count, alignof(T))) #define arena_allocate_slice(arena, T, count) (Slice(T)){ .pointer = arena_allocate(arena, T, count), .length = count } -u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map); -void os_commit(void* address, u64 size); +EXPORT u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map); +EXPORT void os_commit(void* address, u64 size); -u8 os_file_descriptor_is_valid(FileDescriptor fd); -FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions); -void os_file_close(FileDescriptor fd); -u64 os_file_get_size(FileDescriptor fd); -void os_file_write(FileDescriptor fd, String content); -FileDescriptor os_stdout_get(); -void os_directory_make(String path); +EXPORT u8 os_file_descriptor_is_valid(FileDescriptor fd); +EXPORT FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions); +EXPORT void os_file_close(FileDescriptor fd); +EXPORT u64 os_file_get_size(FileDescriptor fd); +EXPORT void os_file_write(FileDescriptor fd, String content); +EXPORT FileDescriptor os_stdout_get(); +EXPORT void os_directory_make(String path); -void calibrate_cpu_timer(); +EXPORT void calibrate_cpu_timer(); diff --git a/bootstrap/nest/llvm.cpp b/bootstrap/nest/llvm.cpp index 459a368..2041b77 100644 --- a/bootstrap/nest/llvm.cpp +++ b/bootstrap/nest/llvm.cpp @@ -1,7 +1,10 @@ +#define unreachable() __builtin_unreachable() +#include #include #include #include +#include #include #include #include @@ -12,68 +15,38 @@ #include #include +#include #define string_ref(lit) StringRef(lit, strlit_len(lit)) namespace llvm { - // #define LLVMAttributeMembers(cb) \ - // cb(LLVMAttribute, naked), \ - // cb(LLVMAttribute, noreturn), \ - // cb(LLVMAttribute, nounwind), \ - // cb(LLVMAttribute, inreg), \ - // cb(LLVMAttribute, noalias), \ - // cb(LLVMAttribute, signext), \ - // cb(LLVMAttribute, zeroext), \ - // - // typedef enum LLVMAttributeId : u32 - // { - // LLVMAttributeMembers(NamedEnumMemberEnum) - // LLVM_ATTRIBUTE_COUNT, - // } LLVMAttribute; - // - // String llvm_attribute_names[] = { - // LLVMAttributeMembers(NamedEnumMemberString) - // }; - // - // STRUCT(LLVMAttributeLookupTable) - // { - // u32 ids[LLVM_ATTRIBUTE_COUNT]; - // }; - // - // fn u32 llvm_attribute_id(String string) - // { - // auto result = LLVMGetEnumAttributeKindForName(string_to_c(string), string.length); - // static_assert(sizeof(result) == sizeof(u32)); - // return result; - // } - -#define llvm_initialize_target(target) \ +#define llvm_initialize_macro(target) \ LLVMInitialize ## target ## Target();\ LLVMInitialize ## target ## TargetInfo();\ LLVMInitialize ## target ## TargetMC();\ LLVMInitialize ## target ## AsmParser();\ LLVMInitialize ## target ## AsmPrinter() - fn void llvm_initialize_cpu(CpuArchitecture architecture) + fn void target_initialize(CpuArchitecture architecture) { // These are meant to be called globally, so if this code is ever threaded, we need to call this code only once switch (architecture) { case CPU_ARCH_X86_64: { - llvm_initialize_target(X86); + llvm_initialize_macro(X86); } break; case CPU_ARCH_AARCH64: { - llvm_initialize_target(AArch64); + llvm_initialize_macro(AArch64); } break; } } - extern "C" void llvm_codegen(CodegenOptions options) + EXPORT void llvm_codegen(CodegenOptions options, String object_path) { - llvm_initialize_cpu(options.target.cpu); + target_initialize(options.target.cpu); auto context = LLVMContext(); auto module = Module(string_ref("first"), context); @@ -142,7 +115,7 @@ namespace llvm module.setTargetTriple(target_triple); // TODO: - auto cpu_model = string_ref("baseline"); + auto cpu_model = string_ref(""); auto cpu_features = string_ref(""); TargetOptions target_options; @@ -154,5 +127,29 @@ namespace llvm auto* target_machine = target->createTargetMachine(target_triple, cpu_model, cpu_features, target_options, relocation_model, code_model, codegen_optimization_level, jit); auto data_layout = target_machine->createDataLayout(); module.setDataLayout(data_layout); + + // TODO: optimizations + + SmallString<0> object_string; + raw_svector_ostream object_stream(object_string); + auto file_type = CodeGenFileType::ObjectFile; + legacy::PassManager pass; + + assert(target_machine->isCompatibleDataLayout(module.getDataLayout())); + raw_pwrite_stream* dwo_stream = 0; + if (target_machine->addPassesToEmitFile(pass, object_stream, dwo_stream, file_type)) { + failed_execution(); + } + + pass.run(module); + + assert(object_path.pointer); + assert(object_path.length); + + file_write(FileWriteOptions{ + .path = object_path, + .content = { .pointer = (u8*)object_string.str().data(), .length = object_string.str().size() }, + .executable = 1, + }); } } diff --git a/bootstrap/nest/main.c b/bootstrap/nest/main.c index c05d230..f95f75b 100644 --- a/bootstrap/nest/main.c +++ b/bootstrap/nest/main.c @@ -23572,13 +23572,33 @@ may_be_unused fn String write_macho(Thread* restrict thread, ObjectOptions optio fn void code_generation(Thread* restrict thread, CodegenOptions options) { - // TODO: delete, this is testing - llvm_codegen(options); - auto cfg_builder = cfg_builder_init(thread); auto* restrict builder = &cfg_builder; VirtualBuffer(u8) code = {}; + auto object_path = arena_join_string(thread->arena, (Slice(String)) array_to_slice(((String[]) { + strlit("nest/"), + options.test_name, + strlit(".o"), + // options.backend == COMPILER_BACKEND_C ? strlit(".c") : strlit(".o"), + }))); + + auto exe_path_view = s_get_slice(u8, object_path, 0, object_path.length - 2); + u32 extra_bytes = 0; +#if _WIN32 + extra_bytes = strlen(".exe"); +#endif + String exe_path = { + .pointer = arena_allocate_bytes(thread->arena, exe_path_view.length + extra_bytes + 1, 1), + .length = exe_path_view.length + extra_bytes, + }; + + memcpy(exe_path.pointer, exe_path_view.pointer, exe_path_view.length); +#if _WIN32 + memcpy(exe_path.pointer + exe_path_view.length, ".exe", extra_bytes); +#endif + exe_path.pointer[exe_path_view.length + extra_bytes] = 0; + for (u32 function_i = 0; function_i < thread->buffer.functions.length; function_i += 1) { Function* restrict function = &thread->buffer.functions.pointer[function_i]; @@ -24309,66 +24329,14 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options) } } - auto object_path = arena_join_string(thread->arena, (Slice(String)) array_to_slice(((String[]) { - strlit("nest/"), - options.test_name, - strlit(".o"), - // options.backend == COMPILER_BACKEND_C ? strlit(".c") : strlit(".o"), - }))); - - auto exe_path_view = s_get_slice(u8, object_path, 0, object_path.length - 2); - u32 extra_bytes = 0; -#if _WIN32 - extra_bytes = strlen(".exe"); -#endif - String exe_path = { - .pointer = arena_allocate_bytes(thread->arena, exe_path_view.length + extra_bytes + 1, 1), - .length = exe_path_view.length + extra_bytes, - }; - - memcpy(exe_path.pointer, exe_path_view.pointer, exe_path_view.length); -#if _WIN32 - memcpy(exe_path.pointer + exe_path_view.length, ".exe", extra_bytes); -#endif - exe_path.pointer[exe_path_view.length + extra_bytes] = 0; - switch (options.backend) { - // case COMPILER_BACKEND_C: - // { - // auto lowered_source = c_lower(thread); - // // print("Transpiled to C:\n```\n{s}\n```\n", lowered_source); - // - // file_write(object_path, lowered_source); - // - // char* command[] = { - // clang_path, "-g", - // "-o", exe_path, - // string_to_c(object_path), - // 0, - // }; - // - // run_command((CStringSlice) array_to_slice(command), envp); - // todo(); - // } break; - // case COMPILER_BACKEND_INTERPRETER: - // { - // // auto* main_function = &thread->buffer.functions.pointer[thread->main_function]; - // // auto* interpreter = interpreter_create(thread); - // // interpreter->function = main_function; - // // interpreter->arguments = (Slice(String)) array_to_slice(((String[]) { - // // test_name, - // // })); - // // auto exit_code = interpreter_run(interpreter, thread); - // // print("Interpreter exited with exit code: {u32}\n", exit_code); - // // syscall_exit(exit_code); - // todo(); - // } break; - // case COMPILER_BACKEND_LLVM: - // { - // // TODO - // } break; - case COMPILER_BACKEND_NEST: + case COMPILER_BACKEND_LLVM: + { + // TODO: delete, this is testing + llvm_codegen(options, object_path); + } break; + case COMPILER_BACKEND_NEST: { auto code_slice = (Slice(u8)) { .pointer = code.pointer, .length = code.length, }; auto object_options = (ObjectOptions) { @@ -24396,39 +24364,11 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options) } break; } - auto fd = os_file_open(object_options.exe_path, (OSFileOpenFlags) { - .write = 1, - .truncate = 1, - .create = 1, - .executable = 1, - }, (OSFilePermissions) { - .readable = 1, - .writable = 1, - .executable = 1, - }); -// #if _WIN32 -// if (!os_file_descriptor_is_valid(fd)) -// { -// auto err = GetLastError(); -// LPSTR lpMsgBuf; -// DWORD bufSize = FormatMessageA( -// FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, -// NULL, -// err, -// LANG_NEUTRAL, // Use default language -// (LPSTR)&lpMsgBuf, -// 0, -// NULL -// ); -// unused(bufSize); -// print("Error opening file \"{s}\": {cstr}\n", object_options.exe_path, lpMsgBuf); -// failed_execution(); -// } -// #endif - assert(os_file_descriptor_is_valid(fd)); - - os_file_write(fd, (String) { executable.pointer, executable.length }); - os_file_close(fd); + file_write((FileWriteOptions) { + .path = exe_path, + .content = (String) { executable.pointer, executable.length }, + .executable = 1, + }); } break; case COMPILER_BACKEND_COUNT: unreachable(); @@ -24851,7 +24791,12 @@ void entry_point(int argc, char* argv[], char* envp[]) } String source_file_path = arguments.pointer[1]; - CompilerBackend compiler_backend = arguments.pointer[2].pointer[0]; + CompilerBackend compiler_backend = string_to_compiler_backend(arguments.pointer[2]); + if (compiler_backend == COMPILER_BACKEND_COUNT) + { + print("Invalid backend: {s}\n", arguments.pointer[2]); + failed_execution(); + } u8 emit_ir = arguments.length >= 4 && arguments.pointer[3].pointer[0] == 'y'; Target target = { @@ -24879,7 +24824,6 @@ void entry_point(int argc, char* argv[], char* envp[]) if (thread->main_function == -1) { - failed_execution(); } diff --git a/bootstrap/runner/runner.c b/bootstrap/runner/runner.c index a4e72a6..331b0af 100644 --- a/bootstrap/runner/runner.c +++ b/bootstrap/runner/runner.c @@ -22,28 +22,12 @@ typedef enum CMakeBuildType fn void run(Arena* arena, char** envp, String compiler_path, CompilerBackend compiler_backend, u8 debug, char* nest_source_path) { CStringSlice args = {}; - char* compiler_backend_string; - switch (compiler_backend) - { - // case COMPILER_BACKEND_C: - // compiler_backend_string = "c"; - // break; - // case COMPILER_BACKEND_INTERPRETER: - // compiler_backend_string = "i"; - // break; - - // TODO: change ch - case COMPILER_BACKEND_NEST: - compiler_backend_string = "m"; - break; - case COMPILER_BACKEND_COUNT: - unreachable(); - } + auto compiler_backend_string = compiler_backend_to_string(compiler_backend); #define common_compile_and_run_args \ string_to_c(compiler_path), \ nest_source_path, \ - compiler_backend_string, \ + string_to_c(compiler_backend_string), \ 0, if (debug) @@ -113,26 +97,12 @@ fn void run_tests(Arena* arena, String compiler_path, TestOptions const * const for (u32 engine_i = 0; engine_i < test_options->compiler_backends.length; engine_i += 1) { CompilerBackend compiler_backend = test_options->compiler_backends.pointer[engine_i]; - char* compiler_backend_string; - switch (compiler_backend) - { - // case COMPILER_BACKEND_C: - // compiler_backend_string = "c"; - // break; - // case COMPILER_BACKEND_INTERPRETER: - // compiler_backend_string = "i"; - // break; - case COMPILER_BACKEND_NEST: - compiler_backend_string = "m"; - break; - case COMPILER_BACKEND_COUNT: - unreachable(); - } + auto compiler_backend_string = compiler_backend_to_string(compiler_backend); char* arguments[] = { string_to_c(compiler_path), test_path_c, - compiler_backend_string, + string_to_c(compiler_backend_string), 0, }; @@ -185,7 +155,11 @@ void entry_point(int argc, char* argv[], char* envp[]) char* c_argument = argv[i]; auto argument = cstr(c_argument); - if (string_starts_with(argument, strlit("build_type="))) + if (string_to_compiler_backend(argument) != COMPILER_BACKEND_COUNT) + { + preferred_compiler_backend = string_to_compiler_backend(argument); + } + else if (string_starts_with(argument, strlit("build_type="))) { auto release_start = cast_to(u32, s32, string_first_ch(argument, '=') + 1); auto release_string = s_get_slice(u8, argument, release_start, argument.length); @@ -201,18 +175,6 @@ void entry_point(int argc, char* argv[], char* envp[]) assert(build_type != CMAKE_BUILD_TYPE_COUNT); } - // else if (s_equal(argument, strlit("i"))) - // { - // preferred_compiler_backend = COMPILER_BACKEND_INTERPRETER; - // } - // else if (s_equal(argument, strlit("c"))) - // { - // preferred_compiler_backend = COMPILER_BACKEND_C; - // } - else if (s_equal(argument, strlit("m"))) - { - preferred_compiler_backend = COMPILER_BACKEND_NEST; - } else if (s_equal(argument, strlit("test"))) { command = COMMAND_RUN_TESTS; @@ -314,10 +276,10 @@ void entry_point(int argc, char* argv[], char* envp[]) // strlit("tests/comparison.nat"), }; CompilerBackend all_compiler_backends[] = { - // COMPILER_BACKEND_INTERPRETER, - // COMPILER_BACKEND_C, COMPILER_BACKEND_NEST, + COMPILER_BACKEND_LLVM, }; + static_assert(array_length(all_compiler_backends) == COMPILER_BACKEND_COUNT); Slice(CompilerBackend) compiler_backend_selection; diff --git a/bootstrap/std/os.c b/bootstrap/std/os.c index 77e3e7e..630ab06 100644 --- a/bootstrap/std/os.c +++ b/bootstrap/std/os.c @@ -1350,6 +1350,23 @@ String file_read(Arena* arena, String path) return result; } +void file_write(FileWriteOptions options) +{ + auto fd = os_file_open(options.path, (OSFileOpenFlags) { + .write = 1, + .truncate = 1, + .create = 1, + .executable = options.executable, + }, (OSFilePermissions) { + .readable = 1, + .writable = 1, + .executable = options.executable, + }); + assert(os_file_descriptor_is_valid(fd)); + + os_file_write(fd, options.content); + os_file_close(fd); +} void run_command(Arena* arena, CStringSlice arguments, char* envp[]) {