pub fn module_to_object(
module: &Module<'_>,
opt_level: OptLevel,
) -> Result<Vec<u8>, LLVMCompileError> {
static INITIALIZED: OnceLock<()> = OnceLock::new();
INITIALIZED.get_or_init(|| unsafe {
LLVM_InitializeAllTargets();
LLVM_InitializeAllTargetInfos();
LLVM_InitializeAllTargetMCs();
LLVM_InitializeAllAsmPrinters();
LLVM_InitializeAllAsmParsers();
});
unsafe {
let llvm_context = LLVMContextCreate();
let op = module.as_operation().to_raw();
let llvm_module = mlirTranslateModuleToLLVMIR(op, llvm_context);
let mut null = null_mut();
let mut error_buffer = addr_of_mut!(null);
let target_triple = LLVMGetDefaultTargetTriple();
let target_cpu = LLVMGetHostCPUName();
let target_cpu_features = LLVMGetHostCPUFeatures();
let mut target: MaybeUninit<LLVMTargetRef> = MaybeUninit::uninit();
if LLVMGetTargetFromTriple(target_triple, target.as_mut_ptr(), error_buffer) != 0 {
let error = CStr::from_ptr(*error_buffer);
let err = error.to_string_lossy().to_string();
LLVMDisposeMessage(*error_buffer);
Err(LLVMCompileError(err))?;
} else if !(*error_buffer).is_null() {
LLVMDisposeMessage(*error_buffer);
error_buffer = addr_of_mut!(null);
}
let target = target.assume_init();
let machine = LLVMCreateTargetMachine(
target,
target_triple.cast(),
target_cpu.cast(),
target_cpu_features.cast(),
match opt_level {
OptLevel::None => LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
OptLevel::Less => LLVMCodeGenOptLevel::LLVMCodeGenLevelLess,
OptLevel::Default => LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,
OptLevel::Aggressive => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
},
LLVMRelocMode::LLVMRelocDynamicNoPic,
LLVMCodeModel::LLVMCodeModelDefault,
);
let mut out_buf: MaybeUninit<LLVMMemoryBufferRef> = MaybeUninit::uninit();
let ok = LLVMTargetMachineEmitToMemoryBuffer(
machine,
llvm_module,
LLVMCodeGenFileType::LLVMObjectFile,
error_buffer,
out_buf.as_mut_ptr(),
);
if ok != 0 {
let error = CStr::from_ptr(*error_buffer);
let err = error.to_string_lossy().to_string();
LLVMDisposeMessage(*error_buffer);
Err(LLVMCompileError(err))?;
} else if !(*error_buffer).is_null() {
LLVMDisposeMessage(*error_buffer);
}
let out_buf = out_buf.assume_init();
let out_buf_start: *const u8 = LLVMGetBufferStart(out_buf).cast();
let out_buf_size = LLVMGetBufferSize(out_buf);
// keep it in rust side
let data = std::slice::from_raw_parts(out_buf_start, out_buf_size).to_vec();
LLVMDisposeMemoryBuffer(out_buf);
LLVMDisposeTargetMachine(machine);
LLVMDisposeModule(llvm_module);
LLVMContextDispose(llvm_context);
Ok(data)
}
}