#include #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Frontend/Driver/CodeGenOptions.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/SubtargetFeature.h" #include "llvm/Target/TargetMachine.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/FileSystem.h" #define EXPORT extern "C" #define fn static typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; using namespace llvm; struct BBLLVMString { const char* pointer; size_t length; inline StringRef string_ref() const { return { pointer, length }; } }; EXPORT Module* llvm_context_create_module(LLVMContext& context, BBLLVMString name) { return new Module(name.string_ref(), context); } EXPORT Value* llvm_builder_create_add(IRBuilder<>& builder, Value* left, Value* right, bool nuw, bool nsw) { auto* result = builder.CreateAdd(left, right, "", nuw, nsw); return result; } EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, BBLLVMString name) { auto* function = Function::Create(function_type, linkage_type, address_space, name.string_ref(), module); return function; } EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, BBLLVMString name, bool is_packed) { auto types = ArrayRef(type_pointer, type_count); auto* struct_type = StructType::create(context, types, name.string_ref(), is_packed); return struct_type; } EXPORT StructType* llvm_context_get_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, bool is_packed) { auto types = ArrayRef(type_pointer, type_count); auto* struct_type = StructType::get(context, types, is_packed); return struct_type; } EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, BBLLVMString name, Function* parent) { auto* basic_block = BasicBlock::Create(context, name.string_ref(), parent); return basic_block; } fn BBLLVMString stream_to_string(raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based stream.flush(); auto string = stream.str(); auto length = string.length(); char* result = 0; if (length) { result = new char[length]; memcpy(result, string.c_str(), length); } return { result, length }; } EXPORT bool llvm_function_verify(Function& function, BBLLVMString* error_message) { std::string message_buffer; raw_string_ostream message_stream(message_buffer); bool result = verifyFunction(function, &message_stream); auto size = message_stream.str().size(); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } EXPORT bool llvm_module_verify(const Module& module, BBLLVMString* error_message) { std::string message_buffer; raw_string_ostream message_stream(message_buffer); bool result = verifyModule(module, &message_stream); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } EXPORT BBLLVMString llvm_module_to_string(Module* module) { std::string buffer; raw_string_ostream stream(buffer); module->print(stream, 0); return stream_to_string(stream); } EXPORT BBLLVMString llvm_default_target_triple() { auto triple = llvm::sys::getDefaultTargetTriple(); auto length = triple.length(); char* pointer = 0; if (length) { pointer = new char[length]; memcpy(pointer, triple.c_str(), length); } return { pointer, length }; } EXPORT BBLLVMString llvm_host_cpu_name() { auto cpu = llvm::sys::getHostCPUName(); return { cpu.data(), cpu.size() }; } EXPORT BBLLVMString llvm_host_cpu_features() { SubtargetFeatures Features; for (const auto &[Feature, IsEnabled] : sys::getHostCPUFeatures()) { Features.AddFeature(Feature, IsEnabled); } auto feature_string = Features.getString(); auto length = feature_string.length(); char* result = 0; if (length) { result = new char[length]; memcpy(result, feature_string.c_str(), length); } return { result, length }; } enum class BBLLVMEmitDwarfUnwindType : u8 { always = 0, no_compact_unwind = 1, normal = 2, }; enum class BBLLVMDwarfDirectory : u8 { disable = 0, enable = 1, normal = 2, }; enum class BBLLVMDebugCompressionType : u8 { none = 0, zlib = 1, zstd = 2, }; #define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) struct BBLLVMMCTargetOptions { BBLLVMString abi_name; BBLLVMString assembly_language; BBLLVMString split_dwarf_file; BBLLVMString as_secure_log_file; const char* argv0; BBLLVMString* argv_pointer; u64 argv_count; BBLLVMString* integrated_assembler_search_path_pointer; u64 integrated_assembler_search_path_count; u32 relax_all:1; u32 no_exec_stack:1; u32 fatal_warnings:1; u32 no_warn:1; u32 no_deprecated_warn:1; u32 no_type_check:1; u32 save_temp_labels:1; u32 incremental_linker_compatible:1; u32 fdpic:1; u32 show_mc_encoding:1; u32 show_mc_inst:1; u32 asm_verbose:1; u32 preserve_asm_comments:1; u32 dwarf64:1; u32 crel:1; u32 x86_relax_relocations:1; u32 x86_sse2_avx:1; u32 emit_dwarf_unwind:2; u32 use_dwarf_directory:2; u32 debug_compression_type:2; u32 emit_compact_unwind_non_canonical:1; u32 ppc_use_full_register_names:1; u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; }; static_assert(sizeof(BBLLVMMCTargetOptions) == 112); static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); enum class BBLLVMCodeModel : u8 { none = 0, tiny = 1, small = 2, kernel = 3, medium = 4, large = 5, }; enum class BBLLVMRelocationModel : u8 { default_relocation = 0, static_relocation = 1, pic = 2, dynamic_no_pic = 3, ropi = 4, rwpi = 5, ropi_rwpi = 6, }; enum class BBLLVMCodeGenerationOptimizationLevel : u8 { none = 0, // -O0 less = 1, // -O1 normal = 2, // -O2, -Os aggressive = 3 // -O3 }; enum class BBLLVMGlobalISelAbortMode : u8 { disable = 0, enable = 1, disable_with_diag = 2, }; enum class BBLLVMSwiftAsyncFramePointerMode : u8 { deployment_based = 0, always = 1, never = 2, }; enum class BBLLVMBasicBlockSection : u8 { all = 0, list = 1, labels = 2, preset = 3, none = 4, }; enum class BBLLVMFloatAbi : u8 { normal = 0, soft = 1, hard = 2, }; enum class BBLLVMFPOpFusion : u8 { fast = 0, standard = 1, strict = 2, }; enum class BBLLVMThreadModel : u8 { posix = 0, single = 1, }; enum class BBLLVMEAbi : u8 { unknown = 0, normal = 1, eabi4 = 2, eabi5 = 3, gnu = 4, }; enum class BBLLVMDebuggerKind : u8 { normal = 0, gdb = 1, lldb = 2, sce = 3, dbx = 4, }; enum class BBLLVMExceptionHandling : u8 { none = 0, dwarf_cfi = 1, setjmp_longjmp = 2, arm = 3, win_eh = 4, wasm = 5, aix = 6, zos = 7, }; #define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) struct BBLLVMTargetOptions { u64 unsafe_fp_math:1; u64 no_infs_fp_math:1; u64 no_nans_fp_math:1; u64 no_trapping_fp_math:1; u64 no_signed_zeroes_fp_math:1; u64 approx_func_fp_match:1; u64 enable_aix_extended_altivec_abi:1; u64 honor_sign_dependent_rounding_fp_math:1; u64 no_zeroes_in_bss:1; u64 guaranteed_tail_call_optimization:1; u64 stack_symbol_ordering:1; u64 enable_fast_isel:1; u64 enable_global_isel:1; u64 global_isel_abort_mode:2; u64 swift_async_frame_pointer:2; u64 use_init_array:1; u64 disable_integrated_assembler:1; u64 function_sections:1; u64 data_sections:1; u64 ignore_xcoff_visibility:1; u64 xcoff_traceback_table:1; u64 unique_section_names:1; u64 unique_basic_block_section_names:1; u64 separate_named_sections:1; u64 trap_unreachable:1; u64 no_trap_after_noreturn:1; u64 tls_size:8; u64 emulated_tls:1; u64 enable_tls_descriptors:1; u64 enable_ipra:1; u64 emit_stack_size_section:1; u64 enable_machine_outliner:1; u64 enable_machine_function_splitter:1; u64 supports_default_outlining:1; u64 emit_address_significance_table:1; u64 bb_address_map:1; u64 bb_sections:3; u64 emit_call_site_information:1; u64 supports_debug_entry_values:1; u64 enable_debug_entry_values:1; u64 value_tracking_variable_locations:1; u64 force_dwarf_frame_section:1; u64 xray_function_index:1; u64 debug_strict_dwarf:1; u64 hotpatch:1; u64 ppc_gen_scalar_mass_entries:1; u64 jmc_instrument:1; u64 enable_cfi_fixup:1; u64 mis_expect:1; u64 xcoff_read_only_pointers:1; u64 float_abi:2; u64 thread_model:1; u32 fp_op_fusion_mode:2; u32 eabi_version:3; u32 debugger_kind:3; u32 exception_handling:3; u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; unsigned loop_alignment; int binutils_version[2]; BBLLVMMCTargetOptions mc; }; static_assert(sizeof(BBLLVMTargetOptions) == 136); static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); #define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) struct BBLLVMTargetMachineCreate { BBLLVMTargetOptions target_options; BBLLVMString target_triple; BBLLVMString cpu_model; BBLLVMString cpu_features; BBLLVMRelocationModel relocation_model; BBLLVMCodeModel code_model; BBLLVMCodeGenerationOptimizationLevel optimization_level; bool jit; u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; }; static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, BBLLVMString* error_message) { std::string error_message_string; const Target* target = TargetRegistry::lookupTarget(create.target_triple.string_ref(), error_message_string); TargetMachine* target_machine; if (target) { std::optional code_model; switch (create.code_model) { case BBLLVMCodeModel::none: code_model = std::nullopt; break; case BBLLVMCodeModel::tiny: code_model = CodeModel::Tiny; break; case BBLLVMCodeModel::small: code_model = CodeModel::Small; break; case BBLLVMCodeModel::kernel: code_model = CodeModel::Kernel; break; case BBLLVMCodeModel::medium: code_model = CodeModel::Medium; break; case BBLLVMCodeModel::large: code_model = CodeModel::Large; break; } std::optional relocation_model; switch (create.relocation_model) { case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break; case BBLLVMRelocationModel::static_relocation: relocation_model = Reloc::Static; break; case BBLLVMRelocationModel::pic: relocation_model = Reloc::PIC_; break; case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = Reloc::DynamicNoPIC; break; case BBLLVMRelocationModel::ropi: relocation_model = Reloc::ROPI; break; case BBLLVMRelocationModel::rwpi: relocation_model = Reloc::RWPI; break; case BBLLVMRelocationModel::ropi_rwpi: relocation_model = Reloc::ROPI_RWPI; break; } CodeGenOptLevel optimization_level; switch (create.optimization_level) { case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = CodeGenOptLevel::None; break; case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = CodeGenOptLevel::Less; break; case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = CodeGenOptLevel::Default; break; case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = CodeGenOptLevel::Aggressive; break; } // INFO: This calls the default constructor, so all LLVM defaults are set and we only override what we control TargetOptions target_options; target_options.UnsafeFPMath = create.target_options.unsafe_fp_math; target_options.NoInfsFPMath = create.target_options.no_infs_fp_math; target_options.NoNaNsFPMath = create.target_options.no_nans_fp_math; target_options.NoTrappingFPMath = create.target_options.no_trapping_fp_math; target_options.NoSignedZerosFPMath = create.target_options.no_signed_zeroes_fp_math; target_options.ApproxFuncFPMath = create.target_options.approx_func_fp_match; target_options.EnableAIXExtendedAltivecABI = create.target_options.enable_aix_extended_altivec_abi; target_options.HonorSignDependentRoundingFPMathOption = create.target_options.honor_sign_dependent_rounding_fp_math; target_options.NoZerosInBSS = create.target_options.no_zeroes_in_bss; target_options.GuaranteedTailCallOpt = create.target_options.guaranteed_tail_call_optimization; target_options.StackSymbolOrdering = create.target_options.stack_symbol_ordering; target_options.EnableFastISel = create.target_options.enable_fast_isel; target_options.EnableGlobalISel = create.target_options.enable_global_isel; auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create.target_options.global_isel_abort_mode; switch (global_isel_abort_mode) { case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = GlobalISelAbortMode::Disable; break; case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = GlobalISelAbortMode::Enable; break; case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = GlobalISelAbortMode::DisableWithDiag; break; } auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create.target_options.swift_async_frame_pointer; switch (swift_async_frame_pointer) { case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::DeploymentBased; break; case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Always; break; case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Never; break; } target_options.UseInitArray = create.target_options.use_init_array; target_options.DisableIntegratedAS = create.target_options.disable_integrated_assembler; target_options.FunctionSections = create.target_options.function_sections; target_options.DataSections = create.target_options.data_sections; target_options.IgnoreXCOFFVisibility = create.target_options.ignore_xcoff_visibility; target_options.XCOFFTracebackTable = create.target_options.xcoff_traceback_table; target_options.UniqueSectionNames = create.target_options.unique_section_names; target_options.UniqueBasicBlockSectionNames = create.target_options.unique_basic_block_section_names; target_options.SeparateNamedSections = create.target_options.separate_named_sections; target_options.TrapUnreachable = create.target_options.trap_unreachable; target_options.NoTrapAfterNoreturn = create.target_options.no_trap_after_noreturn; target_options.TLSSize = create.target_options.tls_size; target_options.EmulatedTLS = create.target_options.emulated_tls; target_options.EnableTLSDESC = create.target_options.enable_tls_descriptors; target_options.EnableIPRA = create.target_options.enable_ipra; target_options.EmitStackSizeSection = create.target_options.emit_stack_size_section; target_options.EnableMachineOutliner = create.target_options.enable_machine_outliner; target_options.EnableMachineFunctionSplitter = create.target_options.enable_machine_function_splitter; target_options.SupportsDefaultOutlining = create.target_options.supports_default_outlining; target_options.EmitAddrsig = create.target_options.emit_address_significance_table; target_options.BBAddrMap = create.target_options.bb_address_map; auto bb_sections = (BBLLVMBasicBlockSection) create.target_options.bb_sections; switch (bb_sections) { case BBLLVMBasicBlockSection::all: target_options.BBSections = BasicBlockSection::All; break; case BBLLVMBasicBlockSection::list: target_options.BBSections = BasicBlockSection::List; break; case BBLLVMBasicBlockSection::labels: target_options.BBSections = BasicBlockSection::Labels; break; case BBLLVMBasicBlockSection::preset: target_options.BBSections = BasicBlockSection::Preset; break; case BBLLVMBasicBlockSection::none: target_options.BBSections = BasicBlockSection::None; break; } target_options.EmitCallSiteInfo = create.target_options.emit_call_site_information; target_options.SupportsDebugEntryValues = create.target_options.supports_debug_entry_values; target_options.EnableDebugEntryValues = create.target_options.enable_debug_entry_values; target_options.ValueTrackingVariableLocations = create.target_options.value_tracking_variable_locations; target_options.ForceDwarfFrameSection = create.target_options.force_dwarf_frame_section; target_options.XRayFunctionIndex = create.target_options.xray_function_index; target_options.DebugStrictDwarf = create.target_options.debug_strict_dwarf; target_options.Hotpatch = create.target_options.hotpatch; target_options.PPCGenScalarMASSEntries = create.target_options.ppc_gen_scalar_mass_entries; target_options.JMCInstrument = create.target_options.jmc_instrument; target_options.EnableCFIFixup = create.target_options.enable_cfi_fixup; target_options.MisExpect = create.target_options.mis_expect; target_options.XCOFFReadOnlyPointers = create.target_options.xcoff_read_only_pointers; auto float_abi = (BBLLVMFloatAbi) create.target_options.float_abi; switch (float_abi) { case BBLLVMFloatAbi::normal: target_options.FloatABIType = FloatABI::Default; break; case BBLLVMFloatAbi::soft: target_options.FloatABIType = FloatABI::Soft; break; case BBLLVMFloatAbi::hard: target_options.FloatABIType = FloatABI::Hard; break; } auto thread_model = (BBLLVMThreadModel) create.target_options.thread_model; switch (thread_model) { case BBLLVMThreadModel::posix: target_options.ThreadModel = ThreadModel::POSIX; break; case BBLLVMThreadModel::single: target_options.ThreadModel = ThreadModel::Single; break; } auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create.target_options.fp_op_fusion_mode; switch (fp_op_fusion_mode) { case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = FPOpFusion::Fast; break; case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = FPOpFusion::Standard; break; case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = FPOpFusion::Strict; break; } auto eabi_version = (BBLLVMEAbi) create.target_options.eabi_version; switch (eabi_version) { case BBLLVMEAbi::unknown: target_options.EABIVersion = EABI::Unknown; break; case BBLLVMEAbi::normal: target_options.EABIVersion = EABI::Default; break; case BBLLVMEAbi::eabi4: target_options.EABIVersion = EABI::EABI4; break; case BBLLVMEAbi::eabi5: target_options.EABIVersion = EABI::EABI5; break; case BBLLVMEAbi::gnu: target_options.EABIVersion = EABI::GNU; break; } auto debugger_kind = (BBLLVMDebuggerKind) create.target_options.debugger_kind; switch (debugger_kind) { case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = DebuggerKind::Default; break; case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = DebuggerKind::GDB; break; case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = DebuggerKind::LLDB; break; case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = DebuggerKind::SCE; break; case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = DebuggerKind::DBX; break; } auto exception_handling = (BBLLVMExceptionHandling) create.target_options.exception_handling; switch (exception_handling) { case BBLLVMExceptionHandling::none: target_options.ExceptionModel = ExceptionHandling::None; break; case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = ExceptionHandling::DwarfCFI; break; case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = ExceptionHandling::SjLj; break; case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = ExceptionHandling::ARM; break; case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = ExceptionHandling::WinEH; break; case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = ExceptionHandling::Wasm; break; case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = ExceptionHandling::AIX; break; case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = ExceptionHandling::ZOS; break; } target_options.LoopAlignment = create.target_options.loop_alignment; target_options.BinutilsVersion = { create.target_options.binutils_version[0], create.target_options.binutils_version[1] }; if (create.target_options.mc.abi_name.length) { target_options.MCOptions.ABIName = { create.target_options.mc.abi_name.pointer, create.target_options.mc.abi_name.length }; } if (create.target_options.mc.assembly_language.length) { target_options.MCOptions.AssemblyLanguage = { create.target_options.mc.assembly_language.pointer, create.target_options.mc.assembly_language.length }; } if (create.target_options.mc.split_dwarf_file.length) { target_options.MCOptions.SplitDwarfFile = { create.target_options.mc.split_dwarf_file.pointer, create.target_options.mc.split_dwarf_file.length }; } if (create.target_options.mc.as_secure_log_file.length) { target_options.MCOptions.AsSecureLogFile = { create.target_options.mc.as_secure_log_file.pointer, create.target_options.mc.as_secure_log_file.length }; } target_options.MCOptions.Argv0 = create.target_options.mc.argv0; if (create.target_options.mc.argv_count) { // TODO: __builtin_trap(); } if (create.target_options.mc.integrated_assembler_search_path_count) { // TODO: __builtin_trap(); } target_options.MCOptions.MCRelaxAll = create.target_options.mc.relax_all; target_options.MCOptions.MCNoExecStack = create.target_options.mc.no_exec_stack; target_options.MCOptions.MCFatalWarnings = create.target_options.mc.fatal_warnings; target_options.MCOptions.MCNoWarn = create.target_options.mc.no_warn; target_options.MCOptions.MCNoDeprecatedWarn = create.target_options.mc.no_deprecated_warn; target_options.MCOptions.MCNoTypeCheck = create.target_options.mc.no_type_check; target_options.MCOptions.MCSaveTempLabels = create.target_options.mc.save_temp_labels; target_options.MCOptions.MCIncrementalLinkerCompatible = create.target_options.mc.incremental_linker_compatible; target_options.MCOptions.FDPIC = create.target_options.mc.fdpic; target_options.MCOptions.ShowMCEncoding = create.target_options.mc.show_mc_encoding; target_options.MCOptions.ShowMCInst = create.target_options.mc.show_mc_inst; target_options.MCOptions.AsmVerbose = create.target_options.mc.asm_verbose; target_options.MCOptions.PreserveAsmComments = create.target_options.mc.preserve_asm_comments; target_options.MCOptions.Dwarf64 = create.target_options.mc.dwarf64; target_options.MCOptions.Crel = create.target_options.mc.crel; target_options.MCOptions.X86RelaxRelocations = create.target_options.mc.x86_relax_relocations; target_options.MCOptions.X86Sse2Avx = create.target_options.mc.x86_sse2_avx; auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create.target_options.mc.emit_dwarf_unwind; switch (emit_dwarf_unwind) { case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Always; break; case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::NoCompactUnwind; break; case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Default; break; } auto use_dwarf_directory = (BBLLVMDwarfDirectory) create.target_options.mc.use_dwarf_directory; switch (use_dwarf_directory) { case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break; case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break; } auto debug_compression_type = (BBLLVMDebugCompressionType) create.target_options.mc.debug_compression_type; switch (debug_compression_type) { case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = DebugCompressionType::None; break; case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib; break; case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd; break; } target_options.MCOptions.EmitCompactUnwindNonCanonical = create.target_options.mc.emit_compact_unwind_non_canonical; target_options.MCOptions.PPCUseFullRegisterNames = create.target_options.mc.ppc_use_full_register_names; target_machine = target->createTargetMachine(create.target_triple.string_ref(), create.cpu_model.string_ref(), create.cpu_features.string_ref(), target_options, relocation_model, code_model, optimization_level, create.jit); } else { auto length = error_message_string.length(); char* result = new char[length]; memcpy(result, error_message_string.c_str(), length); *error_message = { result, length }; target_machine = 0; } return target_machine; } EXPORT void llvm_module_set_target(Module& module, TargetMachine& target_machine) { module.setDataLayout(target_machine.createDataLayout()); auto& triple_string = target_machine.getTargetTriple().getTriple(); module.setTargetTriple(StringRef(triple_string)); } enum class BBLLVMOptimizationLevel : u8 { O0 = 0, O1 = 1, O2 = 2, O3 = 3, Os = 4, 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); EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) { // TODO: PGO // TODO: CS profile PipelineTuningOptions pipeline_tuning_options; pipeline_tuning_options.LoopUnrolling = options.loop_unrolling; pipeline_tuning_options.LoopInterleaving = options.loop_interleaving; pipeline_tuning_options.LoopVectorization = options.loop_vectorization; pipeline_tuning_options.SLPVectorization = options.slp_vectorization; pipeline_tuning_options.MergeFunctions = options.merge_functions; pipeline_tuning_options.CallGraphProfile = options.call_graph_profile; pipeline_tuning_options.UnifiedLTO = options.unified_lto; // TODO: instrumentation LoopAnalysisManager loop_analysis_manager; FunctionAnalysisManager function_analysis_manager; CGSCCAnalysisManager cgscc_analysis_manager; ModuleAnalysisManager module_analysis_manager; PassBuilder pass_builder(&target_machine, pipeline_tuning_options); if (options.assignment_tracking && options.debug_info != 0) { pass_builder.registerPipelineStartEPCallback([&](ModulePassManager& MPM, OptimizationLevel Level) { MPM.addPass(AssignmentTrackingPass()); }); } Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); function_analysis_manager.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); pass_builder.registerModuleAnalyses(module_analysis_manager); pass_builder.registerCGSCCAnalyses(cgscc_analysis_manager); pass_builder.registerFunctionAnalyses(function_analysis_manager); pass_builder.registerLoopAnalyses(loop_analysis_manager); pass_builder.crossRegisterProxies(loop_analysis_manager, function_analysis_manager, cgscc_analysis_manager, module_analysis_manager); ModulePassManager module_pass_manager; if (options.verify_module) { module_pass_manager.addPass(VerifierPass()); } bool thin_lto = false; bool lto = false; OptimizationLevel optimization_level; switch ((BBLLVMOptimizationLevel)options.optimization_level) { case BBLLVMOptimizationLevel::O0: optimization_level = OptimizationLevel::O0; break; case BBLLVMOptimizationLevel::O1: optimization_level = OptimizationLevel::O1; break; case BBLLVMOptimizationLevel::O2: optimization_level = OptimizationLevel::O2; break; case BBLLVMOptimizationLevel::O3: optimization_level = OptimizationLevel::O3; break; case BBLLVMOptimizationLevel::Os: optimization_level = OptimizationLevel::Os; break; case BBLLVMOptimizationLevel::Oz: optimization_level = OptimizationLevel::Oz; break; } // TODO: thin lto post-link // TODO: instrument if (thin_lto) { __builtin_trap(); // TODO } else if (lto) { __builtin_trap(); // TODO } else if (lto) { __builtin_trap(); // TODO } else { module_pass_manager.addPass(pass_builder.buildPerModuleDefaultPipeline(optimization_level, lto)); } // TODO: if emit bitcode/IR module_pass_manager.run(module, module_analysis_manager); } enum class BBLLVMCodeGenerationFileType : u8 { assembly_file = 0, object_file = 1, null = 2, }; #define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) struct BBLLVMCodeGenerationPipelineOptions { BBLLVMString output_dwarf_file_path; BBLLVMString output_file_path; u64 code_generation_file_type:2; u64 optimize_when_possible:1; u64 verify_module:1; u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; }; static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); enum class BBLLVMCodeGenerationPipelineResult : u8 { success = 0, failed_to_create_file = 1, failed_to_add_emit_passes = 2, }; EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(Module& module, TargetMachine& target_machine, BBLLVMCodeGenerationPipelineOptions options) { // We still use the legacy PM to run the codegen pipeline since the new PM // does not work with the codegen pipeline. // FIXME: make the new PM work with the codegen pipeline. legacy::PassManager CodeGenPasses; if (options.optimize_when_possible) { CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine.getTargetIRAnalysis())); } raw_pwrite_stream* dwarf_object_file = 0; if (options.output_dwarf_file_path.length) { __builtin_trap(); } if (options.optimize_when_possible) { Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); CodeGenPasses.add(new TargetLibraryInfoWrapperPass(*TLII)); } std::unique_ptr stream; if (options.output_file_path.length) { std::error_code error_code; stream = std::make_unique(options.output_file_path.string_ref(), error_code, sys::fs::OF_None); if (error_code) { return BBLLVMCodeGenerationPipelineResult::failed_to_create_file; } } else { stream = std::make_unique(); } CodeGenFileType file_type; switch ((BBLLVMCodeGenerationFileType)options.code_generation_file_type) { case BBLLVMCodeGenerationFileType::assembly_file: file_type = CodeGenFileType::AssemblyFile; break; case BBLLVMCodeGenerationFileType::object_file: file_type = CodeGenFileType::ObjectFile; break; case BBLLVMCodeGenerationFileType::null: file_type = CodeGenFileType::Null; break; } auto disable_verify = !options.verify_module; if (target_machine.addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify)) { return BBLLVMCodeGenerationPipelineResult::failed_to_add_emit_passes; } CodeGenPasses.run(module); return BBLLVMCodeGenerationPipelineResult::success; }