From b75897cf232965f0904b4a5eafda93571b2883e4 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 25 Jun 2025 19:07:54 -0600 Subject: [PATCH] Rewrite LLVM bindings --- CMakeLists.txt | 23 ++++----- src/compiler.bbb | 130 ++++++++++++++++++++++------------------------- src/compiler.cpp | 4 +- src/emitter.cpp | 69 +++++++++++++++---------- src/llvm.hpp | 49 +----------------- 5 files changed, 118 insertions(+), 157 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e57d33..a23d9ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,6 @@ add_executable(bb ) add_library(c_abi tests/c_abi.c) -add_library(llvm_bindings src/llvm.cpp) include_directories(src) add_compile_definitions( @@ -29,25 +28,25 @@ add_compile_definitions( $<$>:BB_DEBUG=0> ) +find_library(llvm_bindings NAMES libllvm_bindings.dylib libllvm_bindings.lib libllvm_bindings.a libllvm_bindingsELF.dll.a libllvm_bindingsELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) -# find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) -# find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) -# find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) -# find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) -target_link_libraries(llvm_bindings PUBLIC +target_link_libraries(bb PUBLIC ${LLVM_AVAILABLE_LIBS} ${LLD_COMMON} - # ${LLD_COFF} + ${LLD_COFF} ${LLD_ELF} - # ${LLD_MACHO} - # ${LLD_MINGW} - # ${LLD_WASM} + ${LLD_MACHO} + ${LLD_MINGW} + ${LLD_WASM} + ${llvm_bindings} ) -target_link_libraries(bb PUBLIC llvm_bindings) - add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -fno-signed-char -fwrapv -fno-strict-aliasing) add_compile_definitions(CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}") add_compile_definitions(BB_CI=${BB_CI}) diff --git a/src/compiler.bbb b/src/compiler.bbb index ac95311..4780872 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -835,6 +835,8 @@ fail = fn () noreturn exit(1); } +LLVMError = opaque; + LLVMContext = opaque; LLVMModule = opaque; @@ -859,6 +861,8 @@ LLVMTargetDataLayout = opaque; LLVMTargetMachine = opaque; LLVMTargetMachineOptions = opaque; +LLVMPassBuilderOptions = opaque; + LLVMIntrinsicIndex = enum u32 { "llvm.ctlz", @@ -2646,43 +2650,10 @@ LLVMOptimizationLevel = enum u3 Oz = 5, } -LLVMOptimizationOptions = bits u64 -{ - optimization_level: LLVMOptimizationLevel, - debug_info: u1, - loop_unrolling: u1, - loop_interleaving: u1, - loop_vectorization: u1, - slp_vectorization: u1, - merge_functions: u1, - call_graph_profile: u1, - unified_lto: u1, - assignment_tracking: u1, - verify_module: u1, - reserved: u51, -} - -LLVMCodeGenerationFileType = enum u8 +LLVMCodeGenerationFileType = enum u32 { assembly = 0, object = 1, - null = 2, -} - -LLVMCodeGenerationOptions = struct -{ - output_dwarf_file_path: []u8, - output_file_path: []u8, - file_type: LLVMCodeGenerationFileType, - optimize_when_possible: u8, - verify_module: u8, -} - -LLVMCodeGenerationResult = enum u8 -{ - success = 0, - failed_to_create_file = 1, - failed_to_emit_passes = 2, } LLVMICmpPredicate = enum u32 @@ -2931,8 +2902,19 @@ llvm_module_create_function = fn (module: &LLVMModule, function_type: &LLVMType, [extern] LLVMSetFunctionCallConv = fn [cc(c)] (function: &LLVMValue, calling_convention: LLVMCallingConvention) void; [extern] LLVMSetInstructionCallConv = fn [cc(c)] (call: &LLVMValue, calling_convention: LLVMCallingConvention) void; -[extern] llvm_module_run_optimization_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMOptimizationOptions) void; -[extern] llvm_module_run_code_generation_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMCodeGenerationOptions) LLVMCodeGenerationResult; +[extern] LLVMCreatePassBuilderOptions = fn [cc(c)] () &LLVMPassBuilderOptions; + +[extern] LLVMPassBuilderOptionsSetVerifyEach = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetDebugLogging = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetLoopInterleaving = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetLoopVectorization = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetSLPVectorization = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetLoopUnrolling = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; +[extern] LLVMPassBuilderOptionsSetMergeFunctions = fn [cc(c)] (options: &LLVMPassBuilderOptions, value: s32) void; + +[extern] LLVMRunPasses = fn [cc(c)] (module: &LLVMModule, passes: &u8, target_machine: &LLVMTargetMachine, options: &LLVMPassBuilderOptions) &LLVMError; + +[extern] LLVMTargetMachineEmitToFile = fn [cc(c)] (target_machine: &LLVMTargetMachine, module: &LLVMModule, file_name: &u8, file_type: LLVMCodeGenerationFileType, error_message: &&u8) s32; LLDResult = struct { @@ -17016,38 +16998,52 @@ LLVMObjectGenerate = struct has_debug_info: u1, } -generate_object = fn (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: LLVMObjectGenerate) LLVMCodeGenerationResult +generate_object = fn (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: LLVMObjectGenerate) u1 { if (options.run_optimization_passes) { - >prefer_speed = options.optimization_level == .O2 or options.optimization_level == .O3; - >options: LLVMOptimizationOptions = { - .optimization_level = options.optimization_level, - .debug_info = options.has_debug_info, - .loop_unrolling = prefer_speed, - .loop_interleaving = prefer_speed, - .loop_vectorization = prefer_speed, - .slp_vectorization = prefer_speed, - .merge_functions = prefer_speed, - .call_graph_profile = 0, - .unified_lto = 0, - .assignment_tracking = options.has_debug_info, - .verify_module = 1, - zero, - }; - llvm_module_run_optimization_pipeline(module, target_machine, &options); + >prefer_speed: s32 = @extend(options.optimization_level == .O2 or options.optimization_level == .O3); + >pass_builder_options = LLVMCreatePassBuilderOptions(); + LLVMPassBuilderOptionsSetVerifyEach(pass_builder_options, 1); + LLVMPassBuilderOptionsSetDebugLogging(pass_builder_options, 0); + LLVMPassBuilderOptionsSetLoopInterleaving(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetLoopVectorization(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetSLPVectorization(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetLoopUnrolling(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetMergeFunctions(pass_builder_options, prefer_speed); + + >passes: &u8 = undefined; + switch (options.optimization_level) + { + .O0 => { passes = "default"; }, + .O1 => { passes = "default"; }, + .O2 => { passes = "default"; }, + .O3 => { passes = "default"; }, + .Os => { passes = "default"; }, + .Oz => { passes = "default"; }, + } + + >error = LLVMRunPasses(module, passes, target_machine, pass_builder_options); + if (error) + { + report_error(); + } } - >code_generation_options: LLVMCodeGenerationOptions = { - .output_file_path = options.path, - .file_type = .object, - .optimize_when_possible = @extend(options.optimization_level > .O0), - .verify_module = 1, - zero, - }; + >file_name = options.path.pointer; + >error_message: &u8 = zero; + >result = LLVMTargetMachineEmitToFile(target_machine, module, file_name, .object, &error_message); + if (result != 0) + { + assert(error_message != zero); + @trap(); + } + else + { + assert(!error_message); + } - >result = llvm_module_run_code_generation_pipeline(module, target_machine, &code_generation_options); - return result; + return result == 0; } link = fn (module: &Module) void @@ -18106,10 +18102,7 @@ emit = fn (module: &Module) void .has_debug_info = module.has_debug_info, }); - if (object_generation_result != .success) - { - report_error(); - } + assert(object_generation_result); link(module); } @@ -18370,7 +18363,6 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile, envp: &&u8) []u8 >objects = [ output_object_path ][..]; >c_abi_library = "build/libc_abi.a"; - >llvm_bindings_library = "build/libllvm_bindings.a"; >library_buffer: [256][]u8 = undefined; >library_directory: []u8 = zero; @@ -18488,8 +18480,10 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile, envp: &&u8) []u8 library_buffer[library_count] = "lldELF"; library_count += 1; + library_buffer[library_count] = "llvm_bindings"; + library_count += 1; + library_names = library_buffer[..library_count]; - library_paths = { .pointer = &llvm_bindings_library, .length = 1 }; } else if (string_equal(base_name, "tests")) { diff --git a/src/compiler.cpp b/src/compiler.cpp index 94e0624..823ca93 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -346,8 +346,10 @@ fn String compile_file(Arena* arena, Compile options) library_buffer[library_count] = string_literal("lldELF"); library_count += 1; + library_buffer[library_count] = string_literal("llvm_bindings"); + library_count += 1; + library_names = { library_buffer, library_count }; - library_paths = { &llvm_bindings_library, 1 }; } else if (base_name.equal(string_literal("tests"))) { diff --git a/src/emitter.cpp b/src/emitter.cpp index 42dd043..3bd9e25 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -9176,36 +9176,52 @@ struct ObjectGenerate bool has_debug_info; }; -fn BBLLVMCodeGenerationPipelineResult generate_object(LLVMModuleRef module, LLVMTargetMachineRef target_machine, ObjectGenerate options) +fn bool generate_object(LLVMModuleRef module, LLVMTargetMachineRef target_machine, ObjectGenerate options) { if (options.run_optimization_passes) { - // BBLLVM bool prefer_speed = options.optimization_level == BBLLVMOptimizationLevel::O2 || options.optimization_level == BBLLVMOptimizationLevel::O3; - BBLLVMOptimizationPipelineOptions optimization_options = { - .optimization_level = (u64)options.optimization_level, - .debug_info = options.has_debug_info, - .loop_unrolling = prefer_speed, - .loop_interleaving = prefer_speed, - .loop_vectorization = prefer_speed, - .slp_vectorization = prefer_speed, - .merge_functions = prefer_speed, - .call_graph_profile = false, - .unified_lto = false, - .assignment_tracking = options.has_debug_info, - .verify_module = true, - }; - llvm_module_run_optimization_pipeline(module, target_machine, optimization_options); + auto pass_builder_options = LLVMCreatePassBuilderOptions(); + LLVMPassBuilderOptionsSetVerifyEach(pass_builder_options, 1); + LLVMPassBuilderOptionsSetDebugLogging(pass_builder_options, 0); + LLVMPassBuilderOptionsSetLoopInterleaving(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetLoopVectorization(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetSLPVectorization(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetLoopUnrolling(pass_builder_options, prefer_speed); + LLVMPassBuilderOptionsSetMergeFunctions(pass_builder_options, prefer_speed); + + const char* passes; + switch (options.optimization_level) + { + case BBLLVMOptimizationLevel::O0: passes = "default"; break; + case BBLLVMOptimizationLevel::O1: passes = "default"; break; + case BBLLVMOptimizationLevel::O2: passes = "default"; break; + case BBLLVMOptimizationLevel::O3: passes = "default"; break; + case BBLLVMOptimizationLevel::Os: passes = "default"; break; + case BBLLVMOptimizationLevel::Oz: passes = "default"; break; + } + + auto error = LLVMRunPasses(module, passes, target_machine, pass_builder_options); + if (error) + { + report_error(); + } } - BBLLVMCodeGenerationPipelineOptions code_generation_options = { - .output_file_path = options.path, - .file_type = BBLLVMCodeGenerationFileType::object_file, - .optimize_when_possible = options.optimization_level > BBLLVMOptimizationLevel::O0, - .verify_module = true, - }; - auto result = llvm_module_run_code_generation_pipeline(module, target_machine, &code_generation_options); - return result; + auto file_name = cstr(options.path); + char* error_message = 0; + auto result = LLVMTargetMachineEmitToFile(target_machine, module, file_name, LLVMObjectFile, &error_message); + if (result) + { + assert(error_message); + trap(); + } + else + { + assert(!error_message); + } + + return !result; } fn void link(Module* module) @@ -10057,10 +10073,7 @@ void emit(Module* module) .run_optimization_passes = module->build_mode != BuildMode::debug_none, .has_debug_info = module->has_debug_info, }); - if (object_generation_result != BBLLVMCodeGenerationPipelineResult::success) - { - report_error(); - } + assert(object_generation_result); link(module); } diff --git a/src/llvm.hpp b/src/llvm.hpp index dafd084..eab0a12 100644 --- a/src/llvm.hpp +++ b/src/llvm.hpp @@ -7,6 +7,7 @@ #include #include #include +#include struct LLDResult { @@ -15,31 +16,6 @@ struct LLDResult bool success; }; -enum class BBLLVMCodeGenerationPipelineResult : u8 -{ - success = 0, - failed_to_create_file = 1, - failed_to_add_emit_passes = 2, -}; - -enum class BBLLVMCodeGenerationFileType : u8 -{ - assembly_file = 0, - object_file = 1, - null = 2, -}; - -struct BBLLVMCodeGenerationPipelineOptions -{ - String output_dwarf_file_path; - String output_file_path; - BBLLVMCodeGenerationFileType file_type; - bool optimize_when_possible; - bool verify_module; -}; - -static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); - enum class BBLLVMOptimizationLevel : u8 { O0 = 0, @@ -50,26 +26,6 @@ enum class BBLLVMOptimizationLevel : u8 Oz = 5, }; -#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) -struct BBLLVMOptimizationPipelineOptions -{ - u64 optimization_level:3; - u64 debug_info:1; - u64 loop_unrolling:1; - u64 loop_interleaving:1; - u64 loop_vectorization:1; - u64 slp_vectorization:1; - u64 merge_functions:1; - u64 call_graph_profile:1; - u64 unified_lto:1; - u64 assignment_tracking:1; - u64 verify_module:1; - u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); -static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); - enum class DwarfType { void_type = 0x0, @@ -123,9 +79,6 @@ extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type); -extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options); -extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options); - #define lld_api_args() char* const* argument_pointer, u64 argument_count, bool exit_early, bool disable_output #define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) extern "C" lld_api_function_decl(elf);