Rewrite LLVM bindings
Some checks failed
CI / ci (Release, ubuntu-latest) (pull_request) Failing after 2s
CI / ci (Release-assertions, ubuntu-latest) (pull_request) Failing after 2s

This commit is contained in:
David Gonzalez Martin 2025-06-25 19:07:54 -06:00
parent 279036435a
commit 27a85a07d5
5 changed files with 110 additions and 153 deletions

View File

@ -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(
@ -31,23 +30,19 @@ add_compile_definitions(
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(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_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_ELF}
# ${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})

View File

@ -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<O0>"; },
.O1 => { passes = "default<O1>"; },
.O2 => { passes = "default<O2>"; },
.O3 => { passes = "default<O3>"; },
.Os => { passes = "default<Os>"; },
.Oz => { passes = "default<Oz>"; },
}
>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"))
{

View File

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

View File

@ -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<O0>"; break;
case BBLLVMOptimizationLevel::O1: passes = "default<O1>"; break;
case BBLLVMOptimizationLevel::O2: passes = "default<O2>"; break;
case BBLLVMOptimizationLevel::O3: passes = "default<O3>"; break;
case BBLLVMOptimizationLevel::Os: passes = "default<Os>"; break;
case BBLLVMOptimizationLevel::Oz: passes = "default<Oz>"; 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);
}

View File

@ -7,6 +7,7 @@
#include <llvm-c/Target.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/TargetMachine.h>
#include <llvm-c/Transforms/PassBuilder.h>
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);