Code generation pipeline

This commit is contained in:
David Gonzalez Martin 2025-02-19 07:08:11 -06:00
parent b7eff075fc
commit 8126a5e9e8
3 changed files with 155 additions and 12 deletions

View File

@ -388,7 +388,7 @@ const OptimizationLevel = enum(u3) {
}; };
/// This is ABI-compatible with C++ /// This is ABI-compatible with C++
pub const OptimizationOptions = packed struct(u64) { pub const OptimizationPipelineOptions = packed struct(u64) {
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
debug_info: u1, debug_info: u1,
loop_unrolling: u1, loop_unrolling: u1,
@ -411,15 +411,15 @@ pub const OptimizationOptions = packed struct(u64) {
}); });
comptime { comptime {
assert(@sizeOf(OptimizationOptions) == @sizeOf(u64)); assert(@sizeOf(OptimizationPipelineOptions) == @sizeOf(u64));
assert(padding_bit_count == 51); assert(padding_bit_count == 51);
} }
const OptimizationOptionsCreate = packed struct { const Create = packed struct {
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
debug_info: u1, debug_info: u1,
}; };
pub fn default(create: OptimizationOptionsCreate) OptimizationOptions { pub fn default(create: Create) OptimizationPipelineOptions {
const pref_speed = @intFromBool(create.optimization_level.prefers_speed()); const pref_speed = @intFromBool(create.optimization_level.prefers_speed());
return .{ return .{
.optimization_level = create.optimization_level, .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 { pub const Architecture = enum {
X86, X86,
}; };
@ -468,6 +503,7 @@ pub const Module = opaque {
pub const create_di_builder = api.LLVMCreateDIBuilder; pub const create_di_builder = api.LLVMCreateDIBuilder;
pub const set_target = api.llvm_module_set_target; pub const set_target = api.llvm_module_set_target;
pub const run_optimization_pipeline = api.llvm_module_run_optimization_pipeline; 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 { pub fn to_string(module: *Module) []const u8 {
return api.llvm_module_to_string(module).to_slice().?; return api.llvm_module_to_string(module).to_slice().?;
@ -743,5 +779,17 @@ pub fn experiment() void {
}; };
module.set_target(target_machine); 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;
}
} }

View File

@ -4,6 +4,7 @@
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h" #include "llvm/IR/Verifier.h"
#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfo.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassBuilder.h"
@ -18,6 +19,8 @@
#include "llvm/MC/TargetRegistry.h" #include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/FileSystem.h"
#define EXPORT extern "C" #define EXPORT extern "C"
#define fn static #define fn static
@ -713,8 +716,8 @@ enum class BBLLVMOptimizationLevel : u8
Oz = 5, Oz = 5,
}; };
#define BB_LLVM_OPTIMIZATION_OPTIONS_PADDING_BIT_COUNT (51) #define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51)
struct BBLLVMOptimizationOptions struct BBLLVMOptimizationPipelineOptions
{ {
u64 optimization_level:3; u64 optimization_level:3;
u64 debug_info:1; u64 debug_info:1;
@ -727,13 +730,13 @@ struct BBLLVMOptimizationOptions
u64 unified_lto:1; u64 unified_lto:1;
u64 assignment_tracking:1; u64 assignment_tracking:1;
u64 verify_module: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(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64));
static_assert(BB_LLVM_OPTIMIZATION_OPTIONS_PADDING_BIT_COUNT == 51); 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: PGO
// TODO: CS profile // 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); 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<TargetLibraryInfoImpl> TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary));
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(*TLII));
}
std::unique_ptr<raw_pwrite_stream> stream;
if (options.output_file_path.length)
{
std::error_code error_code;
stream = std::make_unique<llvm::raw_fd_ostream>(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<llvm::raw_null_ostream>();
}
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;
}

View File

@ -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_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_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 { pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type {
const arch_name = @tagName(llvm_arch); const arch_name = @tagName(llvm_arch);