From 8126a5e9e8925e0550252ea47ad055caed5bed94 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 19 Feb 2025 07:08:11 -0600 Subject: [PATCH] Code generation pipeline --- src/LLVM.zig | 58 +++++++++++++++++++++++--- src/llvm.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++--- src/llvm_api.zig | 3 +- 3 files changed, 155 insertions(+), 12 deletions(-) diff --git a/src/LLVM.zig b/src/LLVM.zig index 9f2b3fa..3ea9f6d 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -388,7 +388,7 @@ const OptimizationLevel = enum(u3) { }; /// This is ABI-compatible with C++ -pub const OptimizationOptions = packed struct(u64) { +pub const OptimizationPipelineOptions = packed struct(u64) { optimization_level: OptimizationLevel, debug_info: u1, loop_unrolling: u1, @@ -411,15 +411,15 @@ pub const OptimizationOptions = packed struct(u64) { }); comptime { - assert(@sizeOf(OptimizationOptions) == @sizeOf(u64)); + assert(@sizeOf(OptimizationPipelineOptions) == @sizeOf(u64)); assert(padding_bit_count == 51); } - const OptimizationOptionsCreate = packed struct { + const Create = packed struct { optimization_level: OptimizationLevel, debug_info: u1, }; - pub fn default(create: OptimizationOptionsCreate) OptimizationOptions { + pub fn default(create: Create) OptimizationPipelineOptions { const pref_speed = @intFromBool(create.optimization_level.prefers_speed()); return .{ .optimization_level = create.optimization_level, @@ -437,6 +437,41 @@ pub const OptimizationOptions = packed struct(u64) { } }; +/// This is ABI-compatible with C++ +pub const CodeGenerationPipelineOptions = extern struct { + output_dwarf_file_path: String, + output_file_path: String, + flags: packed struct(u64) { + code_generation_file_type: enum(u2) { + assembly_file = 0, + object_file = 1, + null = 2, + }, + optimize_when_possible: u1, + verify_module: u1, + reserved: PaddingType = 0, + }, + + const padding_bit_count = 60; + const PaddingType = @Type(.{ + .int = .{ + .signedness = .unsigned, + .bits = padding_bit_count, + }, + }); + + comptime { + assert(@sizeOf(CodeGenerationPipelineOptions) == 5 * @sizeOf(u64)); + assert(padding_bit_count == 60); + } +}; + +pub const CodeGenerationPipelineResult = enum(u8) { + success = 0, + failed_to_create_file = 1, + failed_to_add_emit_passes = 2, +}; + pub const Architecture = enum { X86, }; @@ -468,6 +503,7 @@ pub const Module = opaque { pub const create_di_builder = api.LLVMCreateDIBuilder; pub const set_target = api.llvm_module_set_target; pub const run_optimization_pipeline = api.llvm_module_run_optimization_pipeline; + pub const run_code_generation_pipeline = api.llvm_module_run_code_generation_pipeline; pub fn to_string(module: *Module) []const u8 { return api.llvm_module_to_string(module).to_slice().?; @@ -743,5 +779,17 @@ pub fn experiment() void { }; module.set_target(target_machine); - module.run_optimization_pipeline(target_machine, OptimizationOptions.default(.{ .optimization_level = .O3, .debug_info = 1 })); + module.run_optimization_pipeline(target_machine, OptimizationPipelineOptions.default(.{ .optimization_level = .O3, .debug_info = 1 })); + const result = module.run_code_generation_pipeline(target_machine, CodeGenerationPipelineOptions{ + .output_file_path = String.from_slice(".zig-cache/foo.o"), + .output_dwarf_file_path = .{}, + .flags = .{ + .code_generation_file_type = .object_file, + .optimize_when_possible = 1, + .verify_module = @intFromBool(lib.optimization_mode == .Debug or lib.optimization_mode == .ReleaseSafe), + }, + }); + if (result != .success) { + unreachable; + } } diff --git a/src/llvm.cpp b/src/llvm.cpp index 848149e..abf16ba 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -4,6 +4,7 @@ #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" @@ -18,6 +19,8 @@ #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/FileSystem.h" + #define EXPORT extern "C" #define fn static @@ -713,8 +716,8 @@ enum class BBLLVMOptimizationLevel : u8 Oz = 5, }; -#define BB_LLVM_OPTIMIZATION_OPTIONS_PADDING_BIT_COUNT (51) -struct BBLLVMOptimizationOptions +#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) +struct BBLLVMOptimizationPipelineOptions { u64 optimization_level:3; u64 debug_info:1; @@ -727,13 +730,13 @@ struct BBLLVMOptimizationOptions u64 unified_lto:1; u64 assignment_tracking:1; u64 verify_module:1; - u64 reserved:BB_LLVM_OPTIMIZATION_OPTIONS_PADDING_BIT_COUNT; + u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; }; -static_assert(sizeof(BBLLVMOptimizationOptions) == sizeof(u64)); -static_assert(BB_LLVM_OPTIMIZATION_OPTIONS_PADDING_BIT_COUNT == 51); +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, BBLLVMOptimizationOptions options) +EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) { // TODO: PGO // TODO: CS profile @@ -811,3 +814,94 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& 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; +} diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 78fb0c6..6ceb27e 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -74,7 +74,8 @@ pub extern fn llvm_host_cpu_features() llvm.String; pub extern fn llvm_create_target_machine(create: *const llvm.Target.Machine.Create, error_message: *llvm.String) ?*llvm.Target.Machine; pub extern fn llvm_module_set_target(module: *llvm.Module, target_machine: *llvm.Target.Machine) void; -pub extern fn llvm_module_run_optimization_pipeline(module: *llvm.Module, target_machine: *llvm.Target.Machine, options: llvm.OptimizationOptions) void; +pub extern fn llvm_module_run_optimization_pipeline(module: *llvm.Module, target_machine: *llvm.Target.Machine, options: llvm.OptimizationPipelineOptions) void; +pub extern fn llvm_module_run_code_generation_pipeline(module: *llvm.Module, target_machine: *llvm.Target.Machine, options: llvm.CodeGenerationPipelineOptions) llvm.CodeGenerationPipelineResult; pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type { const arch_name = @tagName(llvm_arch);