Skip to content

Instantly share code, notes, and snippets.

@tpisto
Last active April 18, 2022 14:32
Show Gist options
  • Save tpisto/a27e1a6447a9534214c2fb71dbe64145 to your computer and use it in GitHub Desktop.
Save tpisto/a27e1a6447a9534214c2fb71dbe64145 to your computer and use it in GitHub Desktop.
OrcV2CBindingsBasicUsage.c in Rust llvm-sys
// https://github.com/tpisto
// Nearly 1:1 translation of /llvm/examples/OrcV2Examples/OrcV2CBindingsBasicUsage/OrcV2CBindingsBasicUsage.c
// to Rust. In order to keep it even more 1:1 readable, I kept variable names as they are in the C -file.
extern crate llvm_sys as llvm;
use libc::*;
use llvm::core::{
LLVMAddFunction, LLVMAppendBasicBlock, LLVMBuildAdd, LLVMBuildRet, LLVMCreateBuilder,
LLVMFunctionType, LLVMGetGlobalPassRegistry, LLVMGetParam, LLVMInt32Type,
LLVMModuleCreateWithNameInContext, LLVMPositionBuilderAtEnd, LLVMShutdown,
};
use llvm::initialization::LLVMInitializeCore;
use llvm::orc2::lljit::{
LLVMOrcCreateLLJIT, LLVMOrcCreateLLJITBuilder, LLVMOrcDisposeLLJIT,
LLVMOrcLLJITAddLLVMIRModule, LLVMOrcLLJITGetMainJITDylib, LLVMOrcLLJITLookup,
};
use llvm::orc2::{
LLVMOrcCreateNewThreadSafeContext, LLVMOrcCreateNewThreadSafeModule,
LLVMOrcDisposeThreadSafeContext, LLVMOrcOpaqueThreadSafeModule,
LLVMOrcThreadSafeContextGetContext,
};
use llvm::support::LLVMParseCommandLineOptions;
use llvm::target::{LLVM_InitializeNativeAsmPrinter, LLVM_InitializeNativeTarget};
use std::ffi::CString;
use std::mem::MaybeUninit;
macro_rules! cstr {
($str: expr) => {
concat!($str, "\0").as_ptr() as *const _
};
}
macro_rules! llvm_success_or_panic {
($e: expr) => {
let status: llvm_sys::error::LLVMErrorRef = $e;
if !status.is_null() {
let error_message = llvm_sys::error::LLVMGetErrorMessage(status);
let error_str = std::ffi::CStr::from_ptr(error_message).to_str().unwrap();
panic!("{}", error_str);
}
};
}
macro_rules! llvm_status_guard {
($e: expr) => {
match $e {
1 => panic!("llvm_status_guard fail!"),
_ => (),
}
};
}
#[allow(non_snake_case)]
fn createDemoModule() -> *mut LLVMOrcOpaqueThreadSafeModule {
unsafe {
// Create a new ThreadSafeContext and underlying LLVMContext.
let TSCtx = LLVMOrcCreateNewThreadSafeContext();
// Get a reference to the underlying LLVMContext.
let Ctx = LLVMOrcThreadSafeContextGetContext(TSCtx);
// Create a new LLVM module.
let M = LLVMModuleCreateWithNameInContext(cstr!("demo"), Ctx);
// Add a "sum" function"
// - Create the function type and function instance.
let mut ParamTypes = [LLVMInt32Type(), LLVMInt32Type()];
let SumFunctionType = LLVMFunctionType(LLVMInt32Type(), ParamTypes.as_mut_ptr(), 2, 0);
let SumFunction = LLVMAddFunction(M, cstr!("sum"), SumFunctionType);
// - Add a basic block to the function.
let EntryBB = LLVMAppendBasicBlock(SumFunction, cstr!("entry"));
// - Add an IR builder and point it at the end of the basic block.
let Builder = LLVMCreateBuilder();
LLVMPositionBuilderAtEnd(Builder, EntryBB);
// - Get the two function arguments and use them co construct an "add"
// instruction.
let SumArg0 = LLVMGetParam(SumFunction, 0);
let SumArg1 = LLVMGetParam(SumFunction, 1);
let Result = LLVMBuildAdd(Builder, SumArg0, SumArg1, cstr!("sum"));
// - Build the return instruction.
LLVMBuildRet(Builder, Result);
// Our demo module is now complete. Wrap it and our ThreadSafeContext in a
// ThreadSafeModule.
let TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx);
// Dispose of our local ThreadSafeContext value. The underlying LLVMContext
// will be kept alive by our ThreadSafeModule, TSM.
LLVMOrcDisposeThreadSafeContext(TSCtx);
// Return the result.
TSM
}
}
#[allow(non_snake_case)]
fn main() {
// Parse command line arguments and initialize LLVM Core.
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
// convert the strings to raw pointers
let c_args = args
.iter()
.map(|arg| arg.as_ptr())
.collect::<Vec<*const c_char>>();
unsafe {
// Parse command line arguments and initialize LLVM Core.
LLVMParseCommandLineOptions(c_args.len() as i32, c_args.as_ptr(), cstr!(""));
LLVMInitializeCore(LLVMGetGlobalPassRegistry());
// Initialize native target codegen and asm printer.
llvm_status_guard!(LLVM_InitializeNativeTarget());
llvm_status_guard!(LLVM_InitializeNativeAsmPrinter());
let mut J = MaybeUninit::uninit().assume_init();
let jit_builder = LLVMOrcCreateLLJITBuilder();
llvm_success_or_panic!(LLVMOrcCreateLLJIT(&mut J, jit_builder));
// Create our demo module.
let TSM = createDemoModule();
// Add our demo module to the JIT.
let MainJD = LLVMOrcLLJITGetMainJITDylib(J);
llvm_success_or_panic!(LLVMOrcLLJITAddLLVMIRModule(J, MainJD, TSM));
// Look up the address of our demo entry point.
let mut sum_addr = MaybeUninit::uninit().assume_init();
llvm_success_or_panic!(LLVMOrcLLJITLookup(J, &mut sum_addr, cstr!("sum")));
// If we made it here then everything succeeded. Execute our JIT'd code.
let Sum: extern "C" fn(i32, i32) -> i32 = std::mem::transmute(sum_addr);
let Result = Sum(1, 2);
// Print the result
println!("1 + 2 = {}", Result);
// jit_cleanup:
// Destroy our JIT instance. This will clean up any memory that the JIT has
// taken ownership of. This operation is non-trivial (e.g. it may need to
// JIT static destructors) and may also fail. In that case we want to render
// the error to stderr, but not overwrite any existing return value.
llvm_success_or_panic!(LLVMOrcDisposeLLJIT(J));
// llvm_shutdown:
LLVMShutdown();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment