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++
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;
}
}

View File

@ -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<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_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);